经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
zephyr的GPIOTE驱动开发记录——基于nordic的NCS
来源:cnblogs  作者:星辰_stars  时间:2022/11/17 9:13:15  对本文有异议

简介:

  本次测试了zephyr的中断驱动方式(GPIOTE),在这可以去看zephyr的官方文档对zephyr的中断定义,连接如下,Interrupts — Zephyr Project Documentation (nordicsemi.com) ;版本可能不对应,但是原理是一致的,今天记录的就是其中的零延迟中断,就是减少中断时间,让来自外部的中断能快速响应,进入到我们的中断服务程序中进行快速执行(也就是ISR)。

 根据文档,就作者理解来如下,如有更好的理解可以进行指正,有些时候在执行某些线程时对时间有要求或者在临界区进行操作时,不能够被外部中断(ISQ)打断,可以禁止(中断服务程序)ISR的执行。通过IRQ禁止达到在处理某些线程时不会被打断,但是会让中断处理被延迟,但这时候又出现一个矛盾,有些中断是我想要及时处理的,那么我们需要不被屏蔽掉,就是这个中断是比前面列举的线程执行更重要的事,那么怎么办,可以直接使用零延迟中断进行定义,让这些中断直接得到响应,在零延迟中断中又分为两种:一是常规的ISR,二是直接的ISR(某些情况下比常规的更快),常规的ISR可能还是会被打断,导致一下开销产生,具体在zephyr中有4点列举:

 

如果某个任务完全不想要被打断,快速的执行,那么就可以使用直接ISR(direct ISR)。在作者看来正常情况下(没有其余中断打断的情况下),他们两的时间应该是一致的。具体可以点击上面链接,直接看官方描述。

本次使用的是在开发之前默认你已经配置好开发环境,如果是第一次开发,建议去安装下面给出官方环境参考文档,或者去比例比例观看环境搭建的学习视频(VS code),也可以参看我文章中的关于9160开机测试的文章。官方连接如下:开发你的第一个nRF Connect SDK(NCS)/Zephyr应用程序 - iini - 博客园 (cnblogs.com)

参考资料:

  nordic的官方讲解视频,可以在哔哩哔哩上搜索nordic半导体去看关于其中一个视频:zephyr的设备驱动程序模型,中断和电源管理视频,中文讲解( https://www.bilibili.com/video/BV1MU4y177Zhis_story_h5=false&p=1&share_from=ugc&share_medium=android&share_plat=android&share_session_id=c0145896-48dc-4bbf-b938f1f4b3a4644a&share_source=WEIXIN&share_tag=s_i×tamp=1668583626&unique_k=29oQkX4),或者直接参看zephyr的官方。

本次测试环境:VS code、NCS1.8

一、建立工程

建立一个zephyr的工程,如果你有NCS,并且已经安装好相关可以进行开发的环境,那么可以打开一个hello Word的工程进行添加,如果没可以zephyr的SDK,可以依据nordic官方NCS进行开发,它也有如STM32等芯片底层文件,因为nordic只是在zephyr的SDK中加入了自己的产品形成了NCS包,其余zephyr原本有的并没有删减,所以你可以在NCS中建立如STM32芯片的工程进行开发,且上层的驱动都是抽象的,只是对应余硬件的定义换成了具体的芯片定义,我们只用管APP开发,所以一套代码,可以建立成不同芯片的工程,并且在编译下载后依然可以运行,不止局限于nordic的开发。

1、zephyr工程建立

对于zephyr可以直接建立一个文件夹,然后再里面包含如下的几个文件就可以进行编译开发了,

1)、其中src中放置我们的.c文件(APP),便于管理;

2)、CMakeLists.txt是工程创建的直接根本文件,具体内容可以是如下:

  1. #这是cmake的版本
  2. cmake_minimum_required(VERSION 3.20.0)
  3. #添加的库
  4. find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
  5. #建立的工程名字(本次为hello_world,可以改为GPIOTE等)
  6. project(hello_world)
  7. #添加.c文件,稍后在src中建立一个main.c
  8. target_sources(app PRIVATE src/main.c)

3)、prj.conf为配置文件,很多时候还需要一个overlay文件,可以进行设备树驱动的更改,有一个默认的,如果需要定义更改,就用overlay文件进行实现

 由于本次我使用的是nordic的SDK,NCS,我可以在VS code上安装好相关插件然后直接镜像创建一个工程在其余文件中(根据自己选择,但是保证和NCS处于同一个磁盘中),如果不会请参看前面提到的教学文档与视频,在看后,你就可以理解为什么只是这几个文件就可以建立一个工程了。

因此我们根据NCS中的hello_word建立一个镜像工程,并把该工程的文件夹名字命令为gpiote,且工程也建立为gpiote,,然后建立一个可以跑在nrf5340的应用核的工程,如下,该工程主要功能是,通过串口打印出,hello world+板子信息。

 2、添加自定义.c文件

原本已经有一个.c文件了,该文件中主要就是串口打印信息,本次测试是需要测试中断,所以我们在定义一个名字为gpiote.c的文件,添加到我们工程,然后再进行代码编写,在src中加入一个gpiote.c文件,

然后把gpiote.c加入到工程,这就需要我们打开我们的CMakeLists.txt,添加如图所示代码:

 然后点击全编译,我们就可以看到我们的工程下加入了gpiote.c文件:

全编译如下:

 3、overlay文件加入

这里有一个隐藏的规则,如果你看了前面推荐的官网连接,那么应该知道,在工程目录下建立文件名和我们使用的板子一致时,可以不用在CMakeLists.txt中进行文件添加,编译器建立工程时可以识别这overlay文件,知道你要更改默认的devicetree定义,会把你加入进入,如果不知道请去看下前面给出的连接,那么zephyr第一那些板子呢,他们的名是什么,可以直接在vs code确看,就行是你建立工程时选择的板子名字:

由于我使用的是nrf5340,那么我就建立一个同名的overlay文件,最后我们工程目录如下,就看我框选部分,其余是建立hello_word镜像工程时产生的:

 

 二、设备树更改

这里注意的是我使用了1.8的NCS,如果你使用高版本的NCS如2.1,那么overlay文件会有一点便跟,你可以参考其余工程。

主要是添加一个中断口定义,我们在nrf5340dk_nrf5340_cpuapp.overlay中进行处理,在添加前我们来看一下设备树文件zephyr.dts,建立编译工程后,可以在如下目录找到它:

 可以看到已经有一个buttons的设备定义了,我想自己加一个自己的按键定义,作为中断触发源,我使用的是官方开发板,按键依然是那几个,但是我可以再定义一个,然后起一个其他名字,在overlay中添加如下代码:

  1. /*参数加入devicetree的位置*/
  2. /{
  3. /*其别名,这主要给test_button其一个别名,然后可以在APP中通过别名gpiote定位到我们定义的按键*/
  4. aliases {
  5. gpiote = &test_button;
  6. };
  7. /*在原有的buttons下定义一个测试IO口,并且定位为GPIO0的0x17脚,即P0.23,名字为test_gpiote*/
  8. buttons{
  9. test_button: test_button {
  10. gpios = < &gpio0 0x17 0x11 >;
  11. label = "test_gpiote";
  12. };
  13. };
  14. };

截图如下:

 编译后可以在zephyr.dts中看到本次定义:

 三、应用代码编写

1、常规方式:

在gpiote.c中的代码如下:

  1. #include "device.h"
  2. #include "irq.h"
  3. #include <zephyr.h>
  4. #include <sys/printk.h>
  5. #include <sys/util.h>
  6. #include <device.h>
  7. #include <devicetree.h>
  8. #include <drivers/gpio.h>
  9. #include <nrfx.h>
  10. #include <dk_buttons_and_leds.h>
  11.  
  12. #define PIN DT_GPIO_PIN(DT_ALIAS(gpiote), gpios)
  13. /* 建立一个gpio引脚的类*/
  14. struct gpio_pin {
  15. const char * const port;
  16. const uint8_t number;
  17. };
  18. /* 定义gpio_pin类型的变量,用于读取设备定义信息,这以数组的形式定义,
  19. 便于有多个按键时可以直接定义,ARRAY_SIZE用于计算大小*/
  20. static const struct gpio_pin init_pin[] ={
  21. {DT_GPIO_LABEL(DT_ALIAS(gpiote), gpios),
  22. DT_GPIO_PIN(DT_ALIAS(gpiote), gpios)},
  23. };
  24. /* */
  25. static const struct device * init_device[ARRAY_SIZE(init_pin)];
  26. /* 回调的变量*/
  27. static struct gpio_callback gpiote_cb;
  28. /*回调函数*/
  29. void gpio_init_handle(const struct device *port,
  30. struct gpio_callback *cb,
  31. gpio_port_pins_t pins)
  32. {
  33. printk("run to gpiote test\n");
  34. }
  35. /*GPIOte程序*/
  36. void gpiote_test(void)
  37. {
  38. /*如果时有多个按键可以增加数组个数*/
  39. int err;
  40. uint32_t pin_mask = 0;
  41. // gpio_flags_t flags = (IS_ENABLED(CONFIG_DK_LIBRARY_INVERT_BUTTONS) ?
  42. // GPIO_PULL_UP : GPIO_PULL_DOWN);
  43. /*获取设备*/
  44. init_device[0]=device_get_binding(init_pin[0].port);
  45. if (!init_device[0]) {
  46. printk("Cannot bind gpio device");
  47. }
  48. /*配置gpio口,输入上拉*/
  49. err = gpio_pin_configure(init_device[0], init_pin[0].number,
  50. GPIO_INPUT | GPIO_PULL_UP);
  51. if (err) {
  52. printk("Cannot configure button gpio");
  53. }
  54. /*中断配置*/
  55. err = gpio_pin_interrupt_configure(init_device[0],
  56. init_pin[0].number, GPIO_INT_DISABLE);
  57. if (err) {
  58. printk("Cannot disable callbacks()");
  59. }
  60. pin_mask |= BIT(init_pin[0].number);
  61. /*回调设置*/
  62. pin_mask |= BIT(init_pin[0].number);
  63. /*回调设置*/
  64. gpio_init_callback(&gpiote_cb, gpio_init_handle, pin_mask);
  65. /*将刚刚绑定的结构添加到向量表中*/
  66. err = gpio_add_callback(init_device[0], &gpiote_cb);
  67. if (err) {
  68. printk("Cannot add callback");
  69. }
  70. /*将GPIO中断配置为下降沿触发,并启用它*/
  71. err = gpio_pin_interrupt_configure(init_device[0],
  72. init_pin[0].number, GPIO_INT_EDGE_FALLING);
  73. if (err) {
  74. printk("Cannot disable callbacks()");
  75. }
  76. printk("test start\n");
  77. while(1)
  78. {
  79. }
  80. }
  81. /*创建一个区别于main.c中的线程,用于初始haulgpiote功能 */
  82. K_THREAD_DEFINE(gpiote_test_id,1024,gpiote_test,NULL,NULL,NULL,7,0,0);

在此程序的基础上,你可以定义多个按键并放入设备模型数组,虽然我本次测试只使用了一个按键,如果你添加的是多个按键,记得初始化时用for循环,把每一个设备都添加一下,我这只有一个设备说以只使用了数组的第0位的设备(也只有一个)。

结果:

 2、zephyr中的direct ISR(直接中断模式)

APP我们不用更改,只要把驱动中的IRQ_CONNECT();替换为IRQ_DIRECT_CONNECT();然后再加入zephy官方文档定义的代码:

 你可以在工程的如下地方找到这个文件,然后更改原始定义,改部分代码已经更改:

 可以直接替换代码:

  1. #define CONFIG_direct_isr
  2. #ifdef CONFIG_direct_isr
  3. ISR_DIRECT_DECLARE(gpiote_event_handler_direct)
  4. {
  5. gpiote_event_handler();
  6. ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */
  7. return 1; /* We should check if scheduling decision should be made */
  8. }
  9. #endif
  10. static int gpio_nrfx_init(const struct device *port)
  11. {
  12. static bool gpio_initialized;
  13. if (!gpio_initialized) {
  14. gpio_initialized = true;
  15. #ifdef CONFIG_direct_isr
  16. IRQ_DIRECT_CONNECT(DT_IRQN(GPIOTE_NODE), DT_IRQ(GPIOTE_NODE, priority),
  17. gpiote_event_handler_direct, 0);
  18. #else
  19. IRQ_CONNECT(DT_IRQN(GPIOTE_NODE), DT_IRQ(GPIOTE_NODE, priority),
  20. gpiote_event_handler, NULL, 0);
  21. #endif
  22. irq_enable(DT_IRQN(GPIOTE_NODE));
  23. nrf_gpiote_int_enable(NRF_GPIOTE, NRF_GPIOTE_INT_PORT_MASK);
  24. }
  25. return 0;
  26. }

编译下载即可:

四、中断向量表查看

在如下目录可以看到我们的中断服务程序入口:其中21753就是本次中断ISR的如果地址:

 在这个数组下还有中断向量表,可以自行查看:

 

GPIO测试到此结束。如有错漏欢迎评论指正。

 

原文链接:https://www.cnblogs.com/HW-liu/p/16896552.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号