#include <stdbool.h>
#include "nrf.h"
#include "nrf_drv_gpiote.h"
#include "app_error.h"
#include "boards.h"
/*
#ifdef BSP_BUTTON_0:
这是一个条件编译指令,其作用是检查 BSP_BUTTON_0 这个宏是否已经被定义。BSP 一般代表板级支持包(Board Support Package),BSP_BUTTON_0 可能是板子上某个按钮所对应的引脚编号。
#define PIN_IN BSP_BUTTON_0:若 BSP_BUTTON_0 已被定义,此指令会将 PIN_IN 定义为 BSP_BUTTON_0 的值。如此一来,后续代码里就能用 PIN_IN 来代表输入引脚。
#ifndef PIN_IN:该指令用于检查 PIN_IN 这个宏是否未被定义。
#error "Please indicate input pin":要是 PIN_IN 未被定义,编译器会停止编译,并输出错误信息 “Please indicate input pin”,以此提示开发者要明确指定输入引脚。
*/
#ifdef BSP_BUTTON_0//检查 BSP_BUTTON_0 这个宏是否已经被定义,(在pca10056中有定义)
#define PIN_IN BSP_BUTTON_0//若 BSP_BUTTON_0 已被定义,此指令会将 PIN_IN 定义为 BSP_BUTTON_0 的值
#endif
#ifndef PIN_IN//要是 PIN_IN 未被定义
#error "Please indicate input pin"
#endif
#ifdef BSP_LED_0
#define PIN_OUT BSP_LED_0
#endif
#ifndef PIN_OUT
#error "Please indicate output pin"
#endif
/**
GPIOTE中断处理
in_pin_handler,这是一个回调函数,通常用于处理 GPIO(通用输入输出)引脚事件。当 GPIO 引脚发生特定事件时,该函数会被调用。
nrf_drv_gpiote_pin_t pin:表示触发事件的 GPIO 引脚编号。nrf_drv_gpiote_pin_t 是一个自定义的数据类型,用于表示 GPIO 引脚。
nrf_gpiote_polarity_t action:表示触发事件的极性,比如上升沿触发、下降沿触发等。nrf_gpiote_polarity_t 也是一个自定义的数据类型,用于表示极性。
函数体
if(nrf_gpio_pin_read(PIN_IN) == 0) // 按键防抖
{
nrf_gpio_pin_toggle(PIN_OUT);
}
按键防抖检查: nrf_gpio_pin_read(PIN_IN):调用 nrf_gpio_pin_read 函数读取 PIN_IN 引脚的电平状态。PIN_IN 是之前代码中通过预处理指令定义的输入引脚。
if(nrf_gpio_pin_read(PIN_IN) == 0):检查 PIN_IN 引脚的电平是否为低电平(通常按键按下时引脚电平为低)。这里的判断起到了简单的按键防抖作用,
因为按键按下时可能会产生抖动,通过检查引脚电平是否持续为低,可以减少误触发的可能性。
?引脚状态切换: nrf_gpio_pin_toggle(PIN_OUT):如果 PIN_IN 引脚的电平为低,调用 nrf_gpio_pin_toggle 函数切换 PIN_OUT 引脚的电平状态。
PIN_OUT 是之前代码中通过预处理指令定义的输出引脚。也就是说,当输入引脚检测到按键按下时,会切换输出引脚的电平,通常用于控制 LED 灯的亮灭。
总结 这段代码定义了一个 GPIO 引脚事件处理函数,当输入引脚检测到按键按下(低电平)时,会切换输出引脚的电平状态,实现了简单的按键控制功能,同时包含了基本的按键防抖逻辑。
*/
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)//nrfx_gpiote_pin_t 其实就是uint32_t的意思,是为了便于理解,才这样取了一个别名
{
if(nrf_gpio_pin_read(PIN_IN)== 0)//按键防抖
{
nrf_gpio_pin_toggle(PIN_OUT);//led反转
}
}
/**
配置GPIOTE初始化
*/
static void gpio_init(void)
{
nrf_gpio_cfg_output(PIN_OUT);//设置led引脚为输出
ret_code_t err_code;//typedef ret_code_t nrfx_err_t;//typedef 原类型 新类型名;
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(PIN_IN, true);
}
/**
主函数,循环等待中断
*/
int main(void)
{
gpio_init();
while (true)
{
// Do nothing.
}
}
程序理解:
1、
#ifdef BSP_BUTTON_0
#define PIN_IN BSP_BUTTON_0
#endif
#ifndef PIN_IN
#error "Please indicate input pin"
#endif
#ifdef BSP_LED_0
#define PIN_OUT BSP_LED_0
#endif
#ifndef PIN_OUT
#error "Please indicate output pin"
#endif
#ifdef BSP_BUTTON_0:
这是一个条件编译指令,其作用是检查 BSP_BUTTON_0 这个宏是否已经被定义(经查询发现在pca10056.h中有定义)。BSP 一般代表板级支持包(Board Support Package),BSP_BUTTON_0 可能是板子上某个按钮所对应的引脚编号。
#define PIN_IN BSP_BUTTON_0:
若 BSP_BUTTON_0 已被定义,此指令会将 PIN_IN 定义为 BSP_BUTTON_0 的值。如此一来,后续代码里就能用 PIN_IN 来代表输入引脚。
#ifndef PIN_IN:
该指令用于检查 PIN_IN 这个宏是否未被定义。
#error "Please indicate input pin":
要是 PIN_IN 未被定义,编译器会停止编译,并输出错误信息 “Please indicate input pin”,以此提示开发者要明确指定输入引脚。
2、配置GPIOTE初始化
static void gpio_init(void)
{
nrf_gpio_cfg_output(PIN_OUT);//设置led引脚为输出
ret_code_t err_code;//typedef ret_code_t nrfx_err_t;//typedef 原类型 新类型名;
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(PIN_IN, true);
}
(1)nrf_gpio_cfg_output函数是nrf_gpio.h中定义的一个函数,其作用是将指定的引脚配置为输出模式。
(2)ret_code_t err_code是定义一个ret_code_t类型的变量err_code,
ret_code_t 是一个自定义的数据类型,通常用于表示函数执行的返回状态码。ret_code_t有一个别名是nrfx_err_t,查询有下面的信息:
/typedef ret_code_t nrfx_err_t
nrfx_err_t是一个枚举类型,其内容如下:
typedef enum {
NRFX_SUCCESS = (NRFX_ERROR_BASE_NUM + 0), ///< Operation performed successfully.
NRFX_ERROR_INTERNAL = (NRFX_ERROR_BASE_NUM + 1), ///< Internal error.
NRFX_ERROR_NO_MEM = (NRFX_ERROR_BASE_NUM + 2), ///< No memory for operation.
NRFX_ERROR_NOT_SUPPORTED = (NRFX_ERROR_BASE_NUM + 3), ///< Not supported.
NRFX_ERROR_INVALID_PARAM = (NRFX_ERROR_BASE_NUM + 4), ///< Invalid parameter.
NRFX_ERROR_INVALID_STATE = (NRFX_ERROR_BASE_NUM + 5), ///< Invalid state, operation disallowed in this state.
NRFX_ERROR_INVALID_LENGTH = (NRFX_ERROR_BASE_NUM + 6), ///< Invalid length.
NRFX_ERROR_TIMEOUT = (NRFX_ERROR_BASE_NUM + 7), ///< Operation timed out.
NRFX_ERROR_FORBIDDEN = (NRFX_ERROR_BASE_NUM + 8), ///< Operation is forbidden.
NRFX_ERROR_NULL = (NRFX_ERROR_BASE_NUM + 9), ///< Null pointer.
NRFX_ERROR_INVALID_ADDR = (NRFX_ERROR_BASE_NUM + 10), ///< Bad memory address.
NRFX_ERROR_BUSY = (NRFX_ERROR_BASE_NUM + 11), ///< Busy.
NRFX_ERROR_ALREADY_INITIALIZED = (NRFX_ERROR_BASE_NUM + 12), ///< Module already initialized.
NRFX_ERROR_DRV_TWI_ERR_OVERRUN = (NRFX_ERROR_DRIVERS_BASE_NUM + 0), ///< TWI error: Overrun.
NRFX_ERROR_DRV_TWI_ERR_ANACK = (NRFX_ERROR_DRIVERS_BASE_NUM + 1), ///< TWI error: Address not acknowledged.
NRFX_ERROR_DRV_TWI_ERR_DNACK = (NRFX_ERROR_DRIVERS_BASE_NUM + 2) ///< TWI error: Data not acknowledged.
} nrfx_err_t;
(3) nrfx_gpiote_init 函数的作用是初始化通用外设 I/O 任务和事件 (GPIOTE) 驱动程序。
该函数会对 GPIOTE 相关的状态、引脚使用情况、通道等进行初始化设置,同时配置中断优先级并使能中断。
(4)nrfx_gpiote_in_init 函数用于初始化一个 GPIO(通用输入输出)引脚,使其作为 GPIOTE(通用外设中断和事件)输入引脚。
它会检查引脚是否可用,分配一个 GPIOTE 通道,根据配置对引脚进行相应设置,并记录操作结果。
函数参数
pin:类型为 nrfx_gpiote_pin_t,表示要初始化的 GPIO 引脚编号。
p_config:指向 nrfx_gpiote_in_config_t 类型的常量结构体指针,包含了该引脚的配置信息,如是否高精度模式、是否跳过 GPIO 设置、上拉 / 下拉配置等。
evt_handler:类型为 nrfx_gpiote_evt_handler_t,是一个事件处理函数指针,当该引脚触发事件时会调用此函数。
(5)APP_ERROR_CHECK(err_code);
经查询有如下宏定义:
#define APP_ERROR_HANDLER(ERR_CODE) \
do \
{ \
app_error_handler_bare((ERR_CODE)); \
} while (0)
#endif
/**@brief Macro for calling error handler function if supplied error code any other than NRF_SUCCESS.
*
* @param[in] ERR_CODE Error code supplied to the error handler.
*/
#define APP_ERROR_CHECK(ERR_CODE) \
do \
{ \
const uint32_t LOCAL_ERR_CODE = (ERR_CODE); \
if (LOCAL_ERR_CODE != NRF_SUCCESS) \
{ \
APP_ERROR_HANDLER(LOCAL_ERR_CODE); \
} \
} while (0)
/**@brief Macro for calling error handler function if supplied boolean value is false.
*
* @param[in] BOOLEAN_VALUE Boolean value to be evaluated.
*/
#define APP_ERROR_CHECK_BOOL(BOOLEAN_VALUE) \
do \
{ \
const uint32_t LOCAL_BOOLEAN_VALUE = (BOOLEAN_VALUE); \
if (!LOCAL_BOOLEAN_VALUE) \
{ \
APP_ERROR_HANDLER(0); \
} \
} while (0)
#define APP_ERROR_CHECK(ERR_CODE) \
do \
{ \
const uint32_t LOCAL_ERR_CODE = (ERR_CODE); \
if (LOCAL_ERR_CODE != NRF_SUCCESS) \
{ \
APP_ERROR_HANDLER(LOCAL_ERR_CODE); \
} \
} while (0)
APP_ERROR_CHECK宏,它在嵌入式系统或者其他需要严格错误处理的程序中非常常见,这个宏用于检查传入的错误码 ERR_CODE 是否为 NRF_SUCCESS(通常表示操作成功)。若错误码不等于 NRF_SUCCESS,就调用 APP_ERROR_HANDLER 宏来处理该错误。
#define APP_ERROR_HANDLER(ERR_CODE) \
do \
{ \
app_error_handler_bare((ERR_CODE)); \
} while (0)
APP_ERROR_HANDLER(ERR_CODE)宏,此宏的作用是调用 app_error_handler_bare 函数来处理错误。app_error_handler_bare 函数应当是一个自定义的错误处理函数,负责对传入的错误码进行处理,比如输出错误信息、记录日志或者进行复位操作等。
经查询app_error_handler_bare 函数是app_error.c中定义的一个函数,其代码如下:
void app_error_handler_bare(ret_code_t error_code)
{
error_info_t error_info =
{
.line_num = 0,
.p_file_name = NULL,
.err_code = error_code,
};
app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info));
UNUSED_VARIABLE(error_info);
}
此函数中用到的error_info_t是一个结构体类型,其在app_error.c中的定义如下:
typedef struct
{
uint32_t line_num; /**< The line number where the error occurred. */
uint8_t const * p_file_name; /**< The file in which the error occurred. */
uint32_t err_code; /**< The error code representing the error that occurred. */
} error_info_t;
app_error_fault_handler函数app_error_weak.c中定义如下:
__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
__disable_irq();
NRF_LOG_FINAL_FLUSH();
#ifndef DEBUG
NRF_LOG_ERROR("Fatal error");
#else
switch (id)
{
#if defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT
case NRF_FAULT_ID_SD_ASSERT:
NRF_LOG_ERROR("SOFTDEVICE: ASSERTION FAILED");
break;
case NRF_FAULT_ID_APP_MEMACC:
NRF_LOG_ERROR("SOFTDEVICE: INVALID MEMORY ACCESS");
break;
#endif
case NRF_FAULT_ID_SDK_ASSERT:
{
assert_info_t * p_info = (assert_info_t *)info;
NRF_LOG_ERROR("ASSERTION FAILED at %s:%u",
p_info->p_file_name,
p_info->line_num);
break;
}
case NRF_FAULT_ID_SDK_ERROR:
{
error_info_t * p_info = (error_info_t *)info;
NRF_LOG_ERROR("ERROR %u [%s] at %s:%u\r\nPC at: 0x%08x",
p_info->err_code,
nrf_strerror_get(p_info->err_code),
p_info->p_file_name,
p_info->line_num,
pc);
NRF_LOG_ERROR("End of error report");
break;
}
default:
NRF_LOG_ERROR("UNKNOWN FAULT at 0x%08X", pc);
break;
}
#endif
NRF_BREAKPOINT_COND;
// On assert, the system can only recover with a reset.
#ifndef DEBUG
NRF_LOG_WARNING("System reset");
NVIC_SystemReset();
#else
app_error_save_and_stop(id, pc, info);
#endif // DEBUG
}
此函数的简单解释:
这个函数是一个弱定义的错误处理函数,用于处理系统中出现的严重错误。当系统遇到致命错误时,会调用此函数进行相应的错误处理,如记录错误信息、根据调试模式采取不同的恢复措施等。 函数定义和参数
__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
• __WEAK:这是一个弱定义修饰符,意味着该函数可以被用户在其他地方重新定义。若用户重新定义了此函数,链接时会使用用户定义的版本;若没有重新定义,则使用此处的默认版本。
• app_error_fault_handler:函数名,用于处理系统的严重错误。
• uint32_t id:错误类型的标识符,用于区分不同类型的错误。
• uint32_t pc:程序计数器(Program Counter)的值,它记录了错误发生时程序执行的位置。
• uint32_t info:额外的错误信息,根据不同的错误类型,这个信息可能包含不同的内容。
函数主体
1. 禁用中断和刷新日志
__disable_irq();
NRF_LOG_FINAL_FLUSH();
• __disable_irq():禁用所有中断,防止在错误处理过程中被其他中断打断,确保错误处理的完整性。
• NRF_LOG_FINAL_FLUSH():将日志缓冲区中的所有日志信息刷新到输出设备(如串口),确保在系统进入错误处理状态前,所有的日志信息都能被记录下来。
2. 根据调试模式处理错误
#ifndef DEBUG
NRF_LOG_ERROR("Fatal error");
#else
switch (id)
{
#if defined(SOFTDEVICE_PRESENT) && SOFTDEVICE_PRESENT
case NRF_FAULT_ID_SD_ASSERT:
NRF_LOG_ERROR("SOFTDEVICE: ASSERTION FAILED");
break;
case NRF_FAULT_ID_APP_MEMACC:
NRF_LOG_ERROR("SOFTDEVICE: INVALID MEMORY ACCESS");
break;
#endif
case NRF_FAULT_ID_SDK_ASSERT:
{
assert_info_t * p_info = (assert_info_t *)info;
NRF_LOG_ERROR("ASSERTION FAILED at %s:%u",
p_info->p_file_name,
p_info->line_num);
break;
}
case NRF_FAULT_ID_SDK_ERROR:
{
error_info_t * p_info = (error_info_t *)info;
NRF_LOG_ERROR("ERROR %u [%s] at %s:%u\r\nPC at: 0x%08x",
p_info->err_code,
nrf_strerror_get(p_info->err_code),
p_info->p_file_name,
p_info->line_num,
pc);
NRF_LOG_ERROR("End of error report");
break;
}
default:
NRF_LOG_ERROR("UNKNOWN FAULT at 0x%08X", pc);
break;
}
#endif
• 非调试模式(#ifndef DEBUG):直接记录一个致命错误日志。
• 调试模式(#else 分支):使用 switch 语句根据 id 的值来判断错误类型,并记录相应的错误信息。
NRF_FAULT_ID_SD_ASSERT:软设备断言失败,记录相应的错误信息。 ◦ NRF_FAULT_ID_APP_MEMACC:软设备内存访问无效,记录相应的错误信息。 ◦ NRF_FAULT_ID_SDK_ASSERT:SDK 断言失败,将 info 转换为 assert_info_t 类型的指针,从中提取出错的文件名和行号,并记录错误信息。
NRF_FAULT_ID_SDK_ERROR:SDK 错误,将 info 转换为 error_info_t 类型的指针,从中提取错误码、错误描述、出错的文件名和行号,以及程序计数器的值,并记录详细的错误信息。 default:未知错误,记录错误发生的程序计数器的值。
3. 断点条件
NRF_BREAKPOINT_COND;
这是一个断点条件,在调试时可以触发断点,方便开发者进行调试。
4. 根据调试模式进行恢复操作
#ifndef DEBUG
NRF_LOG_WARNING("System reset");
NVIC_SystemReset();
#else
app_error_save_and_stop(id, pc, info);
#endif // DEBUG
• 非调试模式(#ifndef DEBUG):记录系统即将复位的警告信息,然后调用 NVIC_SystemReset() 函数进行系统复位,使系统重新启动。
• 调试模式(#else 分支):调用 app_error_save_and_stop(id, pc, info) 函数,该函数可能会保存错误信息并停止系统运行,方便开发者进行调试和分析。
总结 app_error_fault_handler 函数是一个用于处理系统严重错误的通用函数,它会根据不同的错误类型记录详细的错误信息,并根据调试模式采取不同的恢复措施。在非调试模式下,系统会直接复位;在调试模式下,系统会保存错误信息并停止运行,以便开发者进行调试。
(4) nrfx_gpiote_in_config_t是nrfx_gpiote.c中定义的一个结构类型,定义如下:
typedef struct
{
nrf_gpiote_polarity_t sense; /**< Transition that triggers the interrupt. */
nrf_gpio_pin_pull_t pull; /**< Pulling mode. */
bool is_watcher : 1; /**< True when the input pin is tracking an output pin. */
bool hi_accuracy : 1; /**< True when high accuracy (IN_EVENT) is used. */
bool skip_gpio_setup : 1; /**< Do not change GPIO configuration */
} nrfx_gpiote_in_config_t;
(5)GPIOTE_CONFIG_IN_SENSE_TOGGLE是一个nrfx_gpiote.c中定义宏,其内容如下 :
#define NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = false, \
}
宏的解释如下:
这段代码定义了一个宏 NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE,其主要用途是生成一个用于配置 GPIO 任务和事件(GPIOTE)输入引脚的结构体初始化列表。
下面对代码进行详细解释。
宏定义概述
#define NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu) \
{ \
.sense = NRF_GPIOTE_POLARITY_TOGGLE, \
.pull = NRF_GPIO_PIN_NOPULL, \
.is_watcher = false, \
.hi_accuracy = hi_accu, \
.skip_gpio_setup = false, \
}
此宏接收一个参数 hi_accu,它会返回一个初始化好的结构体,用于配置 GPIOTE 输入引脚的各项参数。
代码逐行解释
1. 宏定义与参数
#define NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(hi_accu)
NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE 是宏的名称,调用该宏可生成 GPIOTE 输入引脚的配置结构体。
• hi_accu 是宏的参数,代表高准确性模式的开关状态,它会被用于初始化结构体中的 hi_accuracy 成员。
2. 结构体成员初始化
2.1 检测极性(.sense)
.sense = NRF_GPIOTE_POLARITY_TOGGLE,
• .sense 是结构体中的一个成员,用于指定 GPIOTE 输入引脚的检测极性。
• NRF_GPIOTE_POLARITY_TOGGLE 表示该引脚会检测电平的翻转(即从高电平到低电平或者从低电平到高电平的变化)。
2.2 上拉 / 下拉电阻(.pull)
.pull = NRF_GPIO_PIN_NOPULL,
• .pull 成员用于配置引脚的上拉或下拉电阻。
• NRF_GPIO_PIN_NOPULL 表明不使用上拉或下拉电阻,引脚处于浮空状态。
2.3 观察器模式(.is_watcher)
.is_watcher = false,
• .is_watcher 成员用于指定该引脚是否处于观察器模式。 • false 表示不启用观察器模式。
2.4 高准确性模式(.hi_accuracy)
.hi_accuracy = hi_accu,
• .hi_accuracy 成员用于指定是否启用高准确性模式。
• hi_accu 是宏的参数,调用宏时传入的值会被赋给该成员。
2.5 跳过 GPIO 设置(.skip_gpio_setup)
.skip_gpio_setup = false,
• .skip_gpio_setup 成员用于指定是否跳过 GPIO 的基本设置。
• false 表示不跳过,会进行正常的 GPIO 设置。
总结 NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE 宏提供了一种便捷的方式来配置 GPIOTE 输入引脚,使其检测电平的翻转。通过传入不同的 hi_accu 参数,可以灵活控制是否启用高准确性模式。在实际使用时,这个宏可以与相关的 GPIOTE 初始化函数结合,对 GPIO 引脚进行快速配置。
例如:
nrfx_gpiote_in_config_t config = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
这样就创建了一个配置结构体 config,该结构体将 GPIOTE 输入引脚配置为检测电平翻转,不使用上拉或下拉电阻,不启用观察器模式,启用高准确性模式,并且不跳过 GPIO 的基本设置。
(6)rr_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
nrf_drv_gpiote_in_init 函数用于根据 in_config 结构体中的配置信息初始化指定的输入引脚 PIN_IN。
• PIN_IN 是之前通过预处理指令定义的一个宏,代表要配置为输入的引脚,通常连接到一个按键。
• &in_config 是输入配置结构体的指针,传递给函数以指定输入引脚的具体配置。
• in_pin_handler 是一个回调函数,当输入引脚检测到指定的电平变化时,该函数会被调用。开发者可以在 in_pin_handler 函数中实现具体的处理逻辑,比如控制 LED 灯的状态。
(7)nrf_drv_gpiote_in_event_enable(PIN_IN, true);
nrf_drv_gpiote_in_event_enable 函数用于启用指定输入引脚的事件检测功能。
• PIN_IN 是要启用事件检测的输入引脚。
• true 表示启用事件检测,这样当 PIN_IN 引脚的电平发生翻转时,就会触发 in_pin_handler 回调函数。
该函数在7、nRF52xx蓝牙学习(nrf_gpiote.c库函数学习)中单独有说明。
(8)in_pin_handler函数代码
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)//nrfx_gpiote_pin_t 其实就是uint32_t的意思,是为了便于理解,才这样取了一个别名
{
if(nrf_gpio_pin_read(PIN_IN)== 0)//按键防抖
{
nrf_gpio_pin_toggle(PIN_OUT);//led反转
}
}
这段代码定义了一个名为 in_pin_handler 的函数,它是一个 GPIO(通用输入输出)引脚事件处理函数,通常作为回调函数使用,用于处理特定 GPIO 引脚的电平变化事件。
下面为你详细解释代码各部分:
函数定义和参数
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
• 函数名:in_pin_handler,它是专门处理 GPIO 引脚事件的函数。
• 参数: ◦ nrf_drv_gpiote_pin_t pin:此参数代表触发事件的 GPIO 引脚编号。nrf_drv_gpiote_pin_t 是自定义的数据类型,用于标识 GPIO 引脚。
◦ nrf_gpiote_polarity_t action:该参数表示触发事件的极性,也就是引脚电平变化的类型,例如上升沿触发(从低电平变为高电平)或者下降沿触发(从高电平变为低电平)。nrf_gpiote_polarity_t 同样是自定义的数据类型。
函数体 按键状态检查
if(nrf_gpio_pin_read(PIN_IN) == 0)
• nrf_gpio_pin_read 是一个函数,其作用是读取指定 GPIO 引脚的当前电平状态。
• PIN_IN 是一个宏定义,代表输入引脚,通常连接着一个按键。
• if(nrf_gpio_pin_read(PIN_IN) == 0) 用于检查 PIN_IN 引脚的电平是否为低电平。在常见的按键电路中,按键按下时引脚电平会被拉低,所以这里通过检查低电平来判断按键是否被按下。同时,这也起到了简单的按键防抖作用,因为按键按下时可能会产生抖动,持续检测低电平可以减少误触发的可能性。
切换输出引脚状态
nrf_gpio_pin_toggle(PIN_OUT);
• nrf_gpio_pin_toggle 是一个函数,用于切换指定 GPIO 引脚的电平状态。
• PIN_OUT 是一个宏定义,代表输出引脚,通常连接着一个 LED 灯。
• 当 PIN_IN 引脚检测到低电平(即按键按下)时,调用 nrf_gpio_pin_toggle(PIN_OUT) 函数会切换 PIN_OUT 引脚的电平状态。如果 PIN_OUT 引脚原来是高电平,调用后会变为低电平;如果原来是低电平,调用后会变为高电平。这样就实现了通过按键控制 LED 灯亮灭状态的切换。
总结 in_pin_handler 函数是一个 GPIO 引脚事件处理函数,当输入引脚 PIN_IN 检测到按键按下(低电平)时,会切换输出引脚 PIN_OUT 的电平状态,从而实现按键控制 LED 灯亮灭的功能,同时包含了简单的按键防抖逻辑。