服务器之家:专注于服务器技术及软件下载分享
分类导航

Linux|Centos|Ubuntu|系统进程|Fedora|注册表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服务器之家 - 服务器系统 - Linux - 手把手教你中断唤醒系统

手把手教你中断唤醒系统

2021-12-28 23:45嵌入式Linux系统开发Jasonangel Linux

中断唤醒系统和普通的驱动区别在于,多了两个函数:suspend 和 resume,在 suspend 函数中,调用 enable_irq_wake,表示该中断号在系统休眠时也是 enable 状态,可以触发中断。

手把手教你中断唤醒系统

在消费类电子中,功耗是很重要的,甚至项目后期一直在调功耗,看看哪里还可以再省电。由此就有了 Linux 电源管理子系统,该子系统包含很多方面:什么时候可以降帧、什么时候可以关掉其他 CPU core、系统运行时如果某外设很少用需要让它运行时休眠、系统休眠时要保证哪些外设可以唤醒系统。

博主今天要讨论的,就是一个按键如何唤醒系统,类似于手机的电源键。

这个功能并不是新功能,所以 Linux 内部有一个 demo 可以使用,先教大家如何使用该 demo,然后较大家如何撰写中断唤醒系统驱动。

官方 demo

demo 目录:/kernel4.14/drivers/input/keyboard/gpio_keys.c

该驱动是专门为按键准备的,是一个身经百战的驱动,任何时候测试按键中断或者中断唤醒系统都可以用它,很多时候比自己写的驱动靠谱。

要想使用该驱动,首先在该目录的 Makefile 中增加:

  1. obj-y += gpio_keys.o

设备树中增加:

  1. gpio-keys {
  2. compatible = "gpio-keys";
  3. #address-cells = <1>;
  4. #size-cells = <0>;
  5. autorepeat;
  6. key0 {
  7. label = "GPIO Key Enter";
  8. linux,code = ;
  9. gpios = <&gpio1 18 GPIO_ACTIVE_LOW>;
  10. gpio-key,wakeup;
  11. };
  12. };

compatible 属性是 “gpio-keys”,gpio_keys.c 文件的674行会匹配这个属性,匹配到了该驱动就会运行。

linux,code 属性是按键值,Linux 对所有按键事件都有编号,所以KEY_ENTER 实际是一个数字,是驱动向上层报告的一个按键值。

gpios 属性是标明哪一个 GPIO 口,低电平触发,大家可以自己选一个 GPIO。

gpio-key,wakeup 是代表此GPIO支持中断唤醒,你也可以写成:wakeup-source。新老版本而已。

修改就是这么简单,不过语法要符合各位手中的开发板平台。然后编译出内核和设备树文件,下载到板子中。(Linux 内核根目录会有 .config 文件,确保 CONFIG_PM_SLEEP=y 有打开)

如果驱动加载成功,在 /proc/interrupts 中可以看到:

手把手教你中断唤醒系统

从左往右第一列是软件中断号(唯一)。

第二列是 CPU,表示该中断在该CPU上触发了多少次,多核会有多列。

第三列是中断控制器,imx6ull开发板根中断控制器是GPC,外部中断控制器是gpio-mxc,两者是级联关系。

第四列是硬件中断号,也就是GPIO口编号。

第五列表示该中断是边沿触发还是电平触发。

第六列是中断名称,可以找到一个 GPIO Key Enter,如果驱动加载成功就能看到,如果失败就看不到。

验证方法

在内核中,休眠方式有很多种,可以通过下面命令查看

  1. # cat /sys/power/state

常用的休眠方式有freeze、standby、mem、disk

freeze:冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态,唤醒最快,耗电比其它standby, mem, disk方式高

standby:除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电比其它 mem, disk方式高

mem:将运行状态数据存到内存,并关闭外设,进入等待模式,唤醒较慢,耗电比disk方式高

disk:将运行状态数据存到硬盘,然后关机,唤醒最慢

示例:

  1. # echo mem > /sys/power/state

系统进入睡眠后,基本都会停掉UI、停掉串口,串口无法操作,如图:

手把手教你中断唤醒系统

按下按键,系统恢复:

手把手教你中断唤醒系统

当然这里的 log 并不完整,输入 dmesg 可以看到完整 log:

手把手教你中断唤醒系统

PM:power manager

具体干了什么,图中有解释,分为 suspend 过程和 resume 过程。

其实一个中断让它支持唤醒系统,最主要是多了两个函数:suspend、resume。

suspend 函数在系统整体 suspend 的时候,会调用每个外设注册的 suspend,我们在这个函数中调用 enable_irq_wake,表示该中断在系统休眠时是 enable 状态。

resume 函数在系统整体 resume 的时候,会调用每个外设注册的 resume 函数,在 resume 函数中调用 disable_irq_wake ,表示该中断在系统运行时不需要。两者成对使用。

具体参看下面文章,写的很好:

http://www.wowotech.net/irq_subsystem/irq_handle_procedure.html

大家也可以研究一下 gpio_keys.c,该驱动看起来比较复杂,但是很完善,毕竟身经百战,什么因素都考虑到了,测试就用它!

博主写的 demo

博主下面给的是简化版,并且自测OK,分享给大家,以后如果需要可以copy

xxx.c

  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include
  10. #include
  11. #include
  12. #include
  13. #include
  14. #include
  15. #include
  16. #include of.h>
  17. #include
  18. #include
  19. #include
  20. #include
  21. #include
  22. static int gpionum = 0;
  23. static int irqnum = 0;
  24. static irqreturn_t my_handler(int irq, void *dev_id)
  25. {
  26. printk("%s\r\n",__FUNCTION__);
  27. return IRQ_HANDLED;
  28. }
  29. static int gpio_keys_probe(struct platform_device *pdev)
  30. {
  31. int ret = 0;
  32. struct device_node *node = NULL;; /* 设备节点*/
  33. node = of_find_compatible_node(NULL,NULL,"atkalpha-key");
  34. if (node == NULL){
  35. printk("%s:atkalpha-key node not find!\r\n",__FUNCTION__);
  36. return -EINVAL;
  37. }
  38. /* 提取 GPIO */
  39. gpionum = of_get_named_gpio(node,"key-gpio", 0);
  40. if (gpionum < 0) {
  41. printk("of_get_named_gpio can't get key\r\n");
  42. }
  43. /* 初始化 key 所使用的 IO,并且设置成中断模式 */
  44. gpio_request(gpionum, "key-gpio");
  45. gpio_direction_input(gpionum);
  46. irqnum = gpio_to_irq(gpionum);
  47. ret = request_irq(irqnum,my_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "my-key", NULL);
  48. if(ret < 0){
  49. printk("irq %d request failed!\r\n", irqnum);
  50. return -EFAULT;
  51. }
  52. return 0;
  53. }
  54. static const struct of_device_id gpio_keys_of_match[] = {
  55. { .compatible = "atkalpha-key", },
  56. { },
  57. };
  58. MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
  59. static int gpio_keys_remove(struct platform_device *pdev)
  60. {
  61. return 0;
  62. }
  63. static int gpio_keys_suspend(struct device *dev)
  64. {
  65. printk("%s\r\n",__FUNCTION__);
  66. enable_irq_wake(irqnum);
  67. return 0;
  68. }
  69. static int gpio_keys_resume(struct device *dev)
  70. {
  71. printk("%s\r\n",__FUNCTION__);
  72. disable_irq_wake(irqnum);
  73. return 0;
  74. }
  75. static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
  76. static struct platform_driver gpio_keys_device_driver = {
  77. .probe = gpio_keys_probe,
  78. .remove = gpio_keys_remove,
  79. .driver = {
  80. .name = "my-key",
  81. .pm = &gpio_keys_pm_ops,
  82. .of_match_table = of_match_ptr(gpio_keys_of_match),
  83. }
  84. };
  85. static int __init gpio_keys_init(void)
  86. {
  87. return platform_driver_register(&gpio_keys_device_driver);
  88. }
  89. static void __exit gpio_keys_exit(void)
  90. {
  91. platform_driver_unregister(&gpio_keys_device_driver);
  92. }
  93. module_init(gpio_keys_init);
  94. module_exit(gpio_keys_exit);
  95. MODULE_LICENSE("GPL");
  96. MODULE_AUTHOR("Jason");
  97. MODULE_DESCRIPTION("Keyboard driver for GPIOs");
  98. MODULE_ALIAS("platform:gpio-keys");

xxx.dts

  1. key {
  2. #address-cells = <1>;
  3. #size-cells = <1>;
  4. compatible = "atkalpha-key";
  5. key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
  6. interrupt-parent = <&gpio1>;
  7. interrupts = <18 irq_type_edge_both="">; /* FALLING RISING */
  8. gpio-key,wakeup;
  9. status = "okay";
  10. };

最后再总结一下:中断唤醒系统和普通的驱动区别在于,多了两个函数:suspend 和 resume,在 suspend 函数中,调用 enable_irq_wake,表示该中断号在系统休眠时也是 enable 状态,可以触发中断。在 resume 函数中,调用 disable_irq_wake ,恢复原始的中断触发路径。

然后使用 SIMPLE_DEV_PM_OPS 宏将 suspend 和 resume 函数注册到 gpio_keys_pm_ops 操作集,最终由 platform 注册到系统中。这样完成后,系统休眠过程中就会调用到设备注册的 suspend,系统唤醒过程中就会调用设备注册的 resume 函数。

至于 probe 函数的书写,我在 GPIO 子系统和中断子系统系列文章都讲过这些函数的使用,大家可以去我的网站查看:

http://www.linuxer.vip

note:该 demo 只用来唤醒系统,如果你的中断是在 I2C 等设备驱动中,唤醒系统后要立刻在中断处理函数中进行 I2C 通信,写法不太一样,但是框架相同。

另外,该驱动的中断处理函数中没做什么东西,因此唤醒后执行完中断处理函数后又会睡过去。如果你想要该中断唤醒系统后让系统一直处于唤醒状态,请在中断处理函数中使用 __pm_stay_awake() 和 __pm_relax()函数。

原文链接:https://mp.weixin.qq.com/s/FooG30_Fr4svQcqbK9y_9g

延伸 · 阅读

精彩推荐
  • LinuxLinux系统下无法卸载挂载的目录怎么办?

    Linux系统下无法卸载挂载的目录怎么办?

    我们在日常运维中经常性会遇到需要进行磁盘的扩容、卸载、挂载等操作,但是有时候这个系统上跑的应用并没有停止或者有其他的运维同事在操作这个目...

    今日头条10302020-12-30
  • Linuxlinux设置tomcat自启动的方法

    linux设置tomcat自启动的方法

    这篇文章主要介绍了linux设置tomcat自启动的方法,需要的朋友可以参考下...

    Linux教程网8512021-10-10
  • Linuxlinux top命令详解

    linux top命令详解

    这篇文章主要介绍了linux top命令详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    sparkdev5622022-03-01
  • Linuxssh 登录很慢该如何解决

    ssh 登录很慢该如何解决

    这篇文章主要介绍了ssh 登录很慢该如何解决的相关资料,这里提供了两种方法,DNS反向解析及关闭ssh的gssapi认证的解决办法,需要的朋友可以参考下...

    linuxeye9922021-12-16
  • Linux在Linux系统中创建新的亚马逊AWS访问密钥的方法

    在Linux系统中创建新的亚马逊AWS访问密钥的方法

    如何在Linux系统中创建新的亚马逊AWS访问密钥?我在配置一个需要访问我的亚马逊AWS帐号的应用时被要求提供AWS访问密钥ID和秘密访问密钥,我怎样创建一个...

    Linux教程网6182019-10-30
  • Linux理解 Linux/Unix 登录脚本的技巧

    理解 Linux/Unix 登录脚本的技巧

    有一些常见的情况,例如从Debian的包管理程序到Iaas的管理中,很多任务需要设置环境变量才能正常运行。 有时,程序通常只需要在 登陆时运行一次,例如...

    未知1042023-05-12
  • Linuxlinux驱动程序开发详细介绍

    linux驱动程序开发详细介绍

    前提,一般来说内核代码的错误可能会引起一个用户进程的死亡,或者整个系统的瘫痪,更严重的后果,可能导致磁盘损伤~因此建议最好有一台实验机进行...

    Linux教程网5392019-12-17
  • Linux将 Linux 终端与 Nautilus 文件管理器结合起来

    将 Linux 终端与 Nautilus 文件管理器结合起来

    Nautilus 是 GNOME 桌面环境中的图形化文件浏览器。你可以使用它来访问和管理系统中的文件和文件夹。 尽管并非所有人都喜欢使用终端来管理文件和目录,...

    未知812023-08-08