Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

 现在,我们来编写自己第一个字符设备驱动 —— 点亮LED。(不完善,后面再完善)

硬件平台:Exynos4412(FS4412)


编写驱动分下面几步:

a -- 查看原理图、数据手册,了解设备的操作方法;

b -- 在内核中找到相近的驱动程序,以它为模板进行开发,有时候需要从零开始;

c -- 实现驱动程序的初始化:比如向内核注册这个驱动程序,这样应用程序传入文件名,内核才能找到相应的驱动程序;

d -- 设计所要实现的操作,比如 open、close、read、write 等函数;

e -- 实现中断服务(中断不是每个设备驱动所必须的);

f -- 编译该驱动程序到内核中,或者用 insmod 命令加载;

g-- 测试驱动程序;


下面是一个点亮LED 的驱动:

第一步,当然是查看手册,查看原理图,找到相应寄存器;


查看手册,四个LED 所用寄存器为:

led2

GPX2CON    0x11000c40
GPX2DAT     0x11000c44

led3

GPX1CON    0x11000c20
GPX1DAT     0x11000c24

led4  3-4 3-5

GPF3CON   0x114001e0
GPF3DAT    0x114001e4


这里要注意:arm体系架构是io内存,必须要映射   ioremap( );  其作用是物理内存向虚拟内存的映射。 用到 writel   readl这两个函数,详细解释会在后面不上,先看一下简单用法:

以LED2为例,下面是地址映射及读写:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. int *pgpx2con  ;  
  2. int *pgpx2dat;  
  3.   
  4. pgpx2con = ioremap( GPX2CON, 4);  
  5. pgpx2dat = ioremap(GPX2DAT,4);  
  6. readl(pgpx2con);  
  7. writel(0x01, pgpx2dat );  

下面是驱动程序,后面会更完善
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. #include <linux/device.h>  
  5. #include <asm/io.h>  
  6. #include <asm/uaccess.h>  
  7.   
  8. static int major = 250;  
  9. static int minor=0;  
  10. static dev_t devno;  
  11. static struct class *cls;  
  12. static struct device *test_device;  
  13.   
  14. #define  GPX2CON    0x11000c40  
  15. #define  GPX2DAT    0x11000c44  
  16. #define  GPX1CON    0x11000c20  
  17. #define  GPX1DAT    0x11000c24  
  18. #define  GPF3CON    0x114001e0  
  19. #define  GPF3DAT    0x114001e4  
  20.   
  21. static int *pgpx2con  ;  
  22. static int *pgpx2dat;  
  23.   
  24. static int *pgpx1con  ;  
  25. static int *pgpx1dat;  
  26.   
  27. static int *pgpf3con  ;  
  28. static int *pgpf3dat;  
  29.   
  30. void fs4412_led_off(int num);  
  31.   
  32. void fs4412_led_on(int num)  
  33. {  
  34.     switch(num)  
  35.     {  
  36.         case 1:  
  37.             writel(readl(pgpx2dat) |(0x1<<7), pgpx2dat);  
  38.             break;  
  39.         case 2:  
  40.             writel(readl(pgpx1dat) |(0x1<<0), pgpx1dat);            
  41.             break;            
  42.         case 3:  
  43.             writel(readl(pgpf3dat) |(0x1<<4), pgpf3dat);    
  44.             break;  
  45.         case 4:  
  46.             writel(readl(pgpf3dat) |(0x1<<5), pgpf3dat);            
  47.             break;    
  48.         default:  
  49.             fs4412_led_off(1);  
  50.             fs4412_led_off(2);  
  51.             fs4412_led_off(3);  
  52.             fs4412_led_off(4);  
  53.             break;  
  54.               
  55.     }  
  56. }  
  57.   
  58. void fs4412_led_off(int num)  
  59. {  
  60.     switch(num)  
  61.     {  
  62.         case 1:  
  63.             writel(readl(pgpx2dat) &(~(0x1<<7)), pgpx2dat);  
  64.             break;  
  65.         case 2:  
  66.             writel(readl(pgpx1dat)&(~(0x1<<0)), pgpx1dat);              
  67.             break;            
  68.         case 3:  
  69.             writel(readl(pgpf3dat) &(~(0x1<<4)), pgpf3dat);     
  70.             break;  
  71.         case 4:  
  72.             writel(readl(pgpf3dat) &(~(0x1<<5)), pgpf3dat);             
  73.             break;            
  74.     }  
  75. }  
  76.   
  77. static int led_open (struct inode *inode, struct file *filep)  
  78. {//open  
  79.     fs4412_led_off(1);  
  80.     fs4412_led_off(2);  
  81.     fs4412_led_off(3);  
  82.     fs4412_led_off(4);    
  83.     return 0;  
  84. }  
  85.   
  86. static int led_release(struct inode *inode, struct file *filep)  
  87. {//close  
  88.     fs4412_led_off(1);  
  89.     fs4412_led_off(2);  
  90.     fs4412_led_off(3);  
  91.     fs4412_led_off(4);    
  92.     return 0;  
  93. }  
  94.   
  95. static ssize_t led_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)  
  96. {  
  97.     return 0;  
  98. }  
  99.   
  100. static ssize_t led_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)  
  101. {  
  102.     int led_num;  
  103.   
  104.     if(len !=4)  
  105.     {  
  106.         return -EINVAL;  
  107.     }  
  108.     if(copy_from_user(&led_num,buf,len))  
  109.     {  
  110.         return -EFAULT;   
  111.     }  
  112.   
  113.     fs4412_led_on(led_num);  
  114.     printk("led_num =%d \n",led_num);  
  115.   
  116.     return 0;  
  117. }  
  118.   
  119. static struct file_operations hello_ops=  
  120. {  
  121.     .open     = led_open,  
  122.     .release = led_release,  
  123.     .read     = led_read,  
  124.     .write    = led_write,  
  125. };  
  126.   
  127. static void fs4412_led_init(void)  
  128. {  
  129.     pgpx2con = ioremap(GPX2CON,4);  
  130.     pgpx2dat = ioremap(GPX2DAT,4);  
  131.   
  132.     pgpx1con = ioremap(GPX1CON,4);  
  133.     pgpx1dat =ioremap(GPX1DAT,4);  
  134.   
  135.     pgpf3con  = ioremap(GPF3CON,4);  
  136.     pgpf3dat =ioremap(GPF3DAT,4);  
  137.   
  138.     writel((readl(pgpx2con)& ~(0xf<<28)) |(0x1<<28),pgpx2con) ;  
  139.     writel((readl(pgpx1con)& ~(0xf<<0)) |(0x1<<0),pgpx1con) ;     
  140.     writel((readl(pgpf3con)& ~(0xff<<16)) |(0x11<<16),pgpf3con) ;     
  141. }  
  142.   
  143. static int led_init(void)  
  144. {  
  145.     int ret;      
  146.     devno = MKDEV(major,minor);  
  147.     ret = register_chrdev(major,"led",&hello_ops);  
  148.   
  149.     cls = class_create(THIS_MODULE, "myclass");  
  150.     if(IS_ERR(cls))  
  151.     {  
  152.         unregister_chrdev(major,"led");  
  153.         return -EBUSY;  
  154.     }  
  155.     test_device = device_create(cls,NULL,devno,NULL,"led");//mknod /dev/hello  
  156.     if(IS_ERR(test_device))  
  157.     {  
  158.         class_destroy(cls);  
  159.         unregister_chrdev(major,"led");  
  160.         return -EBUSY;  
  161.     }     
  162.     fs4412_led_init();  
  163.     return 0;  
  164. }  
  165.   
  166. void fs4412_led_unmap(void)  
  167. {  
  168.     iounmap(pgpx2con);  
  169.     iounmap(pgpx2dat );  
  170.   
  171.     iounmap(pgpx1con);  
  172.     iounmap(pgpx1dat );  
  173.   
  174.     iounmap(pgpf3con );  
  175.     iounmap(pgpf3dat );  
  176. }  
  177.   
  178. static void led_exit(void)  
  179. {  
  180.     fs4412_led_unmap();  
  181.     device_destroy(cls,devno);  
  182.     class_destroy(cls);   
  183.     unregister_chrdev(major,"led");  
  184.     printk("led_exit \n");  
  185. }  
  186.   
  187. MODULE_LICENSE("GPL");  
  188. module_init(led_init);  
  189. module_exit(led_exit);  

测试程序:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6. main()  
  7. {  
  8.     int fd,i,lednum;  
  9.   
  10.     fd = open("/dev/led",O_RDWR);  
  11.     if(fd<0)  
  12.     {  
  13.         perror("open fail \n");  
  14.         return ;  
  15.     }  
  16.     for(i=0;i<100;i++)  
  17.     {  
  18.         lednum=0;  
  19.         write(fd,&lednum,sizeof(int));  
  20.         lednum = i%4+1;  
  21.         write(fd,&lednum,sizeof(int));    
  22.         sleep(1);  
  23.     }  
  24.     close(fd);  
  25. }  

makefile:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ifneq  ($(KERNELRELEASE),)  
  2. obj-m:=hello.o  
  3. $(info "2nd")  
  4. else  
  5. #KDIR := /lib/modules/$(shell uname -r)/build  
  6. KDIR := /home/xiaoming/linux-3.14-fs4412  
  7. PWD:=$(shell pwd)  
  8. all:  
  9.     $(info "1st")  
  10.     make -C $(KDIR) M=$(PWD) modules  
  11.     arm-none-linux-gnueabi-gcc test.c  
  12.     sudo cp hello.ko a.out /rootfs/test/  
  13. clean:  
  14.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
  15. endif  

编译结束后,将a.out 和 hello.ko 拷贝到开发板中:

# insmod hello.ko

#mknod /dev/hello c 250 0

#./a.out

会看到跑马灯效果。

后面会对该驱动完善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

angularJS+requireJS实现controller及directive的按需加载

最近因为项目的比较大&#xff0c;需要加载的js文件较多&#xff0c;为了提高首屏页面的加载速度&#xff0c;需要对js文件进行按需加载&#xff0c;然后网上参考了一些资料&#xff0c;自己也深入研究一番之后&#xff0c;实现了按需加载控制器js文件及指令js文件的效果&#…

Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

前面在 Linux 字符设备驱动开发基础 &#xff08;三&#xff09;—— 字符设备驱动结构&#xff08;中&#xff09; &#xff0c;我们已经介绍了两种重要的数据结构 struct inode{...}与 struct file{...} &#xff0c;下面来介绍另一个比较重要数据结构 struct _file_operatio…

Android开发群

为什么80%的码农都做不了架构师&#xff1f;>>> 我的自建Android应用开发群&#xff0c;欢迎大家来聊聊呀&#xff01;201427584 转载于:https://my.oschina.net/catia/blog/176384

Python的multiprocessing多进程

https://www.cnblogs.com/tkqasn/p/5701230.html 由于GIL的存在&#xff0c;python中的多线程其实并不是真正的多线程&#xff0c;如果想要充分地使用多核CPU的资源&#xff0c;在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing&#xff0…

Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 &#xff08;一&#xff09;—— 字符设备驱动结构&#xff08;上&#xff09; 下面继续学习字符设备另外几个重要的数据结构。 先看下面这张图&#xff0c;这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序…

Python的gevent协程及协程概念

https://www.cnblogs.com/tkqasn/p/5705338.html 何为协程 协程&#xff0c;又称微线程。英文名Coroutine。 协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换&#xff0c;而是由程序自身控制&#xff0c;因此&#xff0c;没有线程切换的开销&#xff0c;…

技术人生:三亚之行

人生收获 此次是公司组团去的三亚&#xff0c;地接的导游非常热情&#xff0c;如同大多数人一样&#xff0c;导游也会在这短短的几天内&#xff0c;尽可能的表现自己&#xff0c;此文聊聊导游说的三句话。 旅游三不“较”&#xff1a; 不比较不计较不睡觉人生何尝不是如此&…

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法&#xff0c;利用cat /proc/devices查看申请到的设备名&#xff0c;设备号。 第一种是使用mknod手工创建&#xff1a;mknod filename type major minor 第二种是自动创建设备节点&#xff1a;利用udev&#xff08;mdev&#xff09;来实现设备…

Python数据库使用-SQLite

https://www.liaoxuefeng.com/wiki/897692888725344/926177394024864 SQLite是一种嵌入式数据库&#xff0c;它的数据库就是一个文件。由于SQLite本身是C写的&#xff0c;而且体积很小&#xff0c;所以&#xff0c;经常被集成到各种应用程序中&#xff0c;甚至在iOS和Android的…

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。使用驱动程序&#xff1a; 字符设备&#xff1a;是指只能一个字节一个字节读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需要按照先后数…

Python数据库使用MySQL

https://www.liaoxuefeng.com/wiki/897692888725344/932709047411488 MySQL是Web世界中使用最广泛的数据库服务器。SQLite的特点是轻量级、可嵌入&#xff0c;但不能承受高并发访问&#xff0c;适合桌面和移动应用。而MySQL是为服务器端设计的数据库&#xff0c;能承受高并发访…

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Python的time模块和datatime模块

https://www.cnblogs.com/tkqasn/p/6001134.html

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Linux 驱动开发之内核模块开发 (二)—— 内核模块编译 Makefile 入门

一、模块的编译 我们在前面内核编译中驱动移植那块&#xff0c;讲到驱动编译分为静态编译和动态编译&#xff1b;静态编译即为将驱动直接编译进内核&#xff0c;动态编译即为将驱动编译成模块。 而动态编译又分为两种&#xff1a; a -- 内部编译 在内核源码目录内编译 b -- 外部…

Exynos4412 文件系统制作(三)—— 文件系统移植

根文件系统一直以来都是所有类Unix操作系统的一个重要组成部分&#xff0c;也可以认为是嵌入式Linux系统区别于其他一些传统嵌入式操作系统的重要特征&#xff0c;它给Linux带来了许多强大和灵活的功能&#xff0c;同时也带来了一些复杂性。我们需要清楚的了解根文件系统的基本…

Snapchat, 给年轻人要的安全感

这几天&#xff0c;Snapchat因拒绝Facebook和谷歌的收购请求而名声大噪。40亿美金的收购请求&#xff0c;并不是任何一个人都可以淡然处之的。一、关于SnapchatSnapchat由两位斯坦福大学生创办&#xff0c;在2011 年9月上线。Snapchat的主要是所有照片都有一个1到10秒的生命期&…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…

python的urllib2模块

https://www.cnblogs.com/erliang/p/4063883.html https://blog.csdn.net/u014343243/article/details/49308043 https://docs.python.org/zh-cn/2.7/library/urllib2.html