作业要求:
编写led驱动,通过应用程序控制三盏灯亮灭
作业答案:
作业效果:
mychrdev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "head.h"unsigned int major; // 保存申请的设备号
char kbuf[128] = {0}; // 用于保存向内核中传输的消息
// 用来接收映射成功的内存首地址
unsigned int *moder_e;
unsigned int *odr_e;
unsigned int *moder_f;
unsigned int *odr_f;
unsigned int *rcc;/*
// 延时函数
void delay(int ms)
{int i, j;for (i = 0; i < ms; i++){for (j = 0; j < 2000; j++){}}
}
*/// 封装常用的操作方法
int mychrdev_open(struct inode *inode, struct file *file)
{printk("调用mychrdev_open成功,%d\n", __LINE__);return 0;
}ssize_t mychrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *lof)
{printk("调用成功mychrdev_read,%d\n", __LINE__);// 用户调用了read后会调用这个函数// 所以是从内核空间向用户空间写unsigned int ret; // 用于接收返回值,判断是否成功ret = copy_to_user(ubuf, kbuf, size);if (ret){printk("数据拷贝失败\n");return -ret;}return 0;
}ssize_t mychrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lof)
{printk("调用mychrdev_write成功,%d\n", __LINE__);// 用户调用了write后会调用这个函数// 所以是从用户空间向内核空间写unsigned int ret; // 用于接收返回值,判断是否成功ret = copy_from_user(kbuf, ubuf, size);if (ret){printk("数据拷贝失败\n");return -ret;}if (kbuf[0] == '0') // 开1号灯{(*odr_e) |= (0x1 << 10);}else if (kbuf[0] == '1') // 开2号灯{(*odr_f) |= (0x1 << 10);}else if (kbuf[0] == '2') // 开3号灯{(*odr_e) |= (0x1 << 8);}else if (kbuf[0] == '3') // 开三个灯{(*odr_e) |= (0x1 << 10);(*odr_f) |= (0x1 << 10);(*odr_e) |= (0x1 << 8);}else if (kbuf[0] == '4') // 关1号灯{(*odr_e) &= (~(0x1 << 10));}else if (kbuf[0] == '5') // 关2号灯{(*odr_f) &= (~(0x1 << 10));}else if (kbuf[0] == '6') // 关3号灯{(*odr_e) &= (~(0x1 << 8));}else if (kbuf[0] == '7') // 关三个灯{(*odr_e) &= (~(0x1 << 10));(*odr_f) &= (~(0x1 << 10));(*odr_e) &= (~(0x1 << 8));}return 0;
}int mychrdev_close(struct inode *inode, struct file *file)
{printk("调用mychrdev_close成功,%d\n", __LINE__);return 0;
}// 定义操作方法指针
struct file_operations fops = {.open = mychrdev_open,.read = mychrdev_read,.write = mychrdev_write,.release = mychrdev_close};static int __init mycdev_init(void)
{// 注册字符设备驱动// 填0代表动态申请主设备号major = register_chrdev(0, "mychrdev", &fops);// 申请失败if (major < 0){printk("注册字符设备驱动失败 %d\n", __LINE__);}printk("注册字符设备驱动成功 major = %d\n", major);// 进行LED寄存器的地址映射moder_e = ioremap(GPIOE_MODER, 4);if (moder_e == NULL){printk("地址映射失败%d\n", __LINE__);return 0;}moder_f = ioremap(GPIOF_MODER, 4);if (moder_f == NULL){printk("地址映射失败%d\n", __LINE__);return 0;}odr_e = ioremap(GPIOE_ODR, 4);if (odr_e == NULL){printk("地址映射失败%d\n", __LINE__);return 0;}odr_f = ioremap(GPIOF_ODR, 4);if (odr_f == NULL){printk("地址映射失败%d\n", __LINE__);return 0;}rcc = ioremap(RCC_GPIO, 4);if (rcc == NULL){printk("地址映射失败%d\n", __LINE__);return 0;}printk("地址映射成功\n");// 硬件初始化// 使能时钟(*rcc) |= (0X3 << 4);// 设置PE10、PF10、PE8为输出(*moder_e) &= (~(0X3 << 20));(*moder_e) |= (0X1 << 20);(*moder_f) &= (~(0X3 << 20));(*moder_f) |= (0X1 << 20);(*moder_e) &= (~(0X3 << 16));(*moder_e) |= (0X1 << 16);// 默认输出低电平(*odr_e) &= (~(0x1 << 10));(*odr_f) &= (~(0x1 << 10));(*odr_e) &= (~(0x1 << 8));return 0;
}
static void __exit mycdev_exit(void)
{// 取消内存映射iounmap(moder_e);iounmap(odr_e);iounmap(moder_f);iounmap(odr_f);iounmap(rcc);// 注销字符设备驱动unregister_chrdev(major, "mychrdev");printk("注销字符设备驱动成功 %d\n", __LINE__);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
led.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc,const char * argv[])
{//用于接收读取到的数据char buf[128] = {};//打开自定义字符设备文件int fd = open("/dev/mychrdev",O_RDWR);//当打开失败时if(fd < 0){printf("打开自定义字符设备文件失败\n");return -1;}//开关灯的逻辑控制while (1){printf("请输入控制码:0(开1号灯) 1(开2号灯) 2(开3号灯) \3(开三个灯) 4(关1号灯) 5(关2号灯) \6(关3号灯) 7(关三个灯)>");fgets(buf, sizeof(buf), stdin); // 从终端输入一个数据buf[strlen(buf) - 1] = '\0';write(fd, buf, sizeof(buf));}//关闭close(fd);return 0;
}
head.h
#ifndef __HEAD_H__
#define __HEAD_H__#define GPIOE_MODER 0X50006000
#define GPIOF_MODER 0X50007000
#define GPIOE_ODR 0X50006014
#define GPIOF_ODR 0X50007014
#define RCC_GPIO 0X50000A28#endif