3027|2

121

帖子

2642

TA的资源

纯净的硅(中级)

楼主
 

Mini2440 按键驱动程序详解 [复制链接]



  1. /*mini2440_buttons_my.c*/
  2. /*后面加了_my*/
  3. /*按键驱动程序*/
  4. /*mini2440所用到的按键资源*/
  5. /**************************************************/
  6. /* 按键          对应的IO寄存器     对应的中断引脚*/
  7. /* K1             GPG0                 EINT8      */
  8. /* K2             GPG3                 EINT11     */
  9. /* K3             GPG5                 EINT13     */
  10. /* K4             GPG6                 EINT14     */
  11. /* K5             GPG7                 EINT15     */
  12. /* K6             GPG11                EINT19     */
  13. /**************************************************/
  14. /*要搞清楚谁是输入*/
  15. /*在这里,按键控制对应的中断引脚,从而控制对应的IO寄存器*/
  16. /*相当于信息从外面输入*/
  17. /*我们要做的是根据对应的输入信息,来采取相应的响应动作*/
  18. /*这就达到了中断响应的目的*/
  19. /*其核心就是要检测*/
  20. /*那么,该如何去检测呢?*/
  21. /*通过什么来检测呢?*/
  22. /*如何得知一个设备究竟用到哪些资源呢?*/
  23. /*这是个非常重要的问题*/
  24. /*我想应该看具体的电路原理图*/
  25. /*只有看图,才能了解具体的电路连接情况*/
  26. /*从而得知设备所需的硬件资源*/
  27. /*厂商的原理图通常给的都比较详细*/
  28. /*引用的头文件*/
  29. #include /*模块有关的*/
  30. #include /*内核有关的*/
  31. #include /*文件系统有关的*/
  32. #include /*init*/
  33. #include /*delay*/
  34. #include /*poll*/
  35. #include /*中断*/
  36. #include /*linux中断*/
  37. #include /*uaccess*/
  38. #include /*寄存器设置*/
  39. #include /*hardware*/
  40. /*定义宏*/
  41. #define BUTTON_MAJOR 221 /*主设备号,本来是232,我改为221*/
  42. #define DEVICE_NAME "buttons_my" /*设备名,本来是buttons,我加上了_my*/
  43. /*定义按钮中断的描述结构体*/
  44. /*由它把按钮中断的信息综合起来*/
  45. /*各个成员表示什么意思?*/
  46. struct button_irq_desc
  47. {
  48. int irq; /*中断号*/
  49.    /*中断号唯一表示一个中断*/
  50. int pin; /*中断控制的寄存器*/
  51.     /*该寄存器的值由中断引脚设置*/
  52.      /*我们希望从该寄存器读出控制信息*/
  53. int pin_setting; /*中断的引脚*/
  54.      /*该引脚的电平由按键来控制*/
  55.       /*从而最终我们由按键控制了寄存器的值*/
  56. int number; /*编号*/
  57. char *name; /*名称*/
  58. };
  59. /*指定6个按键的信息*/
  60. static struct button_irq_desc button_irqs [] =
  61. {
  62. {IRQ_EINT8,S3C2410_GPG0,S3C2410_GPG0_EINT8,0,"KEY1"}, /*K1*/
  63. {IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,1,"KEY2"}, /*K2*/
  64. {IRQ_EINT13,S3C2410_GPG5,S3C2410_GPG5_EINT13,2,"KEY3"}, /*K3*/
  65. {IRQ_EINT14,S3C2410_GPG6,S3C2410_GPG6_EINT14,3,"KEY4"}, /*K4*/
  66. {IRQ_EINT15,S3C2410_GPG7,S3C2410_GPG7_EINT15,4,"KEY5"}, /*K5*/
  67. {IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,5,"KEY6"}, /*K6*/
  68. }
  69. /*这样,资源就组织起来了*/
  70. /*事实上,在这里我们不仅组织起了硬件资源*/
  71. /*我们也把一定的软件资源也糅合进去了*/
  72. /*像中断号*/

  73. /*key_values数组*/
  74. /*存放各个按键在发生中断情况下的值*/
  75. /*volatile是什么意思呢?*/
  76. /*这个数组是我们存放按键操作结果的,因此非常重要*/
  77. static volatile int key_values [] = {0,0,0,0,0,0};

  78. /*宏DECLARE_WAIT_QUEUE_HEAD(),是干什么的呢?*/
  79. /*该宏应该是创建了一个等待队列*/
  80. /*等待队列,是进程调度的一种重要方法*/
  81. /*等待队列也很有意思,button_waitq,表示按键等待的队列*/
  82. /*就是说,按键一按下,就会激活其等待队列里的进程,来做相应的处理*/
  83. /*因此,按键的等待队列,或者说中断所设置的等待队列,*/
  84. /*是中断处理中非常重要的资源,它大大扩展了中断处理的能力*/

  85. static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*button_waitq是什么呢?*/
  86.        /*应该是等待队列的名称*/

  87. /*key_values数组中是否有数据的标志,0表示无数据可读,1表示有数据可读*/
  88. static volatile int ev_press = 0; /*初始为0*/

  89. /*中断服务程序buttons_interrupt()的申明*/
  90. /*即当检测到有中断时,就会执行该中断服务程序*/
  91. /*那么如何检测到有中断发生呢?*/
  92. /*并且中断发生了,知道发生了什么样的中断呢?*/
  93. /*中断有很多种,该中断服务程序究竟该服务于哪一个中断呢?*/
  94. /*显然,要把中断号与中断服务程序联结起来,构成一个整体*/
  95. /*这个工作可以在open函数里做*/
  96. /*参数irq---中断号*/
  97. /*中断服务程序应该是与中断号一一对应的*/
  98. /*对应于某个中断号的中断一发生,就会调用该中断号对应的服务程序*/
  99. /*那么,检测中断的发生,就成了先决条件*/
  100. /*参数dev_id ---具体是哪一个按钮*/
  101. static irqreturn_t buttons_interrupt(int irq,void *dev_id);

  102. /*mini2440_buttons_open()函数申明*/
  103. /*驱动函数open调用的具体函数*/
  104. /*由open函数具体实现硬件的初始化工作*/
  105. /*以及软件的初始化工作*/
  106. /*为我们的键盘设备的运行创造好环境*/
  107. static int mini2440_buttons_open(struct inode *inode,struct file *file);

  108. /*mini2440_buttons_close()函数的申明*/
  109. /*release调用的具体函数*/
  110. /*设备软件环境的拆卸*/
  111. /*具体就是中断的释放工作*/
  112. /*因为中断资源,也是系统宝贵的资源,所以不用的时候,要释放*/
  113. static int mini2440_buttons_close(struct inode *inode,struct file *file);

  114. /*mini2440_buttons_read()函数的申明*/
  115. /*read调用的具体函数*/
  116. /*由它读取键盘输入的结果*/
  117. /*实质上就是读取key_values数组的值*/
  118. /*它完成了键盘作为输入设备的核心功能*/
  119. /*数组是否可读,要根据标志位ev_press来判断*/
  120. /*如果数组可读,则读取数据到用户buffer中*/
  121. /*如果数组不可读,则进程进入等待队列,等待到数组可读为止*/
  122. /*等待队列机制,是中断管理中常用到的机制*/
  123. /*因为有些进程经常需要等待某一事件的发生*/
  124. static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
  125.         /*注意__user,指的是用户空间*/
  126.         /*即要把键盘的输入结果读取到用户空间去*/

  127. /*mini2440_buttons_poll()函数的申明*/
  128. /*poll调用的具体函数*/
  129. /*poll实质上是select的调用函数*/
  130. /*如果有按键数据,则select会立刻返回*/
  131. /*如果没有按键数据,则等待*/
  132. /*实质上这是键盘等待输入的机制*/
  133. static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait);


  134. /*file_operations结构体*/
  135. /*驱动函数的设置*/
  136. /*分别将前面的驱动函数设置进来*/
  137. static struct file_operations mini2440_buttons_fops =
  138. {
  139. .owner = THIS_MODULE,
  140. .open = mini2440_buttons_open, /*open()*/
  141. .release = mini2440_buttons_close, /*release()*/
  142. .read = mini2440_buttons_read, /*read()*/
  143. .poll = mini2440_buttons_poll /*poll()*/
  144. };

  145. /*mini2440_buttons_init()函数的申明*/
  146. /*module_init调用的具体函数*/
  147. /*模块创建时的初始化函数*/
  148. /*主要做的工作是注册设备和创建设备*/
  149. /*而具体的硬件初始化工作,它可以不做*/
  150. /*而把它留给fops里的函数来做*/
  151. static int __init mini2440_buttons_init(void);

  152. /*mini2440_buttons_exit()函数的申明*/
  153. /*模块卸载时的扫尾工作*/
  154. /*主要是设备的卸载工作*/
  155. static void __exit mini2440_buttons_exit(void);

  156. /*模块创建时的入口点*/
  157. module_init(mini2440_buttons_init);

  158. /*模块卸载时的入口点*/
  159. module_exit(mini2440_buttons_exit);
  160. /*驱动程序的一些信息*/
  161. MODULE_AUTHOR("http://www.arm9.net"); /*驱动程序的作者*/
  162. MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver"); /*描述信息*/
  163. MODULE_LICENSE("GPL"); /*遵循的协议*/

复制代码

最新回复

不错,项一下!  详情 回复 发表于 2009-9-7 11:38
点赞 关注

回复
举报

121

帖子

2642

TA的资源

纯净的硅(中级)

沙发
 
  1. /********************************************************************/
  2. /*********************下面是前面申明函数的实现***********************/
  3. /********************************************************************/


  4. /**********************mini2440_buttons_init()***********************/

  5. static int __init mini2440_buttons_init(void)
  6. {
  7. int ret; /*设备注册的返回值*/

  8. /*注册设备驱动程序*/
  9. /*设备号,设备名,和驱动函数*/

  10. ret = register_chrdev(BUTTON_MAJOR,DEVICE_NAME,&mini2440_buttons_fops);

  11. /*对注册失败的处理*/

  12. if(ret < 0)
  13. {
  14. printk(DEVICE_NAME " can't register major number\n");
  15. return ret;
  16. }

  17. /*创建设备*/
  18. /*devfs_mk_cdev()函数是内核态的设备创建函数*/
  19. /*而mknod是用户态的设备创建函数*/

  20. devfs_mk_cdev(MKDEV(BUTTON_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);

  21. printk(DEVICE_NAME " initialized\n");

  22. return 0;
  23. }


  24. /******************mini2440_buttons_exit()****************************/

  25. static void __exit mini2440_buttons_exit(void)
  26. {
  27. /*移除设备*/

  28. devfs_remove(DEVICE_NAME);

  29. /*注消设备驱动*/

  30. unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME);
  31. }


  32. /*****************mini2440_buttons_open()******************************/

  33. static int mini2440_buttons_open(struct inode *inode,struct file *file)
  34. {
  35. int i; /*循环变量,因为有6个按钮*/

  36. int err; /*中断注册函数的返回值*/

  37. /*对每个按钮分别处理,用for循环来做*/
  38. /*具体地是要联结寄存器和相应的引脚*/
  39. /*联结中断号和相应的中断服务程序*/
  40. /*这一步类似于前面所说的驱动的注册*/
  41. /*我们可以成功称作中断的注册*/

  42. for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
  43. {
  44. /*寄存器与中断引脚的联结*/

  45. s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);

  46. /*中断的注册*/
  47. /*request_irq()函数*/
  48. /*要注意其输入参数*/
  49. /*&button_irqs[i]是该中断享有的资源*/
  50. /*会被传入buttons_interrupt,进行处理*/

  51. err = request_irq(button_irqs[i].irq,buttons_interrupt,NULL,button_irqs[i].name,(void *)&button_irqs[i]);

  52. /*中断类型的设置*/
  53. /*set_irq_type()函数*/
  54. /*IRQT_BOTHEDGE的中断类型代表什么样的中断呢?*/

  55. /*有几个非常重要的问题*/
  56. /*中断注册后,并设置好其中断类型之后,当有中断发生时,*/
  57. /*即按下某个按钮时,系统能够自动检测到有中断发生吗?*/
  58. /*检测到有中断发生,它能够自动辨别是几号中断吗?*/
  59. /*知道了是几号中断,那么它能自动调用其中断服务程序吗?*/
  60. /*对这几个问题的解答,够成了linux系统中断处理机制的核心*/

  61. set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);

  62. /*注册失败的处理*/

  63. if(err)
  64. break; /*跳出循环*/
  65. }

  66. /*若有一个按钮中断注册失败*/
  67. /*则还需把前面注册成功的中断给拆了*/

  68. if(err)
  69. {
  70. i--; /*回到前面一个按钮的处理*/

  71. for(;i >=0; i--) /*依此拆除*/
  72. {
  73. /*使中断不起作用*/

  74. disable_irq(button_irqs[i].irq);

  75. /*释放中断资源*/

  76. free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
  77. }

  78. return -EBUSY; /*中断注册没成功的最终的返回值*/
  79. }

  80. return 0; /*正常返回*/
  81. }


  82. /**************************buttons_interrupt()*****************************/
  83. /*此中断服务程序,在每中断一次,就要对key_values数组设一下值*/
  84. /*并对数组可读标志位ev_press设一下值*/
  85. /*并唤醒在等待队列里的进程*/
  86. /*这是中断处理经常要做的事情*/
  87. /*在这里,等待队列button_waitq里经常等待的进程是数组的读取进程*/
  88. /*就是说,读取进程在没有读到数据的时候就一直在等待,等待按键的输入*/
  89. /*读取进程在等待,并不代表所有进程在等待,其它进程该干啥干啥去*/

  90. static irqreturn_t buttons_interrupt(int irq,void *dev_id)
  91. {
  92. /*button_irq_desc结构体变量*/
  93. /*对传入的资源进行处理*/

  94. struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

  95. /*获取寄存器的值*/
  96. /*这一步至关重要*/
  97. /*s3c2410_gpio_getpin()函数直接获取寄存器的值*/

  98. /*要注意,按一下按钮,会发生两次中断*/

  99. /*即按下是一次中断,放开又是一次中断*/


  100. int up = s3c2410_gpio_getpin(button_irqs->pin);

  101. /*通过电路原理图,可以知道没按下的时候,中断引脚应该是高电平*/
  102. /*从而寄存器的值应该是1*/
  103. /*变量取up也是有意义的,表示默认状态是弹起的状态*/
  104. /*当按下按钮的状态下,寄存器的值就应该是0*/

  105. /*下面对up的值进行处理*/
  106. /*即是要把数据经过一定的变换存入key_values数组中*/

  107. if(up) /*如果是弹起的状态*/
  108. /*那么就要在key_values数组的相应位存入很大的一个值*/
  109. /*同时又要能从值里辨别出是哪个按键*/

  110. key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
  111. /*比如K1键开启的状态下,key_values[0]被置为(0+1)+0x80,即为129*/

  112. else /*如果按键是闭合的状态*/
  113. /*那么就要在key_values数组的相应位存入一个很小的数*/
  114. /*同时又要能从值中辨别出是哪个键*/

  115. key_values[button_irqs->number] = (button_irqs->number + 1);
  116. /*比如K1键闭合,则key_values[0]被置为(0+1),即为1*/

  117. /*对数组可读标志位进行设置*/

  118. ev_press = 1; /*表示数组已经可读了*/

  119. /*唤醒休眠的进程?*/
  120. /*button_waitq队列里存放有相应的处理进程*/
  121. /*如读取数组的值的进程*/
  122. /*要注意wake_up_interruptible()这些函数的用法*/

  123. wake_up_interruptible(&button_waitq);

  124. /*返回*/

  125. return IRQ_RETVAL(IRQ_HANDLED); /*?*/
  126. }

  127. /**********************mini2440_buttons_close()*****************************/

  128. static int mini2440_buttons_close(struct inode *inode,struct file *file)
  129. {
  130. int i; /*循环变量,要操作好几个按键*/

  131. /*for循环,对各个按键依此释放中断*/

  132. for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
  133. {
  134. /*使中断失效*/

  135. disable_irq(button_irqs[i].irq);

  136. /*释放资源*/

  137. free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
  138. }

  139. /*返回*/

  140. return 0;
  141. }


  142. /**********************mini2440_buttons_read()***************************/

  143. /*要注意,该read函数,只读取一次中断的值,而不是连续地读入*/
  144. /*要做到连续地读入,则需要做一个循环,不断地调用该read函数,但那不是驱动程序里该做的事情*/

  145. static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
  146. {
  147. unsigned long err; /*copy_to_user()函数的返回值*/

  148. /*如果key_values 数组里没有值,则会此进程会休眠*/
  149. /*一直到中断来临之后,中断服务程序会唤醒此休眠进程从而继续读取值*/
  150. /*key_values数组里有没有值,是靠ev_press标志位来判断的*/
  151. /*有值,就是1,无值,就是0*/

  152. /*进程等待队列的机制,是进程调度的一种方法*/

  153. if(!ev_press) /*标志位为0,即无数据时*/
  154. {
  155. if(filp->f_flags & O_NONBLOCK) /*??*/
  156. return -EAGAIN;
  157. else /*进程休眠,放进button_waitq等待队列*/
  158. /*这里,把ev_press标志位设成了休眠进程的标志位了?*/
  159. /*这是为了便于利用poll_wait函数*/
  160. /*也就是利于select函数*/
  161. wait_event_interruptible(button_waitq,ev_press);
  162. /*在中断处理函数中,此进程会被唤醒*/
  163. /*唤醒前,ev_press 已被置1了*/
  164. /*唤醒后的执行点从这里开始*/
  165. }

  166. /*下面就是标志位为1,即有数据可读的的处理情况*/

  167. /*那就开始往用户空间读数据呗*/

  168. err = copy_to_user(buff,(const void *)key_values,min(sizeof(key_values),count));
  169. /*copy_to_user()函数的使用*/

  170. /*对key_values数组清零*/

  171. memset((void *)key_values,0,sizeof(key_values));

  172. /*对标志位置0*/
  173. /*表示读取过了*/

  174. ev_press = 0;

  175. /*对err的处理*/

  176. if(err) /*读取错误*/
  177. return -EFAULT;
  178. else /*读取正确*/
  179. /*则返回读取到的字节数*/
  180. return min(sizeof(key_values),count);
  181. }


  182. /************************mini2440_buttons_poll()***********************/

  183. static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait)
  184. {
  185. unsigned int mask = 0; /* */

  186. /*poll_wait()函数*/
  187. /*会监测进程队列button_waitq里的进程*/
  188. /*例如,如果mini2440_button_read所在的进程的标志位ev_press置为1了*/
  189. /*那么就不会再等待了*/
  190. /*这实质上就是select函数的运行机制*/

  191. poll_wait(file,&button_waitq,wait);

  192. if(ev_press)
  193. mask |= POLLIN | POLLRDNORM; /*??*/

  194. return mask;
  195. }

  196. ==================================================================================

  197. 下面是测试代码:
  198. /*按键测试程序*/

  199. #include /*标准输入输出头文件*/

  200. #include /*标准库*/

  201. #include /*一些宏的定义在这里*/

  202. #include /*设备的控制*/

  203. #include /*定义了一些类型*/

  204. #include /*状态*/

  205. #include /*文件控制*/

  206. #include /*选择?*/

  207. #include /*时间方面的函数*/

  208. #include /*有关错误方面的宏*/

  209. /*主函数入口*/

  210. int main(void)
  211. {
  212. int i; /*键盘输出时用到的循环变量*/

  213. int buttons_fd; /*buttons设备号*/

  214. int key_value[4]; /*四个按键的取值*/

  215. /*打开键盘设备文件*/

  216. buttons_fd = open("/dev/buttons",0); /*以0方式打开*/

  217. /*打开出错处理*/

  218. if(buttons_fd < 0) /*打开出错就会返回一个负值*/
  219. {
  220. perror("open device buttons"); /*perror函数?*/

  221. exit(1); /*返回1*/
  222. }

  223. /*for无限循环,等待用户输入*/
  224. /*这是很典型的程序执行方式*/

  225. for(;;)
  226. {
  227. fd_set rds; /*fd_set是types.h中定义的类型,实质上是int型*/
  228. /*rds用来存储设备号*/

  229. int ret; /*for循环内定义的局部变量ret*/

  230. FD_ZERO(&rds); /*rds初始化*/
  231. /*FD_ZERO是哪里定义的呢?*/

  232. FD_SET(buttons_fd,&rds); /*将buttons设备号赋给rds*/
  233. /*FD_SET是哪里定义的呢?*/

  234. /*使用系统调用select检查是否能够从/dev/buttons设备读取数据*/
  235. /*select函数是干什么的呢?*/

  236. ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
  237. /*返回值ret*/
  238. /*返回值的具体意义是什么呢?*/

  239. /*对ret的处理*/

  240. if(ret < 0) /*当ret小于0*/
  241. {
  242. perror("select");
  243. exit(1);
  244. }

  245. if(ret == 0) /*当ret等于0*/
  246. {
  247. printf("Timeout.\n");
  248. }
  249. else /*能够读到数据*/
  250. if(FD_ISSET(buttons_fd,&rds)) /*??*/
  251. {
  252. /*读取键盘驱动发出的数据*/
  253. /*key_value和键盘驱动中定义一致*/

  254. int ret = read(buttons_fd,key_value,sizeof(key_value)); /*注意此处的ret和前面的ret有何不同*/
  255. /*注意键盘设备读取的特点*/

  256. /*对ret的处理*/
  257. if(ret != sizeof(key_value)) /*没有接收够*/
  258. {
  259. if(errno != EAGAIN) /*???*/
  260. perror("read buttons\n");
  261. continue;
  262. }
  263. else /*正确接收,则打印到标准终端*/
  264. {
  265. for(i = 0;i < 4;i++) /*最开始定义的循环变量i*/
  266. printf("K%d %s, key value = 0x%02x\n",i,(key_value[i] & 0x80) ? "released" : key_value[i] ? "pressed down" : "",key_value[i]);
  267. /*这一连串的输出,要注意格式*/
  268. }
  269. }
  270. }

  271. /*关闭设备*/

  272. close(buttons_fd);

  273. return 0; /*主函数返回*/


  274. END!!








  275. 原文地址 http://hi.baidu.com/zhxust/blog/item/26b07c3ec76370f2828b13a3.html?
复制代码
 
 

回复

2131

帖子

0

TA的资源

至上芯片

板凳
 
不错,项一下!
 
个人签名处处留心皆学问!
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表