当前位置: 首页 > 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       12

void 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] 标志位清零,目的是清除已经处理过的中断事件,以便后续能正确检测新的中断事件

相关文章:

  • 新增一种线性回归的增量学习框架,已更新31个模型!Matlab回归预测大合集又更新啦!
  • 【备赛】蓝桥杯实现多个LED联合控制
  • Java 21新特性实战:虚拟线程如何让吞吐量提升10倍?
  • WPS宏开发手册——附录
  • PostgreSQL迁移
  • vs中两个项目同在一个解决方案时,只生成一个的bug
  • Redis 渐进式rehash怎么判定rehash完成了?
  • Java基础 4.6
  • 算法专题(八):分治-归并排序
  • Cyber Weekly #50
  • TCPIP详解 卷1协议 一 概述
  • 【C/C++】打开转盘锁(leetcode T752)
  • Java EE期末总结(第四章)
  • VBA之Excel应用第四章第二节:单元格对象的行、列属性
  • Flask学习笔记 - 数据库
  • 网络安全应急响应-用户信息排查
  • 鸿蒙Arkts开发飞机大战小游戏,包含无敌模式,自动射弹,暂停和继续
  • 手写JSX实现虚拟DOM
  • Spring Boot 中的 Bean
  • 【C语言】数据在内存中的储存(整形)
  • 海拉尔做网站的公司/推荐6个免费国外自媒体平台
  • 烟台赶集网网站建设/优化排名软件
  • 网站建设客户管理系统/广州网站营销优化qq
  • 网站用户运营/今日热点新闻一览
  • 如何建设一个人工智能网站/市场营销策划公司排名
  • 设计衣服的网站/网络推广哪个平台好