现在,我们来编写自己第一个字符设备驱动 —— 点亮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
  1. int *pgpx2con  ;
  2. int *pgpx2dat;
  3. pgpx2con = ioremap( GPX2CON, 4);
  4. pgpx2dat = ioremap(GPX2DAT,4);
  5. readl(pgpx2con);
  6. writel(0x01, pgpx2dat );

下面是驱动程序,后面会更完善

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

测试程序:

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

makefile:

[cpp] view plaincopy
  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

会看到跑马灯效果。

后面会对该驱动完善。

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

  1. 树莓派底层IO驱动开发示例(一个简单io口驱动的开发)

    一.驱动代码的开发 1.树莓派寄存器的介绍 点击查看:树莓派(bcm2835芯片手册) GPFSEL0 GPIO Function Select 0: 功能选择 输入/输出 GPSET0 GPIO P ...

  2. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

    文章目录 前言 1.驱动的三种编写方法 2.怎么使用设备树写驱动程序 2.1.设备树节点要与platform_driver能匹配 2.2.修改platform_driver的源码 3.实验和调试技巧 ...

  3. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之设备树模型

    文章目录 前言 1.设备树的作用 2.设备树的语法 2.1.设备树的逻辑图和dts文件.dtb文件 2.1.1.1Devicetree格式 1DTS文件的格式 node的格式 properties的格 ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  5. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之第一个驱动

    文章目录 前言 1.Hello驱动 1.1.APP打开的文件在内核中如何表示? 1.2.打开字符设备节点时,内核中也有对应的struct file 1.3.如何编写驱动程序? 1.4.驱动程序代码 1 ...

  6. linux驱动开发篇(三)—— 总线设备驱动模型

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  7. 驱动开发基础知识——设备树

    BSP开发工程师[原来BSP就是那些被指臃肿的文件啊 BSP的出生 Linux经过不断的发展,原先嵌入式系统的三层结构逐步演化成为一种四层结构. 这个新增加的中间层次位于操作系统和硬件之间,包含了系统 ...

  8. linux课程_【课程完结】嵌入式Linux应用/驱动开发基础知识两大篇章已全部录制完毕 共72集...

    完结撒花 <第四篇嵌入式Linux应用开发基础知识> <第五篇嵌入式Linux驱动开发基础知识> 两大篇章已全部录制完毕 共计 72 集 01 嵌入式Linux应用开发基础知识 ...

  9. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之Pinctrl子系统和GPIO子系统的使用

    文章目录 前言 1.Pinctrl子系统 1.1.为什么有Pinctrl子系统 1.2.重要的概念 1.3.代码中怎么引用pinctrl 2.GPIO子系统 2.1.为什么有GPIO子系统 2.2.在 ...

最新文章

  1. 自动驾驶感知系统盘点
  2. 服务器报错:“/usr/local/var/run/nginx.pid”failed
  3. 如果用神经网络分类处于纠缠态的一对粒子?
  4. 如何判断基因组的重复区域_Nat Comm. | 15万人类基因组中多核苷酸变异(MNV)的起源及功能研究...
  5. iphone-common-codes-ccteam源代码 CCUIKit.m
  6. python缓存技术_Python中整数的缓存机制讲解
  7. html表格中绑定显示xml文档内容的简单实例,JS读取XML文件数据并以table形式显示数据的方法(兼容IE与火狐)...
  8. 转:ORA-01126: 数据库必须已装载到此实例并且不在任何实例中打开
  9. python调用scp上传目录_Python调用scp向服务器上传文件示例
  10. WIndow Document
  11. 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)...
  12. SEO工具,站长必备
  13. codeforce 985C Liebig's Barrels
  14. Mac废纸篓无法清空怎么办?
  15. 计算机设备耗材管理系统,实验室耗材管理方法、系统、计算机设备和存储介质与流程...
  16. Maven -- dependency详解
  17. pygame交换式拼图设计
  18. iOS13的暗黑模式
  19. MySql的flush用法
  20. Gram矩阵与卷积网络中的卷积的直观理解

热门文章

  1. Nagios 安装及常见错误
  2. linux-shell面试题 之二
  3. openfire 的配置文件
  4. Amazon S3 设置对象的生命周期Lifecycle
  5. [转]C# WInForm 无框窗体移动
  6. 机器学习 啤酒数据集_啤酒数据集上的神经网络
  7. 49. 字母异位词分组
  8. 1137. 第 N 个泰波那契数
  9. 熊猫数据集_为数据科学拆箱熊猫
  10. mfcc中的fft操作_简化音频数据:FFT,STFT和MFCC