久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4802|回復: 0
打印 上一主題 下一主題
收起左側(cè)

Tiny6410 簡單的LED字符設(shè)備驅(qū)動 io驅(qū)動

[復制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:104287 發(fā)表于 2016-1-31 02:34 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
Step1:驅(qū)動加載函數(shù): ***_init
該函數(shù)內(nèi)容為驅(qū)動硬件時僅執(zhí)行一次的函數(shù),其作用是硬件初始化。如配置IO端口輸出方向,配置IO上拉等;該函數(shù)傳入在module_init(***_init);函數(shù)中以使得在加載驅(qū)動時自動執(zhí)行;

Step2:寫openreadwriterelease函數(shù);
這三個函數(shù)的作用是為了傳入file_operations結(jié)構(gòu)體;在應(yīng)用程序調(diào)用驅(qū)動時這三個函數(shù)作為借口被調(diào)用。
其中open的作用是在編寫應(yīng)用程序時加載驅(qū)動程序;其原型為pen(strpath, authority )如:fd = open("/dev/led",O_RDWR);//open函數(shù)加載驅(qū)動,返回值為描述符,返回值為0時則成功加載驅(qū)動。
Write函數(shù)的原型為: (structfile *filp, const char __user *buf, size_t count,loff_t *f_pos)   

Step3:將step2中的函數(shù)賦給operations結(jié)構(gòu)體
struct file_operations led_fops =  
{   
    .owner= THIS_MODULE,   
    .open= led_open,   
    .read= led_read,   
    .write= led_write,   
    .release= led_release,   
};

Step4:在***_init函數(shù)中注冊字符型設(shè)備驅(qū)動模塊;如以下為注冊led驅(qū)動函數(shù):
register_chrdev(LED_MAJOR,"led",&led_fops);
int register_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);
其中參數(shù)major如果等于0,則表示采用系統(tǒng)動態(tài)分配的主設(shè)備號;不為0,則表示靜態(tài)注冊。
注銷字符設(shè)備可以使用unregister_chrdev函數(shù)。

Step5:注銷字符型設(shè)備驅(qū)動模塊:
在led_exit(可以任意名字,最后把函數(shù)名傳給module_exit即可)使用unregister_chrdev(LED_MAJOR,"led"); 函數(shù)注銷驅(qū)動。并賦給module_exit(led_exit);


·一個活生生的例子   

1.查看用戶手冊

led1led2led3led4 連接的分別是 GPK4GPK5GPK6GPK7
2、查詢6410芯片手冊




下面還需要3個步驟:
1、設(shè)置GPIOOUTPUT
   GPK4GPK5GPK6GPK7設(shè)置為輸出output=0001
   GPKCON019:28都配置為0001

2、設(shè)置GPIO的數(shù)據(jù)。
   GPKDATA4:7位賦值為0

3、設(shè)置GPKUP為上拉。
   GPKUP4:7位設(shè)置為10

3、代碼
led_driver.cStep1:驅(qū)動加載函數(shù): ***_init

該函數(shù)內(nèi)容為驅(qū)動硬件時僅執(zhí)行一次的函數(shù),其作用是硬件初始化。如配置IO端口輸出方向,配置IO上拉等;該函數(shù)傳入在module_init(***_init);函數(shù)中以使得在加載驅(qū)動時自動執(zhí)行;

Step2:寫openreadwriterelease函數(shù);
這三個函數(shù)的作用是為了傳入file_operations結(jié)構(gòu)體;在應(yīng)用程序調(diào)用驅(qū)動時這三個函數(shù)作為借口被調(diào)用。
其中open的作用是在編寫應(yīng)用程序時加載驅(qū)動程序;其原型為pen(strpath, authority )如:fd = open("/dev/led",O_RDWR);//open函數(shù)加載驅(qū)動,返回值為描述符,返回值為0時則成功加載驅(qū)動。
Write函數(shù)的原型為: (structfile *filp, const char __user *buf, size_t count,loff_t *f_pos)   

Step3:將step2中的函數(shù)賦給operations結(jié)構(gòu)體
struct file_operations led_fops =  
{   
    .owner= THIS_MODULE,   
    .open= led_open,   
    .read= led_read,   
    .write= led_write,   
    .release= led_release,   
};

Step4:在***_init函數(shù)中注冊字符型設(shè)備驅(qū)動模塊;如以下為注冊led驅(qū)動函數(shù):
register_chrdev(LED_MAJOR,"led",&led_fops);
int register_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);
其中參數(shù)major如果等于0,則表示采用系統(tǒng)動態(tài)分配的主設(shè)備號;不為0,則表示靜態(tài)注冊。
注銷字符設(shè)備可以使用unregister_chrdev函數(shù)。

Step5:注銷字符型設(shè)備驅(qū)動模塊:
在led_exit(可以任意名字,最后把函數(shù)名傳給module_exit即可)使用unregister_chrdev(LED_MAJOR,"led"); 函數(shù)注銷驅(qū)動。并賦給module_exit(led_exit);


·一個活生生的例子   

1.查看用戶手冊

led1led2led3led4 連接的分別是 GPK4GPK5GPK6GPK7
2、查詢6410芯片手冊




下面還需要3個步驟:
1、設(shè)置GPIOOUTPUT
   GPK4GPK5GPK6GPK7設(shè)置為輸出output=0001
   GPKCON019:28都配置為0001

2、設(shè)置GPIO的數(shù)據(jù)。
   GPKDATA4:7位賦值為0

3、設(shè)置GPKUP為上拉。
   GPKUP4:7位設(shè)置為10

3、代碼
led_driver.c

  1.     #include <linux/module.h>  /*它定義了模塊的 API、類型和宏(MODULE_LICENSE、MODULE_AUTHOR等等),所有的內(nèi)核模塊都必須包含這個頭文件。*/   
  2.         
  3.     #include <linux/kernel.h>  /*使用內(nèi)核信息優(yōu)先級時要包含這個文件,一般在使用printk函數(shù)時使用到優(yōu)先級信息*/  
  4.       
  5.     #include <linux/fs.h>   
  6.     #include <asm/uaccess.h> /* copy_to_user,copy_from_user */     
  7.     #include <linux/pci.h>     /*readl writel*/  
  8.     #include <mach/map.h>     
  9.     #include <mach/regs-gpio.h>      
  10.     #include <mach/gpio-bank-k.h>      
  11.       
  12.         
  13.     #define LED_MAJOR   243  
  14.       
  15.     #define LED_ON      1  
  16.     #define LED_OFF     0  
  17.     #define LED_1_ON    2  
  18.     #define LED_1_OFF   3  
  19.     #define LED_2_ON    4  
  20.     #define LED_2_OFF   5  
  21.     #define LED_3_ON    6  
  22.     #define LED_3_OFF   7  
  23.     #define LED_4_ON    8  
  24.     #define LED_4_OFF   9  
  25.       
  26.       
  27.     static int led_open (struct inode *inode,struct file *filp)   
  28.         
  29.     {   
  30.         unsigned tmp;      
  31.       
  32.         tmp = readl(S3C64XX_GPKCON);      
  33.         tmp = (tmp&0x0000ffff)| 0x1111ffff;  
  34.         writel(tmp, S3C64XX_GPKCON);     
  35.       
  36.         printk("#########open######\n");   
  37.         return 0;   
  38.     }   
  39.         
  40.     static int led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)   
  41.     {      
  42.         return count;   
  43.     }   
  44.         
  45.         
  46.     static int led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)  
  47.       
  48.     {   
  49.         char wbuf[10];   
  50.         unsigned tmp;      
  51.          
  52.         if(copy_from_user(wbuf,buf,count))  
  53.             return -EFAULT;   
  54.       
  55.             switch(wbuf[0])   
  56.             {   
  57.               
  58.             case LED_ON:   
  59.                     tmp = readl(S3C64XX_GPKDAT);      
  60.                 tmp &= (0x0f);      
  61.                 writel(tmp, S3C64XX_GPKDAT);  
  62.                 printk("turn on!\n");      
  63.                     break;  
  64.       
  65.             case LED_OFF:   
  66.                 tmp  = readl(S3C64XX_GPKDAT);      
  67.                 tmp |= (0xf0);      
  68.                 writel(tmp, S3C64XX_GPKDAT);   
  69.                 printk("turn off!\n");     
  70.                 break;            
  71.       
  72.             case LED_1_ON:   
  73.                 tmp = readl(S3C64XX_GPKDAT);      
  74.                 tmp &= (0xef);      
  75.                 writel(tmp, S3C64XX_GPKDAT);   
  76.                 printk("turn off!\n");     
  77.                 break;   
  78.       
  79.             case LED_1_OFF:   
  80.                     tmp = readl(S3C64XX_GPKDAT);      
  81.                 tmp |= (0xf0);      
  82.                 writel(tmp, S3C64XX_GPKDAT);  
  83.                 printk("turn on!\n");      
  84.                     break;   
  85.       
  86.             case LED_2_ON:   
  87.                 tmp = readl(S3C64XX_GPKDAT);      
  88.                 tmp &= (0xdf);      
  89.                 writel(tmp, S3C64XX_GPKDAT);   
  90.                 printk("turn off!\n");     
  91.                 break;   
  92.       
  93.             case LED_2_OFF:   
  94.                     tmp = readl(S3C64XX_GPKDAT);      
  95.                 tmp |= (0xf0);      
  96.                 writel(tmp, S3C64XX_GPKDAT);  
  97.                 printk("turn on!\n");      
  98.                     break;   
  99.       
  100.             case LED_3_ON:   
  101.                 tmp = readl(S3C64XX_GPKDAT);      
  102.                 tmp &= (0xbf);      
  103.                 writel(tmp, S3C64XX_GPKDAT);   
  104.                 printk("turn off!\n");     
  105.                 break;   
  106.       
  107.             case LED_3_OFF:   
  108.                     tmp = readl(S3C64XX_GPKDAT);      
  109.                 tmp |= (0xf0);      
  110.                 writel(tmp, S3C64XX_GPKDAT);  
  111.                 printk("turn on!\n");      
  112.                     break;   
  113.       
  114.             case LED_4_ON:   
  115.                 tmp = readl(S3C64XX_GPKDAT);      
  116.                 tmp &= (0x7f);      
  117.                 writel(tmp, S3C64XX_GPKDAT);   
  118.                 printk("turn off!\n");     
  119.                 break;   
  120.       
  121.             case LED_4_OFF:   
  122.                     tmp  = readl(S3C64XX_GPKDAT);      
  123.                 tmp |= (0xf0);      
  124.                 writel(tmp, S3C64XX_GPKDAT);  
  125.                 printk("turn on!\n");      
  126.                     break;   
  127.               
  128.       
  129.             default :   
  130.                     break;   
  131.             }   
  132.          return 0;   
  133.     }   
  134.         
  135.     int led_release (struct inode *inode, struct file *filp)   
  136.     {   
  137.         printk("#########release######\n");   
  138.         return 0;   
  139.     }   
  140.         
  141.     struct file_operations led_fops =  
  142.     {   
  143.         .owner = THIS_MODULE,   
  144.         .open = led_open,   
  145.         .read = led_read,   
  146.         .write = led_write,   
  147.         .release = led_release,   
  148.     };   
  149.         
  150.     int __init led_init (void)   
  151.     {     
  152.         int rc;   
  153.         printk ("Test led dev\n");   
  154.         rc = register_chrdev(LED_MAJOR,"led",&led_fops);   
  155.       
  156.         if (rc <0)   
  157.         {   
  158.             printk ("register %s char dev error\n","led");   
  159.             return -1;   
  160.         }   
  161.         printk ("ok!\n");   
  162.         return 0;   
  163.     }   
  164.         
  165.     void __exit led_exit (void)   
  166.     {   
  167.         unregister_chrdev(LED_MAJOR,"led");   
  168.         printk ("module exit\n");   
  169.         return ;   
  170.     }   
  171.         
  172.     module_init(led_init);   
  173.     module_exit(led_exit);   


  174. Makefile


  175. [cpp] view plain copy
  176. print?

  177.     obj-m := led_driver.o   
  178.     KDIR :=/home/workdir/kernel/linux-2.6.38  
  179.     all:   
  180.         make -C $(KDIR) M=$(shell pwd) modules   
  181.     install:   
  182.         cp driver_led.ko /tftpboot/   
  183.     clean:   
  184.         make -C $(KDIR) M=$(shell pwd) clean   




  185. 測試文件


  186. test_led.c

  187. [cpp] view plain copy
  188. print?

  189.     #include <stdio.h>   
  190.     #include <sys/types.h>   
  191.     #include <sys/stat.h>   
  192.     #include <fcntl.h>   
  193.       
  194.       
  195.     #define LED_OFF     0  
  196.     #define LED_ON      1  
  197.     #define LED_1_ON    2  
  198.     #define LED_1_OFF   3  
  199.     #define LED_2_ON    4  
  200.     #define LED_2_OFF   5  
  201.     #define LED_3_ON    6  
  202.     #define LED_3_OFF   7  
  203.     #define LED_4_ON    8  
  204.     #define LED_4_OFF   9  
  205.       
  206.     int main (void)   
  207.     {   
  208.         int  i=0;  
  209.         int  fd;   
  210.         char buf[10]={  
  211.                 LED_ON ,   LED_OFF ,  
  212.                 LED_1_ON,  LED_1_OFF,  
  213.                 LED_2_ON,  LED_2_OFF,  
  214.                 LED_3_ON,  LED_3_OFF,  
  215.                 LED_4_ON,  LED_4_OFF,            
  216.              };   
  217.       
  218.         fd = open("/dev/led",O_RDWR);   
  219.         if (fd < 0)   
  220.         {   
  221.             printf ("Open /dev/led file error\n");   
  222.             return -1;   
  223.         }      
  224.       
  225.         while(i<10)   
  226.         {   
  227.             write(fd,&buf[i],4);   
  228.             sleep(1);   
  229.             i++;  
  230.         }   
  231.         close (fd);   
  232.         return 0;   
  233.         
  234.     }   
復制代碼


上述編譯沒有問題,就可以下到板子測試了。

加載驅(qū)動          insmod  led_driver.ko
創(chuàng)建設(shè)備文件    mknod /dev/led c 243 0  其中243要跟驅(qū)動文件中的設(shè)備號一致

運行測試文件    ./test_led
完成。

------------------------------------------------------------------------------------------
參考資料:
  1. #include <linux/module.h>/*它定義了模塊的 API、類型和宏(MODULE_LICENSE、MODULE_AUTHOR等等),所有的內(nèi)核模塊都必須包含這個頭文件。/
  2. #include <linux/kernel.h>/*使用內(nèi)核信息優(yōu)先級時要包含這個文件,一般在使用printk函數(shù)時使用到優(yōu)先級信息*/
  3. #include <linux/init.h>//頭文件:module_init、module_exit等宏定義。
  4. #include <linux/fs.h>////struct file_operations
  5. #include <asm/irq.h>
  6. #include <mach/regs-gpio.h>// S3C2410 GPIO寄存器定義
  7. #include <mach/hardware.h>// s3c2410_gpio_setpin, s3c2410_gpio_cfgpin等
  8. #include <linux/device.h>//class_create device_create(注意,有些2.6.27以前是的可能是class_device_create,如果出現(xiàn)implicate 錯誤時,看一下這個頭問題里邊是哪一個),udev,自動在/dev下創(chuàng)建設(shè)備節(jié)點
  9. #include <linux/cdev.h>//字符設(shè)備節(jié)點注冊,函數(shù)有cdev_init,cdev_add,cdev_del等早期的辦法是register_chrdev,unregister_chrdev這種方法應(yīng)避免使用。
  10. #define DEVICE_NAME "leds" /* 加載模式后,執(zhí)行”cat /proc/devices”命令看到的設(shè)備名稱 */
  11. #define LED_MAJOR 231 /* 主設(shè)備號 */
  12. /* 應(yīng)用程序執(zhí)行ioctl(fd, cmd, arg)時的第2個參數(shù) */
  13. #define IOCTL_LED_ON 1
  14. #define IOCTL_LED_OFF 0
  15. /* 用來指定LED所用的GPIO引腳 */
  16. static unsigned long led_table [] =
  17. {
  18. S3C2410_GPB5,
  19. S3C2410_GPB6,
  20. S3C2410_GPB7,
  21. S3C2410_GPB8,
  22. };
  23. /* 用來指定GPIO引腳的功能:輸出 */
  24. static unsigned int led_cfg_table [] =
  25. {
  26. S3C2410_GPB5_OUTP,
  27. S3C2410_GPB6_OUTP,
  28. S3C2410_GPB7_OUTP,
  29. S3C2410_GPB8_OUTP,
  30. };
  31. struct leds_type
  32. {
  33. struct cdev cdev;
  34. };
  35. struct leds_type *my_leds_dev;
  36. /* 應(yīng)用程序?qū)υO(shè)備文件/dev/EmbedSky-leds執(zhí)行open(...)時,
  37. * 就會調(diào)用EmbedSky_leds_open函數(shù)
  38. */
  39. static int EmbedSky_leds_open(struct inode *inode, struct file *file)
  40. {
  41. int i;
  42. for (i = 0; i < 4; i++)
  43. {
  44. // 設(shè)置GPIO引腳的功能:本驅(qū)動中LED所涉及的GPIO引腳設(shè)為輸出功能
  45. s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
  46. }
  47. return 0;
  48. }
  49. /* 應(yīng)用程序?qū)υO(shè)備文件/dev/EmbedSky-leds執(zhí)行ioclt(...)時,
  50. * 就會調(diào)用EmbedSky_leds_ioctl函數(shù)
  51. */
  52. static int EmbedSky_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  53. {
  54. if (arg > 4)
  55. {
  56. return -EINVAL;
  57. }
  58. switch(cmd)
  59. {
  60. case IOCTL_LED_ON:
  61. // 設(shè)置指定引腳的輸出電平為0
  62. s3c2410_gpio_setpin(led_table[arg], 0);
  63. return 0;
  64. case IOCTL_LED_OFF:
  65. // 設(shè)置指定引腳的輸出電平為1
  66. s3c2410_gpio_setpin(led_table[arg], 1);
  67. return 0;
  68. default:
  69. return -EINVAL;
  70. }
  71. }
  72. /* 這個結(jié)構(gòu)是字符設(shè)備驅(qū)動程序的核心
  73. * 當應(yīng)用程序操作設(shè)備文件時所調(diào)用的open、read、write等函數(shù),
  74. * 最終會調(diào)用這個結(jié)構(gòu)中指定的對應(yīng)函數(shù)
  75. */
  76. static struct file_operations EmbedSky_leds_fops =
  77. {
  78. .owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創(chuàng)建的__this_module變量 */
  79. .open = EmbedSky_leds_open,
  80. .ioctl = EmbedSky_leds_ioctl,
  81. };
  82. static char __initdata banner[] = "TQ2440/SKY2440 LEDS, (c) 2008,2009 www.embedsky.net/n";
  83. static struct class *led_class;
  84. /*
  85. * 執(zhí)行“insmod EmbedSky_leds.ko”命令時就會調(diào)用這個函數(shù)
  86. */
  87. static int __init EmbedSky_leds_init(void)
  88. {
  89. int ret;
  90. dev_t devno=MKDEV(LED_MAJOR,0);
  91. printk("init led/n");
  92. printk(banner);
  93. /* 注冊字符設(shè)備驅(qū)動程序
  94. * 參數(shù)為主設(shè)備號、設(shè)備名字、file_operations結(jié)構(gòu);
  95. * 這樣,主設(shè)備號就和具體的file_operations結(jié)構(gòu)聯(lián)系起來了,
  96. * 操作主設(shè)備為LED_MAJOR的設(shè)備文件時,就會調(diào)用EmbedSky_leds_fops中的相關(guān)成員函數(shù)
  97. ret = register_chrdev_region(devno, 1,DEVICE_NAME);//獲得設(shè)備編號
  98. my_leds_dev=kmalloc(sizeof(struct leds_type),GFP_KERNEL);
  99. /*這個必須有不然會在加載模塊時出現(xiàn)Unable to handle kernel NULL pointer dereference at virtual addres 00000000 錯誤,這是由于在這里my_leds_dev僅僅是個指針,沒有相應(yīng)大小的分配內(nèi)存,所以使用時會出錯,,,尋找這個錯誤是比較麻煩的*/
  100. if(!my_leds_dev)
  101. {
  102. ret=-ENOMEM;
  103. goto fail_malloc;
  104. }
  105. memset(my_leds_dev,0,sizeof(struct leds_type));
  106. cdev_init(&(my_leds_dev->cdev),&EmbedSky_leds_fops);
  107. ret=cdev_add(&(my_leds_dev->cdev),devno,1);
  108. /*注意:與早期的設(shè)備注冊方法不同,早期的直接register_chrdev()就可以,*/
  109. if(ret)printk(KERN_NOTICE"ERROR %d",ret);
  110. //注冊一個類,使mdev可以在"/dev/"目錄下面建立設(shè)備節(jié)點
  111. led_class = class_create(THIS_MODULE, DEVICE_NAME);
  112. if(IS_ERR(led_class))
  113. {
  114. printk("Err: failed in EmbedSky-leds class. /n");
  115. return -1;
  116. }
  117. //創(chuàng)建一個設(shè)備節(jié)點,節(jié)點名為DEVICE_NAME
  118. device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME);
  119. printk(DEVICE_NAME " initialized/n");
  120. return 0;
  121. fail_malloc: unregister_chrdev_region(devno,1);
  122. return ret;
  123. }
  124. /*
  125. * 執(zhí)行”rmmod EmbedSky_leds.ko”命令時就會調(diào)用這個函數(shù)
  126. */
  127. static void __exit EmbedSky_leds_exit(void)
  128. {
  129. /* 卸載驅(qū)動程序 */
  130. unregister_chrdev(LED_MAJOR, DEVICE_NAME);
  131. device_destroy(led_class, MKDEV(LED_MAJOR, 0)); //刪掉設(shè)備節(jié)點
  132. class_destroy(led_class); //注銷類
  133. }
  134. /* 這兩行指定驅(qū)動程序的初始化函數(shù)和卸載函數(shù) */
  135. module_init(EmbedSky_leds_init);
  136. module_exit(EmbedSky_leds_exit);
  137. /* 描述驅(qū)動程序的一些信息,不是必須的 */
  138. MODULE_AUTHOR("http://www.embedsky.net"); // 驅(qū)動程序的作者
  139. MODULE_DESCRIPTION("TQ2440/SKY2440 LED Driver"); // 一些描述信息
  140. MODULE_LICENSE("GPL"); // 遵循的協(xié)議
  141. 上面代碼中,led_table數(shù)組相當于對應(yīng)了GPB的四個IO口的索引,通過這四個值,對這四個IO口進行相關(guān)操作。例如:
  142. S3C2410_GPB5 = S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
  143. = S3C2410_GPIO_BANKB + 5
  144. = 32*1 + 5
  145. 在s3c2410_gpio_setpin(S3C2410_GPB5,0)中,該函數(shù)首先通過S3C2410_GPB5獲得GPB的虛擬地址和偏移地址,再對GPB5的GPBDAT寄存器進行操作,具體
  146. void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
  147. {
  148. void __iomem *base = S3C2410_GPIO_BASE(pin);
  149. unsigned long offs = S3C2410_GPIO_OFFSET(pin);
  150. unsigned long flags;
  151. unsigned long dat;
  152. local_irq_save(flags);
  153. dat = __raw_readl(base + 0x04);//讀取GPIO的DAT數(shù)據(jù)到dat
  154. dat &= ~(1 << offs); //先將要設(shè)置的IO口拉低
  155. dat |= to << offs; //再將形參的to值賦給dat
  156. __raw_writel(dat, base + 0x04);//最后將DAT值寫進GPIO的DAT
  157. local_irq_restore(flags);
  158. }

  159. 上面的 函數(shù)調(diào)用了兩個子函數(shù),具體定義如下

  160. #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
  161. #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
  162. 其中S3C24XX_VA_GPIO定義如下:

  163. #define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000)
  164. #define S3C2410_ADDR(x) (0xF0000000 + (x))
  165. 這里S3C2410_ADDR的基地址為0xF0000000(??),也即2440所有寄存器的虛擬地址的基地址。0x00E00000表示2440的GPIO的偏移地址,也就是說其GPIO的虛擬地址首地址為0xF0E00000。

  166. 再看看S3C2410_GPIO_BASE(pin)的定義,我們不仿把S3C2410_GPB5的值放進去計算,可以得到(S3C2410_GPB5&~31)=32。其目的就是去掉GPB的偏移值,然后再右移一位,和GPIO的虛擬地址首地址相加。因此,S3C2410_GPIO_BASE(pin)只代表了對應(yīng)GPIO組的虛擬地址,如GPB的虛擬地址為10000(B)+0xF0E00000=0xF0E00010。依此類推,可以得到所有GPIO的偏移地址,具體如下表:


  167. BANK
  168. (pin&~31)
  169. (pin&~31)>>1
  170. S3C2410_GPIO_BASE(pin)

  171. GPA
  172. 32*0
  173. 0000,0000
  174. 0x00
  175. 0xF0E00000

  176. GPB
  177. 32*1
  178. 0010,0000
  179. 0x10
  180. 0xF0E00010

  181. GPC
  182. 32*2
  183. 0100,0000
  184. 0x20
  185. 0xF0E00020

  186. GPD
  187. 32*3
  188. 0110,0000
  189. 0x30
  190. 0xF0E00030

  191. GPE
  192. 32*4
  193. 1000,0000
  194. 0x40
  195. 0xF0E00040

  196. GPF
  197. 32*5
  198. 1010,0000
  199. 0x50
  200. 0xF0E00050

  201. GPG
  202. 32*6
  203. 1100,0000
  204. 0x60
  205. 0xF0E00060

  206. GPH
  207. 32*7
  208. 1110,0000
  209. 0x70
  210. 0xF0E00070


  211. S3C2410_GPIO_OFFSET用于獲得具體GPIO的偏移地址。如GPB5,則S3C2410_GPIO_OFFSET(pin) = (pin)&31 = (32*1 + 5) & 31 = 5。有了*base和off,就可以操作具體的寄存器了。
  212. 函數(shù)s3c2410_gpio_cfgpin()用于配置GPCON寄存器。具體代碼
  213. void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
  214. {
  215. void __iomem *base = S3C2410_GPIO_BASE(pin);
  216. unsigned long mask;
  217. unsigned long con;
  218. unsigned long flags;
  219. if (pin < S3C2410_GPIO_BANKB)
  220. {
  221. mask = 1 << S3C2410_GPIO_OFFSET(pin);//GPA的寄存器只占一位
  222. }
  223. else
  224. {
  225. mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;//非GPA的寄存器占兩位
  226. }
  227. local_irq_save(flags);
  228. con = __raw_readl(base + 0x00);//先保留GPCON的值
  229. con &= ~mask; //再將要設(shè)置的管腳的CON值清零
  230. con |= function; //然后將形參傳進來的配置賦給CON
  231. __raw_writel(con, base + 0x00); //最后將CON值寫進GPCON寄存器
  232. local_irq_restore(flags);
  233. }
  234. 上面的LED驅(qū)動程序中,led_cfg_table數(shù)組給出了GPB相應(yīng)管腳的屬性設(shè)置,調(diào)用上面的函數(shù)后即設(shè)置為Output。

  235. 到此為止,整個S3C2440的IO口操作,應(yīng)該就一目了然了

  236. #define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
  237. # define __chk_io_ptr(x) (void)0

  238. __chk_io_ptr()是編譯器為了更細致地檢查參數(shù)的屬性,用于調(diào)試,正常編譯時沒有作用。
  239. volatile為了防止Compiler優(yōu)化。

  240. 內(nèi)核中,對所有的地址都是通過虛擬地址進行訪問的.因此,不能直接訪問0x56000010的物理地址,如果要對0x56000010的物理地址進行訪問(一般是外設(shè)寄存器),那么需要把0x56000010的物理地址映射為虛擬地址,然后對該虛擬地址進行訪問就是對實際的物理地址進行訪問了。
  241. 需要注意的是:在進行映射時,該虛擬地址需要disable cache和Write Buffer, 否則就是加了volatile也是沒有用的

  242. 這個IOREMAP的實現(xiàn)過程中
  243. /*
  244. * figure out the physical address offset in a page size
  245. * PAGE_MASK = (1 << 10)
  246. */
  247. offset = phys_addr &~ PAGE_MASK;
  248. /*
  249. * figure out physical address with page align
  250. */
  251. phys_addrs &= PAGE_MASK;
  252. /*
  253. * get the real size with page align
  254. */
  255. size = PAGE_ALIGN(last_addr) - phys_addrs;
  256. 下面是通過vmlist中查找以size大小的空閑塊,所以從這里可以看出,已經(jīng)做過了頁的對齊,只以映射的大小

  257. 雜項設(shè)備(misc device)

  258. 雜項設(shè)備也是在嵌入式系統(tǒng)中用得比較多的一種設(shè)備驅(qū)動。在 Linux 內(nèi)核的include/linux目錄下有Miscdevice.h文件,要把自己定義的misc device從設(shè)備定義在這里。其實是因為這些字符設(shè)備不符合預先確定的字符設(shè)備范疇,所有這些設(shè)備采用主編號10 ,一起歸于misc device,其實misc_register就是用主標號10調(diào)用register_chrdev()的。

  259. 也就是說,misc設(shè)備其實也就是特殊的字符設(shè)備。

  260. 字符設(shè)備(char device)

  261. 使用register_chrdev(LED_MAJOR,DEVICE_NAME,&dev_fops)注冊字符設(shè)備驅(qū)動程序時,如果有多個設(shè)備使用該函數(shù)注冊驅(qū)動程序,LED_MAJOR不能相同,否則幾個設(shè)備都無法注冊(我已驗證)。如果模塊使用該方式注冊并且 LED_MAJOR為0(自動分配主設(shè)備號 ),使用insmod命令加載模塊時會在終端顯示分配的主設(shè)備號和次設(shè)備號,在/dev目錄下建立該節(jié)點,比如設(shè)備leds,如果加載該模塊時分配的主設(shè)備號和次設(shè)備號為253和0,則建立節(jié)點:mknod leds c 253 0。使用register_chrdev (LED_MAJOR,DEVICE_NAME,&dev_fops)注冊字符設(shè)備驅(qū)動程序時都要手動建立節(jié)點 ,否則在應(yīng)用程序無法打開該設(shè)備。

  262. __raw_readl和__raw_writel

  263. Linux對I/O的操作都定義在asm/io.h中,相應(yīng)的在arm平臺下,就在asm-arm/io.h中。

  264. #define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))

  265. #define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))

  266. #define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))在include/linux/compiler.h中:

  267. #ifdef __CHECKER__……
  268. extern void __chk_io_ptr(void __iomem *);
  269. #else……
  270. # define __chk_io_ptr(x) (void)0……
  271. #endif

  272. __raw_readl(a)展開是:((void)0, *(volatile unsigned int _force *)(a))。在定義了__CHECKER__的時候先調(diào)用__chk_io_ptr檢查該地址,否則__chk_io_ptr什么也不做,*(volatile unsigned int _force *)(a)就是返回地址為a處的值。(void)xx的做法有時候是有用的,例如編譯器打開了檢查未使用的參數(shù)的時候需要將沒有用到的參數(shù)這么弄一下才能編譯通過。

  273. CPU對I/O的物理地址的編程方式有兩種:一種是I/O映射,一種是內(nèi)存映射。__raw_readl和__raw_writel等是原始的操作I/O的方法,由此派生出來的操作方法有:inb、outb、_memcpy_fromio、readb、writeb、ioread8、iowrite8等。
復制代碼


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 午夜影院网站 | 亚洲网站在线观看 | 色免费视频 | 成人免费大片黄在线播放 | 激情六月天 | 国产色视频网站 | 米奇7777狠狠狠狠视频 | 在线观看免费av网 | 亚洲a在线观看 | 亚洲天堂中文字幕 | 国产免费麻豆视频 | 91精品国产乱码久久久久久久久 | 亚洲一区二区在线播放 | 日韩在线一区二区三区 | 国产视频中文字幕在线观看 | 龙珠z国语版在线观看 | 免费在线观看黄色av | 国产在线一区二区三区 | 成年人在线观看视频 | 亚洲精品久久久蜜桃网站 | 亚洲精品久久久久久久久久吃药 | 日韩精品av一区二区三区 | 人人种亚洲 | 亚洲精品一区二区 | 国产在线一区二区三区 | 免费在线看黄视频 | 亚洲性人人天天夜夜摸 | 毛片一区二区三区 | 91久久夜色 | 亚洲一区二区三区免费 | 亚洲视频免费在线观看 | 久草热在线| 国产乱码精品一品二品 | 中文字幕97 | 国产亚洲一区二区三区在线 | www.天天干.com| 天天操天天摸天天爽 | 成人伊人 | 欧美精品久久久久 | 精品伊人 | 亚洲激情专区 |