1. Home
  2. 博客

    nRF Connect SDK 基础课程第五课:串行通信(UART)

nRF Connect SDK 基础课程第五课:串行通信(UART)

Nordic Semiconductor

如果说前序课程完成了 GPIO 外设、设备树与驱动模型的基础入门,那么第五课《串行通信(UART)》则是嵌入式开发中数据交互的核心进阶内容。本课以通用异步收发器(UART)为核心,从协议原理、驱动 API 到实操开发,完整讲解 nRF Connect SDK 中 UART 异步(中断驱动)通信的实现方法,最终通过串口指令控制开发板 LED,掌握嵌入式设备与 PC、传感器、外设间最常用的串口通信能力。

本课面向嵌入式固件开发、物联网终端开发工程师,是 nRF Connect SDK 外设开发的必备核心课程,为后续无线通信、复杂外设交互打下基础。

课程定位:掌握嵌入式最通用的串口通信能力

UART 是嵌入式领域无时钟同步、点对点的经典串行通信协议,广泛用于传感器数据读取、调试日志打印、PC 与开发板交互(USB 转 UART)等场景。

本课基于 Zephyr RTOS 驱动框架,聚焦异步中断驱动模式的 UART 开发,摒弃低效轮询方式,实现数据收发的高效、低功耗处理。学完本课,你将具备独立完成 UART 初始化、配置、收发、回调处理的全流程开发能力,适配 Nordic 全系列 DK 开发板。

核心学习目标

完成本课学习后,你将建立 UART 通信的完整知识与开发体系,核心掌握:

  • 理解 UART 协议原理、硬件连接、流控制机制
  • 掌握 nRF Connect SDK 中 UART 异步驱动 API 的使用
  • 完成 UART 波特率、数据位、校验位、停止位等硬件参数配置
  • 实现 UART 异步收发数据、中断回调处理
  • 通过串口指令完成开发板 LED 控制实操
  • 理解 UART 三种访问方式(轮询、中断、异步)的差异

核心知识模块:从协议原理到驱动开发全拆解

本课围绕UART 协议原理 → UART 驱动框架 → 异步 API 开发 → 实操练习四层架构展开,理论与代码一一对应。

模块一:UART 协议原理 —— 硬件通信基础

UART(Universal Asynchronous Receiver/Transmitter)是点对点异步硬件通信协议,通信双方无需时钟线同步,仅通过 TX、RX、GND 三根线完成数据传输。

1. 核心硬件连接

  • TX:发送引脚,本地 TX 连接对端 RX
  • RX:接收引脚,本地 RX 连接对端 TX
  • GND:共地,保证电平参考一致
  • Nordic 全系列 DK 板载USB 转 UART芯片,可直接通过 USB 与 PC 串口通信

2. 数据帧格式

UART 数据按帧传输,固定结构:

  1. 起始位:1 个时钟周期低电平,标志传输开始
  2. 数据位:通常 8 位,依次传输
  3. 校验位:可选,用于传输校验(奇校验 / 偶校验 / 无校验)
  4. 停止位:1 个时钟周期高电平,标志传输结束

3. 关键参数

  • 波特率:通信速率,常用 115200、9600 波特,双方必须一致
  • 硬件流控制:通过 RTS(请求发送)、CTS(清除发送)信号,解决收发速率不匹配导致的数据丢失

4. UART 硬件流控制(RTS/CTS)

当收发设备处理速度不同时,流控制可避免数据丢失:

  • 设备通过 RTS 信号告知对方自身可接收数据
  • 设备读取 CTS 信号判断对方可接收数据
  • Nordic SoC UART 外设支持自动硬件流控制

模块二:UART 驱动框架 ——Zephyr 三层访问方式

nRF Connect SDK 基于 Zephyr 提供三种 UART 访问方式,本课重点讲解异步 API(Asynchronous API),这是最高效、最常用的方式。

1. 三种访问方式对比

方式

特点

适用场景

轮询 Polling

非阻塞读、阻塞写,简单低效

简单测试、小数据量

中断驱动 Interrupt

中断处理数据,需配合 FIFO

中等数据量

异步 Asynchronous

基于 EasyDMA,后台收发,支持超时

工业级、低功耗、大数据量

2. 异步 API 核心优势

  • 后台收发数据,不阻塞主线程
  • 支持接收超时、缓冲区控制
  • 适配 Nordic 芯片 EasyDMA 特性
  • 覆盖绝大多数嵌入式 UART 场景

模块三:UART 异步驱动 API 开发全流程

本课所有代码基于 nRF Connect SDK v3.0.0 及以上,兼容 nRF52/nRF53/nRF54/nRF7002 DK。

1. 开启 UART 驱动(prj.conf)

在工程配置文件中开启串口驱动与异步 API:

CONFIG_SERIAL=y

CONFIG_UART_ASYNC_API=y
  • CONFIG_SERIAL:板级设备树默认开启
  • CONFIG_UART_ASYNC_API:必须手动开启异步 API

2. 头文件引入

在 main.c 中引入 UART 驱动头文件:

#include <zephyr/drivers/uart.h>

3. 获取 UART 设备指针

通过设备树宏获取 UART 硬件实例,nRF54 系列使用 uart20,其余使用 uart0

// 通用 DK

const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));



// nRF54 系列 DK

const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart20));



// 检查设备就绪

if (!device_is_ready(uart)) {

printk("UART device not ready\r\n");

return 1;

}

4. UART 动态配置

通过 uart_config 结构体配置波特率、校验位、停止位等参数:

const struct uart_config uart_cfg = {

.baudrate = 115200,

.parity = UART_CFG_PARITY_NONE,

.stop_bits = UART_CFG_STOP_BITS_1,

.data_bits = UART_CFG_DATA_BITS_8,

.flow_ctrl = UART_CFG_FLOW_CTRL_NONE

};



// 应用配置

int err = uart_configure(uart, &uart_cfg);

if (err == -ENOSYS) {

return -ENOSYS;

}

5. UART 回调函数(ISR)定义

异步模式通过回调函数处理收发事件,回调优先级高于所有线程,需保持简洁:

static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)

{

switch (evt->type) {

// 发送完成

case UART_TX_DONE:

break;

// 接收数据就绪

case UART_RX_RDY:

break;

// 接收关闭,重新开启

case UART_RX_DISABLED:

uart_rx_enable(dev, rx_buf, sizeof(rx_buf), RECEIVE_TIMEOUT);

break;

default:

break;

}

}

常用 UART 事件

事件

含义

UART_TX_DONE

发送缓冲区数据全部发送完成

UART_TX_ABORTED

发送被中止

UART_RX_RDY

接收数据就绪(超时 / 缓冲区满)

UART_RX_DISABLED

接收关闭,需重新使能

6. 注册回调函数

err = uart_callback_set(uart, uart_cb, NULL);

if (err) {

return 1;

}

7. UART 数据发送

定义发送缓冲区,调用 uart_tx 发送数据:

// 发送缓冲区

static uint8_t tx_buf[] = {"nRF Connect SDK Fundamentals\r\n"};



// 启动发送(无流控制,永久超时)

err = uart_tx(uart, tx_buf, sizeof(tx_buf), SYS_FOREVER_US);

if (err) {

return 1;

}

8. UART 数据接收

  1. 定义接收缓冲区与超时
#define RECEIVE_BUFF_SIZE 10

#define RECEIVE_TIMEOUT 100 // 微秒

static uint8_t rx_buf[RECEIVE_BUFF_SIZE] = {0};
  1. 使能接收
err = uart_rx_enable(uart, rx_buf, sizeof(rx_buf), RECEIVE_TIMEOUT);

if (err) {

return 1;

}
  1. 接收数据读取(UART_RX_RDY 事件中)
case UART_RX_RDY:

// 数据长度

uint8_t len = evt->data.rx.len;

// 数据偏移

uint8_t offset = evt->data.rx.offset;

// 读取数据

uint8_t data = evt->data.rx.buf[offset];

break;

模块四:实操练习 ——UART 控制 LED

本课实操基于 Blinky 示例,通过串口发送指令(1/2/3)翻转对应 LED,不支持 Thingy 系列设备。

1. 实操目标

  • 初始化 UART 异步通信
  • 开机发送欢迎信息
  • 串口发送 1/2/3 控制 LED 翻转
  • 实现连续接收

2. LED 设备获取与配置

// 获取 LED 设备树实例

static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);

static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios);

static const struct gpio_dt_spec led2 = GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios);



// 配置 LED 为输出

gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE);

gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE);

gpio_pin_configure_dt(&led2, GPIO_OUTPUT_ACTIVE);

3. 优化 UART 回调函数(LED 控制)

static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)

{

switch (evt->type) {

case UART_RX_RDY:

if (evt->data.rx.len == 1) {

// 接收 1 翻转 LED0

if (evt->data.rx.buf[evt->data.rx.offset] == '1') {

gpio_pin_toggle_dt(&led0);

}

// 接收 2 翻转 LED1

else if (evt->data.rx.buf[evt->data.rx.offset] == '2') {

gpio_pin_toggle_dt(&led1);

}

// 接收 3 翻转 LED2

else if (evt->data.rx.buf[evt->data.rx.offset] == '3') {

gpio_pin_toggle_dt(&led2);

}

}

break;

case UART_RX_DISABLED:

// 重新使能接收,实现连续接收

uart_rx_enable(dev, rx_buf, sizeof(rx_buf), RECEIVE_TIMEOUT);

break;

default:

break;

}

}

4. 编译与测试

  1. 编译工程并烧录到 DK
  2. 打开串口终端(波特率 115200)
  3. 终端显示欢迎信息
  4. 键盘输入 1/2/3,对应 LED 翻转

5. 注意事项

  • nRF7002 DK 仅 2 个 LED,指令为 1/2
  • nRF54 DK LED 为 0 索引,指令为 0/1/2
  • 串口终端建议使用字符模式,避免发送回车换行符

课后测评

为了巩固本课核心知识, Nordic 官方设置了针对性测评,帮助你验证对 UART 异步通信及驱动 API 的掌握程度,测评链接如下:

测评核心考察点

  1. UART 协议基础:包括数据帧格式、关键参数(波特率、校验位、流控制)的理解,判断不同参数配置对通信的影响。
  2. 异步 API 应用:考查 UART 异步模式的核心优势、回调函数的使用场景、uart_rx_enable 与 uart_tx 函数的参数含义及错误处理。
  3. 实操场景落地:结合 LED 控制实操,考查串口指令解析、LED 翻转逻辑实现的代码逻辑,以及不同开发板(如 nRF7002、nRF54)的适配注意事项。

完成测评后,可清晰知晓自身知识薄弱点,针对性复盘本课内容,为后续学习高级通信外设(如 I2C、SPI 及无线通信协议)筑牢基础。

本课学习价值

第五课是 nRF Connect SDK 外设开发的关键承上启下课程

  • 巩固设备树、驱动模型、GPIO 操作等前序知识

  • 掌握嵌入式最通用的 UART 异步通信开发

  • 建立中断驱动、低功耗、异步处理的开发思维

  • 为后续 I2C、SPI、无线通信开发打下框架基础

     

短距离

适用于短距离物联网的蓝牙低功耗及多协议系统级芯片(支持Thread、Matter、Zigbee协议)

长距离

适用于LTE-M/NB-IoT、GNSS、DECT NR+和NTN的蜂窝物联网系统级封装

Wi-Fi

低功耗Wi-Fi 6协同ICs,支持2.4 GHz/5 GHz频段选项及WPA3加密协议

电源管理ICs

适用于电池供电设备的电源管理IC(PMIC),nPM系列提供充电与稳压功能选项。

AI及软件工具

工具与NPU加速边缘人工智能开发和部署

订阅Nordic新闻简报

了解最新信息!订阅后即可获取最新Nordic及物联网资讯

立即订阅