Linux设备驱动的Hello World—LED驱动 .

来源:互联网 发布:广东省直公务员知乎 编辑:程序博客网 时间:2024/05/18 02:54
要看懂驱动源码,肯定是要从最基本的看起,C语言中,如printf("hello world\n");而对于驱动,肯定是LED,呵呵,恰好年轻时写过一个,还保留着,而且是流水灯式的,下面以ARM270(共有8个LED灯)为例。

一、无操作系统时的LED驱动

    在嵌入式系统的设计中,LED一般直接由CPU的GPIO(通用可编程 I/O 口)控制。GPIO一般由两组寄存器控制,即一组控制寄存器和一组数据寄存器。控制寄存器可设置GPIO 口的工作方式为输入或输出。当引脚被设置为输出时,向数据寄存器的对应位写入1和0会分别在引脚上产生高电平和低电平;当引脚设置为输入时,读取数据寄存器的对应位可获得引脚上相应的电平信号。则在无操作系统的情况下,设备驱动代码如下所示。

    //片选B-CS4基地址为0x10000000,数码管的偏移地址为0x500000.

    #defineSHOW_LED (*((volatile unsigned int *)0x10500000))

    #defineCTRL _LED (*((volatile unsigned int *)0x40E00068))  //GPIO80设置为转换功能2

    //初始化LED ,一般不需要初始化,因为boot已经对其进行初始化了。

    voidLightInit(void)

    {

        CTRL_LED = 0x1400; /*设置GPIO为输出*/

    }

    //点亮第n个LED

    voidLightOn(void)

    {

        SHOW_LED &= ~(1 << n);/*在GPIO上输出低电平*/

    }

    //熄灭第n个LED

    voidLightOff(void)

    {

        SHOW_LED |= (1 << n); /*在GPIO上输出高电平*/

    }

    上述程序中的LightInit()、LightOn()、LightOff()等函数都将作为 LED驱动提供给应用程序的外部接口函数。 程序中ToVirtual()等函数的作用是当系统启动了硬件MMU之后,根据物理地址和虚拟地址的映射关系,将寄存器的物理地址转化为虚拟地址。

 

二、Linux系统下的LED驱动

    在Linux 操作系统下编写LED 设备的驱动时,操作硬件的LightInit()、LightOn()、LightOff()这些函数仍然需要,但是,需要遵循Linux编程的命名习惯,重新将其命名为light_init()、light_on()、light_off()。这些函数将被LED 驱动中独立于设备的针对内核的接口进行调用。

led.c:

[cpp] view plaincopyprint?
  1. //head   
  2. #include <linux/init.h>   
  3. #include <linux/module.h>  
  4. #include <linux/kernel.h>   
  5. #include <linux/fs.h>   
  6. #include <asm/uaccess.h>   
  7. #include <asm/io.h>   
  8. #include <linux/cdev.h>   
  9.   
  10. #define BASE_NUM 0   
  11. #define COUNT 1   
  12. #define DEV_NAME "led"   
  13.   
  14. static char k_buf[128] = {0};  
  15. //static struct cdev *led_cdev = NULL;  
  16. static struct cdev led_cdev;  
  17. static dev_t dev = 0;  
  18. //#define VADDR __REG(0x10500000)   
  19. static volatile unsigned int pa = 0x10500000;  
  20. static volatile unsigned int va;  
  21.   
  22. //description   
  23. MODULE_AUTHOR("huangminqiang2007@163.com");  
  24. MODULE_DESCRIPTION("THIS IS FOR TESTING");  
  25. MODULE_LICENSE("GPL");  
  26.   
  27. //open close read write   
  28. //open   
  29. static int led_open(struct inode *i_node, struct file *filep)  
  30. {  
  31.     return 0;  
  32. }  
  33.   
  34. //close   
  35. static int led_close(struct inode *i_node, struct file *filep)  
  36. {  
  37.     return 0;  
  38. }  
  39.   
  40. //read   
  41. static int led_read(struct file *filep,char *buf,  
  42.             size_t size,loff_t *offset)  
  43. {  
  44.     int cnt = -1;  
  45.       
  46.     if(NULL == filep)  
  47.     {  
  48.         goto _out;  
  49.     }     
  50.       
  51.     cnt = copy_to_user(buf,k_buf,sizeof(k_buf));  
  52.       
  53.     return (sizeof(k_buf)-cnt);  
  54. _out:  
  55.     return -1;  
  56. }  
  57.   
  58. //write   
  59. static int led_write(struct file *filep,const char *buf,  
  60.             size_t size,loff_t *offset)  
  61. {  
  62.     if(NULL == filep)  
  63.     {  
  64.         goto _out;  
  65.     }     
  66.   
  67.     copy_from_user(k_buf,buf,size);  
  68.     *(char *)va = k_buf[0];   
  69.     //writew(k_buf[0],va);   
  70.     //*(char *)VADDR = k_buf[0];   
  71.     return size;  
  72. _out:  
  73.     return -1;  
  74. }  
  75.   
  76. //file operations   
  77. static struct file_operations led_ops = {  
  78.     .owner=     THIS_MODULE,  
  79.     .open=      led_open,  
  80.     .release=   led_close,    
  81.     .read=      led_read,  
  82.     .write=     led_write,  
  83. };  
  84.   
  85. //init   
  86. static int __init led_init(void)  
  87. {  
  88.     int cnt = -1;  
  89.       
  90.     //paddr to vaddr   
  91.     (int *)va = ioremap(pa,4);  
  92.   
  93.     //alloc cdev region   
  94.     cnt = alloc_chrdev_region(&dev,BASE_NUM,COUNT,DEV_NAME);  
  95.     if(0 > cnt)  
  96.     {  
  97.         goto _out;  
  98.     }  
  99.           
  100.     //alloc cdev,如果直接定义变量则不用申请内存。   
  101.     //led_cdev = cdev_alloc();  
  102.     //init cdev   
  103.     cdev_init(&led_cdev,&led_ops);  
  104.     //add cdev   
  105.     cnt = cdev_add(&led_cdev,dev,COUNT);  
  106.     //printk major_num   
  107.     printk("<0>" "major number : %d\n",MAJOR(dev));  
  108.   
  109.   
  110.     if(0 > cnt)  
  111.     {  
  112.         goto _out;  
  113.     }  
  114.   
  115.     printk("<0>" "init ok!\n");  
  116.   
  117.     return 0;  
  118. _out:  
  119.     return -1;  
  120. }  
  121.   
  122. //exit   
  123. static void __exit led_exit(void)  
  124. {  
  125.     //unregister cdev region   
  126.     unregister_chrdev_region(dev,COUNT);  
  127.     //del cdev   
  128.     cdev_del(&led_cdev);  
  129.   
  130.     iounmap((char *)va);  
  131.   
  132.     printk("<0>" "exit ok!\n");  
  133. }  
  134.   
  135. //add to kernel   
  136. module_init(led_init);  
  137. module_exit(led_exit);  
Makefile:
[cpp] view plaincopyprint?
  1. EXTRA_CFLAGS += $(DEBFLAGS) -Wall  
  2.   
  3. ifneq ($(KERNELRELEASE),)  
  4.     obj-m := led.o  
  5. else  
  6.     KDIR ?= /opt/270-s-2.6.9/linux-2.6.9_270-s  #之前的内核位置  
  7.     PWD := $(shell pwd)  
  8. all:  
  9.     make $(EXTRA_CFLAGS) -C $(KDIR) M=$(PWD) modules  
  10. endif  
  11. clean:  
  12.     rm *.o *.ko led.mod.c   
test.c:
[cpp] view plaincopyprint?
  1. #include <stdio.h>   
  2. #include <sys/types.h>   
  3. #include <sys/stat.h>   
  4. #include <fcntl.h>   
  5.   
  6. #define DEV_NAME "/dev/test"   
  7. #define FLAGS (O_RDWR | O_CREAT)  
  8.   
  9. int main(void)  
  10. {  
  11.     int fd = -1;  
  12.     int i = -1;  
  13.     int cnt = -1;  
  14.     unsigned char led = 0xff;  
  15.       
  16.     //open   
  17.     fd = open(DEV_NAME, FLAGS);  
  18.     if(0 > fd)  
  19.     {  
  20.         perror("open err:");  
  21.         goto _out;  
  22.     }     
  23.       
  24.     //流水灯   
  25.     while(1)  
  26.     {  
  27.         for(i = 0; i < 8; i++)  
  28.         {  
  29.             led &= ~(1 << i);  
  30.             cnt = write(fd, &led, 1);  
  31.             if(0 > cnt)  
  32.             {  
  33.                 perror("write err:");  
  34.                 goto _out;  
  35.             }  
  36.             led = 0xff;  
  37.             sleep(1);  
  38.         }  
  39.     }  
  40.   
  41.     //close   
  42.     close(fd);  
  43.   
  44.     return 0;  
  45. _out:  
  46.     return -1;  
  47. }  

    由于代码比较多,2个目录,一个驱动driver目录,一个应用程序目录test,上面程序的下载代码地址:http://download.csdn.net/detail/huangminqiang201209/4904596

大致过程如下:

    1.生成led.ko

    A.编写led.c

        1)驱动描述:MODULE_DESCRIPTION,MODULE_LICENSE等。

        2)系统操作函数:open、read、write、close等。

        3)file_operation结构体的封装,2.6内核的。

        4)注册设备:__init中register_chrdev。

        5) 注销:__exit中unregister。

        6) 模块的导入导出内核:module_init(led_init); module_exit(led_exit);

B.编写Makefile(这个Makefile还是有一点点复杂的哦),与led.c同一目录

C.make生成led.ko

    还有一种生成*.ko的方法是编译内核模块,Makeconfigure:Y/N/M选择(呵呵,在此之前还要对Makefile、Kconfig等进行配置)M是将该功能编译成可以在需要时动态插入到内核中的模块,而Y是直接编译进内核,可以省略后面的几步,直接运行app。

2.生成应用程序APP

    1)编写main.c,应用程序

    2)/usr/local/hybus-arm-linux-R1.1/bin/arm-linux-gcc main.c -o app

3.led.koapp通过NFS挂在到开发板上。

4.创建led结点

    mknod /dev/led c 2500

5.装载led模块并查看led.ko

   insmod led.ko  lsmod led.ko

6.运行应用程序./app

7.卸载led模块

    rmmod led.ko

0 0
原创粉丝点击