1、nrfx_err_t nrfx_gpiote_init
函数代码如下 :
nrfx_err_t nrfx_gpiote_init(void)
{
nrfx_err_t err_code;
if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
uint8_t i;
for (i = 0; i < MAX_PIN_NUMBER; i++)
{
if (nrf_gpio_pin_present_check(i))
{
pin_in_use_clear(i);
}
}
for (i = 0; i < (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS); i++)
{
channel_free(i);
}
memset(m_cb.configured_pins, 0, sizeof(m_cb.configured_pins));
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_GPIOTE), NRFX_GPIOTE_CONFIG_IRQ_PRIORITY);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_GPIOTE));
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
err_code = NRFX_SUCCESS;
NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
(1)nrfx_gpiote_init
函数不接收参数,返回值类型为 nrfx_err_t
,这是一个用于表示错误码的类型。
(2)nrfx_err_t err_code;
定义一个nrfx_err_t类型的变量err_code.
nrfx_err_t是一个枚举类型,在nrfx_errors.h中其定义如下:
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)m_cb
m_cb是一个静态变量,定义如下 :
static gpiote_control_block_t m_cb;
其类型为结构体变量gpiote_control_block_t,该类型是在nrfx_gpiote.c中定义的:
typedef struct
{
nrfx_gpiote_evt_handler_t handlers[GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
int8_t pin_assignments[MAX_PIN_NUMBER];
int8_t port_handlers_pins[NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
uint8_t configured_pins[((MAX_PIN_NUMBER)+7) / 8];
nrfx_drv_state_t state;
} gpiote_control_block_t;
这段代码使用 typedef
关键字定义了一个名为 gpiote_control_block_t
的结构体类型。这个结构体主要用于存储和管理 Nordic Semiconductor 的 GPIO(通用输入输出)任务和事件(GPIOTE)驱动的相关配置信息和状态。
结构体成员详细解释
nrfx_gpiote_evt_handler_t handlers[GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
- 成员类型:
nrfx_gpiote_evt_handler_t
类型的数组。nrfx_gpiote_evt_handler_t
应该是一个函数指针类型,用于指向处理 GPIOTE 事件的回调函数。 - 数组大小:数组的大小为
GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS
。GPIOTE_CH_NUM
可能表示 GPIOTE 模块的通道数量,NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS
可能表示低功耗事件的数量。 - 作用:该数组用于存储每个通道和低功耗事件对应的事件处理函数指针,以便在相应的事件发生时调用。
int8_t pin_assignments[MAX_PIN_NUMBER];
- 成员类型:
int8_t
类型的数组。int8_t
是一个有符号的 8 位整数类型。 - 数组大小:数组的大小为
MAX_PIN_NUMBER
,表示系统中可用的最大引脚数量。 - 作用:该数组用于记录每个引脚的分配情况。数组的每个元素对应一个引脚,元素的值可以表示该引脚的使用状态或分配给哪个通道等信息。
int8_t port_handlers_pins[NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS];
- 成员类型:
int8_t
类型的数组。 - 数组大小:数组的大小为
NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS
,即低功耗事件的数量。 - 作用:该数组用于记录每个低功耗事件对应的引脚信息。每个元素对应一个低功耗事件,元素的值表示与该低功耗事件相关联的引脚编号。
uint8_t configured_pins[((MAX_PIN_NUMBER)+7) / 8];
- 成员类型:
uint8_t
类型的数组。uint8_t
是一个无符号的 8 位整数类型。 - 数组大小:数组的大小为
((MAX_PIN_NUMBER)+7) / 8
。这种计算方式是为了使用位掩码的方式来表示引脚的配置状态。因为一个uint8_t
类型的变量有 8 位,可以表示 8 个引脚的状态,所以通过这种方式可以用最少的字节数来存储所有引脚的配置状态。 - 作用:该数组用于记录哪些引脚已经被配置。数组的每个元素的每一位对应一个引脚,位值为 1 表示该引脚已配置,位值为 0 表示未配置。
nrfx_drv_state_t state;
- 成员类型:
nrfx_drv_state_t
类型的变量。nrfx_drv_state_t
应该是一个枚举类型,用于表示驱动的状态,该枚举类型定义如下 : -
typedef enum { NRFX_DRV_STATE_UNINITIALIZED, ///< Uninitialized. NRFX_DRV_STATE_INITIALIZED, ///< Initialized but powered off. NRFX_DRV_STATE_POWERED_ON, ///< Initialized and powered on. } nrfx_drv_state_t;
- 作用:该变量用于记录 GPIOTE 驱动的当前状态,例如未初始化、已初始化、正在运行等。
gpiote_control_block_t
结构体用于集中管理 GPIOTE 驱动的相关配置信息和状态,包括事件处理函数指针、引脚分配情况、低功耗事件对应的引脚、引脚配置状态以及驱动的整体状态。通过使用这个结构体,可以方便地对 GPIOTE 驱动进行初始化、配置和状态管理。
(4)、if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
{
err_code = NRFX_ERROR_INVALID_STATE;
NRFX_LOG_WARNING("Function: %s, error code: %s.",
__func__,
NRFX_LOG_ERROR_STRING_GET(err_code));
return err_code;
}
状态检查:查看 m_cb.state(可能是一个全局或者静态的状态变量)是否处于未初始化状态。若不是,就把 err_code 设为 NRFX_ERROR_INVALID_STATE,记录警告日志,然后返回错误码。
(5)for (i = 0; i < MAX_PIN_NUMBER; i++)
{
if (nrf_gpio_pin_present_check(i))
{
pin_in_use_clear(i);//清除引脚使用状态标志
}
}
引脚清理:借助 for 循环遍历从 0 到 MAX_PIN_NUMBER - 1 的所有引脚。对于存在的引脚(通过 nrf_gpio_pin_present_check 函数检查),调用 pin_in_use_clear 函数将其使用状态清除。
(6) for (i = 0; i < (GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS); i++)
{
channel_free(i);
}
通道清理:使用 for 循环遍历从 0 到 GPIOTE_CH_NUM + NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - 1 的所有通道,调用 channel_free 函数释放这些通道。
channel_freee函数代码如下:
static void channel_free(uint8_t channel_id)
{
m_cb.handlers[channel_id] = FORBIDDEN_HANDLER_ADDRESS;
if (channel_id >= GPIOTE_CH_NUM)
{
m_cb.port_handlers_pins[channel_id - GPIOTE_CH_NUM] = (int8_t)PIN_NOT_USED;
}
}
channel_free
函数的主要功能是释放指定通道(channel_id
)的资源,并且依据通道 ID 是不是大于等于 GPIOTE_CH_NUM
,对相应的端口处理引脚信息进行更新。
函数声明:
static:此关键字表明该函数仅能在当前文件里被调用,具备文件作用域。
void:意味着函数没有返回值。
channel_free:函数名,代表释放通道资源的操作。
uint8_t channel_id:函数的参数,uint8_t 是无符号 8 位整数类型,channel_id 用来指定要释放的通道 ID。
m_cb.handlers[channel_id] = FORBIDDEN_HANDLER_ADDRESS;
功能:把 m_cb.handlers 数组中索引为 channel_id 的元素设定为 FORBIDDEN_HANDLER_ADDRESS。m_cb 可能是一个结构体变量,handlers 是该结构体里的一个数组,用于存储通道处理函数的地址。FORBIDDEN_HANDLER_ADDRESS 或许是一个特殊的地址值,用于表示该通道的处理函数地址无效或者该通道已被释放。
if (channel_id >= GPIOTE_CH_NUM)
条件判断:检查 channel_id 是否大于等于 GPIOTE_CH_NUM。GPIOTE_CH_NUM 可能代表 GPIOTE(通用外设 I/O 任务和事件)通道的数量。
m_cb.port_handlers_pins[channel_id - GPIOTE_CH_NUM] = (int8_t)PIN_NOT_USED;
功能:若 channel_id 大于等于 GPIOTE_CH_NUM,就把 m_cb.port_handlers_pins 数组中索引为 channel_id - GPIOTE_CH_NUM 的元素设置为 PIN_NOT_USED。m_cb.port_handlers_pins 可能是一个存储端口处理引脚信息的数组,PIN_NOT_USED 可能是一个特殊的值,用于表示该引脚未被使用。
总结
此函数的作用是释放指定通道的资源,将通道处理函数地址设为无效值,并且在通道 ID 大于等于 GPIOTE_CH_NUM 时,把对应的端口处理引脚信息设为未使用状态。
(7)、memset(m_cb.configured_pins, 0, sizeof(m_cb.configured_pins));
配置引脚数组清零:利用 memset 函数把 m_cb.configured_pins 数组的所有元素置为 0,这意味着将所有已配置引脚的信息清除。
memset 是 C 标准库中的一个函数,用于将一段内存区域填充为指定的值。它通常用于初始化数组、结构体或动态分配的内存。
#include <string.h> // 需要包含头文件
void *memset(void *ptr, int value, size_t num);
ptr:指向要填充的内存区域的指针。
value:要填充的值(以 int 形式传递,但实际填充时会转换为 unsigned char)。
num:要填充的字节数。
返回值:返回指向 ptr 的指针
(8)、
NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(NRF_GPIOTE), NRFX_GPIOTE_CONFIG_IRQ_PRIORITY);
NRFX_IRQ_ENABLE(nrfx_get_irq_number(NRF_GPIOTE));
整体功能
_NRFX_IRQ_PRIORITY_SET
是一个静态内联函数(priority优先的含义),其主要功能是设置指定中断的优先级。在嵌入式系统中,中断优先级的设置十分重要,它决定了多个中断同时发生时,处理器响应中断的先后顺序。此代码用处是设置 GPIOTE 的中断优先级为 NRFX_GPIOTE_CONFIG_IRQ_PRIORITY,并且使能 GPIOTE 的中断。
NRFX_IRQ_PRIORITY_SET函数
static inline void _NRFX_IRQ_PRIORITY_SET(IRQn_Type irq_number,
uint8_t priority)
{
ASSERT(INTERRUPT_PRIORITY_IS_VALID(priority));
NVIC_SetPriority(irq_number, priority);
}
- 函数声明:
static
:该关键字表明此函数仅能在当前文件内部被调用,具有文件作用域。这样做可以避免函数名与其他文件中的函数名产生冲突。inline
:inline
关键字建议编译器将函数体直接嵌入到调用该函数的地方,而不是进行常规的函数调用。这有助于减少函数调用的开销,提高代码的执行效率,但最终是否内联由编译器决定。void
:表示函数没有返回值。_NRFX_IRQ_PRIORITY_SET
:函数名,用于设置中断优先级。IRQn_Type irq_number
:第一个参数,IRQn_Type
是一个枚举类型,用于表示不同的中断编号。通过这个参数可以指定要设置优先级的具体中断。uint8_t priority
:第二个参数,uint8_t
是无符号 8 位整数类型,用于指定中断的优先级。不同的系统中,中断优先级的取值范围和含义可能不同。- IRQn_Type枚举类型在nrf52840.h中定义如下:
typedef enum {
/* ======================================= ARM Cortex-M4 Specific Interrupt Numbers ======================================== */
Reset_IRQn = -15, /*!< -15 Reset Vector, invoked on Power up and warm reset */
NonMaskableInt_IRQn = -14, /*!< -14 Non maskable Interrupt, cannot be stopped or preempted */
HardFault_IRQn = -13, /*!< -13 Hard Fault, all classes of Fault */
MemoryManagement_IRQn = -12, /*!< -12 Memory Management, MPU mismatch, including Access Violation
and No Match */
BusFault_IRQn = -11, /*!< -11 Bus Fault, Pre-Fetch-, Memory Access Fault, other address/memory
related Fault */
UsageFault_IRQn = -10, /*!< -10 Usage Fault, i.e. Undef Instruction, Illegal State Transition */
SVCall_IRQn = -5, /*!< -5 System Service Call via SVC instruction */
DebugMonitor_IRQn = -4, /*!< -4 Debug Monitor */
PendSV_IRQn = -2, /*!< -2 Pendable request for system service */
SysTick_IRQn = -1, /*!< -1 System Tick Timer */
/* ========================================== nrf52840 Specific Interrupt Numbers ========================================== */
POWER_CLOCK_IRQn = 0, /*!< 0 POWER_CLOCK */
RADIO_IRQn = 1, /*!< 1 RADIO */
UARTE0_UART0_IRQn = 2, /*!< 2 UARTE0_UART0 */
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn= 3, /*!< 3 SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 */
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn= 4, /*!< 4 SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 */
NFCT_IRQn = 5, /*!< 5 NFCT */
GPIOTE_IRQn = 6, /*!< 6 GPIOTE */
SAADC_IRQn = 7, /*!< 7 SAADC */
TIMER0_IRQn = 8, /*!< 8 TIMER0 */
TIMER1_IRQn = 9, /*!< 9 TIMER1 */
TIMER2_IRQn = 10, /*!< 10 TIMER2 */
RTC0_IRQn = 11, /*!< 11 RTC0 */
TEMP_IRQn = 12, /*!< 12 TEMP */
RNG_IRQn = 13, /*!< 13 RNG */
ECB_IRQn = 14, /*!< 14 ECB */
CCM_AAR_IRQn = 15, /*!< 15 CCM_AAR */
WDT_IRQn = 16, /*!< 16 WDT */
RTC1_IRQn = 17, /*!< 17 RTC1 */
QDEC_IRQn = 18, /*!< 18 QDEC */
COMP_LPCOMP_IRQn = 19, /*!< 19 COMP_LPCOMP */
SWI0_EGU0_IRQn = 20, /*!< 20 SWI0_EGU0 */
SWI1_EGU1_IRQn = 21, /*!< 21 SWI1_EGU1 */
SWI2_EGU2_IRQn = 22, /*!< 22 SWI2_EGU2 */
SWI3_EGU3_IRQn = 23, /*!< 23 SWI3_EGU3 */
SWI4_EGU4_IRQn = 24, /*!< 24 SWI4_EGU4 */
SWI5_EGU5_IRQn = 25, /*!< 25 SWI5_EGU5 */
TIMER3_IRQn = 26, /*!< 26 TIMER3 */
TIMER4_IRQn = 27, /*!< 27 TIMER4 */
PWM0_IRQn = 28, /*!< 28 PWM0 */
PDM_IRQn = 29, /*!< 29 PDM */
MWU_IRQn = 32, /*!< 32 MWU */
PWM1_IRQn = 33, /*!< 33 PWM1 */
PWM2_IRQn = 34, /*!< 34 PWM2 */
SPIM2_SPIS2_SPI2_IRQn = 35, /*!< 35 SPIM2_SPIS2_SPI2 */
RTC2_IRQn = 36, /*!< 36 RTC2 */
I2S_IRQn = 37, /*!< 37 I2S */
FPU_IRQn = 38, /*!< 38 FPU */
USBD_IRQn = 39, /*!< 39 USBD */
UARTE1_IRQn = 40, /*!< 40 UARTE1 */
QSPI_IRQn = 41, /*!< 41 QSPI */
CRYPTOCELL_IRQn = 42, /*!< 42 CRYPTOCELL */
PWM3_IRQn = 45, /*!< 45 PWM3 */
SPIM3_IRQn = 47 /*!< 47 SPIM3 */
} IRQn_Type;
ASSERT(INTERRUPT_PRIORITY_IS_VALID(priority));
- 断言检查:
ASSERT
是一个断言宏,通常用于在调试阶段检查某个条件是否为真。如果条件为假,程序会触发断言失败,一般会导致程序停止执行,并给出相应的错误信息。INTERRUPT_PRIORITY_IS_VALID(priority)
是一个自定义的函数或宏,用于检查传入的中断优先级priority
是否合法。这可以避免因传入无效的优先级值而导致系统出现不可预期的行为。
NVIC_SetPriority(irq_number, priority);
设置中断优先级:
NVIC_SetPriority
是一个标准的 CMSIS(Cortex Microcontroller Software Interface Standard)函数,用于设置中断向量控制器(NVIC)中指定中断的优先级。irq_number
是要设置优先级的中断编号,priority
是要设置的优先级值。该函数会将相应的优先级值写入 NVIC 的寄存器中,从而实现中断优先级的设置。
nrfx_get_irq_number(NRF_GPIOTE)函数代码如下 :
__STATIC_INLINE IRQn_Type nrfx_get_irq_number(void const * p_reg)
{
return (IRQn_Type)NRFX_IRQ_NUMBER_GET(p_reg);
}
其中:NRFX_IRQ_NUMBER_GET(p_reg)是一个带参数的宏,有如下连环定义,有点奇葩:
#define NRFX_IRQ_NUMBER_GET(base_addr) NRFX_PERIPHERAL_ID_GET(base_addr)
#define NRFX_PERIPHERAL_ID_GET(base_addr) (uint8_t)((uint32_t)(base_addr) >> 12)
下面宏定义的理解如下 :
此宏名为 NRFX_PERIPHERAL_ID_GET,其功能是从外设的基地址中提取外设的 ID。通常,在嵌入式系统里,每个外设都会被分配一个特定的基地址,该地址在内存中是一个 32 位的无符号整数。而外设 ID 往往是一个 8 位的无符号整数,用于唯一标识系统中的每个外设。
宏定义解析
1. 参数: ◦ base_addr:这是宏的参数,代表外设的基地址。在实际使用时,你需要把外设的基地址作为参数传递给这个宏。
2. 类型转换: ◦ (uint32_t)(base_addr):把 base_addr 强制转换为 32 位无符号整数类型。这样做是为了确保后续的位操作能正确进行,防止出现类型不匹配的问题。
3. 右移操作: ◦ (uint32_t)(base_addr) >> 12:将转换后的 32 位无符号整数右移 12 位。右移操作会把二进制数的所有位向右移动指定的位数,右边移出的位会被丢弃,左边空出的位用 0 填充。右移 12 位的目的是提取出基地址中包含外设 ID 的部分。在一些系统中,外设 ID 可能被编码在基地址的特定位段中,通过右移操作可以将这部分位移动到最低 8 位。
4. 类型转换为 uint8_t: ◦ (uint8_t)((uint32_t)(base_addr) >> 12):将右移后的结果强制转换为 8 位无符号整数类型。经过右移 12 位后,基地址中包含外设 ID 的部分会位于最低 8 位,将其转换为 uint8_t 类型就得到了最终的外设 ID。 示例代码 以下是一个简单的示例,展示了如何使用这个宏:
#include <stdio.h>
#include <stdint.h>
#define NRFX_PERIPHERAL_ID_GET(base_addr) (uint8_t)((uint32_t)(base_addr) >> 12)
int main() {
uint32_t peripheral_base_addr = 0x40005000;
uint8_t peripheral_id = NRFX_PERIPHERAL_ID_GET(peripheral_base_addr);
printf("Peripheral ID: %u\n", peripheral_id);
return 0;
}
在这个示例中,我们定义了一个外设的基地址 peripheral_base_addr,然后使用 NRFX_PERIPHERAL_ID_GET 宏提取该外设的 ID,并将其存储在 peripheral_id 变量中。最后,使用 printf 函数输出外设 ID。
问题:为什么右移12位就能得到外设id号?
在嵌入式系统中,宏 NRFX_PERIPHERAL_ID_GET 将基地址右移 12 位来提取外设 ID,这通常与硬件设计和内存映射的规则有关,下面详细解释可能的原因:
1. 外设地址分配规则 在很多嵌入式系统里,外设的基地址是按照一定的规则进行分配的。通常会把地址空间划分为多个固定大小的块,每个块对应一个外设或者一类外设。每个块的大小是 字节,这里的 n 是一个整数。 当 n = 12 时,块的大小就是 字节(即 4KB)。也就是说,每个外设的基地址会以 4KB 为边界进行对齐。这样一来,基地址的低 12 位就可以用来表示外设内部的偏移地址,而高几位则用来表示外设的 ID。通过右移 12 位,就可以把高几位的外设 ID 移动到低 8 位,从而提取出外设 ID。
//2. 示例说明
//假设地址空间的分配规则是每个外设占用 4KB 的地址空间,以下是一个简单的地址分配示例:
// 外设编号 基地址范围 基地址示例 基地址二进制表示 右移 12 位后的结果
0 0x40000000 - 0x40000FFF 0x40000000 0100 0000 0000 0000 0000 0000 0000 0000 0x40
1 0x40001000 - 0x40001FFF 0x40001000 0100 0000 0000 0000 0001 0000 0000 0000 0x40
2 0x40002000 - 0x40002FFF 0x40002000 0100 0000 0000 0000 0010 0000 0000 0000 0x40
从这个示例可以看出,每个外设的基地址低 12 位是不同的,它们表示外设内部的偏移地址;而高几位是相同的,这些相同的高几位就可以用来表示外设 ID。通过右移 12 位,就可以把高几位的外设 ID 提取出来。
3. 硬件设计的一致性 右移 12 位这种设计是为了保证硬件设计的一致性和可扩展性。通过固定的地址对齐和 ID 提取方式,硬件工程师可以方便地规划外设的地址空间,软件工程师也可以方便地编写代码来管理和访问不同的外设。 综上所述,偏移 12 位是为了根据硬件设计的地址分配规则,从外设的基地址中准确提取出外设 ID。
(9)、nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
1. nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
- 功能概述:这行代码的作用是清除通用外设 I/O 任务和事件(GPIOTE)模块的端口事件标志。在嵌入式系统里,GPIOTE 模块用于处理 GPIO(通用输入输出)引脚的任务和事件。当某个 GPIO 引脚的状态发生变化时,GPIOTE 模块会记录相应的事件,这些事件可能会触发中断或者其他操作。清除事件标志可以避免之前的事件对后续操作产生干扰。
- 详细解释:
nrf_gpiote_event_clear
:这是一个由 Nordic Semiconductor 提供的函数,专门用于清除 GPIOTE 模块的事件标志。NRF_GPIOTE_EVENTS_PORT
:这是一个宏定义,代表 GPIOTE 模块的端口事件。它表明要清- 除的是整个端口的事件标志,而不是某个特定引脚的事件标志。
- nrf_gpiote_event_clear函数定义如下 :
-
__STATIC_INLINE void nrf_gpiote_event_clear(nrf_gpiote_events_t event) { *(uint32_t *)nrf_gpiote_event_addr_get(event) = 0; #if __CORTEX_M == 0x04 volatile uint32_t dummy = *((volatile uint32_t *)nrf_gpiote_event_addr_get(event)); (void)dummy; #endif }
nrf_gpiote_events_t是一个枚举类型,其定义如下:
typedef enum
{
NRF_GPIOTE_EVENTS_IN_0 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[0]), /**< In event 0. */
NRF_GPIOTE_EVENTS_IN_1 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[1]), /**< In event 1. */
NRF_GPIOTE_EVENTS_IN_2 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[2]), /**< In event 2. */
NRF_GPIOTE_EVENTS_IN_3 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[3]), /**< In event 3. */
#if (GPIOTE_CH_NUM > 4) || defined(__NRFX_DOXYGEN__)
NRF_GPIOTE_EVENTS_IN_4 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[4]), /**< In event 4. */
NRF_GPIOTE_EVENTS_IN_5 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[5]), /**< In event 5. */
NRF_GPIOTE_EVENTS_IN_6 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[6]), /**< In event 6. */
NRF_GPIOTE_EVENTS_IN_7 = offsetof(NRF_GPIOTE_Type, EVENTS_IN[7]), /**< In event 7. */
#endif
NRF_GPIOTE_EVENTS_PORT = offsetof(NRF_GPIOTE_Type, EVENTS_PORT), /**< Port event. */
} nrf_gpiote_events_t;
nrf_gpiote_event_addr_get函数代码如下 :
__STATIC_INLINE uint32_t nrf_gpiote_event_addr_get(nrf_gpiote_events_t event)
{
return ((uint32_t)NRF_GPIOTE + event);
}
2. nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
- 功能概述:此代码用于使能 GPIOTE 模块的端口中断。当中断被使能后,当 GPIOTE 模块检测到端口上的事件(如引脚状态改变)时,会触发相应的中断服务程序,从而执行预先定义好的操作。
- 详细解释:
nrf_gpiote_int_enable
:这是一个用于使能 GPIOTE 模块中断的函数。- 函数代码如下:
-
__STATIC_INLINE void nrf_gpiote_int_enable(uint32_t mask) { NRF_GPIOTE->INTENSET = mask; }
GPIOTE_INTENSET_PORT_Msk
:这是一个宏定义,代表端口中断使能的掩码。掩码是一个二进制值,用于指定哪些中断源需要被使能。在这个例子中,GPIOTE_INTENSET_PORT_Msk
表示使能整个端口的中断。
3. m_cb.state = NRFX_DRV_STATE_INITIALIZED;
- 功能概述:这行代码的作用是将一个回调结构体
m_cb
中的状态字段state
设置为NRFX_DRV_STATE_INITIALIZED
。通常,m_cb
是一个自定义的结构体,用于存储驱动程序的相关信息,而state
字段则用于表示驱动程序的当前状态。将状态设置为NRFX_DRV_STATE_INITIALIZED
意味着驱动程序已经完成初始化,可以正常工作。 - 详细解释:
m_cb
:这是一个自定义的结构体变量,具体的定义通常在代码的其他部分给出。state
:这是m_cb
结构体中的一个成员变量,用于存储驱动程序的状态信息。NRFX_DRV_STATE_INITIALIZED
:这是一个宏定义,代表驱动程序已经初始化完成的状态。
总结
这段代码的主要功能是清除 GPIOTE 模块的端口事件标志,使能端口中断,并将驱动程序的状态设置为已初始化。这些操作通常在驱动程序的初始化阶段执行,以确保 GPIOTE 模块可以正常工作,并能响应端口上的事件。