4

zephyr的GPIOTE驱动开发记录——基于nordic的NCS - 星辰_stars

 1 year ago
source link: https://www.cnblogs.com/HW-liu/p/16896552.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

zephyr的GPIOTE驱动开发记录——基于nordic的NCS

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

2623060-20221116141416653-124213569.png

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

2623060-20221116151741513-330838084.png

如果某个任务完全不想要被打断,快速的执行,那么就可以使用直接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可以直接建立一个文件夹,然后再里面包含如下的几个文件就可以进行编译开发了,

2623060-20221116154143697-1596591549.png

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

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

#这是cmake的版本
cmake_minimum_required(VERSION 3.20.0)

#添加的库
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

#建立的工程名字(本次为hello_world,可以改为GPIOTE等)
project(hello_world)

#添加.c文件,稍后在src中建立一个main.c
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+板子信息。

2623060-20221116160949812-202396417.png

 2、添加自定义.c文件

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

2623060-20221116162617570-1282476647.png

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

2623060-20221116162903770-1617874827.png

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

2623060-20221116163029117-1980519115.png

全编译如下:

2623060-20221116163125561-1187996863.png

 3、overlay文件加入

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

2623060-20221116163938557-568173783.png

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

2623060-20221116164100464-218892725.png

 二、设备树更改

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

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

2623060-20221116164726948-1600587909.png

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

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

截图如下:

2623060-20221116165754933-1191491892.png

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

2623060-20221116165923661-658296822.png

 三、应用代码编写

1、常规方式:

在gpiote.c中的代码如下:

#include "device.h"
#include "irq.h"
#include <zephyr.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <nrfx.h>
#include <dk_buttons_and_leds.h>

#define PIN DT_GPIO_PIN(DT_ALIAS(gpiote), gpios)
/* 建立一个gpio引脚的类*/
struct gpio_pin {
    const char * const port;
    const uint8_t number;
};
/* 定义gpio_pin类型的变量,用于读取设备定义信息,这以数组的形式定义,
便于有多个按键时可以直接定义,ARRAY_SIZE用于计算大小*/
static const struct gpio_pin init_pin[] ={
    {DT_GPIO_LABEL(DT_ALIAS(gpiote), gpios),
     DT_GPIO_PIN(DT_ALIAS(gpiote), gpios)},
};
/* */
static const struct device * init_device[ARRAY_SIZE(init_pin)];
/* 回调的变量*/
static struct gpio_callback gpiote_cb;

/*回调函数*/
void gpio_init_handle(const struct device *port,
                    struct gpio_callback *cb,
                    gpio_port_pins_t pins)
{
 printk("run to gpiote test\n");

}
/*GPIOte程序*/
void gpiote_test(void)
{
    /*如果时有多个按键可以增加数组个数*/
    int err;
    uint32_t pin_mask = 0;

    // gpio_flags_t flags = (IS_ENABLED(CONFIG_DK_LIBRARY_INVERT_BUTTONS) ?
    //              GPIO_PULL_UP : GPIO_PULL_DOWN);
    /*获取设备*/
    init_device[0]=device_get_binding(init_pin[0].port);
    if (!init_device[0]) {
        printk("Cannot bind gpio device");
    }
    /*配置gpio口,输入上拉*/
    err = gpio_pin_configure(init_device[0], init_pin[0].number,
                            GPIO_INPUT | GPIO_PULL_UP);
    if (err) {
        printk("Cannot configure button gpio");
    }

    /*中断配置*/
    err = gpio_pin_interrupt_configure(init_device[0],
            init_pin[0].number, GPIO_INT_DISABLE);
    if (err) {
        printk("Cannot disable callbacks()");
    }
    pin_mask |= BIT(init_pin[0].number);
    /*回调设置*/

    pin_mask |= BIT(init_pin[0].number);
    /*回调设置*/
    gpio_init_callback(&gpiote_cb, gpio_init_handle, pin_mask);

    /*将刚刚绑定的结构添加到向量表中*/
    err = gpio_add_callback(init_device[0], &gpiote_cb);
    if (err) {
        printk("Cannot add callback");
    }
    /*将GPIO中断配置为下降沿触发,并启用它*/
    err = gpio_pin_interrupt_configure(init_device[0],
            init_pin[0].number, GPIO_INT_EDGE_FALLING);
    if (err) {
        printk("Cannot disable callbacks()");
    }
    printk("test start\n");

    while(1)
    {
    }
}

/*创建一个区别于main.c中的线程,用于初始haulgpiote功能 */
K_THREAD_DEFINE(gpiote_test_id,1024,gpiote_test,NULL,NULL,NULL,7,0,0);

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

2623060-20221116171251513-1722292451.png

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

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

2623060-20221116172120274-995706073.png

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

2623060-20221116171825840-223384319.png

 可以直接替换代码:

#define CONFIG_direct_isr

#ifdef CONFIG_direct_isr
ISR_DIRECT_DECLARE(gpiote_event_handler_direct)
{
   gpiote_event_handler();
   ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */
   return 1; /* We should check if scheduling decision should be made */
}
#endif
static int gpio_nrfx_init(const struct device *port)
{
    static bool gpio_initialized;

    if (!gpio_initialized) {
        gpio_initialized = true;
        #ifdef CONFIG_direct_isr
        IRQ_DIRECT_CONNECT(DT_IRQN(GPIOTE_NODE), DT_IRQ(GPIOTE_NODE, priority),
                gpiote_event_handler_direct, 0);

        #else
        IRQ_CONNECT(DT_IRQN(GPIOTE_NODE), DT_IRQ(GPIOTE_NODE, priority),
                gpiote_event_handler, NULL, 0);
        #endif
        irq_enable(DT_IRQN(GPIOTE_NODE));
        nrf_gpiote_int_enable(NRF_GPIOTE, NRF_GPIOTE_INT_PORT_MASK);
    }

    return 0;
}

编译下载即可:

四、中断向量表查看

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

2623060-20221116172705509-660910680.png

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

2623060-20221116172845593-2916236.png

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK