字符设备驱动模型分为三步:
第一步:驱动设备初始化 (分配cdev、初始化cdev、注册cdev、硬件初始化)
第二步:实现设备操作(读写以及控制设备)
第三步: 驱动注销
虽然这是一个最简单的驱动,而且之前也曾经写过两遍,时隔大半年,今天回过头来重新整还是一波三折!感谢那些热心无私的同学的帮助!
先贴下原理图和相关芯片手册上的一些要用到的东西!
这里LED连接的GPIO引脚 输出高电平是LED关 输出低电平LED亮, LED电路部分的这里就不截图了
然后去查看这个GPM的相关寄存器:
这里贴上代码:
led.c
#include<linux/module.h> #include<linux/init.h> #include<linux/cdev.h> #include<linux/fs.h> #include<linux/io.h> /* writel */ #include"led.h" #define GPMCON 0x7F008820 //LED对应的GPIO控制寄存器地址 #define GPMDATA 0x7F008824 unsigned int *led_config;//LED控制寄存器 unsigned int *led_data;//LED数据寄存器 0表示打开LED 1表示关闭LED struct cdev cdev_led; //这里定义一个cdev结构体变量 dev_t devno; //用来申请主设备号的变量 //这里硬件操作放到led_open函数中 //控制LED 设置GPIO为输出模式(放到open中) 然后控制GPIO(放到ioctl中) int led_open(struct inode *node, struct file *filp) { //unsigned int reg; led_config = ioremap(GPMCON, 4);//将物理地址转化为虚拟地址,4个字节 //reg = readl(led_config); //reg |= 0x1111; writel(0x1111, led_config);//将值写入配置寄存器 设置为输入 led_data = ioremap(GPMDATA, 4);//将物理地址转化为虚拟地址,4个字节 return 0; } //第一步定义命令 第二步实现命令 命令一般放在.h头文件中 long led_icotl(struct file *filp, unsigned int cmd, unsigned long arg) { switch(cmd) { case LED_ON: writel(0x00,led_data);//向32位寄存器中写入一个值 return 0; case LED_OFF: writel(0xFF,led_data);//向32位寄存器中写入一个值 return 0; default: return -EINVAL; } } static const struct file_operations led_fops = { .open = led_open, .unlocked_ioctl = led_icotl, }; /* struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); }; */ static int led_init() { cdev_init(&cdev_led, &led_fops);//初始化cdev_led结构;两个参数 alloc_chrdev_region(&devno, 0, 1, "myled");//分配主设备号 采用动态分配方式 cdev_add(&cdev_led, devno, 1);//注册主设备号 return 0; } static void led_exit() { cdev_del(&cdev_led);//第一步 注销设备 unregister_chrdev_region(devno, 1);//第二步 将申请到的主设备号也注销掉 } module_init(led_init); module_exit(led_exit);
led.h
//点亮 熄灭 这里定义两个 而且都不需要传参 具体可以结和前一课 #define LED_MAGIC 'L' #define LED_ON _IO(LED_MAGIC, 0) #define LED_OFF _IO(LED_MAGIC, 1)
obj-m := led.o KDIR := /home/kernel/linux-ok6410 all: make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<sys/ioctl.h> #include<unistd.h> #include<stdio.h> #include"led.h" int main(int argc, char *argv[]) { int fd; int cmd; if(argc < 2) { printf("please enter the second para! \n"); return 0; } cmd = atoi(argv[1]); fd = open("/dev/myled", O_RDWR); if(cmd == 1) ioctl(fd, LED_ON); else ioctl(fd, LED_OFF); return 0; }
同上一篇一样,这里目标文件通过nfs同步到开发板!
insmod led.ko
然后cat /proc/devices 查看myled(上面程序中给led字符设备取的名字)所对应的设备号
然后 maknod /dev/myled c 设备号 0
然后运行应用程序 ./led_app 1 点亮led!
这里发张点亮led的效果图片:
顺便说说前面我驱动程序中应为粗心写错了一个地方:
针对上面的程序相信看懂了吧!然后运行应用程序时就出现了这样的情况!
这里面有很多有用的信息可以供我们分析错误,这里发一个链接,别人写的一篇博客 根据上面的信息来分析问题找出原因所在:http://blog.csdn.net/kangear/article/details/8217329
点击打开链接