5、nRF52xx蓝牙学习(nrf_gpiote.c库函数学习)

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_EVENTSGPIOTE_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:该关键字表明此函数仅能在当前文件内部被调用,具有文件作用域。这样做可以避免函数名与其他文件中的函数名产生冲突。
    • inlineinline 关键字建议编译器将函数体直接嵌入到调用该函数的地方,而不是进行常规的函数调用。这有助于减少函数调用的开销,提高代码的执行效率,但最终是否内联由编译器决定。
    • 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 模块可以正常工作,并能响应端口上的事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值