概述
UART 是最常用的串行通信接口,用于与电脑、传感器、GPS 模块等设备通信。Ai-WB2 模组内置 2 组 UART(UART0 和 UART1):UART0 为系统日志口(blog_info 等日志固定由 UART0 输出),UART1 可自由用作通信串口。本教程实现 UART1 接收数据并原样回显(回环)。
用大白话讲:UART(串口)就像开发板和电脑之间的一根「电话线」,两边按同样的速度逐字说话(发一个字节、听一个字节)。本教程的「回环」就是让开发板当复读机:电脑发什么,它原样回什么,就像对着山谷喊话听到回声。
本教程基于安信可官方 SDK(Ai-Thinker-Open/Ai-Thinker-WB2,版本
release_bl_iot_sdk_1.6.40)的官方示例applications/peripherals/uart编写,代码可在本地 SDK 中直接找到。
按官方示例接线(见 SDK applications/peripherals/uart/README.md),通信串口为 UART1:TX GPIO16、RX GPIO7,与 USB-TTL 模块交叉连接:
| Ai-WB2 引脚 | USB-TTL 模块 |
|---|---|
| IO16(UART1 TX) | RXD |
| IO7(UART1 RX) | TXD |
| GND | GND |
💡 串口收发必须交叉连接(TX 接对方 RX,就像打电话要对着话筒说话);同时务必共地(GND 接 GND,否则电压没有参考点,通信会乱码)。USB-TTL 模块的 VCC 可接 3V3 供电,也可单独用 Type-C 给开发板供电。
📌 注意区分:开发板 USB 口直连电脑时看到的 log 是 UART0(log 口,TX GPIO4 / RX GPIO3,固定输出系统日志);本教程的 UART1 需要额外用 USB-TTL 模块引出。
打开终端,进入官方 uart 示例工程目录:
cd ~/Ai-Thinker-WB2/applications/peripherals/uart
说明:
cd是「进入目录」命令,这里进入 uart 示例工程目录;后续的make编译、make flash烧录命令都必须先在这个目录里执行。
打开 uart/main.c,本步完整代码已移至文末,见:
📜 完整代码 — 位于本页「完整代码」章节,默认折叠,点击展开,与官方示例(
applications/peripherals/uart/uart/main.c)完全一致。
代码要点:
| 代码 | 作用 |
|---|---|
uart_id = 1、tx_pin = 16、rx_pin = 7 |
通信串口 UART1(TX16/RX7),波特率(传输速度,两端必须一致)115200;引脚或波特率配错就收不到数据 |
uart_id = 0、tx_pin = 4、rx_pin = 3 |
系统日志口 UART0,blog_info 固定由此输出,别把通信数据接到这里 |
mode = HOSAL_UART_MODE_POLL |
轮询模式:hosal_uart_receive 会一直等数据,等不到就卡住不往下走 |
hosal_uart_receive(&uart_dev_log, data, sizeof(data)) |
接收电脑发来的数据,返回实际收到几个字节,收不到(返回 0)就不会回显 |
hosal_uart_send(&uart_dev_log, data, ret) |
把收到的数据原样发回(回环),没有这步电脑端就看不到任何回显 |
在工程目录执行编译:
make -j8
说明:
make是「编译工程」命令,把代码变成开发板能运行的固件(程序文件);-j8表示用 8 个核并行编译,速度更快。
编译成功后生成固件 build_out/uart.bin。
开发板保持 USB 连接,确认串口设备号后执行烧录:
make flash p=/dev/ttyUSB0 b=921600
说明:
make flash是「烧录」命令,把编译好的固件写进开发板芯片。p=后面是串口设备号(改成你电脑上实际的串口,可用ls /dev/ttyUSB*查看),b=是烧录波特率(传输速度)。
⏳ 烧录过程中按提示长按开发板 EN 键进入下载模式,等待进度条完成即烧录成功。
烧录完成后开发板自动重启运行:
- 打开两个串口工具:USB 直连口(UART0,波特率 921600,看系统日志)与 USB-TTL 连接的 UART1(波特率 115200,通信测试)。
- UART0 日志口输出
Uart Demo Start。 - 在 USB-TTL 的串口工具里发送任意字符(如
hello),UART1 会原样回显,同时 UART0 日志口打印Get data。
[UART0 日志口]
Uart Demo Start
Get data
Get data
[UART1 通信口]
hello ← 发送
hello ← 回显
💡 示例波特率分别为 115200(UART1)与 9600(uart_dev_echo,本示例未实际使用),收发两端波特率(串口传输速度,两端必须设成一样)必须一致,否则收到乱码。
看到 USB-TTL 发出 hello 后原样回显 hello、日志口打印 Get data 即为成功;如果发送后没有任何回显,说明还没成功,对照文末「常见问题与踩坑提示」排查。
代码执行流程
例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):
本文 API 汇总
hosal_uart_init(uart)
按 dev 结构体中的 uart_id、引脚、波特率等配置 UART 控制器(本教程初始化 UART1 通信口与 UART0 日志口)。
参数:
uart:hosal_uart_dev_t结构体指针,必填。关键字段:config.uart_id(串口号,可选值:0/1/2,本教程通信口用1)、config.tx_pin/config.rx_pin(TX/RX 引脚号,本教程 TX16/RX7)、config.baud_rate(波特率,可选值:115200/921600等)、config.data_width(可选值:HOSAL_DATA_WIDTH_8BIT)、config.parity(可选值:HOSAL_NO_PARITY无校验)、config.stop_bits(可选值:HOSAL_STOP_BITS_1)、config.mode(可选值:HOSAL_UART_MODE_POLL轮询 /HOSAL_UART_MODE_INT中断收发)
返回值:成功返回 0;失败返回负值错误码
hosal_uart_send(uart, txbuf, size)
将缓冲区数据通过串口发出(本教程用于回显收到的数据)。
参数:
uart:hosal_uart_dev_t结构体指针txbuf:待发送数据缓冲区指针,必填size:发送字节数,可选值:1~256
返回值:成功返回实际发送的字节数(≤ size);失败返回负值错误码
hosal_uart_receive(uart, data, expect_size)
从串口读取数据到缓冲区(轮询模式下阻塞等待)。
参数:
uart:hosal_uart_dev_t结构体指针data:接收缓冲区指针,必填expect_size:期望接收字节数
返回值:成功返回实际接收的字节数(可能小于 expect_size);失败返回负值错误码
blog_info(fmt, ...)
输出一条 INFO 级日志(固定由 UART0 输出,受级别过滤)。
参数:
fmt:格式化字符串,同printf用法,必填...:变参,与fmt占位符对应,可省略
返回值:无
xTaskCreate(task, name, stack, param, prio, handle)
创建任务并加入就绪队列,由调度器按优先级调度执行。
参数:
task:任务入口函数指针,形如void task(void *arg),必填name:任务名称字符串(调试用),如"uart_task"stack:任务栈大小(单位:字),可选值:内存允许范围内任意值,如2048(栈过小易溢出死机)param:传给入口函数的参数指针,无参传NULLprio:任务优先级,可选值:0(最低)~19(最高,SDK 配置),如16handle:任务句柄输出指针,不需要可传NULL
返回值:成功返回 pdPASS;失败返回 pdFAIL(如内存不足)
完整代码
以下为 uart/main.c 完整源码,与官方示例(applications/peripherals/uart/uart/main.c)完全一致:
📜 点击展开 uart/main.c 完整代码
/*
* @Author: xuhongv@yeah.net
* @Date: 2022-10-03 15:02:19
* @LastEditors: xuhongv@yeah.net xuhongv@yeah.net
* @LastEditTime: 2022-10-20 17:42:45
* @FilePath: \bl_iot_sdk_for_aithinker\applications\get-started\helloworld\helloworld\main.c
* @Description: Uart
*/
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include "bl_sys.h"
#include <stdio.h>
#include <cli.h>
#include <hosal_uart.h>
#include <blog.h>
#include <hosal_uart.h>
void TaskUart(void *param)
{
uint8_t data[32];
int ret;
hosal_uart_dev_t uart_dev_echo = {
.config = {
.uart_id = 0,
.tx_pin = 4, // TXD GPIO
.rx_pin = 3, // RXD GPIO
.cts_pin = 255,
.rts_pin = 255,
.baud_rate = 9600,
.data_width = HOSAL_DATA_WIDTH_8BIT,
.parity = HOSAL_NO_PARITY,
.stop_bits = HOSAL_STOP_BITS_1,
.mode = HOSAL_UART_MODE_POLL,
},
};
hosal_uart_dev_t uart_dev_log = {
.config = {
.uart_id = 1,
.tx_pin = 16, // TXD GPIO
.rx_pin = 7, // RXD GPIO
.cts_pin = 255,
.rts_pin = 255,
.baud_rate = 115200,
.data_width = HOSAL_DATA_WIDTH_8BIT,
.parity = HOSAL_NO_PARITY,
.stop_bits = HOSAL_STOP_BITS_1,
.mode = HOSAL_UART_MODE_POLL,
},
};
/* Uart init device */
hosal_uart_init(&uart_dev_log);
/* Uart init device */
hosal_uart_init(&uart_dev_echo);
blog_info("Uart Demo Start");
while (1)
{
/* Uart receive poll */
ret = hosal_uart_receive(&uart_dev_log, data, sizeof(data));
if (ret > 0)
{
/* Uart send poll */
hosal_uart_send(&uart_dev_log, data, ret);
blog_info("Get data ");
}
}
}
/**
* @brief main
*
*/
void main(void)
{
xTaskCreate(TaskUart, "TaskUart", 1024, NULL, 15, NULL);
}常见问题与踩坑提示
⚠️ USB-TTL 发送无回显
原因:TX/RX 未交叉连接、未共地,或波特率不一致
解决:确认 IO16 → USB-TTL RXD、IO7 → USB-TTL TXD,GND 必须相连;通信口波特率 115200 且两端一致
⚠️ 收到乱码
原因:波特率不匹配,或 USB-TTL 模块电平不兼容(3.3V vs 5V)
解决:确认两端波特率一致(115200);使用 3.3V 电平的 USB-TTL 模块,勿用 5V 供电的旧模块直连
⚠️ 把日志口和通信口搞混
原因:USB 直连电脑看到的是 UART0 日志,不是 UART1 通信数据
解决:牢记两组串口:UART0 = log(TX4/RX3),UART1 = 通信(TX16/RX7);blog_info 输出永远走 UART0
⚠️ 系统重启后日志口不打印
原因:官方 README 提示某些配置下重启会关闭 log 输出
解决:需要重启后继续打印日志,在 proj_config.mk 中设置 CONFIG_SYS_REBOOT_LOG_DISENABLE:=1
⚠️ 烧录一直卡住等待,进度条不动
原因:未进入下载模式,或数据线只能充电不能传数据
解决:烧录时按提示长按 EN 键进入下载模式;换一根能传数据的 Type-C 数据线后重试
⚠️ 找不到串口设备或提示无权限
原因:Linux 下 /dev/ttyUSB0 不存在或权限不足,Windows 下未安装 USB 转串口驱动
解决:Linux 用 ls /dev/ttyUSB* 确认设备号,权限不足执行 sudo usermod -aG dialout $USER 后重新登录;Windows 在设备管理器安装驱动并确认 COM 口号
运行自检
USB-TTL 发送字符后收到原样回显,且日志口打印 Get data,即 UART 通信验证通过。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

