问:怎么写LED驱动程序?

1.搭建一个字符驱动的框架(上一节已经完成)

2.完善硬件的操作

问:驱动里操作硬件寄存器与单片机操作硬件寄存器有什么不一样的地方?

答:单片机操作的寄存器地址是物理地址,驱动里面操作的必须是虚拟地址,因为驱动是内核的一部分,内核里的地址都是虚拟地址。

问:怎么让物理地址转换为虚拟地址?

答:使用ioremap函数,它的功能就是将物理地址映射为虚拟地址,具体怎么映射需要去看linux内存管理等内容。

问:应用程序如果要传数据给内核怎么办?

答:使用copy_from_user函数,同理如果内核要传数据给应用空间的应用程序则使用copy_to_user函数。

详细请参考驱动源码:

  1. #include <linux/kernel.h>
  2. #include <linux/fs.h>
  3. #include <linux/init.h>
  4. #include <linux/delay.h>
  5. #include <asm/uaccess.h>
  6. #include <asm/irq.h>
  7. #include <asm/io.h>
  8. #include <linux/module.h>
  9. #include <linux/device.h>     //class_create
  10. static struct class *firstdrv_class;
  11. static struct device *firstdrv_device;
  12. volatile unsigned long *gpbcon = NULL;
  13. volatile unsigned long *gpbdat = NULL;
  14. int major;
  15. static int first_drv_open(struct inode * inode, struct file * filp)
  16. {
  17. printk("first_drv_open\n");
  18. /*  LED1,LED2,LED3,LED4对应GPB5、GPB6、GPB7、GPB8
  19. *  配置GPB5,6,7,8为输出
  20. */
  21. *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
  22. *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
  23. return 0;
  24. }
  25. static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
  26. {
  27. int val;
  28. printk("first_drv_write\n");
  29. copy_from_user(&val, buffer, count);
  30. if (val == 1)
  31. {
  32. // 点灯
  33. *gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));
  34. }
  35. else
  36. {
  37. // 灭灯
  38. *gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8);
  39. }
  40. return 0;
  41. }
  42. /* File operations struct for character device */
  43. static const struct file_operations first_drv_fops = {
  44. .owner      = THIS_MODULE,
  45. .open       = first_drv_open,
  46. .write      = first_drv_write,
  47. };
  48. /* 驱动入口函数 */
  49. static int first_drv_init(void)
  50. {
  51. /* 主设备号设置为0表示由系统自动分配主设备号 */
  52. major = register_chrdev(0, "first_drv", &first_drv_fops);
  53. /* 创建firstdrv类 */
  54. firstdrv_class = class_create(THIS_MODULE, "firstdrv");
  55. /* 在firstdrv类下创建xxx设备,供应用程序打开设备*/
  56. firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xxx");
  57. /* 将物理地址映射为虚拟地址 */
  58. gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);
  59. gpbdat = gpbcon + 1;
  60. return 0;
  61. }
  62. /* 驱动出口函数 */
  63. static void first_drv_exit(void)
  64. {
  65. unregister_chrdev(major, "first_drv");
  66. device_unregister(firstdrv_device);  //卸载类下的设备
  67. class_destroy(firstdrv_class);      //卸载类
  68. iounmap(gpbcon);                    //解除映射
  69. }
  70. module_init(first_drv_init);  //用于修饰入口函数
  71. module_exit(first_drv_exit);  //用于修饰出口函数
  72. MODULE_AUTHOR("LWJ");
  73. MODULE_DESCRIPTION("Just for Demon");
  74. MODULE_LICENSE("GPL");  //遵循GPL协议

应用测试程序源码:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. /* first_test on
  8. * first_test off
  9. */
  10. int main(int argc ,char *argv[])
  11. {
  12. int fd;
  13. int val = 0;
  14. fd = open("/dev/xxx",O_RDWR);
  15. if (fd < 0)
  16. {
  17. printf("open error\n");
  18. }
  19. if (argc != 2)
  20. {
  21. printf("Usage:\n");
  22. printf("%s <on|off>\n",argv[0]);
  23. return 0;
  24. }
  25. if(strncmp(argv[1],"on",2) == 0)
  26. {
  27. val = 1;
  28. }
  29. else if (strncmp(argv[1],"off",3) == 0)
  30. {
  31. val = 0;
  32. }
  33. write(fd,&val,4);
  34. return 0;
  35. }

测试步骤:

  1. [WJ2440]# ls
  2. Qt            driver_test   lib           root          udisk
  3. TQLedtest     etc           linuxrc       sbin          usr
  4. app_test      first_drv.ko  mnt           sddisk        var
  5. bin           first_test    opt           sys           web
  6. dev           home          proc          tmp
  7. [WJ2440]# ls -l /dev/xxx
  8. ls: /dev/xxx: No such file or directory
  9. [WJ2440]# insmod first_drv.ko
  10. [WJ2440]# lsmod
  11. first_drv 2300 0 - Live 0xbf003000
  12. [WJ2440]# ls -l /dev/xxx
  13. crw-rw----    1 root     root      252,   0 Jan  2 00:23 /dev/xxx
  14. [WJ2440]# ./first_test
  15. first_drv_open
  16. Usage:
  17. ./first_test <on|off>
  18. [WJ2440]# ./first_test off
  19. first_drv_open
  20. first_drv_write
  21. [WJ2440]# ./first_test on
  22. first_drv_open
  23. first_drv_write
  24. [WJ2440]#

可发现,当执行下面语句时,开发板上的4个LED同时被熄灭:

[WJ2440]# ./first_test off

可发现,当执行下面语句时,开发板上的4个LED同时被点亮:

[WJ2440]# ./first_test on

嵌入式Linux LED小灯点亮实验的更多相关文章

  1. 【.NET 与树莓派】PWM 调节LED小灯的亮度

    在开始本文内容之前,老周先纠正一个错误.在上一篇中,提到过 Arduino 开发板的 Vin 引脚,文中老周说这个供电口的输入电压不能高于 5.5V.这里有错,被卖家给的使用说明忽悠了,上 Ardui ...

  2. Arduino 开关控制小灯持续亮之具体思路

    Arduino 开关控制小灯持续亮之具体思路 为什么写这篇文章: 我们用开关控制灯的亮灭的时候,希望只需要按一下按键就可以做到灯一直亮着.而在<Arduino魔法书>中——有弹性的按键这一 ...

  3. 5-51单片机ESP8266学习-AT指令(8266TCP服务器--用手机TCP调试助手发信息给单片机控制小灯的亮灭)

    http://www.cnblogs.com/yangfengwu/p/8759294.html 源码链接:https://pan.baidu.com/s/1wT8KAOIzvkOXXNpkDI7E8 ...

  4. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  5. 驱动编程思想之初体验 --------------- 嵌入式linux驱动开发之点亮LED

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  6. 单片机与android手机通信(控制LED小灯亮灭)

    1.单片机实验板功能设计 为验证数据通信内容,让单片机板上的四个按键与android手机客户端上的四个LED灯相互控制:为达到上述基本实验要求,采用单字符传输数据即可,硬件需设计两块相同的单片机电路板 ...

  7. 6-51单片机ESP8266学习-AT指令(8266TCP服务器--做自己的AndroidTCP客户端发信息给单片机控制小灯的亮灭)

    http://www.cnblogs.com/yangfengwu/p/8776712.html 先把源码和资料链接放到这里 链接: https://pan.baidu.com/s/1jpHZjW_7 ...

  8. 7-51单片机ESP8266学习-AT指令(8266TCP服务器,编写自己的C#TCP客户端发信息给单片机控制小灯的亮灭)

    http://www.cnblogs.com/yangfengwu/p/8780182.html 自己都是现做现写,如果想知道最终实现的功能,请看最后 先把源码和资料链接放到这里 链接: https: ...

  9. (三)开关检测来控制LED灯的亮灭

    开关检测案例一: 具体电路图如下: K1--K4闭合,控制 D1—D4 亮灭 产生的问题: 1.关于 R8 R9 R7 R10 的阻值选择问题,倘若太大的话,  比如10K 不管开关断开还是闭合,好像 ...

随机推荐

  1. HTML解析利器 - HtmlAgilityPack

    HtmlAgilityPack 是CodePlex 上的一个开源项目.它提供了标准的DOM API 和XPath 导航--即使 HTML 不是适当的格式! 使用HtmlAgilityPack操作HTM ...

  2. Codeforces Round #350 (Div. 2)A,B,C,D1

    A. Holidays time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...

  3. C/C++中的switch使用

    代码: #include <iostream> #include <string> #include <cstdio> using namespace std; i ...

  4. 交叉编译安装ARM平台上的Qt

    一.宿主机环境搭建: 编译需要x11库的支持,在Ubuntu下安装命令: sudo apt-get install libx11-dev libxext-dev libxtst-dev 二.下载源码包 ...

  5. sqlite的事务

    好久没用数据库,知识都忘了,之前用sqlite 逐行insert数据,发现这性能实在是太坑,10w条数据,插入大约花了100来分钟. 后来发现还有事务这么一个东西,可以大幅度降低对io的操作,测试插入 ...

  6. Freemarker日期函数处理【转】

    Freemarker日期函数处理[转] (2012-08-01 14:32:13) 转载▼ 标签: 杂谈   string(当和一个日期值一起使用) 这个内置标签用指定的格式把日期转换成字符串,(把默 ...

  7. jsp 、js和css

    css的一些样式 1.自动换行 .AutoNewline { Word-break: break-all;/*必须*/ width :50px;/*这里是设置多宽就进行换行 */ }

  8. javascript if 和else 语句练习

    1.标准体重://男士体重=身高-100±3<br />//女士体重=身高-110±3<br />//输入性别.身高.体重,查看体重是否标准. <script type= ...

  9. NSIS脚本根据操作系统版本动态决定默认安装目录

    问题描述: 因为windows XP和windows 7的program files不同(有program files(x86)),所以需要动态根据系统的位数设置默认安装目录 References: ...

  10. Remove Invalid Parentheses 解答

    Question Remove the minimum number of invalid parentheses in order to make the input string valid. R ...