Nordic 的 NRF51 和 NRF52 系列芯片在 GPIO 的基础上引入了任务和事件(GPIOTE)
的概念。GPIOTE 能让我们更方便地去操作 GPIO,同时,他还能有效地减少程序的参与、
降低 CPU 的负担。

nRF52832 寄存器类型
说到 GPIOTE ,就需要先了解一下 nRF52832 的寄存器类型,和一般的单片机有所差别,
nRF52832 的寄存器分为下面的三种类型。

Task :任务寄存器,可以由程序或事件触发。
Event:事件寄存器,事件可以产生中断或触发任务。
Register:普通寄存器,和一般单片机的寄存器一样。
Task 和 event 使得操作片上外设十分方便简洁,只需进行少量的配置,即可轻松运用各
种外设。同时,Task 和 event 能有效减少 CPU 的占用时间,降低 CPU 的负荷。

Task 和 Event 更多的是用来和 PPI(可编程外设互连)配合使用,通过 PPI 将某个 Event
和 Task 连接起来,连接后,该 Event 即可触发对应的 Task 执行相应的功能。

示例:实现每隔 1S 翻转一次指示灯的状态。
一般的做法:配置定时器定时时间为 1S,每秒产生一次中断,在中断服务程序中通过软件操作翻转指示灯的状态。在这个过程中,必须要通过软件操作才能实现。
通过 Task 和 Event 实现:配置定时器的相关参数,配置 GPIOTE 的 Task 为翻转管脚状态,配置 PPI 的一个通道用于连接定时器的匹配事件和 GPIOTE 的 Task。这样,当定时器产生匹配事件时,会自动触发 GPIOTE 的 Task,在无需任何软件干预的情况下实现指示灯状态的翻转。
GPIOTE (GPIO Task and Events)功能
nRF52832 的 GPIOTE 共有 8 个通道,每个通道都可以选择一个管脚,选择的管脚可以
配置为 Task mode 或 Event mode。需要注意的是:不能将某个管脚同时分配给多个 GPIOTE
通道,否则会导致无法预料的错误。

GPIOTE 每个通道可以使用的 Task 有 3 个:

设置(Set)
清除(Clear)
切换(Toggle)
GPIOTE 每个通道的事件可以由以下的输入状态产生:

上升边缘(Rising edge)
下降边缘(Falling edge)
任何改变(Any change)
引脚Tasks和Events
Tasks 和 Events 通过 CONFIGn 寄存器配置,每个 CONFIG[n]寄存器对应一组OUT[n] 任务寄存器和 IN[n]事件寄存器。OUT[n]用于写引脚,IN[n]由引脚状态变化触发。

当把某个引脚分配给OUT[n]任务或IN[n]事件后,该引脚就只能被GPIOTE模块写操作,
正常的 GPIO 写入无效。

一旦配置 OUT[n]任务或 IN[n]事件控制某个引脚,那么该管脚的输出值只能通过 GPIOTE 模块操作,使用 GPIO 的寄存器操作会被忽略。

当 GPIOTE 通道被配置用于操作一个任务引脚时,CONFIG[n]寄存器中的 OUTINIT 决定了该引脚的初始值。可以通过配置 OUTINIT 来设置引脚初始化状态为高电平或是低电平。

当在同一个 GPIOTE 通道中同时触发了有冲突的任务(即同一个时钟周期内),那么任务按照下表所示的优先级执行。

优先级    任务
1    OUT
2    CLEAR
3    SET
PORT 事件
GPIOTE 除了 8 个通道外,还包含一个 PORT 事件。PORT 事件由使用 GPIO DETECT 信号的多个引脚触发,PORT 中的任意一个引脚上的上升沿都会触发 PORT 事件。即使外设处于空闲状态,PORT 事件也能使能,此功能不需要任何时钟或其他电源相关的外设。当CPU 和所有外设处于 IDLE 状态时,PORT 事件可以将 CPU 从 WFI 或 WFE 类型休眠中,以及 CPU 空闲模式下唤醒,即在系统 ON 模式下实现最低的功耗。

可以设置一个或多个 GPIO DETECT 用来产生 PORT 事件,PORT 事件可以作为唤醒源,也可以作为中断源产生中断。

GPIOTE 基址:0x40006000

GPIOTE 操作
1、GPIOTE输出
GPIOTE 输出一般不单独使用,因为单独使用时,操作和普通 GPIO 差不多,同样需要程序来操作他,不同的是普通 GPIO 通过 GPIO 的寄存器操作,GPIOTE 通过任务寄存器触发,单独使用时和普通 GPIO 一样同样需要软件干预,体现不了他的优势。GPIOTE 应用的时候一般和 PPI(可编程外设互联)一起用,通过其他外设的事件来触发 GPIOTE 的任务,如使用定时器比较事件等等,这样,整个过程就可以由硬件自动完成,无需软件干预,从而简化程序的流程、降低 CPU 的负担。

GPIOTE 输出的操作流程如下图所示,其中初始化 GPIOTE 模块和初始化 GPIOTE 输出引脚是必须要有的,接下来可以选择是否使能该引脚的 GPIOTE 功能,使能之后,只能通过 GPIOTE 任务触发输出,如果不使能 GPIOTE 功能,则通过写 GPIO 寄存器控制其输出。


1. 初始化 GPIOTE 模块
Nordic 的 SDK 是模块化设计的,使用 GPIOTE 模块时,先要初始化 GPIOTE 模块,初
始化很简单,调用初始化函数 nrf_drv_gpiote_init 即可。

需要注意的是:GPIOTE 模块在一个应用程序中和很多其他的程序模块共享资源,所以
在一个应用程序中 GPIOTE 模块只能初始化一次。

2. 初始化 GPIOTE 输出引脚
初始化 GPIOTE 输出引脚通过调用 nrf_drv_gpiote_out_init 函数来完成,该函数接收两个输入参数:引脚号和 GPIOTE 输出初始化结构体,引脚号指定将要配置的引脚的编号(0~31),GPIOTE 输出初始化结构体 nrf_drv_gpiote_out_config_t 包含下面的三项内容:

引脚的初始状态:高电平或是低电平。
高电平:NRF_GPIOTE_INITIAL_VALUE_HIGH
低电平 NRF_GPIOTE_INITIAL_VALUE_LOW
引脚动作:任务触发后引脚执行的动作。
置位(高电平):NRF_GPIOTE_POLARITY_LOTOHI
清除(低电平):NRF_GPIOTE_POLARITY_HITOLO
翻转:NRF_GPIOTE_POLARITY_TOGGLE
是否为 GPIOTE 任务引脚:如果设置成 GPIOTE 任务引脚,驱动程序才会去配置前面
两项(初始状态和引脚动作)并且为该引脚分配 GPIOTE 通道,否则,将该引脚按照普通
GPIO 处理。需要注意,即使将引脚配置为 GPIOTE 任务引脚,也需要使能该引脚的任
务触发功能,触发任务对该引脚无效。
3. 使能任务触发
初始化GPIOTE输出引脚(配置为任务引脚)为指定的引脚分配了GPIOTE通道并且配置了引脚的初始值和动作,但是并没有配置该引脚的模式即没有配置 CONFIG[n].MODE,使能任务触发就是设置 CONFIG[n].MODE = 3,即将其配置为任务模式。

使能任务触通过调用 nrf_drv_gpiote_out_task_enable 函数实现,只有使能任务触发后,我们才能通过触发 GPIOTE 的任务让引脚执行动作。

4. 触发任务驱动引脚
可以通过下面是三个函数触发 GPIOTE 任务,驱动引脚动作:

nrf_drv_gpiote_out_task_trigger:触发 GPIOTE 的 TASKS_OUT 任务,每触发一次,引
脚状态翻转一次。
nrf_drv_gpiote_set_task_trigger:触发 GPIOTE 的 TASKS_SET 任务,触发后,引脚输出
高电平。
nrf_drv_gpiote_clr_task_trigger:触发 GPIOTE 的 TASKS_CLR 任务,触发后,引脚输出
低电平。
#define LED_1 17
/**********************************************************************
* 描 述 : main 函数
* 入 参 : 无
* 返回值 : 无
*********************************************************************/
int main(void)
{
    ret_code_t err_code;
    //初始化 GPIOTE 程序模块
    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);
    //定义 GPIOTE 输出初始化结构体,并对其成员变量赋值
    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
    //初始化 GPIOTE 输出引脚
    err_code = nrf_drv_gpiote_out_init(LED_1, &config);
    APP_ERROR_CHECK(err_code);
    //使能引脚 LED_1(P0.17)所在 GPIOTE 通道的任务触发
    nrf_drv_gpiote_out_task_enable(LED_1);
    while (true)
    {
        //任务触发驱动引脚 P0.17 状态翻转,即指示灯 D1 翻转状态
        nrf_drv_gpiote_out_task_trigger(LED_1);
        //任务触发驱动引脚 P0.17 输出高电平,即指示灯 D1 熄灭
        //nrf_drv_gpiote_clr_task_trigger(LED_1);
        //任务触发驱动引脚 P0.17 输出低电平,即指示灯 D1 电亮
        //nrf_drv_gpiote_set_task_trigger(LED_1);
        nrf_delay_ms(150);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2、GPIOTE输入
GPIOTE 输入的操作流程如下图所示,包含初始化 GPIOTE 程序模块、初始化 GPIOTE
输入引脚、使能事件模式。



1. 初始化 GPIOTE
和 GPIOTE 输入时一样,调用初始化函数 nrf_drv_gpiote_init 即可。

2. 初始化 GPIOTE 输 输 入 引脚
初始化 GPIOTE 输入引脚通过调用 nrf_drv_gpiote_in_init 函数来完成,该函数接收三个
输入参数:引脚号、GPIOTE 输入初始化结构体和事件句柄,相对于 GPIOTE 输出引脚的初
始化,函数参数多了一个事件句柄,当 GPIOTE 检测到引脚电平变化后,会产生事件,这
时会自动调用这个注册的事件句柄来处理事件。

引 脚 号 指 定 将 要 配 置 的 引 脚 的 编 号 (0~31) , GPIOTE 输 入 初 始 化 结 构 体
nrf_drv_gpiote_in_config_t 包含下面的 4 项内容:

Sense:配置引脚的 Sense 功能,可配置为下面。
高电平到低电平的变化产生事件:NRF_GPIOTE_POLARITY_HITOLO
低电平到高电平的变化产生事件:NRF_GPIOTE_POLARITY_LOTOHI
任意电平变化产生事件:NRF_GPIOTE_POLARITY_TOGGLE
is_watcher:是否连接输入缓冲器,设置为“true”表示连接输入缓冲器。
pull:是否开启引脚的上拉电阻,设置为“true”表示打开上拉。
hi_accuracy:是否为高精度模式,设置为“true”表示使用高精度模式。
3. 使能 事件模式
初始化 GPIOTE 输入引脚时为指定的引脚分配了 GPIOTE 通道,但是并没有配置该引脚的模式即没有配置 CONFIG[n].MODE,使能事件模式就是设置 CONFIG[n].MODE = 1,即将其配置为事件模式,当其检测到引脚电平变化时(初始化 GPIOTE 输入引脚时 Sense 项配置了哪种变化可以产生事件)即产生事件。

#define LED_1 17
#define BUTTON_0 13
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
nrf_drv_gpiote_out_toggle(LED_1);
}
/**********************************************************************
* 描 述 : main 函数
* 入 参 : 无
* 返回值 : 无
**********************************************************************/
int main(void)
{
    ret_code_t err_code;
    //初始化 GPIOTE 程序模块
    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);
    //定义 GPIOTE 输出初始化结构体,并对其成员变量赋值
    nrf_drv_gpiote_out_config_t out_config =
    GPIOTE_CONFIG_OUT_SIMPLE(true);
    //初始化 GPIOTE 输出引脚
    err_code = nrf_drv_gpiote_out_init(LED_1, &out_config);
    APP_ERROR_CHECK(err_code);
    
    //高电平到低电平变化产生事件
    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
    
    //低电平到高电平变化产生事件
    //nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    
    //任意电平变化产生事件
    //nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
    
    //开启 P0.13 引脚的上拉电阻
    in_config.pull = NRF_GPIO_PIN_PULLUP;
    //配置该引脚为 GPIOTE 输入
    err_code = nrf_drv_gpiote_in_init(BUTTON_0, &in_config, in_pin_handler);
    APP_ERROR_CHECK(err_code);
    //使能该引脚所在 GPIOTE 通道的事件模式
    nrf_drv_gpiote_in_event_enable(BUTTON_0, true);
    while (true)
    {
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Sense 配置电平变化产生事件
//高电平到低电平变化产生事件
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);

//低电平到高电平变化产生事件
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);

//任意电平变化产生事件
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
1
2
3
4
5
6
7
8
3、GPIOTE唤醒系统
————————————————
版权声明:本文为CSDN博主「liefyuan」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_28877125/article/details/85252927