当前位置: 首页 > news >正文

4.nRF52xx蓝牙学习(GPIOTE与外部中断)

       GPIO 任务和事件 (GPIOTE) 模块提供了使用任务和事件访问 GPIO 引脚的功能。每个 GPIOTE 通道可以被分配到一个引脚。GPIOTE 其实就是对 GPIO 口进行操作,同时引入了外部中断的概念。 比如按键控制分为两种情况,第一种是按键扫描,这种情况下,CPU 需要不停的工作,来判断 GPIO 口是否被拉低或者置高,效率是比较低的。另一种方式为外部中断控制,中断控制的效率很高,一 旦系统 IO 口出现上升沿或者下降沿电平就会触发执行中断内的程序。在 nrf52840 内普通 IO 管脚设 置成为 GPIO ,中断和任务管脚设置称为 GPIOTE 。 nRF5x 系列处理器将 GPIO 的中断的快速触发做成了一个单独的模块 GPIOTE ,这个模块不仅 提供了 GPIO 的中断功能,同时提供了通过 task event 的方式来访问 GPIO 的功能。 GPIOTE 的后 缀 T 即为 task ,后缀 E 即为 event 。 Event 称为事件,来源与 GPIO 的输入、定时器的匹配中断等可以产生中断的外设来触发。 Task 称为任务,就是执行某一个特定功能,比如翻转 IO 端口等。那么事件 event 触发应用的任务 task 。 task 和 event 的主要是为了和 52832 中的 PPI (可编程外围设备互联系统)模块的配合使用, PPI 模 块可以将 event task 分别绑定在它的两端,当 event 发生时, taks 就会自动触发。这种机制不需要 CPU 参与,极大的减小了内核负荷,降低了功率,特别适合与 BLE 定功耗蓝牙里进行应用。 GPIOTE 实际上就两种模式,一个任务模式,一个事件模式。 其中任务模式作为输出使用 ,而 事 件模式就作为中断触发使用
任务模式( task ):每个 GPIOTE 通道最多可以使用三个任务来执行对引脚的写操作。两个任
务是固定的输出高电平( SET )和输出低电平( CLR ),一个输出任务( OUT )可配置为执行以下
操作:
置位( Set
清零( Clear
切换( Toggle
事件模式( event ):可以从以下输入条件之一在每个 GPIOTE 通道中生成事件:
上升的边缘
下降的边缘
任何改变
任务模式有三种状态:置位,清零,翻转。事件模式三种触发状态:上升沿触发,下降沿触发,
任意变化触发。 TASK 任务通过通道 OUT[0]~OUT[7] 设置输出三种触发状态, Event 则可以通过检
测信号产生 PORT event 事件,也可以产生 IN[n] event 事件。
整个 GPIOTE 寄存器的个数也是非常少的,如下表 6.1 所示:
GPIOTE 模块提供的了 8 个通道,这 8 个通道都是通过 CONFIG[0]~CONFIG[7] 寄存器来配置。
这八个通道可以通过单独设置来分别和普通的 GPIO 绑定。当需要使用 GPIOTE 的中断功能时可以 设置相关寄存器的相关位,让某个通道作为 event 事件模式,同时配置触发 event 的动作。比如绑定 的引脚有上升沿跳变或者下降沿跳变触发 event , 然后配置中断使能寄存器,配置让其 event 产生时 是触发输入中断。这样就实现了 GPIO 的中断方式。
       
1 GPIO 绑定 GPIOTE 通道
那么如何实现和普通 GPIO 端口的绑定了?关键就是设置 GPIOTE CONFIG[n]n=0~7 寄存器,
该寄存器如下表所示:
如上表所描述,每个 GPIOTE 通道通过 CONFIG.PSEL 字段与一个物理 GPIO 引脚相关联绑定。
CONFIG.MODE 中选择事件模式时: CONFIG.PSEL 绑定的引脚将被配置为输入,从而覆盖 GPIO 中的 DIR 设置。同样,当在 CONFIG.MODE 中选择任务模式时: CONFIG.PSEL 绑定的引脚将被配 置为输出,就覆盖 GPIO 模块中 DIR 寄存器设置和 OUT 值的输出。
当在 CONFIG.MODE 中选择 Disabled 时, CONFIG.PSEL 指定的引脚将使用普通 GPIO PIN
[n] .CNF 寄存器的配置,也就是不绑定。因此只能将一个 GPIOTE 通道分配给一个 GPIO 物理引脚。
2 :当设置为事件模式:
当设置事件模式时,因为事件模式就是输入,通过输入信号可以触发事件中断。基本步骤如下:
首先在寄存器 CONFIG.PSEL 域设置绑定管脚,当设置了一个 GPIO 管脚绑定了 GPIOTE 通道后, 再 CONFIG.MODE 域设置为事件模式;之后在 CONFIG.POLARITY 域中设置触发事件模式的输入 电平。当对应电平输入 GPIOTE 通道后就会产生中断, EVENTS_IN 寄存器就来判断对应端口中断 事件是否发生。
3 :当设置为任务模式:
因为任务模式为输出模式。配置过程首先需要设置 CONFIG.PSEL 域设置绑定 GPIO 管脚,再
设置 CONFIG.MODE 域设置 GPIOTE 为任务模式 ;再来设置 CONFIG.POLARITY 域中设置 OUT[n] 任务输出:
置位( Set
清零( Clear
切换(Toggle
设置完 CONFIG 配置寄存器后,再来触发任务:
TASKS_OUT[n]
触发 CONFIG.POLARITY 域中设置 OUT[n]
TASKS_SET[n] 触发输出高电平( SET
TASKS_CLR[n]
触发输出低电平( CLR
当三个状态触发同时申请,则根据下表的优先级决定先执行那钟设置:
4 : 中断配置:
中断是在事件模式下触发的,如果在配置寄存器 CONFIG[n] 中,绑定了对应的 GPIO 端口,同时配置为事件输入模式,那么可以通过 INTENSET 寄存器使能对 应的中断通道。通过 INTENCLR 寄存器关闭对应的中断通道。 INTENSET 寄存器和 INTENCLR 寄 存器如下表所示
下面以按键中断为例子来简单说明一下:
exit.h函数
#ifndef __EXIT_H
#define	__EXIT_H#include "nrf52840.h"#define KEY_0       11
#define KEY_1       12void EXIT_KEY_Init(void);#endif /* __EXIT_H */

exit.c函数

#include "nrf52840.h"
#include "nrf_gpio.h"
#include "exit.h"
#include "led.h"void Delay(uint32_t temp)
{for(; temp!= 0; temp--);
} 
void EXIT_KEY_Init(void)
{nrf_gpio_cfg_input(KEY_0,NRF_GPIO_PIN_PULLUP);//设置管脚位上拉输入nrf_gpio_cfg_input(KEY_1,NRF_GPIO_PIN_PULLUP);//设置管脚位上拉输入NVIC_EnableIRQ(GPIOTE_IRQn);//中断嵌套设置NRF_GPIOTE->CONFIG[0] =  (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)| (11 << GPIOTE_CONFIG_PSEL_Pos)  | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);//中断配置(详细说明请参看青风教程)NRF_GPIOTE->INTENSET  = GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos;// 使能中断类型:NRF_GPIOTE->CONFIG[1] =  (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)| (12<< GPIOTE_CONFIG_PSEL_Pos)  | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);//中断配置(详细说明请参看青风教程)NRF_GPIOTE->INTENSET  = GPIOTE_INTENSET_IN1_Set << GPIOTE_INTENSET_IN1_Pos;// 使能中断类型:
}void GPIOTE_IRQHandler(void)
{if(nrf_gpio_pin_read(KEY_0)== 0){if ((NRF_GPIOTE->EVENTS_IN[0] == 1) && (NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_IN0_Msk)){NRF_GPIOTE->EVENTS_IN[0] = 0; //中断事件清零.Delay(10000);	if(nrf_gpio_pin_read(KEY_0)== 0){LED1_Toggle();//led灯翻转}}}if(nrf_gpio_pin_read(KEY_1)== 0){if ((NRF_GPIOTE->EVENTS_IN[1] == 1) && (NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_IN1_Msk)){NRF_GPIOTE->EVENTS_IN[1] = 0; //中断事件清零.LED2_Toggle();//led灯翻转}}}

main.c文件:


#include "nrf52840.h"
#include "nrf_gpio.h"
#include "exit.h"
#include "led.h"int main(void)
{LED_Init();LED1_Open();/*config key*/EXIT_KEY_Init();  while(1){}
}

代码个人理解:

                                                         1.

nrf_gpio_cfg_input(KEY_0,NRF_GPIO_PIN_PULLUP);//设置管脚位上拉输入
     nrf_gpio_cfg_input(KEY_1,NRF_GPIO_PIN_PULLUP);//设置管脚位上拉输入

引脚11,12设为输入模式,并且设置上拉电阻,当按键没按下时,保持高电平,当按键按下时,变成低电平,由高到低产生下降沿,再设置中断为下降沿触发。

                                                       2.

NRF_GPIOTE->CONFIG[0] =  (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)
                           | (11 << GPIOTE_CONFIG_PSEL_Pos)  
                           | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);

配置中断通道0与引脚11绑定,此代码的功能是对 GPIOTE 模块的通道 0 进行配置,也就是设置该通道的极性、选择引脚,并且指定工作模式。

(1)NRF_GPIOTE是一个结构体,是一个指向 GPIOTE 模块寄存器基地址的指针。在 nRF 系列微控制器里,所有与 GPIOTE 模块相关的寄存器都能通过这个指针来访问。其成员是GPIOTE寄存器TASKS_OUT[8],TASKS_SET[8],

#define NRF_GPIOTE                  ((NRF_GPIOTE_Type*)        NRF_GPIOTE_BASE)
typedef struct {                                //!< (@ 0x40006000) GPIOTE Structure                                         __OM  uint32_t  TASKS_OUT[8];                 //< (@ 0x00000000) Description collection: Task for writing to pin//  specified in CONFIG[n].PSEL. Action on pin//  is configured in CONFIG[n].POLARITY.                    __IM  uint32_t  RESERVED[4];__OM  uint32_t  TASKS_SET[8];                 //< (@ 0x00000030) Description collection: Task for writing to pin// specified in CONFIG[n].PSEL. Action on pin//  is to set it high.                                       __IM  uint32_t  RESERVED1[4];__OM  uint32_t  TASKS_CLR[8];                 //< (@ 0x00000060) Description collection: Task for writing to pin//   specified in CONFIG[n].PSEL. Action on pin// is to set it low.                                       __IM  uint32_t  RESERVED2[32];__IOM uint32_t  EVENTS_IN[8];                 //< (@ 0x00000100) Description collection: Event generated from//  pin specified in CONFIG[n].PSEL                            __IM  uint32_t  RESERVED3[23];__IOM uint32_t  EVENTS_PORT;                  //< (@ 0x0000017C) Event generated from multiple input GPIO pins// with SENSE mechanism enabled                              __IM  uint32_t  RESERVED4[97];__IOM uint32_t  INTENSET;                     //< (@ 0x00000304) Enable interrupt                                          __IOM uint32_t  INTENCLR;                     //< (@ 0x00000308) Disable interrupt                                        __IM  uint32_t  RESERVED5[129];__IOM uint32_t  CONFIG[8];                    //< (@ 0x00000510) Description collection: Configuration for OUT[n],//   SET[n], and CLR[n] tasks and IN[n] event                  
} NRF_GPIOTE_Type;                              //< Size = 1328 (0x530) 
(2) NRF_GPIOTE->CONFIG[0]

CONFIG 是一个数组,其每个元素都对应着 GPIOTE 模块的一个通道。这里的 CONFIG[0] 表示配置 GPIOTE 模块的通道 0。

(3) (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)
  • GPIOTE_CONFIG_POLARITY_HiToLo:这是一个预定义的常量,代表引脚电平从高到低变化时触发事件或者任务。
  • GPIOTE_CONFIG_POLARITY_Pos:这同样是一个预定义的常量,代表 CONFIG 寄存器中极性配置位的起始位置。
  • << 是左移运算符,将 GPIOTE_CONFIG_POLARITY_HiToLo 的值左移 GPIOTE_CONFIG_POLARITY_Pos 位,从而把极性配置值放到 CONFIG 寄存器的正确位置。
(4 )(11 << GPIOTE_CONFIG_PSEL_Pos)
  • 11:这个数值代表要选择的引脚编号,意味着使用第 11 号引脚。
  • GPIOTE_CONFIG_PSEL_Pos:这是一个预定义的常量,代表 CONFIG 寄存器中引脚选择配置位的起始位置。
  • 左移运算符 << 把引脚编号 11 左移 GPIOTE_CONFIG_PSEL_Pos 位,进而将引脚选择值放到 CONFIG 寄存器的正确位置。
(5)(GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos)
  • GPIOTE_CONFIG_MODE_Event:这是一个预定义的常量,代表 GPIOTE 通道工作在事件模式。
  • GPIOTE_CONFIG_MODE_Pos:这是一个预定义的常量,代表 CONFIG 寄存器中工作模式配置位的起始位置。
  • 左移运算符 << 把 GPIOTE_CONFIG_MODE_Event 的值左移 GPIOTE_CONFIG_MODE_Pos 位,将工作模式配置值放到 CONFIG 寄存器的正确位置。
(6)| 运算符

| 是按位或运算符,它把上述三个经过移位操作后的值组合起来,最终赋值给 NRF_GPIOTE->CONFIG[0],以此完成 GPIOTE 通道 0 的配置。

(7)CONFIG[0] ,通道0,这里与引脚11绑定。

CONFIG[0]是一个32位寄存器,其内容如下 :

A:MODE字段,0-1两位,故GPIOTE_CONFIG_MODE_Pos=0;

B:PSEL字段,8-12五位,故GPIOTE_CONFIG_PSEL_Pos=8;

C:PORT字段,第13位共一位

D:POLARITY字段,16-17共两位,故GPIOTE_CONFIG_POLARITY_Pos=16;

E:OUTINIT字段,第20位共一位。

                                                                    3.

 NRF_GPIOTE->INTENSET  = GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos;

此代码主要用于配置 Nordic Semiconductor 的 nRF 系列微控制器中 GPIOTE(General Purpose Input Output Task and Event)模块的中断使能。具体而言,它开启了 GPIOTE 模块中输入通道 0 的中断功能。

INTENSET 是 GPIOTE 模块中的一个寄存器,其用途是设置中断使能位。当向这个寄存器的某一位写入 1 时,就会使能对应的中断;写入 0 则不会改变该位的状态。

 GPIOTE_INTENSET_IN0_Set
这是一个预定义的常量,通常代表一个值为 1 的常量。它表示要使能输入通道 0 的中断。
GPIOTE_INTENSET_IN0_Pos
这也是一个预定义的常量,代表 INTENSET 寄存器中用于控制输入通道 0 中断使能的位的位置。
GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos
这里使用了左移运算符 <<。左移操作的作用是将 GPIOTE_INTENSET_IN0_Set 的值(即 1)向左移动 GPIOTE_INTENSET_IN0_Pos 位,从而将 1 放置到 INTENSET 寄存器中对应输入通道 0 中断使能的正确位置。
赋值操作
最终,将左移操作的结果赋值给 NRF_GPIOTE->INTENSET 寄存器,这样就完成了对输入通道 0 中断使能的配置。

                                                                           4.

void GPIOTE_IRQHandler(void)
{
   if(nrf_gpio_pin_read(KEY_0)== 0)
     {
    if ((NRF_GPIOTE->EVENTS_IN[0] == 1) && 
        (NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_IN0_Msk))
    {
        NRF_GPIOTE->EVENTS_IN[0] = 0; //ÖжÏʼþÇåÁã.
              Delay(10000);    
             if(nrf_gpio_pin_read(KEY_0)== 0)
             {
              LED1_Toggle();//ledµÆ·­×ª
             }
            
    }
     }

这段代码定义了一个名为 GPIOTE_IRQHandler 的中断处理函数,该函数用于处理 GPIOTE(General Purpose Input Output Task and Event)模块产生的中断。当检测到特定按键(KEY_0)按下,并且 GPIOTE 通道 0 产生中断事件时,会对 LED 灯(LED1)进行状态切换操作。同时,为了避免按键抖动,代码中加入了简单的消抖处理。

GPIOTE_IRQHandler 是一个中断处理函数,当 GPIOTE 模块产生中断时,硬件会自动调用这个函数。在 Nordic 的 nRF 系列微控制器中,中断处理函数的命名是固定的,用于处理特定的中断源。

nrf_gpio_pin_read 是一个用于读取 GPIO 引脚电平状态的函数。
KEY_0 是一个宏定义,代表按键所连接的 GPIO 引脚编号。
此条件判断语句用于检测按键 KEY_0 是否被按下。在很多系统中,按键按下时引脚电平为低电平(即 0)

EVENTS_IN[0] 是 GPIOTE 模块中通道 0 的事件标志位。当该位为 1 时,表示通道 0 产生了中断事件。
INTENSET 是 GPIOTE 模块的中断使能设置寄存器。
GPIOTE_INTENSET_IN0_Msk 是一个掩码,用于检查通道 0 的中断使能位是否被设置。
这个条件判断语句用于确认 GPIOTE 通道 0 确实产生了中断事件,并且该通道的中断功能是被使能的。

将 EVENTS_IN[0] 标志位清零,目的是清除已经处理过的中断事件,以便后续能正确检测新的中断事件


http://www.mrgr.cn/news/97369.html

相关文章:

  • Docker基础2
  • 【前端】Node.js一本通
  • 红宝书第二十九讲:详解编辑器和IDE:VS Code与WebStorm
  • 21 天 Python 计划:MySQL 库相关操作
  • 类与对象(中)(详解)
  • k8s1.24升级1.28
  • [刷题总结] 双指针 滑动窗口
  • 【内网安全】DHCP 饿死攻击和防护
  • Gerapy二次开发:用户管理专栏主页面开发
  • 【ARTS】【LeetCode-2873】有序三元组中的最大值!
  • CSS快速上手
  • 手撕LLM(二):从源码出发,探索LoRA加载、推理全流程
  • CentOS Linux升级内核kernel方法
  • rust 同时处理多个异步任务,并在一个任务完成退出
  • 毕业设计:实现一个基于Python、Flask和OpenCV的人脸打卡Web系统(六)
  • Nginx 生产配置文件
  • 数据分析-Excel-学习笔记Day1
  • TYUTJava阶段测试
  • 新一代AI架构实践:数字大脑AI+智能调度MCP+领域执行APP的黄金金字塔体系
  • java发送http请求