• Linux 中断实验


    一.什么是中断

            中断是指 CPU 在执行程序的过程中,出现了某些突发事件急待处理, CPU 必须暂停当前程序的执行,转去处理突发事件,处理完毕后又返回原程序被中断的位置继续执行。由于中断的存在极大的提高了 CPU 的运行效率,但是设备的中断会打断内核进程中的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。
            举例来说,我现在正在厨房做饭,突然电话响了,然后我关火去接电话,接完电话在回去开火继续做饭,这个过程就是中断的一个过程。

     

    二.中断相关函数

            linux 中断有专门的中断子系统,其实现原理很复杂,但是驱动开发者不需要知道其实现的具体细节, 只需要知道如何应用该子系统提供的 API 函数来编写中断相关驱动代码即可。

    1 获取中断号相关函数

            编写驱动的时候需要用到中断号,每一个中断都有中断号,我们用到中断号,中断信息已经写到了设 备树里面,因此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号。

     

    2 申请中断函数

            同 GPIO 一样,在 Linux 内核里面,如果我们要使用某个中断也是需要申请的,申请中断我们使用的函数是 request_irq
            中断标识可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,这里我们介绍几个常用的中断标志,如下图所示:
    标志
    功能
    IRQF_SHARED
    多个设备共享一个中断线,共享的所有中断都必须指
    定此标志。如果使用共享中断的话, request_irq 函数的 dev 参数就是唯一区分他们的标志。
    IRQF_ONESHOT
    单次中断,中断执行一次就结束。
    IRQF_TRIGGER_NONE无触发。
    IRQF_TRIGGER_RISING上升沿触发。
    IRQF_TRIGGER_FALLING下降沿触发。
    IRQF_TRIGGER_HIGH高电平触发。
    IRQF_TRIGGER_LOW低电平触发。

    3 free_irq 函数

            中断使用完成以后就要通过 free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。free_irq 函数原型如下所示

    4、中断处理函数

            使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数函数如下表所示:

     

     三.设备树中的中断节点

    1. 1 gpio5 : gpio @020ac000{
    2. 2 compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
    3. 3 reg = <0x020ac000 0x4000>;
    4. 4 interrupts = 74 IRQ_TYPE_LEVEL_HIGH>, 75 IRQ_TYPE_LEVEL_HIGH>;
    5. 5 gpio-controller;
    6. 6 #gpio-cells = <2>;
    7. 7 interrupt-controller;
    8. 8 #interrupt-cells = <2>;
    9. 9 };

             第 4 行,interrupts 描述中断源信息,对于 gpio5 来说一共有两条信息,中断类型都是 SPI,触发电平都是 IRQ_TYPE_LEVEL_HIGH。不同之处在于中断源,一个是 74,一个是 75。

    四.代码案例

            

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. //定义结构体表示我们的节点
    11. struct device_node *test_device_node;
    12. struct property *test_node_property;
    13. //要申请的中断号
    14. int irq;
    15. // GPIO 编号
    16. int gpio_nu;
    17. /*** @description: 中断处理函数 test_key
    18. * @param {int} irq :要申请的中断号
    19. * @param {void} *args :
    20. * @return {*}IRQ_HANDLED
    21. */
    22. irqreturn_t test_key(int irq, void *args)
    23. {
    24. printk("test_key \n");
    25. return IRQ_RETVAL(IRQ_HANDLED);
    26. }
    27. /******************************************************************************
    28. * @brief beep_probe : 与设备信息层(设备树)匹配成功后自动执行此函数,
    29. * @param inode : 文件索引
    30. * @param file : 文件
    31. * @return 成功返回 0
    32. ******************************************************************************/
    33. int beep_probe(struct platform_device *pdev)
    34. {
    35. int ret = 0;
    36. // 打印匹配成功进入 probe 函数
    37. printk("beep_probe\n");
    38. // of_find_node_by_path 函数通过路径查找节点,/test_key 是设备树下的节点路径
    39. test_device_node = of_find_node_by_path("/test_key");
    40. if (test_device_node == NULL)
    41. {
    42. //查找节点失败则打印信息
    43. printk("of_find_node_by_path is error \n");
    44. return -1;
    45. }
    46. //of_get_named_gpio 函数获取 GPIO 编号
    47. gpio_nu = of_get_named_gpio(test_device_node, "gpios", 0);
    48. if (gpio_nu < 0)
    49. {
    50. printk("of_get_namd_gpio is error \n");
    51. return -1;
    52. }
    53. //设置 GPIO 为输入模式
    54. gpio_direction_input(gpio_nu);
    55. //获取 GPIO 对应的中断号
    56. // irq = gpio_to_irq(gpio_nu);
    57. irq =irq_of_parse_and_map(test_device_node,0);
    58. printk("irq is %d \n", irq);
    59. /*申请中断,irq:中断号名字
    60. test_key:中断处理函数
    61. IRQF_TRIGGER_RISING:中断标志,意为上升沿触发
    62. "test_key":中断的名字
    63. */
    64. ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);
    65. if (ret < 0)
    66. {
    67. printk("request_irq is error \n");
    68. return -1;
    69. }
    70. return 0;
    71. }
    72. int beep_remove(struct platform_device *pdev)
    73. {
    74. printk("beep_remove\n");
    75. return 0;
    76. }
    77. const struct platform_device_id beep_idtable = {
    78. .name = "keys",
    79. };
    80. const struct of_device_id of_match_table_test[] = {
    81. {.compatible = "keys"},
    82. {},
    83. };
    84. struct platform_driver beep_driver = {
    85. //3. 在 beep_driver 结构体中完成了 beep_probe 和 beep_remove
    86. .probe = beep_probe,
    87. .remove = beep_remove,
    88. .driver = {
    89. .owner = THIS_MODULE,
    90. .name = "beep_test",
    91. .of_match_table = of_match_table_test},
    92. //4 .id_table 的优先级要比 driver.name 的优先级要高,优先与.id_table 进行匹配
    93. .id_table = &beep_idtable};
    94. /**
    95. * @description: 模块初始化函数
    96. * @param {*}
    97. * @return {*}
    98. */
    99. static int beep_driver_init(void)
    100. {
    101. //1.我们看驱动文件要从 init 函数开始看
    102. int ret = 0;
    103. //2.在 init 函数里面注册了 platform_driver
    104. ret = platform_driver_register(&beep_driver);
    105. if (ret < 0)
    106. {
    107. printk("platform_driver_register error \n");
    108. }
    109. printk("platform_driver_register ok \n");
    110. return 0;
    111. }
    112. /**
    113. * @description: 模块卸载函数
    114. * @param {*}
    115. * @return {*}
    116. */
    117. static void beep_driver_exit(void)
    118. {
    119. free_irq(irq, NULL);
    120. platform_driver_unregister(&beep_driver);
    121. printk("gooodbye! \n");
    122. }
    123. module_init(beep_driver_init);
    124. module_exit(beep_driver_exit);
    125. MODULE_LICENSE("GPL");

  • 相关阅读:
    牛客刷题记录(常见笔试题)
    【Vue3】Axios简易二次封装+token+跨域问题+接口同步调用使用。
    Java NIO 通信基础
    CAdUiPaletteSet创建后乱码 2023/10/17 下午11:25:07
    LeetCode栈和队列练习
    易基因|深度综述:表观遗传机制在慢性疼痛中的作用(DNA甲基化+组蛋白修饰+非编码RNA)
    【蓝桥杯省赛真题22】python剩余空间问题 青少年组蓝桥杯比赛python编程省赛真题解析
    Ubuntu20运行SegNeXt代码提取道路水体(四)——成功解决训练与推理自己的数据集iou为0的问题!!
    NumPy 舍入小数、对数、求和和乘积运算详解
    C# RestoreFormer 图像修复
  • 原文地址:https://blog.csdn.net/weixin_66634995/article/details/134297295