Linux混杂设备驱动 - 按键设备驱动

发布时间:2024-09-20  

之前的一篇博客概括了混杂设备驱动模型(http://www.cnblogs.com/ape-ming/p/5101322.html),现在就根据那篇博客所列出来的模板写一个按键设备驱动程序。

根据模板首先要写一个设备加载函数: 


 1 /*

 2  *    函数名     : button_init

 3  *    函数功能: 设备加载

 4  */

 5 static int __init button_init(void)

 6 {

 7     int ret = 0;

 8 

 9     /* 注册混杂设备驱动 */

10     ret = misc_register(&misc);

11     if(ret)

12     {

13         printk('can't register miscdevn');

14         return ret;

15     }

16     return ret;

17 }


button_init()函数完成设备加载,在button_init()里面主要是调用了misc_register()对混杂设备进行注册,这个时候就需要给定一个混杂设备驱动结构体miscdevice:


1 static struct miscdevice misc = 

2 {

3     .minor = MISC_DYNAMIC_MINOR,

4     .name = 'buttons6410',

5     .fops = &fops,

6 };

misc里面的minor赋值MISC_DYNAMIC_MINOR表示动态分配设备号,name随便取,还需要一个file_operations结构体:


1 static struct file_operations fops = 

2 {

3     .owner = THIS_MODULE,

4     .open = button_open,

5     .read = button_read,

6     .release = button_close,

7 };


在这个结构体里面赋值了三个函数open、read、和release:


 1 /*

 2  *    函数名     : button_open

 3  *    函数功能: 文件打开

 4  */

 5 static int button_open(struct inode *node, struct file *filp)

 6 {

 7     int ret = 0;

 8     int i = 0;

 9 

10     /* 申请外部中断 */

11     for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++)

12     {

13         ret = request_irq(button_irq[i].irq,button_interrupt,IRQF_TRIGGER_FALLING,button_irq[i].name,(void*)&button_irq[i]);

14         if(ret != 0)

15         {

16             printk('request_irq failuren');

17         }

18     }

19 

20     /* 初始化工作队列 */

21     INIT_WORK(&button_work,do_buttons);

22 

23     /* 初始化内核定时器 */

24     init_timer(&button_time);

25     button_time.expires = jiffies + HZ/10;    //100ms

26     button_time.function = button_do_time;

27     add_timer(&button_time);

28     

29     return ret;

30 }


在open函数里面申请了四个按键的外部中断,这里调用了内核定时器对按键进行延时消抖(其实没必要这么做的,纯粹练手^_^)所以初始化了一个工作队列把中断提交给底半部进行处理,之后初始化内核定时器。


 1 /*

 2  *    函数名     : button_read

 3  *    函数功能: 文件读

 4  */

 5 static ssize_t button_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

 6 {

 7     ssize_t ret = 0;

 8     size_t size = min(count,sizeof(button_press));

 9 

10     /* 等待队列唤醒 */

11     wait_event_interruptible(q_buttons,isKey_Pressed);

12     isKey_Pressed = 0;

13     

14     if(copy_to_user((void*)buffer,(const void*)&button_press,size))

15     {

16         ret = -1;

17         printk('copy to user failuren');

18     }

19     else

20     {

21         memset((void*)&button_press,0,sizeof(button_press));

22         ret = size;

23     }

24     return ret;

25 }


在read函数里面采用阻塞的办法等待等待队列唤醒,之后调用copytouser()函数把按键的状态传递给用户程序。


当按键按下即触发了外部中断,进入外部中断处理程序:


 1 /*

 2  *    函数名     : button_interrupt

 3  *    函数功能: 外部中断服务程序

 4  */

 5 static irqreturn_t button_interrupt(int irq, void *dev_id)

 6 {

 7     struct button_irqs *button_id = (struct button_irqs*)dev_id;

 8     switch(button_id->id)

 9     {

10         case 0:

11         case 1:

12         case 2:

13         case 3:

14         schedule_work(&button_work);

15         break;

16         default: break;

17     }

18     return IRQ_HANDLED;

19 }


外部中断程序调用schedule_work()提交到底半部进行处理:


1 /*

2  *    函数名     : do_buttons

3  *    函数功能: 工作队列处理函数,处理按键工作

4  */

5 static void do_buttons(struct work_struct *work)

6 {

7     mod_timer(&button_time,jiffies + HZ/10);

8 }


在这个函数中修改定时器的值使定时器开始定时达到延时消抖的目的,这里延时100ms,延时时间到即进入内核定时器中断服务程序:


 1 /*

 2  *    函数名     : button_do_time

 3  *    函数功能: 内核定时器服务程序

 4  */

 5 

 6 static void button_do_time(unsigned long arg)

 7 {

 8     int i = 0;

 9     unsigned short tmp = 0;

10     tmp = readw(S3C64XX_GPNDAT) & 0x000F;

11     switch(tmp)

12     {

13         case 0x0E:

14             button_press[0] = 1;

15         break;

16         case 0x0D:

17             button_press[1] = 1;

18         break;

19         case 0x0B:

20             button_press[2] = 1;

21         break;

22         case 0x07:

23             button_press[3] = 1;

24         break;

25     }

26     for(i = 0;i < sizeof(button_press)/sizeof(button_press[0]);i++)

27     {

28         if(button_press[i] == 0)

29             continue;

30             

31         isKey_Pressed = 1;

32 

33         /* 唤醒等待队列 */

34         wake_up_interruptible(&q_buttons);

35         break;

36     }

37 }


在这个函数里面判断是哪个按键按下了并唤醒等待队列。


最后编写设备驱动卸载函数:


1 /*

2  *    函数名     : button_exit

3  *    函数功能: 设备卸载

4  */

5 static void __exit button_exit(void)

6 {

7     /* 卸载混杂设备驱动 */

8     misc_deregister(&misc);

9 }


这个函数调用misc_deregister()卸载misc驱动。


完整代码:


  1 /*

  2  *    文件名  : buttons.c

  3  *    功能描述: 通过外部中断实现按键驱动程序

  4  *    驱动模型: misc

  5  *    设备节点: /dev/buttons6410

  6  *    MCU     : S3C6410

  7  *    端口连接: KEY0-GPN0  KEY1-GPN1  KEY2-GPN2  KEY3-GPN3

  8  */

  9 

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 15 #include

 16 #include

 17 #include

 18 #include

 19 #include

 20 #include

 21 #include

 22 #include

 23 #include

 24 #include

 25 

 26 #include

 27 #include

 28 #include

 29 

 30 #include

 31 #include

 32 #include

 33 

 34 

 35 volatile int isKey_Pressed = 0;    // 按键按下标志 

 36 struct work_struct button_work;    //定义工作队列

 37 struct timer_list button_time;

 38 struct button_irqs

 39 {

 40     unsigned int irq;

 41     int id;

 42     char* name;

 43 };

 44 

 45 static struct button_irqs button_irq[] = 

 46 {

 47     {IRQ_EINT(0),0,'KEY0'},

 48     {IRQ_EINT(1),1,'KEY1'},

 49     {IRQ_EINT(2),2,'KEY2'},

 50     {IRQ_EINT(3),3,'KEY3'},

 51 };

 52 

 53 /* 初始化等待队列 */

 54 DECLARE_WAIT_QUEUE_HEAD(q_buttons);

 55 

 56 static volatile int button_press[4] = {0};

 57 

 58 /*

 59  *    函数名     : do_buttons

 60  *    函数功能: 工作队列处理函数,处理按键工作

 61  */

 62 static void do_buttons(struct work_struct *work)

 63 {

 64     mod_timer(&button_time,jiffies + HZ/10);

 65 }

 66 

 67 /*

 68  *    函数名     : button_interrupt

 69  *    函数功能: 外部中断服务程序

 70  */

 71 static irqreturn_t button_interrupt(int irq, void *dev_id)

 72 {

 73     struct button_irqs *button_id = (struct button_irqs*)dev_id;

 74     switch(button_id->id)

 75     {

 76         case 0:

 77         case 1:

 78         case 2:

 79         case 3:

 80         schedule_work(&button_work);

 81         break;

 82         default: break;

 83     }

 84     return IRQ_HANDLED;

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>