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
#elseNRF_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%lx\r\n",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
这段代码主要完成以下工作:
-
引入必要的头文件
#include <stdbool.h> #include <stdint.h> #include <stdio.h>
引入标准 C 库头文件,提供基础数据类型和标准输入输出功能
-
引入 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
:板级支持包
-
条件编译处理不同 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. 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. */
这部分代码定义了串口通信的关键参数和测试配置,下面为你详细解释:
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. 数据传输相关宏定义
#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]); }
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) {// 及时处理数据,避免缓冲区溢出 }
4. 实际应用注意事项
回环测试的典型实现
#ifdef ENABLE_LOOPBACK_TEST// 发送测试数据app_uart_put('A');// 接收回环数据uint8_t received;while(app_uart_get(&received) != NRF_SUCCESS);printf("Loopback test: received %c\n", 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溢出错误}
}
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 事件回调的一部分,专门处理两种严重错误:
- 通信错误(如帧错误、奇偶校验错误等)
- FIFO 缓冲区错误(如溢出、下溢等)
当检测到这些错误时,会调用 Nordic SDK 的错误处理宏APP_ERROR_HANDLER
进行处理。
错误类型详解
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
:参数错误
错误处理机制
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 宏展开
该宏通常会:
- 停止所有中断
- 点亮错误指示灯(如果有配置)
- 进入无限循环或触发系统复位
- 调试模式下可能会打印错误代码
实际应用建议
1. 增强错误处理
void uart_error_handle(app_uart_evt_t * p_event)
{switch(p_event->evt_type){case APP_UART_COMMUNICATION_ERROR:// 记录错误日志log_error("UART comm error: 0x%08lX", p_event->data.error_communication);// 尝试恢复if (p_event->data.error_communication == NRF_ERROR_UART_OVERRUN) {uart_recover(); // 自定义恢复函数} else {APP_ERROR_HANDLER(p_event->data.error_communication);}break;case APP_UART_FIFO_ERROR:// 记录错误日志log_error("UART FIFO error: 0x%08lX", p_event->data.error_code);APP_ERROR_HANDLER(p_event->data.error_code);break;default:break;}
}
2. 错误恢复策略
static void uart_recover(void)
{// 清空接收缓冲区while(app_uart_get(NULL) == NRF_SUCCESS);// 重新同步通信send_sync_sequence();
}
3. 调试建议
- 使用调试器捕获错误代码
- 添加错误计数统计
- 实现条件性错误处理(某些错误允许恢复,严重错误触发复位)
通过合理扩展这个错误处理函数,可以提高系统的健壮性,减少因通信错误导致的系统崩溃。
第四
#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
#elseNRF_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%lx\r\n",id1,id2);nrf_delay_ms(1000);}}
代码详细解析
宏定义部分
#define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED
这是一个预处理宏定义,作用是设置 UART 硬件流控模式。具体分析如下:
- 宏名称:
UART_HWFC
- 宏值:
APP_UART_FLOW_CONTROL_DISABLED
,从命名可推断这是禁用硬件流控的常量 - 功能:用于配置 UART 通信时是否启用硬件流控(RTS/CTS)机制
- 背景知识:
- 硬件流控是通过专用引脚(RTS/CTS)控制数据传输的机制,可防止接收端缓冲区溢出
- 当设置为禁用时,UART 将不使用 RTS/CTS 引脚进行流控管理
- 该宏会影响后续 UART 初始化时的流控配置
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位
这段代码是程序的入口函数,首先定义了三个 32 位无符号整数变量:
err_code
:用于存储函数执行后的错误码id1
和id2
:用于存储从芯片读取的设备 ID
接下来两行代码通过访问NRF_FICR
寄存器读取设备 ID:
NRF_FICR
是 Nordic 芯片中存储固定信息的寄存器组(Fixed Information Control Register)DEVICEID
是其中的一个寄存器数组,包含两个 32 位寄存器id1
存储低 31 位 ID(因为最高位通常为保留位)id2
存储高 31 位 ID- 组合这两个值可得到完整的 62 位设备唯一标识符
UART 配置结构体
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
#elseNRF_UARTE_BAUDRATE_115200
#endif};
这部分定义了一个 UART 通信参数结构体,包含以下字段:
字段 | 说明 |
---|---|
RX_PIN_NUMBER | UART 接收引脚编号 |
TX_PIN_NUMBER | UART 发送引脚编号 |
RTS_PIN_NUMBER | 硬件流控请求发送引脚编号 |
CTS_PIN_NUMBER | 硬件流控清除发送引脚编号 |
UART_HWFC | 硬件流控模式(使用之前定义的宏,此处为禁用) |
false | 可能是一个使能标志位,此处设为 false 表示禁用某种功能 |
波特率配置 | 根据芯片是否定义 UART_PRESENT 宏,选择不同的 UART 或 UARTE 模块波特率 |
- 条件编译部分:
#if defined (UART_PRESENT)
用于兼容不同 nRF 芯片型号- nRF51 系列通常使用 UART 模块
- nRF52 系列通常使用 UARTE(增强型 UART)模块
- 两者波特率定义在不同头文件中
UART 初始化
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);
这部分调用 API 初始化 UART FIFO 模式:
-
函数参数说明:
&comm_params
:指向之前定义的通信参数结构体UART_RX_BUF_SIZE
:接收缓冲区大小(宏定义值)UART_TX_BUF_SIZE
:发送缓冲区大小(宏定义值)uart_error_handle
:UART 错误处理函数指针APP_IRQ_PRIORITY_LOWEST
:中断优先级(最低优先级)err_code
:用于接收函数执行结果的变量
-
错误处理机制:
APP_ERROR_CHECK
是一个宏,用于检查函数执行是否成功- 如果
err_code
非零,通常会触发断言或错误处理流程
主循环部分
while (1){printf("打印id:%lx%lx\r\n",id1,id2);nrf_delay_ms(1000);}
这是一个无限循环,实现以下功能:
-
printf
函数:通过 UART 输出设备 ID%lx
格式说明符表示以 16 进制无符号长整型输出- 输出格式为 "打印 id:低 31 位 ID 高 31 位 ID"
- 末尾的
\r\n
是串口通信中常用的换行符组合
-
nrf_delay_ms(1000)
:- 调用 Nordic SDK 提供的延时函数
- 使程序暂停 1000 毫秒(1 秒)
- 实现每隔 1 秒打印一次设备 ID 的功能
整体功能总结
这段代码的主要功能是:
- 配置 UART 通信参数,禁用硬件流控,设置 115200 波特率
- 从芯片固定信息寄存器中读取 62 位唯一设备 ID
- 初始化 UART FIFO 模式并进行错误检查
- 在主循环中每隔 1 秒通过 UART 打印设备 ID
技术背景补充
- 应用场景:这段代码很可能运行在 Nordic Semiconductor 的 nRF51 或 nRF52 系列芯片上,常见于蓝牙或低功耗无线设备
- 开发框架:使用了 Nordic 的 APP_UART API,属于 sdk_config.h 配置的标准应用接口
- 设备 ID 用途:芯片唯一 ID 常用于设备身份识别、加密密钥生成、固件升级标识等场景
- UART 配置:115200 波特率是嵌入式系统中常见的调试串口速率,禁用流控适用于简单调试场景
如果需要进一步了解某个具体函数或寄存器的细节,可以提供更深入的技术文档说明。这段代码是基于 Nordic nRF 系列芯片的 UART 通信及设备 ID 打印程序,以下是对代码的详细解释:
1. 宏定义解析
#define UART_HWFC APP_UART_FLOW_CONTROL_DISABLED
- 功能:禁用硬件流控制(Hardware Flow Control, HWFC)。
- 作用:UART 通信中,硬件流控制通过 RTS/CTS 引脚避免数据溢出,但会增加引脚开销。此处禁用后,通信仅使用 TX/RX 两根线,适用于数据量小或接收端缓冲区足够的场景。
2. 设备 ID 读取
uint32_t id1, id2;
id1 = NRF_FICR->DEVICEID[0]; // 读取ID低31位
id2 = NRF_FICR->DEVICEID[1]; // 读取ID高31位
- NRF_FICR:芯片内部的 Factory Information Configuration Register,存储设备唯一 ID(通常为 64 位)。
- DEVICEID[0]/[1]:
DEVICEID[0]
:低 32 位(实际有效位为低 31 位)。DEVICEID[1]
:高 32 位(实际有效位为低 31 位)。
- 用途:设备 ID 常用于设备标识、加密密钥生成等。
3. UART 参数配置
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#elseNRF_UARTE_BAUDRATE_115200#endif
};
- 引脚配置:
RX_PIN_NUMBER
/TX_PIN_NUMBER
:必选,用于数据收发。RTS_PIN_NUMBER
/CTS_PIN_NUMBER
:虽定义但因UART_HWFC
禁用,实际未使用。
- 波特率:115200bps,需与接收端(如串口调试助手)匹配。
- UART vs UARTE:
UART_PRESENT
:判断芯片是否支持传统 UART 外设。UARTE
:增强型 UART,支持更高性能(如 DMA)。
4. UART 初始化
APP_UART_FIFO_INIT(&comm_params, // UART参数UART_RX_BUF_SIZE, // 接收缓冲区大小UART_TX_BUF_SIZE, // 发送缓冲区大小uart_error_handle, // 错误处理函数APP_IRQ_PRIORITY_LOWEST, // 中断优先级err_code // 错误码输出
);
APP_ERROR_CHECK(err_code); // 检查初始化是否成功
- FIFO 缓冲区:
UART_RX_BUF_SIZE
/TX_BUF_SIZE
:定义接收 / 发送缓冲区大小(通常为 32/64 字节)。- 作用:临时存储数据,避免中断频繁触发。
- 错误处理:
uart_error_handle
:用户自定义函数,处理 UART 错误(如溢出、帧错误)。APP_ERROR_CHECK
:Nordic SDK 提供的宏,若初始化失败会触发断言或复位系统。
5. 主循环功能
while (1) {printf("打印id:%lx%lx\r\n", id1, id2); // 打印设备IDnrf_delay_ms(1000); // 延时1秒
}
- printf 实现:
- 通过 UART 发送格式化字符串,依赖于 SDK 对标准库的重定向(通常重定向到
app_uart_put
函数)。 %lx
:以 16 进制格式打印无符号长整型。
- 通过 UART 发送格式化字符串,依赖于 SDK 对标准库的重定向(通常重定向到
- 延时机制:
nrf_delay_ms(1000)
:阻塞延时 1 秒,期间 CPU 无法处理其他任务。- 适用于简单场景,复杂应用建议使用定时器或 RTOS 调度。
代码潜在问题
- 硬件流控制禁用:若数据量大,可能导致接收缓冲区溢出(需确保
UART_RX_BUF_SIZE
足够)。 - 设备 ID 有效性:
DEVICEID
的高 31 位可能全为 0(部分芯片设计),打印时需注意格式(如补零)。 - 阻塞延时:
nrf_delay_ms
会阻塞 CPU,建议在低功耗或多任务场景使用非阻塞延时。
总结
这段代码实现了:
- 读取芯片唯一 ID。
- 配置 UART 通信(禁用硬件流控制,波特率 115200)。
- 每秒通过串口打印一次设备 ID。
适用于设备标识测试或简单调试场景。