目录
概述
本文将展示如何在 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. 禁用UARTE
UARTE_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. 使能UARTE
UARTE_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, 波特率115200
uarte_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() {
// 禁用UARTE
UARTE_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() {
// 重新初始化UART
uarte_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); // 等待停止完成
// 禁用UARTE
UARTE_ENABLE = 0;
}