Nordic nRF52832 寄存器级 UARTE 发送实现
目录
概述
1 nRF52832的UART寄存器
1.1 寄存器列表
1.2 寄存器介绍
1.2.1 使能控制 (ENABLE)
1.2.2 波特率设置 (BAUDRATE)
1.2.3 配置寄存器 (CONFIG)
1.2.4 引脚配置寄存器
1.2.5 数据传输寄存器
1.2.6 状态与事件寄存器
1.2.6.1 事件寄存器 (EVENTS)
1.2.6.2 错误状态 (ERRORSRC)
1.2.7 中断控制
2 完整实现代码
3 关键操作解析
3.1 UARTE 初始化流程
3.2 数据发送流程
4 低功耗优化技巧
4.1 动态电源管理
4.2 发送超时机制
4.3 中断驱动实现
5 常见问题解决
概述
本文将展示如何在 nRF52832 上直接通过寄存器操作实现 UARTE 发送功能,无需 SDK 或 HAL 库。这种底层实现适用于对功耗和时序有严格要求的场景。
1 nRF52832的UART寄存器
1.1 寄存器列表
nRF52832 使用 UARTE (UART with EasyDMA) 外设实现高效串行通信。关键寄存器:
寄存器 | 地址 | 功能描述 |
---|---|---|
UARTE_ENABLE | 0x40002000 | UARTE 使能控制 |
UARTE_BAUDRATE | 0x40002004 | 波特率设置 |
UARTE_CONFIG | 0x4000205C | 数据格式配置 |
UARTE_PSEL.TXD | 0x4000210C | TX 引脚选择 |
UARTE_TXD.PTR | 0x40002530 | 发送数据指针 |
UARTE_TXD.MAXCNT | 0x40002538 | 发送数据长度 |
UARTE_TASKS_STARTTX | 0x40002508 | 启动发送任务 |
UARTE_EVENTS_ENDTX | 0x40002510 | 发送完成事件 |
1.2 寄存器介绍
1.2.1 使能控制 (ENABLE
)
地址:
0x400020500
功能: 全局 UART 使能
位配置:
0: Disabled (复位状态) 4: Enabled // 必须设置为4才能工作
操作:
NRF_UARTE0->ENABLE = 4; // 启用UART
1.2.2 波特率设置 (BAUDRATE
)
地址:
0x40002524
常用值 (32-bit):
0x0004F000: 9600 baud 0x0009D000: 19200 baud 0x0013B000: 38400 baud 0x00275000: 57600 baud 0x004EA000: 115200 baud (默认) 0x009D5000: 230400 baud 0x01D20000: 921600 baud
示例:
NRF_UARTE0->BAUDRATE = 0x004EA000; // 115200 bps
1.2.3 配置寄存器 (CONFIG
)
地址:
0x4000256C
关键位域:
位域 名称 值 功能 [1:0]
HWFC
0 禁用硬件流控 1 仅使能RTS 2 仅使能CTS 3 使能RTS+CTS [4:2]
PARITY
0 无校验 1 偶校验 2 奇校验 [6:5]
STOP
0 1位停止位 1 2位停止位 - 配置示例 (8N1):
NRF_UARTE0->CONFIG = 0; // 无流控, 无校验, 1位停止位
1.2.4 引脚配置寄存器
引脚选择 (
PSEL
)
寄存器组:
寄存器 地址 功能 PSEL.RTS
0x40002508
RTS 引脚选择 PSEL.TXD
0x4000250C
TX 引脚选择 PSEL.CTS
0x40002510
CTS 引脚选择 PSEL.RXD
0x40002514
RX 引脚选择 - 操作 (配置 TX=P0.06, RX=P0.08):
NRF_UARTE0->PSEL.TXD = 6; NRF_UARTE0->PSEL.RXD = 8; NRF_UARTE0->PSEL.CTS = 0xFFFFFFFF; // 禁用CTS NRF_UARTE0->PSEL.RTS = 0xFFFFFFFF; // 禁用RTS
1.2.5 数据传输寄存器
1)发送控制 (TXD
)
PTR 寄存器 (
TXD.PTR
):
地址:
0x40002544
功能: 设置发送数据缓冲区地址 (DMA模式)
MAXCNT 寄存器 (
TXD.MAXCNT
):
地址:
0x40002548
功能: 设置发送数据长度
任务启动 (
TASKS_STARTTX
):
地址:
0x40002508
触发: 写入1启动发送
2) 接收控制 (RXD
)
PTR 寄存器 (
RXD.PTR
):
地址:
0x40002534
功能: 设置接收数据缓冲区地址
MAXCNT 寄存器 (
RXD.MAXCNT
):
地址:
0x40002538
功能: 设置接收缓冲区大小
任务启动 (
TASKS_STARTRX
):
地址:
0x40002500
触发: 写入1启动接收
1.2.6 状态与事件寄存器
1.2.6.1 事件寄存器 (EVENTS
)
事件 地址 触发条件 EVENTS_CTS
0x40002100
CTS 引脚状态变化 EVENTS_NCTS
0x40002104
CTS 引脚释放 EVENTS_RXDRDY
0x40002108
接收到数据 (每字节触发) EVENTS_ENDRX
0x40002110
完成DMA接收 (整个缓冲区) EVENTS_TXDRDY
0x4000211C
发送完成 (每字节) EVENTS_ENDTX
0x40002120
完成DMA发送 EVENTS_ERROR
0x40002124
帧/奇偶校验错误 EVENTS_RXTO
0x40002144
接收超时
事件清除:
NRF_UARTE0->EVENTS_RXDRDY = 0; // 写0清除事件
1.2.6.2 错误状态 (ERRORSRC
)
地址:
0x40002488
位掩码:
0x01: Overflow error // 溢出错误 0x02: Parity error // 奇偶校验错误 0x04: Framing error // 帧错误 0x08: Break error // 线路断开
错误清除:
NRF_UARTE0->ERRORSRC = NRF_UARTE0->ERRORSRC; // 读取即清除
1.2.7 中断控制
中断使能 (INTEN
)
地址:
0x40002300
关键中断位:
#define UARTE_INTEN_RXDRDY_Msk (1 << 2) // 接收中断 #define UARTE_INTEN_TXDRDY_Msk (1 << 7) // 发送中断 #define UARTE_INTEN_ERROR_Msk (1 << 9) // 错误中断 #define UARTE_INTEN_RXTO_Msk (1 << 17) // 超时中断
2 完整实现代码
#include <stdint.h>// UARTE0 寄存器基地址
#define UARTE0_BASE 0x40002000UL// 寄存器定义
#define UARTE_ENABLE (*(volatile uint32_t*)(UARTE0_BASE + 0x000)
#define UARTE_BAUDRATE (*(volatile uint32_t*)(UARTE0_BASE + 0x004)
#define UARTE_CONFIG (*(volatile uint32_t*)(UARTE0_BASE + 0x05C)
#define UARTE_PSEL_TXD (*(volatile uint32_t*)(UARTE0_BASE + 0x10C)
#define UARTE_TXD_PTR (*(volatile uint32_t*)(UARTE0_BASE + 0x530)
#define UARTE_TXD_MAXCNT (*(volatile uint32_t*)(UARTE0_BASE + 0x538)
#define UARTE_TASKS_STARTTX (*(volatile uint32_t*)(UARTE0_BASE + 0x508)
#define UARTE_EVENTS_ENDTX (*(volatile uint32_t*)(UARTE0_BASE + 0x510)// 波特率定义 (nRF52832 特定值)
#define BAUD_9600 0x00275000
#define BAUD_115200 0x01D7E000
#define BAUD_230400 0x03AFB000
#define BAUD_1000000 0x10000000// 配置位定义
#define CONFIG_HWFC_ENABLED (0x1UL << 0)
#define CONFIG_PARITY_EXCLUDED (0x0UL << 1)
#define CONFIG_PARITY_INCLUDED (0x7UL << 1) // 奇偶校验
#define CONFIG_STOP_1BIT (0x0UL << 4)
#define CONFIG_STOP_2BIT (0x1UL << 4)/*** 初始化 UARTE* * @param tx_pin TX引脚编号 (0-31)* @param baud_rate 波特率 (使用预定义常量)*/
void uarte_init(uint8_t tx_pin, uint32_t baud_rate) {// 1. 禁用UARTEUARTE_ENABLE = 0;// 2. 配置波特率UARTE_BAUDRATE = baud_rate;// 3. 配置数据格式: 8数据位, 1停止位, 无奇偶校验UARTE_CONFIG = CONFIG_PARITY_EXCLUDED | CONFIG_STOP_1BIT;// 4. 配置TX引脚UARTE_PSEL_TXD = tx_pin;// 5. 使能UARTEUARTE_ENABLE = 4; // 4 = 启用UARTE
}/*** 通过寄存器直接发送数据* * @param data 要发送的数据指针* @param len 数据长度*/
void uarte_send(const uint8_t *data, uint32_t len) {// 1. 设置发送数据指针UARTE_TXD_PTR = (uint32_t)data;// 2. 设置发送数据长度UARTE_TXD_MAXCNT = len;// 3. 清除发送完成事件标志UARTE_EVENTS_ENDTX = 0;// 4. 启动发送任务UARTE_TASKS_STARTTX = 1;// 5. 等待发送完成while (UARTE_EVENTS_ENDTX == 0) {// 忙等待 - 实际应用中可替换为中断驱动}// 6. 清除事件标志UARTE_EVENTS_ENDTX = 0;
}/*** 发送单个字符* * @param c 要发送的字符*/
void uarte_putchar(char c) {uarte_send((const uint8_t*)&c, 1);
}/*** 发送字符串* * @param str 要发送的字符串*/
void uarte_puts(const char *str) {uint32_t len = 0;const char *p = str;// 计算字符串长度while (*p++) len++;// 发送数据uarte_send((const uint8_t*)str, len);
}// 示例使用
int main(void) {// 初始化UART: TX引脚P0.06, 波特率115200uarte_init(6, BAUD_115200);// 发送字符串uarte_puts("Hello, nRF52832!\r\n");// 发送单个字符uarte_putchar('A');// 发送缓冲区数据uint8_t data[] = {0xAA, 0xBB, 0xCC, 0xDD};uarte_send(data, sizeof(data));while(1) {// 主循环}
}
3 关键操作解析
3.1 UARTE 初始化流程
// 禁用UARTE
UARTE_ENABLE = 0;// 设置波特率 (115200)
UARTE_BAUDRATE = 0x01D7E000;// 配置数据格式: 8N1
UARTE_CONFIG = (0 << 0) | // HWFC 禁用(0 << 1) | // 奇偶校验禁用(0 << 4); // 1停止位// 设置TX引脚 (P0.06)
UARTE_PSEL_TXD = 6;// 启用UARTE
UARTE_ENABLE = 4;
3.2 数据发送流程
// 设置数据地址
UARTE_TXD_PTR = (uint32_t)tx_buffer;// 设置数据长度
UARTE_TXD_MAXCNT = data_length;// 清除发送完成事件
UARTE_EVENTS_ENDTX = 0;// 启动发送
UARTE_TASKS_STARTTX = 1;// 等待发送完成
while (UARTE_EVENTS_ENDTX == 0);
4 低功耗优化技巧
4.1 动态电源管理
void uarte_sleep() {// 禁用UARTEUARTE_ENABLE = 0;// 配置GPIO为低功耗模式// (假设使用P0.06)*(volatile uint32_t*)0x50000000 = 0; // PIN_CNF[6].DIR = 输入*(volatile uint32_t*)0x50000718 = 0x000C0000; // PIN_CNF[6]: INPUT=连接, PULL=上拉
}void uarte_wake() {// 重新初始化UARTuarte_init(6, BAUD_115200);
}
4.2 发送超时机制
#define UART_TIMEOUT 10000 // 10ms超时bool uarte_send_timeout(const uint8_t *data, uint32_t len) {UARTE_TXD_PTR = (uint32_t)data;UARTE_TXD_MAXCNT = len;UARTE_EVENTS_ENDTX = 0;UARTE_TASKS_STARTTX = 1;uint32_t timeout = UART_TIMEOUT;while (UARTE_EVENTS_ENDTX == 0) {if (--timeout == 0) {// 超时处理UARTE_TASKS_STOPTX = 1;return false; // 发送失败}}return true; // 发送成功
}
4.3 中断驱动实现
// 在头文件中定义中断处理函数原型
void UARTE0_UART0_IRQHandler(void);// 启用UARTE中断
void uarte_enable_interrupts() {// 启用ENDTX中断*(volatile uint32_t*)(UARTE0_BASE + 0x304) = (1 << 4); // INTENSET = ENDTX// 设置中断优先级并使能NVIC_SetPriority(UARTE0_UART0_IRQn, 6);NVIC_EnableIRQ(UARTE0_UART0_IRQn);
}// 中断处理函数
volatile bool tx_complete = false;void UARTE0_UART0_IRQHandler(void) {if (UARTE_EVENTS_ENDTX) {UARTE_EVENTS_ENDTX = 0; // 清除事件tx_complete = true; // 设置完成标志}
}// 中断方式发送
void uarte_send_async(const uint8_t *data, uint32_t len) {tx_complete = false;UARTE_TXD_PTR = (uint32_t)data;UARTE_TXD_MAXCNT = len;UARTE_EVENTS_ENDTX = 0;UARTE_TASKS_STARTTX = 1;// 现在可以在主循环中检查tx_complete标志
}
5 常见问题解决
1) 波特率不匹配
症状: 接收端数据错误
验证: 检查波特率寄存器值是否正确:
// 常用波特率寄存器值
switch (baud_rate) {case 9600: return 0x00275000;case 115200: return 0x01D7E000;case 230400: return 0x03AFB000;case 1000000: return 0x10000000;default: return 0x01D7E000; // 默认为115200
}
2) 低功耗配置冲突
症状: 系统无法进入低功耗模式
解决: 在睡眠前禁用 UARTE:
void prepare_for_sleep() {// 停止所有UART操作UARTE_TASKS_STOPTX = 1;while (UARTE_EVENTS_ENDTX == 0); // 等待停止完成// 禁用UARTEUARTE_ENABLE = 0;
}