16、nrf52840蓝牙学习(唯一ID加密与解密)-ELink墨水屏电子纸社区-FPGA CPLD-ChipDebug

16、nrf52840蓝牙学习(唯一ID加密与解密)

 

唯一ID程序学习:

/******************** (C) COPYRIGHT 2023青风电子 ********************  * 文件名  :main  * 出品论坛 :www.qfv8.com          * 实验平台:青云nRF52xx蓝牙开发板  * 描述    :串口输出  * 作者    :青风  * 店铺    :qfv5.taobao.com **********************************************************************/    #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include "app_uart.h" #include "app_error.h" #include "nrf_delay.h" #include "nrf.h" #include "bsp.h" #if defined (UART_PRESENT) #include "nrf_uart.h" #endif #if defined (UARTE_PRESENT) #include "nrf_uarte.h" #endif   //#define ENABLE_LOOPBACK_TEST  /**< if defined, then this example will be a loopback test, which means that TX should be connected to RX to get data loopback. */  #define MAX_TEST_DATA_BYTES     (15U)                /**< max number of test bytes to be used for tx and rx. */ #define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */  void uart_error_handle(app_uart_evt_t * p_event) {     if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)     {         APP_ERROR_HANDLER(p_event->data.error_communication);     }     else if (p_event->evt_type == APP_UART_FIFO_ERROR)     {         APP_ERROR_HANDLER(p_event->data.error_code);     } }     #define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED   /**  * @brief Function for main application entry.  */ int main(void) {     uint32_t err_code; 		 uint32_t id1,id2;     id1=NRF_FICR->DEVICEID[0]; //读取id低31位   id2=NRF_FICR->DEVICEID[1];//读取id高31位 	     const app_uart_comm_params_t comm_params =       {           RX_PIN_NUMBER,           TX_PIN_NUMBER,           RTS_PIN_NUMBER,           CTS_PIN_NUMBER,           UART_HWFC,           false, #if defined (UART_PRESENT)           NRF_UART_BAUDRATE_115200 #else           NRF_UARTE_BAUDRATE_115200 #endif       };      APP_UART_FIFO_INIT(&comm_params,                          UART_RX_BUF_SIZE,                          UART_TX_BUF_SIZE,                          uart_error_handle,                          APP_IRQ_PRIORITY_LOWEST,                          err_code);      APP_ERROR_CHECK(err_code); 			 			 		 			  while (1)     {  		printf("打印id:%lx%lxrn",id1,id2);     nrf_delay_ms(1000);              }  }   /** @} */ 

第一节

#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include "app_uart.h" #include "app_error.h" #include "nrf_delay.h" #include "nrf.h" #include "bsp.h" #if defined (UART_PRESENT) #include "nrf_uart.h" #endif #if defined (UARTE_PRESENT) #include "nrf_uarte.h" #endif

1.1这段代码主要完成以下工作:

  1. 引入必要的头文件
    #include <stdbool.h> #include <stdint.h> #include <stdio.h> 

     

    引入标准 C 库头文件,提供基础数据类型和标准输入输出功能

  2. 引入 Nordic SDK 组件
    #include "app_uart.h" #include "app_error.h" #include "nrf_delay.h" #include "nrf.h" #include "bsp.h" 

     

    • app_uart.h:提供 UART 应用层驱动
    • app_error.h:错误处理机制
    • nrf_delay.h:延时函数
    • nrf.h:nRF52 芯片寄存器定义
    • bsp.h:板级支持包
  3. 条件编译处理不同 UART 外设
    #if defined (UART_PRESENT) #include "nrf_uart.h" #endif #if defined (UARTE_PRESENT) #include "nrf_uarte.h" #endif 

     

    • UART_PRESENT:表示芯片包含基本 UART 外设
    • UARTE_PRESENT:表示芯片包含增强型 UARTE 外设 (带硬件流控制和 DMA)

1.2串口通信关键组件解析

1. UART vs UARTE
  • UART:基本串口通信模块
  • UARTE:增强型串口模块,相比 UART 增加了以下功能:
    • 硬件流控制 (RTS/CTS)
    • DMA 支持,减轻 CPU 负担
    • 更高的通信可靠性
2. 应用层 UART 驱动 (app_uart.h)

Nordic SDK 提供的应用层 UART 驱动具有以下特点

  • 基于事件驱动架构
  • 支持 FIFO 缓冲区
  • 提供错误回调机制
  • 兼容 UART 和 UARTE 外设
3. 错误处理机制

app_error.h 中定义的错误处理宏:

  • APP_ERROR_CHECK(err_code):检查错误码,非零则触发错误处理
  • APP_ERROR_HANDLER(error_code):错误处理函数,通常会:
    • 记录错误码
    • 点亮错误指示灯
    • 进入无限循环或复位系统

第二节

//#define ENABLE_LOOPBACK_TEST  /**< if defined, then this example will be a loopback test, which means that TX should be connected to RX to get data loopback. */  #define MAX_TEST_DATA_BYTES     (15U)                /**< max number of test bytes to be used for tx and rx. */ #define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */

这部分代码定义了串口通信的关键参数和测试配置,下面为你详细解释:

2.1. 回环测试宏定义

//#define ENABLE_LOOPBACK_TEST  /**< if defined, then this example will be a loopback test, which means that TX should be connected to RX to get data loopback. */ 
  • 功能:启用串口回环测试模式
  • 使用方法:取消注释该行即可启用回环测试
  • 工作原理
    • 将 TX 引脚与 RX 引脚物理连接
    • 发送的数据会被立即回收到接收缓冲区
    • 用于验证 UART 硬件和驱动是否正常工作

2.2. 数据传输相关宏定义

#define MAX_TEST_DATA_BYTES     (15U)                /**< max number of test bytes to be used for tx and rx. */ 
  • 作用:定义测试时最大发送 / 接收数据字节数
  • 典型应用场景
    uint8_t test_data[MAX_TEST_DATA_BYTES] = "Hello UART!"; for (int i = 0; i < MAX_TEST_DATA_BYTES; i++) {     app_uart_put(test_data[i]); } 

2.3. 缓冲区大小定义

#define UART_TX_BUF_SIZE 256                         /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 256                         /**< UART RX buffer size. */ 
  • FIFO 缓冲区作用
    • 发送缓冲区:暂存待发送的数据,避免 CPU 等待串口发送完成
    • 接收缓冲区:暂存接收到的数据,防止数据丢失
  • 参数调整建议
    • 高速通信场景 (如 115200bps 以上):增大缓冲区
    • 空间受限设备:适当减小缓冲区,但需注意:
      // 缓冲区溢出风险示例 if (app_uart_get(&data) == NRF_SUCCESS) {     // 及时处理数据,避免缓冲区溢出 } 

2.4. 实际应用注意事项

回环测试的典型实现
#ifdef ENABLE_LOOPBACK_TEST     // 发送测试数据     app_uart_put('A');          // 接收回环数据     uint8_t received;     while(app_uart_get(&received) != NRF_SUCCESS);     printf("Loopback test: received %cn", received); #endif 
缓冲区溢出处理
static void uart_event_handle(app_uart_evt_t * p_event) {     if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR) {         // 处理通信错误     } else if (p_event->evt_type == APP_UART_FIFO_ERROR) {         // 处理FIFO溢出错误     } } 

2.5. 参数优化建议

  • 低功耗场景
    #define UART_TX_BUF_SIZE 64  // 减小发送缓冲区节省RAM #define UART_RX_BUF_SIZE 64  // 减小接收缓冲区节省RAM 
  • 高速数据传输场景
    #define UART_TX_BUF_SIZE 512  // 增大发送缓冲区处理突发数据 #define UART_RX_BUF_SIZE 512  // 增大接收缓冲区处理突发数据 

通过合理配置这些参数,可以在资源占用和通信性能之间取得最佳平衡。


第三

void uart_error_handle(app_uart_evt_t * p_event) {     if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)     {         APP_ERROR_HANDLER(p_event->data.error_communication);     }     else if (p_event->evt_type == APP_UART_FIFO_ERROR)     {         APP_ERROR_HANDLER(p_event->data.error_code);     } }

这个函数是 UART 错误处理回调函数,用于处理 Nordic SDK 中 UART 通信过程中出现的错误。下面为你详细解析:

函数功能概述

该函数作为 UART 事件回调的一部分,专门处理两种严重错误:

  1. 通信错误(如帧错误、奇偶校验错误等)
  2. FIFO 缓冲区错误(如溢出、下溢等)

当检测到这些错误时,会调用 Nordic SDK 的错误处理宏APP_ERROR_HANDLER进行处理。

3.1错误类型详解

1. 通信错误(APP_UART_COMMUNICATION_ERROR)

可能的错误代码包括:

  • NRF_ERROR_UART_OVERRUN:接收溢出错误(新数据覆盖未读取数据)
  • NRF_ERROR_UART_PARITY:奇偶校验错误
  • NRF_ERROR_UART_FRAMING:帧格式错误
  • NRF_ERROR_UART_BREAK:接收到 BREAK 信号
  • NRF_ERROR_UART_HW_FLOW:硬件流控制错误
2. FIFO 错误(APP_UART_FIFO_ERROR)

可能的错误代码包括:

  • NRF_ERROR_NO_MEM:FIFO 内存不足
  • NRF_ERROR_INVALID_STATE:FIFO 状态无效
  • NRF_ERROR_INVALID_PARAM:参数错误

3.2错误处理机制

1. 错误捕获方式
// 注册错误回调函数 APP_UART_FIFO_INIT(..., uart_error_handle, ...); 

当 UART 驱动检测到错误时,会调用该回调函数。

2. 错误处理流程
void uart_error_handle(app_uart_evt_t * p_event) {     if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)     {         // 通信错误处理         uint32_t error_code = p_event->data.error_communication;         APP_ERROR_HANDLER(error_code);     }     else if (p_event->evt_type == APP_UART_FIFO_ERROR)     {         // FIFO错误处理         uint32_t error_code = p_event->data.error_code;         APP_ERROR_HANDLER(error_code);     } } 
3. APP_ERROR_HANDLER 宏

#define APP_ERROR_HANDLER(ERR_CODE)
do
{
app_error_handler_bare((ERR_CODE));
} while (0)

该宏通常会:

  1. 停止所有中断
  2. 点亮错误指示灯(如果有配置)
  3. 进入无限循环或触发系统复位
  4. 调试模式下可能会打印错误代码

APP_ERROR_HANDLER宏定义是 Nordic nRF5 SDK 中用于错误处理的统一接口,下面我来详细解释它的设计和用途:

  1. APP_ERROR_HANDLER(ERR_CODE) 

     

    • 这是一个错误处理的统一入口,接收一个错误码ERR_CODE
    • 通过do {...} while(0)结构确保宏在任何上下文中都能正确展开
    • 调用app_error_handler_bare函数进行实际的错误处理

3.1其中:void app_error_handler_bare函数定义如下 :

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);
}


app_error_handler_bare 错误处理函数,它是 Nordic SDK(用于 Nordic Semiconductor 的 nRF 系列微控制器)中处理严重错误的核心机制之一, 是一个低级错误处理函数,当系统遇到不可恢复的错误(如硬件故障、内存访问错误或关键 API 调用失败)时被调用。它的主要作用是:

  1. 收集错误信息(如错误码)。
  2. 将错误信息传递给更高级的故障处理函数。
  3. 触发系统复位或进入无限循环,防止系统继续运行在不稳定状态。

3.2  app_error_handler_bare 参数解析

  • ret_code_t error_code:错误码,通常是 Nordic SDK 中定义的错误类型(如 NRF_ERROR_INVALID_PARAMNRF_ERROR_NO_MEM 等)。
  • error_info_t:这是一个自定义结构体,用于存储错误上下文信息,通常包含:
    • line_num:错误发生的源代码行号(这里设为 0,表示未知)。
    • p_file_name:错误发生的源文件名称(这里设为 NULL,表示未知)。
    • err_code:具体的错误码(来自函数参数)。
error_info_t error_info = {     .line_num    = 0,     .p_file_name = NULL,     .err_code    = error_code, }; 

3.2.1 error_info_t结构体详细解释:

这段代码定义了一个名为 error_info_t 的结构体,用于在 Nordic SDK(nRF 系列微控制器开发工具包)中存储错误信息。这个结构体是错误处理机制的核心组件,用于在系统发生故障时捕获和传递上下文信息。

1. uint32_t line_num
  • 作用:记录错误发生的源代码行号。
  • 说明
    • 通常由错误检查宏(如 APP_ERROR_CHECK)自动填充。
    • 值为 0 表示行号未知(如在 app_error_handler_bare 中)。
2. uint8_t const * p_file_name
  • 作用:指向错误发生的源文件名称(字符串常量)。
  • 说明
    • 使用 const 确保不会修改文件名内容。
    • uint8_t 等价于 char,兼容 C 字符串。
    • 值为 NULL 表示文件名未知。
3. uint32_t err_code
  • 作用:存储具体的错误码,指示错误类型。
  • 说明
    • 错误码通常来自 Nordic SDK 的错误枚举(如 NRF_ERROR_INVALID_PARAM)。
    • 不同的错误码对应不同的故障原因(如参数无效、内存不足等)。

4.使用示例

在 Nordic SDK 中,这个结构体通常与错误处理函数配合使用。例如:

// 当检测到错误时,自动填充error_info_t结构体 #define APP_ERROR_CHECK(err_code)      do {          if ((err_code) != NRF_SUCCESS) {              error_info_t error_info = {                  .line_num = __LINE__,       // 当前行号(预处理器宏)                 .p_file_name = __FILE__,     // 当前文件名(预处理器宏)                 .err_code = (err_code)       // 具体错误码             };              app_error_handler((uint32_t)(err_code), __LINE__, __FILE__);          }      } while (0) 

5.当调用 APP_ERROR_CHECK(nrf_gpio_pin_set(LED_PIN)) 时:

  • 如果 nrf_gpio_pin_set() 返回非零错误码,APP_ERROR_CHECK 会自动捕获当前文件名(__FILE__)和行号(__LINE__)。
  • 这些信息会被打包到 error_info_t 结构体中,并传递给错误处理函数。

6.注意事项

  1. 内存占用:结构体大小为 4 + 4 + 4 = 12 字节(32 位系统),设计紧凑。
  2. 字符串生命周期p_file_name 指向的字符串必须是静态常量(如 __FILE__),否则可能导致悬空指针。
  3. 简化版本:在资源受限场景(如中断处理)中,可能使用简化版的错误处理函数(如 app_error_handler_bare),此时 line_num 和 p_file_name 会被设为默认值(0 和 NULL)。

3.3 app_error_handler_bare函数中 app_error_fault_handler函数:

app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info)); 

函数代码如下 :

__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:%urnPC 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 }
  • app_error_fault_handler:Nordic SDK 提供的故障处理函数,负责:
    • 记录错误信息(可能通过串口、调试器或闪存)。
    • 触发硬件断点(如果调试器连接)。
    • 执行系统复位或进入无限循环。
  • 参数说明
    • NRF_FAULT_ID_SDK_ERROR:故障类型,表示这是一个 SDK 错误。
    • 0:附加参数(这里未使用)。
    • (uint32_t)(&error_info):将错误信息结构体的地址转换为 uint32_t 类型传递。

3.3.1app_error_fault_handler函数详细说明:

这个函数 app_error_fault_handler 是 Nordic SDK 中的核心错误处理函数,当系统遇到严重故障(如断言失败、内存访问错误或未处理的错误码)时会被调用。它的主要作用是记录错误信息、暂停系统运行,并在必要时触发复位。

__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) 
  • __WEAK:GCC 编译器属性,表示这个函数可以被用户代码重写(即用户可以提供自定义实现)。
  • 参数
    • id:故障类型 ID(如 NRF_FAULT_ID_SDK_ERRORNRF_FAULT_ID_SDK_ASSERT)。
    • pc:程序计数器(Program Counter)值,指示故障发生时的代码位置。
    • info:额外的错误信息指针(通常是 assert_info_t 或 error_info_t 结构体)。

3.3.2函数执行流程详解

1. 禁用中断并刷新日志
__disable_irq(); NRF_LOG_FINAL_FLUSH(); 
  • __disable_irq():禁用所有中断,防止在错误处理过程中被打断。
  • NRF_LOG_FINAL_FLUSH():确保所有日志信息被发送到输出设备(如串口)。
2. 错误信息记录(DEBUG 模式)
#ifdef DEBUG     // 根据不同的故障ID输出详细错误信息 #else     NRF_LOG_ERROR("Fatal error"); #endif 
  • DEBUG 模式:根据 id 类型输出详细错误信息(如断言位置、错误码含义)。
  • 非 DEBUG 模式:仅输出 “Fatal error”,减少资源消耗。
3. 故障类型分类处理
switch (id) {     case NRF_FAULT_ID_SD_ASSERT:         // 软设备断言失败     case NRF_FAULT_ID_SDK_ASSERT:         // SDK断言失败,输出文件名和行号     case NRF_FAULT_ID_SDK_ERROR:         // SDK错误,输出错误码、错误描述、位置和PC值     default:         // 未知故障 } 
  • NRF_FAULT_ID_SDK_ERROR:对应 app_error_handler_bare 传递的错误,会解析 error_info_t 结构体。
  • nrf_strerror_get():将错误码转换为可读字符串(如 NRF_ERROR_INVALID_PARAM → “Invalid parameter”)。
4. 调试断点与系统暂停
NRF_BREAKPOINT_COND; 
  • 如果调试器已连接,程序会在此处暂停,允许开发者检查寄存器和内存状态。
5. 系统恢复策略
#ifdef DEBUG     app_error_save_and_stop(id, pc, info);  // 停留在错误状态 #else     NVIC_SystemReset();                    // 直接复位系统 #endif 
  • DEBUG 模式:调用 app_error_save_and_stop() 进入无限循环,保留错误现场。
  • 非 DEBUG 模式:调用 NVIC_SystemReset() 强制系统复位,恢复运行。

6.关键技术细节

1. 错误信息结构体解析
  • assert_info_t(用于断言失败):
    typedef struct {     uint32_t        line_num;     uint8_t const * p_file_name; } assert_info_t; 
  • error_info_t(用于错误码):
    typedef struct {     uint32_t        line_num;     uint8_t const * p_file_name;     uint32_t        err_code; } error_info_t; 
2. 调试与生产环境的区别
特性 DEBUG 模式
请登录后发表评论

    没有回复内容