Zephry_GPIO的中断使用详解以及中断原理

Posted 17岁boy想当攻城狮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zephry_GPIO的中断使用详解以及中断原理相关的知识,希望对你有一定的参考价值。

目录 

1. 开发环境

1.1 系统环境与硬件环境

2. GPIO中断

2.1 GPIO中断介绍

2.2 中断的好处

3. 分析Zephry设备树DTC文件

3.1 通过名称找到对应GPIO口

3.2 设备树类型

3.3 实例化设备树类型

3.4 GPIO操作API

4. 示例

4.1 包含头文件与定义操作的PIN脚

4.2 创建中断服务函数

4.3 实例化LED0 与 定义存储callback属性的结构体

4.4 定义一个逻辑变量来表示灯状态

4.5 开启中断

4.6 初始化led与循环控制led

4.7 完整代码:

5. 注意事项


1. 开发环境

1.1 系统环境与硬件环境

系统环境

系统

版本

UBNUTU20.04 LTS

硬件环境

厂商

型号

STM32F746G_DISCOCORTEX-M7 

中断控制器

型号

说明

NVIC (Nested Vectored Interrupt Controller)嵌套向量中断控制器

你可以在你的芯片资料中找到你的芯片是什么中断控制器

NVIC支持嵌套式中断,意思就是当触发中断时支持多层中断,每个中断拥有优先级,每个中断都会有一个优先级,CPU根据优先级高低执行不同的中断

如在执行当前中断时又来了一个中断,那么CPU会判断这个中断的中断优先级与目前执行的中断优先级相比谁更高,若大于当前中断则跳转过去执行新中断,否则挂起等待此次中断执行结束下次处理

NVIC与CPU紧密贴近,CPU可以通过总线与NVIC通讯获取当前中断的状态以确保该执行哪个中断

在操作系统封装之后我们基本上不用去关心使用的什么中断处理器,因为操作系统使用了统一API来管理这些中断处理器,如中断处理器FIQ或IRQ模式

中断信号

说明

FIQ快速中断 IRQ
IRQ外部中断 IR

那么要开启它们的话需要使用特定指令来使能对应的中断

指令

描述

cpsid i禁止IRQ中断
cpsie i使能IRQ中断
cpsid f禁止FIQ中断
cpsie f使能FIQ中断

2. GPIO中断

2.1 GPIO中断介绍

Zephry使用了与Linux内核下一致的Dtc设备树对底层硬件进行了封装 , 大部分的底层对上层是不可见的 , 同时提供了一组API用来便于我们操作这些底层硬件 , 使得开发变得更其简单了.

我们如果想开启一个GPIO中断首先需要知道这个GPIO名称 , 这里不是地址 , 相较于裸机我们需要通过GPIO得到地址并对里面的功能寄存器进行配置 , Zephry下只需要通过GPIO名称然后通过一组特定API就可以对其进行配置了.

Zephry下的GPIO中断与裸机编程时的中断无区别 , 流程都是先对GPIO寄存器进行配置 , 然后使能工作

这里我的中断处理器类型如下:

设置流程如下:

设置INPUT模式->设置INT中断模式->注册中断函数与配置触发条件 (上升沿触发 | 下降沿触发···)->注册到LVT->使能中断

 

2.1.2 中断执行过程

我之前学过x8086架构的,所以这里以x8086架构的中断来说会更加清晰一点

首先第一步当设备通过总线向8259A(x8086架构常用的中断控制器)发送中断信号时,8259A会将中断IRQ号发送给CPU

CPU根据IRQ中断表到中断向量表LVT中寻找对应的节点,LVT是一个队列结构体,IRQ号就是它的索引,在里面会找到一些基本信息与LDT(局部描述符)的段选择符

拿着这个段选择符(GDT全局描述符的索引)到GDT里去寻找中断服务程序的信息,其中就有一个属性叫DPL,它代表当前服务的优先级,这里CPU会根据优先级的不同来决定是否执行它

CPU会从当前CS寄存器(代码段基地址寄存器)里把CPL取出来,CPL是当前代码段的优先级,中断服务与普通程序的代码优先级是不同的,若当前CPL大于DPL那么则不会执行中断服务,否则执行。

其次8259A支持嵌套模式

一般完全嵌套方式:只执行优先级高的,低优先级的屏蔽不执行

特殊完全嵌套方式:优先级高的先执行,优先级低的保存起来等待执行

下图是特殊完全嵌套方式的8259A的中断过程

x8086的cpu会每执行一条语句就去检查一下中断标志位

 那么知道了x8086的以后其实arm架构的也很简单,arm架构与x8086相比就是少了GDT与LDT,它会直接在向量表里就可以找到中断服务的基本信息,其次新中断服务优先级是通过NVIC里的寄存器获得的

2.2 中断的好处

当我们需要为一个外设开发中断时首先应是确定外设接线位置 , 如我想为传感器开发一个驱动用于检测传感器的变化 , 但这个传感器只有在一些特定情况下才有会数据过来 , 如红外传感器只有在红外线有反馈的情况下才会输出数据 , 除此以外大多数情况下都是无数据的 , 难道我们要编写一个程序一直循环监听目标GPIO口是否有数据过来吗? 这样的做法非常不合理 , 因为这会吃尽CPU的占有率 , 一直处于while循环状态下监听不如此时让CPU进入低功耗状态或者我们去做一些其它的事情 , 当标志位溢出产生中断时才唤醒我们的CPU去干活这是最合理的设计.

3. 分析Zephry设备树DTC文件

3.1 通过名称找到对应GPIO口

我的开发板上有一个LED的灯 , 其实这里我们可以不用看它的原理图LED接在哪儿 , 我们只需要根据板子的型号去Zephry下的boards目录下找到你板子的dtc文件目录 , 这里已经把你板子所有的硬件环境配置出来了 , Zephry会调用脚本解析这个文件并生成对应的结构体文件供我们使用.

 如我的板子是stm32f746g_disco

可以在boards下去寻找 , 注意是小写 , Zephry下统一小写格式命名dts文件

find . -iname "stm32f746g_disco.dts"

如果没有找到你的板子可以根据类似的板型做适当的移植

如以下是我的配置:

/dts-v1/;
#include <st/f7/stm32f746Xg.dtsi>
#include <st/f7/stm32f746nghx-pinctrl.dtsi>
#include "arduino_r3_connector.dtsi"
 
/ {
    model = "STMicroelectronics STM32F746G DISCOVERY board";
    compatible = "st,stm32f746g-disco";
 
    chosen {
        zephyr,console = &usart1;
        zephyr,shell-uart = &usart1;
        zephyr,sram = &sram0;
        zephyr,flash = &flash0;
        zephyr,dtcm = &dtcm;
        zephyr,flash-controller = &n25q128a1;
    };
 
    leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };
 
    gpio_keys {
        compatible = "gpio-keys";
        user_button: button {
            label = "User";
            gpios = <&gpioi 11 GPIO_ACTIVE_HIGH>;
        };
    };
 
    aliases {
        led0 = &green_led_1;
        sw0 = &user_button;
        kscan0 = &touch_controller;
    };
};

可以看到里面的led已经定义出来 , 里面有一个aliases的选项树结构 , 这个关键字代表别名这里将green_led_1定义别名为led0 , 同时也有其它的别名

aliases {
        led0 = &green_led_1;
        sw0 = &user_button;
        kscan0 = &touch_controller;
    };

这些别名我们是可以直接在代码里使用的 , Zephry的编译系统会解析dts文件并生成对应的头文件并包含到我们的项目工程里去 , 这样编译期间就可以引用它们了.

其中也可以看到它包含了一些头文件 , 在dtc里dtsi是属于头文件的后缀 , 但是也可以包含.h文件 , 这也是支持的

#include <st/f7/stm32f746Xg.dtsi>
#include <st/f7/stm32f746nghx-pinctrl.dtsi>
#include "arduino_r3_connector.dtsi"

我们可以打开一个头文件看一下 , 就拿"stm32f746Xg.dtsi"头文件来说 , 它位于Zephry项目工程的"dts/arm/st/f7/"目录中 , 它里面定义了soc的基本信息

#include <mem.h>
#include <st/f7/stm32f746.dtsi>
 
/ {
    soc {
        flash-controller@40023c00 {
            flash0: flash@8000000 {
                reg = <0x08000000 DT_SIZE_K(1024)>;
            };
        };
    };
};

它里面包含了一个头文件"stm32f746.dtsi" 这个头文件包含了"stm32f745.dtsi" 而这个文件包含了所有GPIO以及UART等硬件信息

从这个包含角度可以得出stm32f746的架构与stm32f745基本一致.

可以打开stm32f745.dtsi看一下 (位于dts/arm/st/f7/stm32f745.dtsi)

由于代码过长这里只把要分析的部分列出来

#include <st/f7/stm32f7.dtsi>
···

它包含了"stm32f7.dtsi"文件 , 这个文件里定义f7系列的所有基本硬件信息  , 我们在打开它就可以看到所有的硬件信息

这里只列了一小部分

···           
           gpioc: gpio@40020800 {
                compatible = "st,stm32-gpio";
                gpio-controller;
                #gpio-cells = <2>;
                reg = <0x40020800 0x400>;
                clocks = <&rcc STM32_CLOCK_BUS_AHB1 0x00000004>;
                label = "GPIOC";
            };
 
            gpiod: gpio@40020C00 {
                compatible = "st,stm32-gpio";
                gpio-controller;
                #gpio-cells = <2>;
                reg = <0x40020C00 0x400>;
                clocks = <&rcc STM32_CLOCK_BUS_AHB1 0x00000008>;
                label = "GPIOD";
            };
 
            gpioe: gpio@40021000 {
                compatible = "st,stm32-gpio";
                gpio-controller;
                #gpio-cells = <2>;
                reg = <0x40021000 0x400>;
                clocks = <&rcc STM32_CLOCK_BUS_AHB1 0x00000010>;
                label = "GPIOE";
            };
 
            gpiof: gpio@40021400 {
                compatible = "st,stm32-gpio";
                gpio-controller;
                #gpio-cells = <2>;
                reg = <0x40021400 0x400>;
                clocks = <&rcc STM32_CLOCK_BUS_AHB1 0x00000020>;
                label = "GPIOF";
            };
···

可以很清晰的看到所有gpio信息 , 这里可以看下我们led的定义

leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };

它里面定义了一个gpios的口指向gpioi配置属性为即高电平有效 , 就是告诉Zephry想要驱动它必须给高电平 , 有的线路连接极性不同对电平高低有要求 , 有的线路可能需要低电平才能驱动 , 这部分需要看电路原理图.

通常情况下若Zephry不支持你的板子型号 , 我们只需要根据soc型号到Zephry下去寻找对应的DTC配置工程然后做一个简单的移植就可以了.

3.2 设备树类型

gpio_dt_spec类型是一个结构体 , 是专门用于实例化dtc设备树中定义的信息 , 它位于"zephyr/include/drivers"目录中

原型如下

struct gpio_dt_spec {
    const struct device *port;
    gpio_pin_t pin;
    gpio_dt_flags_t dt_flags;
};

参数介绍

变量名

类型

作用

portdevice*指向dtc实例化后的设备句柄
pingpio_pin_t指向设备pin脚
dt_flagsgpio_dt_flags_t设备属性

其中port变量里保存了一些基本的设备运行时的属性与参数 , 其原型如下

struct device {
    const char *name;
    const void *config;
    const void *api;
    struct device_state * const state;
    void * const data;
    const device_handle_t *const handles;
#ifdef CONFIG_PM_DEVICE
    pm_device_control_callback_t pm_control;
    struct pm_device * const pm;
#endif
};

参数介绍

变量名

类型

作用

apiconst void *设备实例公开的API结构的地址
configconst void *设备实例配置信息的地址
datavoid * const设备实例私有数据的地址
handlesdevice_handle_t *指向与设备关联的句柄的可选指针
nameconst char *设备实例的名称 , 在DTC文件中配置
pmpm_device *指向设备实例电源管理数据的指针 (仅设备支持电源情况下可用)
pm_controlpm_device_control_callback_t电源管理功能 (仅设备支持电源情况下可用)
statedevice_state *公共设备状态的地址

基本上当我们在需要使用定义在设备树文件中的总线或外设时都需要用到"gpio_dt_spec"类型

3.3 实例化设备树类型

在实例化设备树时提供了一组宏函数:

  1. DT_ALIAS
  2. DT_GPIO_LABEL
  3. DT_GPIO_PIN
  4. DT_GPIO_FLAGS
  5. GPIO_DT_SPEC_GET_OR

定义在:/zephyr/include/drivers/gpio.h

这组宏函数会在编译期间获取实例化的参数 , Zephry在真正实际编译我们代码之前会先调用脚本将DTC设备树解析并生成与修改对应的.h文件给我们c文件使用 , 会自动包含进来 , 这组宏函数就是获取生成的文件中对应的变量类型

这些工作都是在编译期间完成的.

这里介绍一下每个宏函数的作用以及用法

3.3.1 DT_ALIAS

函数原型

参数说明

作用

DT_ALIAS(alias)别名获取设备树中aliases元素下的别名类型

用法示例:

/*dtc文件  
 leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };
 
    aliases {
        led0 = &green_led_1;
    };
*/
/*用法*/
//从aliases元素中获取led0别名实际的名称
DT_ALIAS(led0) //返回green_led_1元素

3.3.2 DT_GPIO_LABEL

函数原型

参数说明

作用

DT_GPIO_LABEL(node_id, gpio_pha)节点名 , GPIO元素名称获取GPIO名称

用法示例:

/*dtc文件  
 leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };
 
    aliases {
        led0 = &green_led_1;
    };
*/
/*用法*/
//获取实际gpio名称
DT_GPIO_LABEL(green_led_1,gpios) //返回gpioi

3.3.3 DT_GPIO_PIN

函数原型

参数说明

作用

DT_GPIO_PIN(node_id, gpio_pha)节点名 , GPIO元素名称获取GPIO的PIN脚索引

用法示例:

/*dtc文件  
 leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };
 
    aliases {
        led0 = &green_led_1;
    };
*/
/*用法*/
//获取实际pin脚索引
DT_GPIO_LABEL(green_led_1,gpios) //返回1

3.3.4 DT_GPIO_FLAGS

函数原型

参数说明

作用

DT_GPIO_FLAGS(node_id, gpio_pha)节点名 , GPIO元素名称获取GPIO的属性

用法示例:

/*dtc文件  
 leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };
 
    aliases {
        led0 = &green_led_1;
    };
*/
/*用法*/
//获取GPIO属性 , 可以理解为获取GPIO中特殊功能寄存器的状态
DT_GPIO_FLAGS(green_led_1,gpios) //返回GPIO_ACTIVE_HIGH

3.3.5 GPIO_DT_SPEC_GET_OR

函数原型

参数说明

作用

GPIO_DT_SPEC_GET_OR(node_id, prop, default_value)节点名 , GPIO元素名称 , 下标索引获取GPIO的属性 , 并实例化为gpio_dt_spec类型

用法示例:

/*dtc文件  
 leds {
        compatible = "gpio-leds";
        green_led_1: led_1 {
            gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>;
            label = "User LD1";
        };
    };
 
    aliases {
        led0 = &green_led_1;
    };
*/
/*用法*/
//获取GPIO属性 , 可以理解为获取GPIO中特殊功能寄存器的状态
static struct gpio_dt_spec gpio_led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios,{0}) //这里索引用{}为格式 , 从0开始计算

3.4 GPIO操作API

  1. gpio_pin_configure_dt
  2. gpio_pin_interrupt_configure_dt
  3. gpio_init_callback
  4. gpio_add_callback
  5. gpio_pin_configure
  6. gpio_pin_set

定义在:/zephyr/include/drivers/gpio.h

函数统一返回值为:成功返回0,失败返回非0

函数原型:

3.4.1 gpio_pin_configure_dt

函数原型

参数说明

作用

gpio_pin_configure_dt(struct gpio_dt_spec * st, int FLAGS)dt实例化结构体 , 要设置的属性设置GPIO属性

3.4.2 gpio_pin_interrupt_configure_dt

函数原型

参数说明

作用

gpio_pin_interrupt_configure_dt(struct gpio_dt_spec * st, int FLAGS)dt实例化结构体 , 要设置的属性设置GPIO中断

3.4.3 gpio_init_callback

函数原型

参数说明

作用

gpio_init_callback(struct gpio_dt_spec * st, void (*func)(const struct device *,struct gpio_callback* ,uint32_t) ,_addr,bit_pin)dt实例化结构体 , 中断服务函数入口,pin脚需要使用BIT宏转换设置GPIO中断函数

3.4.4 gpio_add_callback

函数原型

参数说明

作用

gpio_add_callback(struct device *port , struct gpio_callback * st)device结构体 , 存储设置属性添加中断服务

3.4.5 gpio_pin_configure

函数原型

参数说明

作用

gpio_pin_configure(struct device *port , pin,int FLAGS)device结构体 , pin脚,属性设置GPIO相应PIN脚属性

3.4.6 gpio_pin_set

函数原型

参数说明

作用

gpio_pin_set(struct device *port , pin,int value)device结构体 , pin脚,值设置GPIO值

属性可选如下参数:

宏定义

说明

宏定义

说明

GPIO_INPUT输入模式
GPIO_OUTPUT输出模式
GPIO_DISCONNECTED禁用输入输出模式
GPIO_OUTPUT_INIT_LOW将输出初始状态设置为低
GPIO_OUTPUT_INIT_HIGH

将输出初始状态设置为高

GPIO_OUTPUT_INIT_LOGICAL根据逻辑级别初始化输出
GPIO_OUTPUT_LOW将GPIO引脚配置为输出,并将其初始化为低状态
GPIO_OUTPUT_HIGH将GPIO引脚配置为输出,并将其初始化为高状态。
GPIO_OUTPUT_INACTIVE将GPIO引脚配置为输出,并将其初始化为逻辑0。
GPIO_OUTPUT_ACTIVE将GPIO引脚配置为输出,并将其初始化为逻辑1。
GPIO_INT_DISABLE禁用GPIO引脚中断
GPIO_INT_LEVELS_LOGICAL电平敏感
GPIO_INT_EDGE边沿敏感
GPIO_INT_LOW_0低电平或逻辑电平为0触发
GPIO_INT_HIGH_1高电平或逻辑电平为1触发
GPIO_INT_EDGE_RISING将GPIO中断配置为在引脚上升沿上触发并启用它
GPIO_INT_EDGE_FALLING将GPIO中断配置为在引脚上升沿或下降沿触发,并启用它
GPIO_INT_EDGE_FALLING将GPIO中断配置为在引脚下降沿上触发并启用它
GPIO_INT_EDGE_BOTH将GPIO中断配置为在引脚上升沿或下降沿触发,并启用它
GPIO_INT_LEVEL_LOW将GPIO中断配置为在引脚物理电平低时触发并启用它
GPIO_INT_LEVEL_HIGH将GPIO中断配置为在引脚物理电平高时触发并启用它
GPIO_INT_EDGE_TO_INACTIVE将GPIO中断配置为在引脚状态更改为逻辑级别0时触发,并启用它
GPIO_INT_EDGE_TO_ACTIVE将GPIO中断配置为在引脚状态更改为逻辑级别1时触发,并启用它
GPIO_INT_LEVEL_INACTIVE将GPIO中断配置为在引脚逻辑级别0上触发并启用它
GPIO_INT_LEVEL_ACTIVE

将GPIO中断配置为在引脚逻辑级别1上触发,并启用它

GPIO_INT_DEBOUNCE启用GPIO引脚去抖动

4. 示例

这里写一个监听LED灯的中断服务

业务需求:

检测LED灯是否亮起,若LED灯亮起则执行中断

4.1 包含头文件与定义操作的PIN脚

#include <zephyr.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <inttypes.h>
#include <sys/util.h>
#include <sys/printk.h>
//pin
#define PIN DT_GPIO_PIN(DT_ALIAS(led0), gpios)

4.2 创建中断服务函数

这里原型上面说过,要求是:void (*func)(const struct device *,struct gpio_callback* ,uint32_t)

这里说一下下面的参数作用:dev指向device设备指针,上面介绍过,可以通过它获取当前中断设备的一些基本信息

cb里面保存了中断服务的一些属性

pins是当前中断引脚的索引

void iqr_func(const struct device *dev,struct gpio_callback* cb,uint32_t pins){
 
    printk("okay\\n");
}

4.3 实例化LED0 与 定义存储callback属性的结构体

上面分析过DTC设备树,我们的别名是LED0,这里可以使用DT_ALIAS

static struct gpio_dt_spec gpio_led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios,{0});
static struct gpio_callback gpio_data;

4.4 定义一个逻辑变量来表示灯状态

bool led_is_on = false;

4.5 开启中断

这里的gpios是在dtc文件里定义的指向GPIO属性的结构元素

//设置中断为input模式,接受来自外部数据
gpio_pin_configure_dt(&gpio_led,GPIO_INPUT);
//将GPIO中断配置为在引脚状态更改为逻辑级别1时触发,并启用它
gpio_pin_interrupt_configure_dt(&gpio_led,GPIO_INT_EDGE_TO_ACTIVE);
//将中断函数与pin脚绑定,并将初始化结构存放到gpio_data中
gpio_init_callback(&gpio_data,my_isr,BIT(gpio_led.pin));
//将刚刚绑定的结构添加到向量表中
gpio_add_callback(gpio_led.port,&gpio_data);
//配置pin脚模式
gpio_pin_configure(gpio_led.port, PIN, GPIO_OUTPUT_ACTIVE | 0);

4.6 初始化led与循环控制led

//led
while (1) {
            //设置循环状态
            gpio_pin_set(gpio_led.port, PIN, (int)led_is_on);
            //状态取反
            led_is_on = !led_is_on;
            //沉睡一秒
            k_msleep(1000);
}

4.7 完整代码:

#include <zephyr.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <inttypes.h>
#include <sys/util.h>
#include <sys/printk.h>
//pin
#define PIN DT_GPIO_PIN(DT_ALIAS(led0), gpios)
 
//中断函数
void iqr_func(const struct device *dev,struct gpio_callback* cb,uint32_t pins){
 
    //向串口打印一行字符
    printk("okay\\n");
}
 
void main(void){
    //实例化dtc设备树
    static struct gpio_dt_spec gpio_led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios,{0});
    //存储初始call数据结构体
    static struct gpio_callback gpio_data;
    //led状态
    bool led_is_on = false;
    //设置中断为input模式,接受来自外部数据
    gpio_pin_configure_dt(&gpio_led,GPIO_INPUT);
    //将GPIO中断配置为在引脚状态更改为逻辑级别1时触发,并启用它
    gpio_pin_interrupt_configure_dt(&gpio_led,GPIO_INT_EDGE_TO_ACTIVE);
    //将中断函数与GPIO 引脚脚绑定,并将初始化结构存放到gpio_data中
    gpio_init_callback(&gpio_data,iqr_func,BIT(gpio_led.pin));
    //将刚刚绑定的结构添加到向量表中
    gpio_add_callback(gpio_led.port,&gpio_data);
    //配置GPIO引脚配置为输出,并将其初始化为逻辑1,使能led灯
    gpio_pin_configure(gpio_led.port, PIN, GPIO_OUTPUT_ACTIVE | 0);
    //led
    while (1) {
            //设置GPIO功能寄存器值,改变输出电流
            gpio_pin_set(gpio_led.port, PIN, (int)led_is_on);
            //状态取反
            led_is_on = !led_is_on;
            //沉睡一秒
            k_msleep(1000);
    }
     
}

上面代码运行并烧录到板子上,读取串口输出可以发现每过一秒输出一次okay

使用west debug即可开始调试

以下是调试过程

(gdb) b main.c:iqr_func
Breakpoint 1 at 0x80004d4: file /home/test/my_work/zephyr/irq/blinky/src/main.c, line 21.
(gdb) c
Info : halted: PC: 0x080004d6
halted: PC: 0x080004d6
Breakpoint 1, iqr_func (dev=0x80033bc <__device_dts_ord_19>, cb=0x20010148 <gpio_data>, pins=2)
    at /home/test/my_work/zephyr/irq/blinky/src/main.c:21
21          printk("okay\\n");

5. 注意事项

中断服务一定要快,不能慢,在中断上下文中CPU对中断服务有要求,不能在中断里嵌套可能造成死循环的函数以及代码,或者嵌套的函数里还嵌套了一曾类似递归的函数或者比较复杂的函数调用等都会导致CPU RESET,因为中断服务需要暂停其它工作,中断服务目的是为了不用用户监听等待,当来工作时候CPU通知一声然后由中断服务去通知对应的服务去处理然后CPU在正常工作。

以上是关于Zephry_GPIO的中断使用详解以及中断原理的主要内容,如果未能解决你的问题,请参考以下文章

5.分析内核中断运行过程,以及中断3大结构体:irq_descirq_chipirqaction

jvm之年轻代(新生代)老年代永久代以及GC原理详解GC优化

Zephry_中断的使用详解

中断系统结构及中断控制详解

中断程序详解(附例题)

谁学过51和ARM,这两个定时器计数器的中断处理函数的主要区别在哪里?特别是ARM,中断处理函数怎没理解?求