问:怎么写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. C#三个平台上的文件选择方法

    wpf: Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = &qu ...

  2. Iterable 超级接口

    这是一个老祖宗,一代一代往下拨 collection 的方法如下,是一个跟接口方法如下,见API collection  : add():添加一个元素 addAll():添加一组元素 clear(); ...

  3. paip.输入法编程----一级汉字1000个

    paip.输入法编程----一级汉字1000个.txt 作者Attilax ,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.csdn. ...

  4. jquery 1.9 之后toggle不能用的问题

    今天用到toggle这个方法,发现不是自己想要的效果,之前有用过好多次,一直都没有问题. 网上查了原因,才知道是版本的问题,jquery1.9之后toggle取消了.那么如果想要继续用toggle的这 ...

  5. 把int类型值转换成int数组(不通过string类型转换)

    只适合初学者 今天同事问了我不通过string类型把int类型值123589转换成int[]数组.我想了想于是写了出来,其实不难.看你小学数学学得好不好.言归正传. 先不说代码,举个列子就知道怎么玩了 ...

  6. php的一些小笔记-文件函数(3)

    文件的写入和读取 1 . 文件的读取 1, file_get_contents 函数是用来将文件的内容读入到一个字符串中的首选方法    结果

  7. ecshop的广告链接地址打开有问题修改affiche.php为普通链接

    相信很多用过ECSHOP的站长都知道,ECSHOP的广告链接地址形式为“affiche.php?ad_id=1&uri=http%3A%2F%2Fwww.ecmoban.com” 它中间用 a ...

  8. Hibernate整合Struts2时报错

    今天在整合Hibernate和Struts2的时候遇到一个问题 总是出现各种异常,经过仔细检查,代码本身并没有问题, ----------------------------------------- ...

  9. VS2010安装MSDN

    VS2010正式版不再有单独的MSDN Library安装选项,以至于很多同学找不到本地的MSDN Library来用,其实VS2010的ISO安装光盘里已经包含有MSDN Library,只不过要手 ...

  10. Windows资源文件里VarFileInfo的Translation(EXE的语言描述信息)

    /* The following line should only be modified for localized versions. */ /* It consists of any numbe ...