嵌入式内核及驱动开发之学习笔记(七) 非阻塞模式+中断实现读取数据

举报
王建峰 发表于 2021/11/19 02:42:12 2021/11/19
【摘要】 当中断发生时,驱动程序会跳转到中断处理的函数入口,实现了中断的捕获和处理,但这样还不够。要让用户能够获取到中断分析的结果,我们将创建一个描述中断事件的结构体对象。硬件产生中断后,驱动代码将对中断事件的分析结果保存在结构体变量中,用户需要的时候,直接通过接口函数获取这个结构体的数据。 内核层: 硬件(中断事件) -->&nbsp...

当中断发生时,驱动程序会跳转到中断处理的函数入口,实现了中断的捕获和处理,但这样还不够。要让用户能够获取到中断分析的结果,我们将创建一个描述中断事件的结构体对象。硬件产生中断后,驱动代码将对中断事件的分析结果保存在结构体变量中,用户需要的时候,直接通过接口函数获取这个结构体的数据。

内核层:

硬件(中断事件) -->  驱动程序(中断处理) --> 结构体变量(添加到对象属性)

应用层:

结构体(添加到对象属性) --> API(内核层对应用层的接口) --> 用户程序


实验:根据之前学到的字符驱动步骤代码规范,再进一步对之前的程序调整,便有了这个程序。对于内核层,当有中断事件,去驱动程序跳到处理函数并将中断信息保存在定义的结构体中;对于应用,使用while循环调用read函数 轮询的方式,这算是一种非阻塞模型,比较耗费CPU的资源。

 

驱动程序

当按键按下(1->0)或者松开(0->1)时,产生一个中断事件,驱动程序进入中断处理key_irq_handler,并将对按键的分析数据存放在key_event类型的结构体中。提供key_drv_read函数,实际上就是应用程序获取这个结构体数据的接口函数


  
  1. //key_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/of.h>
  5. #include <linux/of_irq.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/slab.h>
  8. #include <linux/fs.h>
  9. #include <linux/device.h>
  10. #include <asm/io.h>
  11. #include <asm/uaccess.h>
  12. irqreturn_t key_irq_handler(int irqno, void *devid);
  13. ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *);
  14. ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *);
  15. int key_drv_open (struct inode *, struct file *);
  16. int key_drv_close (struct inode *, struct file *);
  17. #define GPXCON_REG 0x11000C20
  18. #define KEY_ENTER 28
  19. const struct file_operations key_fops = {
  20. .open = key_drv_open,
  21. .read = key_drv_read,
  22. .write = key_drv_write,
  23. .release = key_drv_close,
  24. };
  25. struct key_event{
  26. int code; // 按键的类型
  27. int value; // 状态
  28. };
  29. struct key_desc{
  30. unsigned int dev_major;
  31. struct class *cls;
  32. struct device *dev;
  33. int irqno;
  34. void *reg_base;
  35. struct key_event event;
  36. };
  37. struct key_desc *key_dev;
  38. //static int irqno;
  39. int get_irqno_from_node(void)
  40. {
  41. //从设备树路径,查找节点
  42. struct device_node *np = of_find_node_by_path("/key_int_node");
  43. if(np){
  44. printk("find node ok\n");
  45. }else{
  46. printk("find node failed\n");
  47. }
  48. int irqno = irq_of_parse_and_map(np, 0);
  49. printk("irqno = %d\n", irqno);
  50. return irqno;
  51. }
  52. static int __init key_drv_init(void)
  53. {
  54. int ret;
  55. //对象实例化
  56. key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
  57. //申请主设备号
  58. key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
  59. //创建设备结点
  60. key_dev->cls = class_create(THIS_MODULE, "key_cls");
  61. key_dev->dev = device_create(key_dev->cls, NULL,
  62. MKDEV(key_dev->dev_major,0), NULL, "key0");
  63. //硬件初始化
  64. key_dev->irqno = get_irqno_from_node();
  65. ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  66. "key3_eint10", NULL);
  67. if(ret != 0)
  68. {
  69. printk("request_irq error\n");
  70. return ret;
  71. }
  72. key_dev->reg_base = ioremap(GPXCON_REG, 8);
  73. return 0;
  74. }
  75. static void __exit key_drv_exit(void)
  76. {
  77. iounmap(key_dev->reg_base); //去映射
  78. free_irq(key_dev->irqno, NULL); //释放中断资源
  79. device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); //
  80. class_destroy(key_dev->cls); //
  81. unregister_chrdev(key_dev->dev_major, "key_drv"); //注销主设备号
  82. kfree(key_dev); //释放结构体内存
  83. }
  84. irqreturn_t key_irq_handler(int irqno, void *devid)
  85. {
  86. printk("-------%s-------------\n", __FUNCTION__);
  87. int value = readl(key_dev->reg_base + 4) & (1<<2);
  88. if(value){// 1
  89. printk("key3 up\n");
  90. key_dev->event.code = KEY_ENTER;
  91. key_dev->event.value = 0;
  92. }else{// 0
  93. printk("key3 pressed\n");
  94. key_dev->event.code = KEY_ENTER;
  95. key_dev->event.value = 1;
  96. }
  97. return IRQ_HANDLED;
  98. }
  99. ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  100. {
  101. //printk("-------%s-------------\n", __FUNCTION__);
  102. int ret;
  103. ret = copy_to_user(buf, &key_dev->event, count);
  104. if(ret > 0)
  105. {
  106. printk("copy_to_user error\n");
  107. return -EFAULT;
  108. }
  109. // 清除key_dev->event的数据记录
  110. memset(&key_dev->event, 0, sizeof(key_dev->event));
  111. return count;
  112. }
  113. ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  114. {
  115. printk("-------%s-------------\n", __FUNCTION__);
  116. return 0;
  117. }
  118. int key_drv_open(struct inode *inode, struct file *filp)
  119. {
  120. printk("-------%s-------------\n", __FUNCTION__);
  121. return 0;
  122. }
  123. int key_drv_close (struct inode *inode, struct file *filp)
  124. {
  125. printk("-------%s-------------\n", __FUNCTION__);
  126. return 0;
  127. }
  128. module_init(key_drv_init);
  129. module_exit(key_drv_exit);
  130. MODULE_LICENSE("GPL");

 

应用程序

在应用程序中,不断read获取按键的状态,有按键触发立即打印数据。


  
  1. //key_test.c
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. struct key_event{
  10. int code; // 按键的类型
  11. int value; // 状态
  12. };
  13. #define KEY_ENTER 28
  14. int main(int argc, char *argv[])
  15. {
  16. struct key_event event;
  17. int fd = open("/dev/key0", O_RDWR);
  18. if(fd < 0)
  19. {
  20. perror("open");
  21. exit(1);
  22. }
  23. while(1)
  24. {
  25. read(fd, &event, sizeof(struct key_event));
  26. if(event.code == KEY_ENTER)
  27. {
  28. if(event.value)
  29. {
  30. printf("APP__ key enter pressed\n");
  31. }else{
  32. printf("APP__ key enter up\n");
  33. }
  34. }
  35. }
  36. close(fd);
  37. return 0;
  38. }

 

结果演示  

编译后在开发板上运行,查看串口打印结果

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/84143560

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。