Tiny6410 led 驱动实现分析

http://write.blog.csdn.net/postedit/8984547


本文作为学习自己写驱动的基础贴,重在总结写驱动程序从看开发板硬件使用手册,到开发板data sheet, 原理图,再到coding 的整个流程

以Tiny6410 开发板实现led 驱动为例:

1. 看Tiny6410 硬件使用手册关于 LED 的介绍部分(要实现LED 驱动当然先要看下开发板上的LED 资源情况,有几个LED 之类的)【 01- Tiny6410硬件手册.pdf 】


Tiny6410 led 驱动实现分析_第1张图片


硬件使用手册说明了Tiny6410开发板上面配备了4 个led,并演示了4个led 与 GPIO 的连接情况,使用GPK4~ GPK7 4个GPIO 口

更详细的硬件电路图可以在 【 原理图PCB和封装库/Tiny6410-1107.pdf 】 中,



2. 了解了硬件连接情况之后,就需要看一下 Tiny6410 开发板的 GPIO 介绍,重点是 GPK4~ GPK7 这四个 GPIO 口。

USER manual 【S3C6410X.pdf 】中,第 10章关于GPIO 的讲解:

首先看 GPIO 的总体概述:


Tiny6410 led 驱动实现分析_第2张图片

Tiny6410 led 驱动实现分析_第3张图片


GPK Port GPIO  的memory map:



     


接下来看下 Port K GPIO 的讲解, 也就是 知道了一个 Port GPK 之后,如何使用 这个 port 中的所有 GPIO 口。


Tiny6410 led 驱动实现分析_第4张图片

Tiny6410 led 驱动实现分析_第5张图片


GPK Port 中一共 16 个 GPIO 口,具体使用方法就是, GPK Port 有四个控制寄存器,通过写这四个控制寄存器达到 使用每个 GPIO 的效果:

GPKCON0  GPKCON1 两个 32bits 寄存器用作配置 GPK 中 GPIO 口 用作输入还是输出之类的,要想配置 GPK Port 中 某个Pin 的用途,无非就是通过写这两个配置寄存器相应的bit 位 来实现,因为每一个 bit 位都对应着一个 GPIO Pin 口。

明白了这些硬件相关的原理就一下明白驱动代码中为啥经常看到 1 << n ,将 1 左移 n 位 的做法了。

配置好 GPIO Pin 口的输入输出用途之后,如果是输出口的话,就需要往GPKDAT  数据寄存器写数值了,即 输出的数值是多少。


上面的硬件准备工作做好之后用代码表达出来就简单了,下面开始驱动软件实现部分:


还是先讲解 准备知识部分,一些知识是绕不过去的,而且绕过去了也没啥好处,这些知识点本身往往才是最有价值的。

1. I/O 操作 函数, 上面已经知道了具体的寄存器,怎么操作I/O 地址呢, linux 提供了一组函数:

#include  <linux/io.h> 或

#include <asm/io.h>


[cpp] view plain copy
  1. #define readb(c)        ({ u8  __v = readb_relaxed(c); __iormb(); __v; })  
  2. #define readw(c)        ({ u16 __v = readw_relaxed(c); __iormb(); __v; })  
  3. #define readl(c)        ({ u32 __v = readl_relaxed(c); __iormb(); __v; })  
  4.   
  5. #define writeb(v,c)     ({ __iowmb(); writeb_relaxed(v,c); })  
  6. #define writew(v,c)     ({ __iowmb(); writew_relaxed(v,c); })  
  7. #define writel(v,c)     ({ __iowmb(); writel_relaxed(v,c); })  

readb/writeb 就是操作 8 bit 寄存器,

readw/writew 操作 16 bit 寄存器,

readl/writel 操作32 bit 寄存器。


2. GPK Port 四个寄存器的地址

linux-2.6.36-android/arch/arm/mach-s3c64xx/include/mach

这个路径下面是具体到一个machine 也就是 Tiny 6410 开发板的相关代码。

找这种 GPIO 寄存器地址之类的话当然到相应开发板目录下面找相关的文件。

debug-macro.S  gpio-bank-c.h  gpio-bank-h.h  gpio-bank-n.h  hardware.h  pll.h         regs-fimc.h          regs-lcd.h      regs-srom.h          s3c6400.h     timex.h
dma.h          gpio-bank-d.h  gpio-bank-i.h  gpio-bank-o.h  io.h        pm-core.h     regs-g2d.h           regs-mfc.h      regs-syscon-power.h  s3c6410.h     ts.h
entry-macro.S  gpio-bank-e.h  gpio-bank-j.h  gpio-bank-p.h  irqs.h      pwm-clock.h   regs-gpio.h          regs-modem.h    regs-sys.h           spi-clocks.h  uncompress.h
gpio-bank-a.h  gpio-bank-f.h  gpio-bank-k.h  gpio-bank-q.h  map.h       regs-clock.h  regs-gpio-memport.h  regs-pp.h       regs-tvenc.h         system.h      vmalloc.h
gpio-bank-b.h  gpio-bank-g.h  gpio-bank-l.h  gpio.h         memory.h    regs-fb.h     regs-irq.h           regs-rotator.h  regs-tvscaler.h      tick.h

可以看到 gpio 各个bank 的头文件,看下gpio-bank-k.h

[cpp] view plain copy
  1. /* linux/arch/arm/mach-s3c64xx/include/mach/gpio-bank-k.h 
  2.  * 
  3.  * Copyright 2008 Openmoko, Inc. 
  4.  * Copyright 2008 Simtec Electronics 
  5.  *  Ben Dooks <[email protected]> 
  6.  *  http://armlinux.simtec.co.uk/ 
  7.  * 
  8.  * GPIO Bank K register and configuration definitions 
  9.  * 
  10.  * This program is free software; you can redistribute it and/or modify 
  11.  * it under the terms of the GNU General Public License version 2 as 
  12.  * published by the Free Software Foundation. 
  13. */  
  14.   
  15. #define S3C64XX_GPKCON          (S3C64XX_GPK_BASE + 0x00)  
  16. #define S3C64XX_GPKCON1         (S3C64XX_GPK_BASE + 0x04)  
  17. #define S3C64XX_GPKDAT          (S3C64XX_GPK_BASE + 0x08)  
  18. #define S3C64XX_GPKPUD          (S3C64XX_GPK_BASE + 0x0c)  
  19.   
  20. #define S3C64XX_GPK_CONMASK(__gpio) (0x3 << ((__gpio) * 2))  
  21. #define S3C64XX_GPK_INPUT(__gpio)   (0x0 << ((__gpio) * 2))  
  22. #define S3C64XX_GPK_OUTPUT(__gpio)  (0x1 << ((__gpio) * 2))  

Good, 这下找到了要操作的寄存器宏 !

接下来就可以根据 cscope 跳转include 相应的头文件:

[cpp] view plain copy
  1. #include <mach/map.h>     
  2. #include <mach/regs-gpio.h>      
  3. #include <mach/gpio-bank-k.h>      


准备工作都做好之后coding 就简单了:

LED 设备驱动:

[cpp] view plain copy
  1. /*  
  2.  * s3c6410-led.c file implement Tiny6410 DEV board LED driver. 
  3.  * Author: [email protected] 
  4.  */  
  5.   
  6. #include <linux/module.h>  
  7. #include <linux/init.h>  
  8. #include <linux/device.h>  
  9. #include <linux/fs.h>  
  10. #include <linux/io.h>  
  11.   
  12. /* Tiny6410 board related header files */  
  13. #include <mach/map.h>     
  14. #include <mach/regs-gpio.h>      
  15. #include <mach/gpio-bank-k.h>      
  16.   
  17. #include <asm/uaccess.h>  
  18.    
  19. #define TINY6410_LED_CHAR_MAJOR   243  
  20.   
  21.   
  22. static int tiny6410_led_open(struct inode *inode, struct file *file)  
  23. {    
  24.     int ret = 0;  
  25.     uint32_t temp = 0;    
  26.   
  27.     temp = readl(S3C64XX_GPKCON);  
  28.   
  29.     /* Tiny6410 board has 4 leds, which connect to the GPIO, GPK Port, GPK4 ~ GPK7 GPIO Pins. 
  30.        Config the GPKCON0 register high 16bits to output(0001) , according to s3c6410x USER MANUAL. */  
  31.     temp = (temp & 0x0000ffff) | 0x1111ffff;  
  32.     writel(temp, S3C64XX_GPKCON);  
  33.   
  34.     return ret;    
  35. }  
  36.   
  37. static ssize_t tiny6410_led_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)  
  38. {  
  39.     size_t retlen = 0;  
  40.   
  41.     /* Actually we don't need read func currently, empty func. */  
  42.     retlen = count;  
  43.   
  44.     return retlen;  
  45. }  
  46.     
  47. static ssize_t tiny6410_led_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)  
  48. {  
  49.     char led_number = 0;  
  50.     unsigned temp;  
  51.     size_t retlen;  
  52.       
  53.     if(copy_from_user(&led_number,buf,count))  
  54.         return -EFAULT;    
  55.   
  56.     /* led_number stores the led number, which we will turn on. 
  57.        We can use below expression to turn on specified LED, 
  58.        make sure you don't touch any GPIO except GPK4~GPK7 LEDs GPIO Pins. 
  59.        eg, temp &= ~(1 << (led_number + 4)) 
  60.      */  
  61.     printk("led_number = %d \n", led_number);  
  62.     temp  = readl(S3C64XX_GPKDAT);  
  63.     printk("temp = %x \n", temp);  
  64.     temp |= 0xf0;  
  65.     temp &= ~(1 << (led_number + 4));  
  66.     writel(temp, S3C64XX_GPKDAT);  
  67.   
  68.     return (retlen = 1) ;  
  69. }  
  70.     
  71. static int tiny6410_led_close(struct inode *inode, struct file *filp)    
  72. {  
  73.     return 0;   
  74. }  
  75.   
  76. static const struct file_operations tiny6410_led_fops = {  
  77.     .owner      = THIS_MODULE,  
  78.     .read       = tiny6410_led_read,  
  79.     .write      = tiny6410_led_write,  
  80. #ifdef CONFIG_TINY6410_LED_IOCTL  
  81.     .unlocked_ioctl     = tiny6410_led_ioctl,  
  82. #endif  
  83.     .open       = tiny6410_led_open,  
  84.     .release    = tiny6410_led_close,  
  85. #ifdef CONFIG_TINY6410_LED_MMAP  
  86.     .mmap       = tiny6410_led_mmap,  
  87. #endif  
  88. };  
  89.   
  90. static int __init tiny6410_led_init(void)  
  91. {  
  92.     int status;  
  93.   
  94.     status = register_chrdev(TINY6410_LED_CHAR_MAJOR , "Tiny6410_leds", &tiny6410_led_fops);  
  95.     if (status < 0) {  
  96.         printk(KERN_NOTICE "Can't allocate major number %d for Tiny6410 leds Devices.\n",  
  97.                TINY6410_LED_CHAR_MAJOR);  
  98.     }  
  99.   
  100.     return status;  
  101. }  
  102.   
  103. static void __exit tiny6410_led_exit(void)  
  104. {  
  105.     unregister_chrdev(TINY6410_LED_CHAR_MAJOR , "Tiny6410_leds");  
  106. }  
  107.   
  108. module_init(tiny6410_led_init);  
  109. module_exit(tiny6410_led_exit);  
  110.   
  111. MODULE_ALIAS_CHARDEV_MAJOR(TINY6410_LED_CHAR_MAJOR);  
  112.   
  113. MODULE_LICENSE("GPL");  
  114. MODULE_AUTHOR("Chen Qiang <[email protected]>");  
  115. MODULE_DESCRIPTION("Char-device access to Tiny6410 board LEDs.");  


用户态测试程序:
[cpp] view plain copy
  1. /* 
  2.  * s3c6410-leds_test.c file implement Tiny6410 DEV board userspace simple test program. 
  3.  * Author: [email protected] 
  4.  */  
  5.   
  6. #include <stdio.h>  
  7. #include <stdlib.h>  
  8. #include <fcntl.h>  
  9.   
  10. #define MAX_TINY6410_LEDS_NUMBER 3  
  11.   
  12. int main(int argc, char* argv[])  
  13. {  
  14.     char led_number = 0;  
  15.     int fd;  
  16.     int ret = 0;  
  17.   
  18.     fd = open("/dev/Tiny6410_leds", O_RDWR);  
  19.     if(fd < 0)  
  20.     {  
  21.         printf("Tiny6410_leds device open error ! Remember to create device node by '#mknod /dev/Tiny6410_leds c 243 0' \n ");  
  22.         return (ret = -1);  
  23.     }  
  24.   
  25.     for(;;)  
  26.     {  
  27.         if(led_number > MAX_TINY6410_LEDS_NUMBER)  
  28.             led_number = 0;  
  29.   
  30.         /* led_number is the led which we will turn on. */  
  31.         write(fd, &led_number, 1);  
  32.         sleep(1);  
  33.         led_number ++ ;  
  34.     }  
  35.   
  36.     return ret;  
  37. }  


友善提供的 使用misc device 实现的LED 驱动,使用misc_register 的好处就是,/sys/class/misc/ 类文件已经生成,自动调用device_create 创建leds 的设备。同时 /dev下面也会自动生成 leds 设备文件。
[cpp] view plain copy
  1. #include <linux/miscdevice.h>  
  2. #include <linux/delay.h>  
  3. #include <asm/irq.h>  
  4. //#include <mach/regs-gpio.h>  
  5. #include <mach/hardware.h>  
  6. #include <linux/kernel.h>  
  7. #include <linux/module.h>  
  8. #include <linux/init.h>  
  9. #include <linux/mm.h>  
  10. #include <linux/fs.h>  
  11. #include <linux/types.h>  
  12. #include <linux/delay.h>  
  13. #include <linux/moduleparam.h>  
  14. #include <linux/slab.h>  
  15. #include <linux/errno.h>  
  16. #include <linux/ioctl.h>  
  17. #include <linux/cdev.h>  
  18. #include <linux/string.h>  
  19. #include <linux/list.h>  
  20. #include <linux/pci.h>  
  21. #include <asm/uaccess.h>  
  22. #include <asm/atomic.h>  
  23. #include <asm/unistd.h>  
  24.   
  25. #include <mach/map.h>  
  26. #include <mach/regs-clock.h>  
  27. #include <mach/regs-gpio.h>  
  28.   
  29. #include <plat/gpio-cfg.h>  
  30. #include <mach/gpio-bank-e.h>  
  31. #include <mach/gpio-bank-k.h>  
  32.   
  33. #define DEVICE_NAME "leds"  
  34.   
  35. static long sbc2440_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
  36. {  
  37.     switch(cmd) {  
  38.         unsigned tmp;  
  39.     case 0:  
  40.     case 1:  
  41.         if (arg > 4) {  
  42.             return -EINVAL;  
  43.         }  
  44.         tmp = readl(S3C64XX_GPKDAT);  
  45.         tmp &= ~(1 << (4 + arg));  
  46.         tmp |= ( (!cmd) << (4 + arg) );  
  47.         writel(tmp, S3C64XX_GPKDAT);  
  48.         //printk (DEVICE_NAME": %d %d\n", arg, cmd);  
  49.         return 0;  
  50.     default:  
  51.         return -EINVAL;  
  52.     }  
  53. }  
  54.   
  55. static struct file_operations dev_fops = {  
  56.     .owner          = THIS_MODULE,  
  57.     .unlocked_ioctl = sbc2440_leds_ioctl,  
  58. };  
  59.   
  60. static struct miscdevice misc = {  
  61.     .minor = MISC_DYNAMIC_MINOR,  
  62.     .name = DEVICE_NAME,  
  63.     .fops = &dev_fops,  
  64. };  
  65.   
  66. static int __init dev_init(void)  
  67. {  
  68.     int ret;  
  69.   
  70.     {  
  71.         unsigned tmp;  
  72.         tmp = readl(S3C64XX_GPKCON);  
  73.   
  74.         /* Set S3C64XX_GPKCON high 16bits to 0x0001: output. */  
  75.         tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);  
  76.         writel(tmp, S3C64XX_GPKCON);  
  77.   
  78.         /* Set GPK4 ~ GPK7 Pins to 1, that is to say, 4 leds all off state. */  
  79.         tmp = readl(S3C64XX_GPKDAT);  
  80.         tmp |= (0xF << 4);  
  81.         writel(tmp, S3C64XX_GPKDAT);  
  82.     }  
  83.   
  84.     ret = misc_register(&misc);  
  85.   
  86.     printk (DEVICE_NAME"\tinitialized\n");  
  87.   
  88.     return ret;  
  89. }  
  90.   
  91. static void __exit dev_exit(void)  
  92. {  
  93.     misc_deregister(&misc);  
  94. }  
  95.   
  96. module_init(dev_init);  
  97. module_exit(dev_exit);  
  98. MODULE_LICENSE("GPL");  
  99. MODULE_AUTHOR("FriendlyARM Inc.");  

用户态测试程序:
[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/ioctl.h>  
  5. #include <sys/types.h>  
  6. #include <sys/stat.h>  
  7. #include <fcntl.h>  
  8. #include <sys/select.h>  
  9. #include <sys/time.h>  
  10. #include <string.h>  
  11.   
  12. static int led_fd;  
  13. static int type = 1;  
  14.   
  15. static void push_leds(void)  
  16. {  
  17.     static unsigned step;  
  18.     unsigned led_bitmap;  
  19.     int i;  
  20.   
  21.     switch(type) {  
  22.     case 0:  
  23.         if (step >= 6) {  
  24.             step = 0;  
  25.         }  
  26.         if (step < 3) {  
  27.             led_bitmap = 1 << step;  
  28.         } else {  
  29.             led_bitmap = 1 << (6 - step);  
  30.         }  
  31.         break;  
  32.     case 1:  
  33.         if (step > 255) {  
  34.             step = 0;  
  35.         }  
  36.         led_bitmap = step;  
  37.         break;  
  38.     default:  
  39.         led_bitmap = 0;  
  40.     }  
  41.     step++;  
  42.     for (i = 0; i < 4; i++) {  
  43.         ioctl(led_fd, led_bitmap & 1, i);  
  44.         led_bitmap >>= 1;  
  45.     }  
  46. }  
  47.   
  48. int main(void)  
  49. {  
  50.     int led_control_pipe;  
  51.     int null_writer_fd; // for read endpoint not blocking when control process exit  
  52.   
  53.     double period = 0.5;  
  54.   
  55.     led_fd = open("/dev/leds0", 0);  
  56.     if (led_fd < 0) {  
  57.         led_fd = open("/dev/leds", 0);  
  58.     }  
  59.     if (led_fd < 0) {  
  60.         perror("open device leds");  
  61.         exit(1);  
  62.     }  
  63.     unlink("/tmp/led-control");  
  64.     mkfifo("/tmp/led-control", 0666);  
  65.   
  66.     led_control_pipe = open("/tmp/led-control", O_RDONLY | O_NONBLOCK);  
  67.     if (led_control_pipe < 0) {  
  68.         perror("open control pipe for read");  
  69.         exit(1);  
  70.     }  
  71.     null_writer_fd = open("/tmp/led-control", O_WRONLY | O_NONBLOCK);  
  72.     if (null_writer_fd < 0) {  
  73.         perror("open control pipe for write");  
  74.         exit(1);  
  75.     }  
  76.   
  77.     for (;;) {  
  78.         fd_set rds;  
  79.         struct timeval step;  
  80.         int ret;  
  81.   
  82.         FD_ZERO(&rds);  
  83.         FD_SET(led_control_pipe, &rds);  
  84.         step.tv_sec  = period;  
  85.         step.tv_usec = (period - step.tv_sec) * 1000000L;  
  86.   
  87.         ret = select(led_control_pipe + 1, &rds, NULL, NULL, &step);  
  88.         if (ret < 0) {  
  89.             perror("select");  
  90.             exit(1);  
  91.         }  
  92.         if (ret == 0) {  
  93.             push_leds();  
  94.         } else if (FD_ISSET(led_control_pipe, &rds)) {  
  95.             static char buffer[200];  
  96.             for (;;) {  
  97.                 char c;  
  98.                 int len = strlen(buffer);  
  99.                 if (len >= sizeof buffer - 1) {  
  100.                     memset(buffer, 0, sizeof buffer);  
  101.                     break;  
  102.                 }  
  103.                 if (read(led_control_pipe, &c, 1) != 1) {  
  104.                     break;  
  105.                 }  
  106.                 if (c == '\r') {  
  107.                     continue;  
  108.                 }  
  109.                 if (c == '\n') {  
  110.                     int tmp_type;  
  111.                     double tmp_period;  
  112.                     if (sscanf(buffer,"%d%lf", &tmp_type, &tmp_period) == 2) {  
  113.                         type = tmp_type;  
  114.                         period = tmp_period;  
  115.                     }  
  116.                     fprintf(stderr, "type is %d, period is %lf\n", type, period);  
  117.                     memset(buffer, 0, sizeof buffer);  
  118.                     break;  
  119.                 }  
  120.                 buffer[len] = c;  
  121.             }  
  122.         }  
  123.     }  
  124.   
  125.     close(led_fd);  
  126.     return 0;  
  127. }  

还有一种方法是在 linux/driver/leds 下面已经有现成的 led class 了,使用 platform device 写LED 驱动,这个改天学习一下。


写驱动的时候经常需要操作一个bit 位,一个32bits 的寄存器如果每一位都控制这个GPIO 口的话,如何将其中的一位写0/1 呢:

unsigned int temp;

temp = readl(REG_TO_CONTROL);

temp &= ~(1 << N); // N is the bit you want to change

// 这一步的目的是保证N 位以外的bit 位不变, N bit 位清0, 清0之后就好操作了,对应那一位位或就行了。

temp |= (1 << N); // N bit 位 置1 操作

//如果想置0,可以不操作,因为上面已经清0过了。

// 当然,如果想做的灵活一点可以

temp |= ((cmd) << N) ; // cmd 如果是0 的话相当与 位或0, 还是原先的数值。


你可能感兴趣的:(Tiny6410 led 驱动实现分析)