蜗牛

一起交流,共同进步
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

TQ2440按键驱动及测试程序

Posted on 2010-03-26 14:21  路漫漫...  阅读(4459)  评论(1编辑  收藏  举报

本驱动程序运行于TQ2440开发板,内核版本2.6.31.6

  1 #include <linux/module.h> 
  2 #include <linux/kernel.h> 
  3 #include <linux/fs.h> 
  4 #include <linux/init.h> 
  5 #include <linux/delay.h> 
  6 #include <linux/poll.h> 
  7 #include <linux/irq.h> 
  8 #include <asm/irq.h> 
  9 #include <linux/io.h>
 10 #include <linux/interrupt.h> 
 11 #include <asm/uaccess.h> 
 12 #include <mach/regs-clock.h>
 13 #include <plat/regs-adc.h> 
 14 #include <mach/hardware.h> 
 15 #include <linux/platform_device.h> 
 16 #include <linux/cdev.h> 
 17 #include <linux/miscdevice.h> 
 18 
 19 
 20 #define DEVICE_NAME        "tq2440-buttons"
 21 #define DEV_COUNT          1
 22 #define BUTTON_MAJOR    0
 23 #define BUTTON_MINOR    0
 24 
 25 #define MIN(A,B)        (A)>(B)?(B):(A)
 26 /*等待队列:
 27 *当没有按键被按下时,如果有进程调用tq2440_buttons_read函数
 28 *它将休眠*/
 29 
 30 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
 31 /*中断事件标志,中断服务程序将它置1,tq2440_buttons_read将它清0 */
 32 
 33 static volatile int ev_press = 0;
 34 
 35 /* 按键被按下的次数(准确地说,是发生中断的次数) */
 36 static volatile int  press_cnt[] = {0,0,0,0};
 37 
 38 
 39 static struct class *button_class; 
 40 struct button_irqs_desc      {
 41 int irq;   //中断号
 42 unsigned long flags; //中断标志,用来定义中断的触发方式 
 43 char *name;        //中断名称
 44 };
 45 
 46 /*用来指定按键所用的外部中断引脚及中断触发方式,名字*/
 47 static struct button_irqs_desc button_irqs[] = {
 48 {IRQ_EINT0,IRQ_TYPE_EDGE_FALLING,"KEY1"},  //K1
 49 {IRQ_EINT1,IRQ_TYPE_EDGE_FALLING,"KEY2"},      //K2
 50 {IRQ_EINT2,IRQ_TYPE_EDGE_FALLING,"KEY3"},  //K3
 51 {IRQ_EINT4,IRQ_TYPE_EDGE_FALLING,"KEY4"},  //K4
 52 };
 53 
 54 dev_t dev_num;
 55 static struct cdev * buttons_cdev_p;  //cdev结构体指针
 56 
 57 /*卸载驱动程序*/
 58 static void __exit tq2440_buttons_exit(void)
 59 {
 60     cdev_del(buttons_cdev_p);//注销cdev
 61     kfree(buttons_cdev_p);//释放设备结构体内存
 62     unregister_chrdev_region(dev_num,DEV_COUNT);//释放设备号
 63 
 64 }
 65 
 66 static irqreturn_t buttons_interrupt(int irq,void *dev_id)
 67 {
 68      volatile int *press_cnt = (volatile int *)dev_id;
 69 
 70     *press_cnt = *press_cnt + 1;     /*按键计数器加1  */
 71     ev_press = 1;                    /*表示中断发生了*/
 72     wake_up_interruptible(&button_waitq);  /*唤醒休眠的进程*/
 73 
 74     printk(" IRQ:%d\n",irq);
 75     
 76     return IRQ_RETVAL(IRQ_HANDLED);
 77 }
 78 
 79 /*应用程序执行open("/dev/buttons",...)系统调用时,tq2440_buttons_open函数将
 80  *被调用,它用来注册4个按键的中断处理程序*/
 81 static int tq2440_buttons_open(struct inode *inode,struct file *file)
 82 {
 83     int i;
 84     int err;
 85 
 86     for(i =0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++)  {
 87         //注册中断处理函数
 88         err = request_irq(button_irqs[i].irq,buttons_interrupt,button_irqs[i].flags,
 89                         button_irqs[i].name,(void *)&press_cnt[i]);
 90 
 91         if(err)    
 92             break;
 93         }
 94       if(err) {
 95           //如果出错,释放已经注册的中断
 96           i--;
 97         for(;i>=0;i--)
 98             free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);
 99         return -EBUSY;
100           }
101       return 0;
102 }
103 
104 /*应用程序对设备文件/dev/buttons执行close(...)时,
105  *就会调用tq2440_buttons_close函数*/
106  static int tq2440_buttons_close(struct inode *inode,struct file *file)
107 {
108     int i;
109 
110     for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++)  {
111         //释放已经注册的中断
112         free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);
113         }
114     return 0;
115 }
116 
117 /*应用程序对设备文件/dev/buttons执行read(...)时,
118  *就会调用tq2440_buttons_read函数
119  */
120  static int tq2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
121 {
122     unsigned long err;
123     /*如果ev_press等于0,休眠*/
124     wait_event_interruptible(button_waitq,ev_press);
125 
126     /*执行到这里时ev_press肯定等于1,将它清0 */
127     ev_press = 0;
128 
129     /*将按键状态复制给用户,并请0  */
130     err = copy_to_user(buff,(const void *)press_cnt,MIN(sizeof(press_cnt),count));
131 
132     memset((void *)press_cnt,0,sizeof(press_cnt));
133 
134     return err? -EFAULT:0;
135 }
136 
137 /*这个结构是字符驱动设备程序的核心
138  *当应用程序操作设备文件时所调用的open,read,write等函数
139  *最终会调用这个结构中的对应函数
140  */
141 
142 static struct file_operations tq2440_buttons_fops = {
143     .owner =    THIS_MODULE,/*这是一个宏,指向编译模块时自动创建的__this_module变量*/
144     .open  = tq2440_buttons_open,
145     .release = tq2440_buttons_close,
146     .read   = tq2440_buttons_read,
147 };
148 
149 /*  初始化并注册cdev*/
150 static void buttons_cdev_setup(void)
151 {
152     int err;
153     cdev_init(buttons_cdev_p,&tq2440_buttons_fops);
154     buttons_cdev_p->owner = THIS_MODULE;
155     buttons_cdev_p->ops = &tq2440_buttons_fops;
156     err = cdev_add(buttons_cdev_p,dev_num,1);
157     if(IS_ERR(&err))
158         printk(KERN_NOTICE "Error %d adding buttons",err);
159 }
160 static int __init tq2440_buttons_init(void)
161 {
162     int ret;
163 
164     /*注册字符设备驱动程序
165         *参数为主设备号,设备名字,file_operations结构体
166         *这样,主设备号就和具体的file_operations结构体联系起来了,
167         *操作主设备号为BUTTON_MAJOR的设备文件时,就会调用
168         *tq2440_buttons_fops中的相关成员函数,BUTTON_MAJOR可以设为0,
169         *表示由内核自动分配主设备号
170         */
171  
172        if(BUTTON_MAJOR)   //手动分配设备号
173            {  
174              dev_num=MKDEV(BUTTON_MAJOR, BUTTON_MINOR);
175                ret=register_chrdev_region(dev_num,DEV_COUNT,DEVICE_NAME);
176             }
177     else  {          //动态分配设备号
178          ret = alloc_chrdev_region(&dev_num,BUTTON_MINOR,DEV_COUNT,DEVICE_NAME);        
179     }
180     if(ret<0)     {
181                 printk(DEVICE_NAME " can't register major number \n");
182                 return ret;
183         }
184     /*动态申请cdev结构体的内存*/
185     buttons_cdev_p = kmalloc(sizeof(struct cdev),GFP_KERNEL);
186     if(!buttons_cdev_p)   //申请失败
187         {
188             ret = -ENOMEM;
189             goto fial_malloc;
190         }
191     memset(buttons_cdev_p,0,sizeof(struct cdev));
192     buttons_cdev_setup();
193     //注册一个类,使mdev可以在"/dev/"目录下面
194     //建立设备节点
195     button_class = class_create(THIS_MODULE,DEVICE_NAME);
196     if(IS_ERR(button_class))
197         {
198         printk("Error:Failed to creat button_class \n");
199         return -1;
200         }
201     //创建一个设备节点,节点名为DEVICE_NAME
202     
203     device_create(button_class,NULL,dev_num,NULL,DEVICE_NAME);
204     printk(DEVICE_NAME " initialized \n");
205     return 0;
206     fial_malloc:unregister_chrdev_region(dev_num, 1);
207 }    
208         
209 /*这两行制定驱动程序的初始化函数和卸载函数*/
210 module_init(tq2440_buttons_init);
211 module_exit(tq2440_buttons_exit)

 

驱动测试程序

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/ioctl.h>
 5 
 6 int main(int argc,char **argv)
 7 {
 8     int i;
 9     int ret;
10     int fd;
11     int press_cnt[4];
12 
13     fd=open("/dev/tq2440-buttons",0);
14     if(fd<0)  {
15         printf("Can't open /dev/tq2440-buttons \n");
16         return -1;
17         }
18     //这是个无限循环,进程有可能在read函数中休眠,当有按键按下时,
19     //它才返回
20     while(1) {
21         ret = read(fd,press_cnt,sizeof(press_cnt));
22         if(ret<0)  {
23             printf("read err !\n");
24             continue;
25             }
26     //如果被按下的次数不为0,打印出来 
27     for(i=0;i<sizeof(press_cnt)/sizeof(press_cnt[0]);i++)    {
28         if(press_cnt[i])
29                 printf("Key%d has been pressed %d times \n",i+1,press_cnt[i]);
30         }
31         
32         }        
33         
34 }

 

 

进入根文件系统dev目录,发现已经成功动分配设备,并自动创建文件节点

[nick@TQ2440 /dev]# ls -l tq*
crw-rw----    1 0        0        232,   0 Jan  1 00:00 tq2440-adc
crw-rw----    1 0        0        254,   0 Jan  1 00:00 tq2440-buttons
crw-rw----    1 0        0        231,   0 Jan  1 00:00 tq2440-leds
crw-rw----    1 0        0        204,  64 Jan  1 00:00 tq2440_serial0
crw-rw----    1 0        0        204,  65 Jan  1 00:00 tq2440_serial1
crw-rw----    1 0        0        204,  66 Jan  1 00:00 tq2440_serial2

下载内核后,运行测试程序

[nick@TQ2440 /app]# ./buttons_test &

查看中断已经注册成功

[nick@TQ2440 /proc]# cat interrupts
           CPU0
 16:          0    s3c-ext0  KEY1
 17:          0    s3c-ext0  KEY2
 18:          0    s3c-ext0  KEY3
 30:      27496         s3c  S3C2410 Timer Tick
 32:          0         s3c  s3c2410-lcd
 43:          0         s3c  s3c2440-i2c
 48:          0     s3c-ext  KEY4
 51:       2587     s3c-ext  eth0
 70:        156   s3c-uart0  s3c2440-uart
 71:       1839   s3c-uart0  s3c2440-uart
 83:          0           -  s3c2410-wdt
Err:          0

第一列表示中断号

第二列表示这个中断发生的次数

第三列表示这个中断的硬件访问结构“struct irq_chip *chip"的名字,它在初始化中断体系结构时指定

第四列表示中断的名称

 

这时按下按键,输出如下

 IRQ:48
Key4 has been pressed 1 times
 IRQ:16
Key1 has been pressed 1 times
 IRQ:18
Key3 has been pressed 1 times
 IRQ:48
Key4 has been pressed 1 times
 IRQ:16
Key1 has been pressed 1 times
 IRQ:17
Key2 has been pressed 1 times