10 串口通讯
1 UART介绍
关于UART通信的基本概念,请参考:https://zhuanlan.zhihu.com/p/657771076 . Rockchip UART (Universal Asynchronous Receiver/Transmitter) 基于16550A串⼝标准,完整模块⽀持以下功能:
- 支持5、 6、 7、 8 bits数据位。
- 支持1、 1.5、 2 bits停⽌位。
- 支持奇校验和偶校验,不⽀持mark校验和space校验。
- 支持接收FIFO和发送FIFO, ⼀般为32字节或者64字节。
- 支持最⾼4M波特率,实际⽀持波特率需要芯⽚时钟分频策略配合。
- 支持中断传输模式和DMA传输模式。
- 支持硬件⾃动流控, RTS+CTS。
2 UART板卡接口

3 UART使用---命令行方式
3.1 设备树解析
提示
下文的文件路径:out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/需要先编译码源。
基础定义层(rk3568.dtsi):
uart3: serial@fe670000 {
compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";
reg = <0x0 0xfe670000 0x0 0x100>;
interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
dmas = <&dmac0 6>, <&dmac0 7>;
pinctrl-names = "default";
pinctrl-0 = <&uart3m0_xfer>;
status = "disabled";
};我们对一些基本属性进行解析:
compatible: 指定兼容性,支持RK3568 UART和标准DW APB UARTreg: 寄存器地址范围(0xfe670000-0xfe6700ff)interrupts: 中断号119,高电平触发clocks: 波特率时钟(SCLK_UART3)和APB时钟(PCLK_UART3)dmas: DMA通道6(TX)和7(RX)pinctrl-0: 默认使用uart3m0_xfer引脚组status: 默认禁用状态
引脚配置层(rk3568-pinctrl.dtsi),其中UART3提供2种引脚配置模式:
uart3m0_xfer: uart3m0-xfer {
rockchip,pins =
/* uart3_rxm0 */
<1 RK_PA0 2 &pcfg_pull_up>,
/* uart3_txm0 */
<1 RK_PA1 2 &pcfg_pull_up>;
};
uart3m1_xfer: uart3m1-xfer {
rockchip,pins =
/* uart3_rxm1 */
<3 RK_PC0 4 &pcfg_pull_up>,
/* uart3_txm1 */
<3 RK_PB7 4 &pcfg_pull_up>;
};uart3m0_xfer: 引脚组1,使用PA0作为RX,PA1作为TXuart3m1_xfer: 引脚组2,使用PC0作为RX,PB7作为TX
板级配置层( rk3568-toybrick-x0.dtsi )
&uart3 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart3m1_xfer>;
};&uart3: 引用基础定义中的uart3节点status = "okay": 使能UART3控制器pinctrl-0: 选择M1模式引脚(GPIO3_C0/GPIO3_B7)
3.2 应用层测试UART的方法
在过去进行单片机开发调试时,我们经常用到的是使用沁恒电子的CH340系列的USB 转串口模块,此外还有常见的FT232RL USB转串口芯片, 本次测试使用的是搭载FT232RL芯片的USB转串口模块,两者使用上没有任何区别,实际使用时,我们只需要把模块的TX RX 与 开发板的 RX TX 进行对接即可

在本实验中,我们依然选择此方式通过USB与板子进行UART通信。
和之前一样,驱动文件放置在 /dev 目录下 通过指令
ls /dev/tty*即可查看到所有的终端设备如下: 
其中tty前缀 为虚拟终端,ttyS 前缀为是本节需要研究的串口终端.
(ttyS3为UART3,ttyS8为UART8(该串口被蓝牙模块占用))
Busybox 是一个集成了上百个常用 Linux 命令的单一可执行文件,其中的stty是set tty 的缩写,是一个专门用于改变并打印终端行设置的命令。 常用指令如下:
查看端口
busybox stty -F /dev/ttySx //ttyS为具体查看的端口设置波特率
busybox stty -F /dev/ttyS3 baudrate设置波特率为9600,8位数据为,一位停止位,无校验位
busybox stty -F /dev/ttyS3 9600 cs8 -cstopb -parenb禁用硬件流控
busybox stty -F /dev/ttyS3 -crtsctsmicrocom 是 BusyBox 的一个组件,核心功能就是打开一个指定的串口设备,实现数据的接收
以波特率115200 运行串口
microcom -s 115200 /dev/ttyS33.2 具体功能演示
使用ssty工具查询开发板串口UART3参数
busybox stty -F /dev/ttyS3
使用ssty工具修改串口波特率为115200,其中ispeed为输入速率,ospeed为输出速率
busybox stty -F /dev/ttyS3 ispeed 115200 ospeed 115200
(注意:每一次设备开机需要重新设置一遍波特率,重启默认会重置波特率为9600)
备注
串口工具下载地址和路径: https://pan.baidu.com/s/1ZUn2BNg-Sb6M-fWhDqAFMw?pwd=smcc 提取码:smcc ShimetaPi开源鸿蒙资料>02-软件工具>Rockchip>OpenHarmony>串口工具>sscom5.13.1.exe
按上述配置配置好串口调试助手后,在板卡端使用如下命令测试串口发送数据是否成功:
#在板卡上的终端执行如下指令
#使用echo命令向终端设备文件写入字符串"Hello!"、"OpenHarmony!"
echo Hello! > /dev/ttyS3
echo "OpenHarmony" > /dev/ttyS3
#PC上的串口调试助手会接收到内容
PC段接收成功接收到数据如图所示,板卡发送数据正常。 下面对通过PC端发送数据,测试板卡的串口能否正常接收数据。 使用上文中提到的microcom工具, 在板卡上的终端执行如下指令,连接到串口设备 ttyS3 并进行双向通信,此时microcom命令会等待串口数据并把接收的数据显示在终端
microcom -s 115200 /dev/ttyS3
板卡终端成功显示接收的数据。 PC端发送数据,板卡成功接收到数据,板卡接收数据正常。
4. UART使用---NAPI方式
资料路径
hap包: \05-开发资料\01-OpenHarmory 开发资料\外设测试APP\HAP\UART_TEST.hap
工程码源:\05-开发资料\01-OpenHarmory 开发资料\外设测试APP\SRC\UART_TEST
这里使用读写系统节点 /dev/ttyS3 的方式构建NAPI.
4-1 测试环境准备
首先对 /dev/ttyS3 节点进行权限设置:
chmod 777 /dev/ttyS3 //路径4-2 测试程序使用介绍
以下是我们做的串口测试程序,实现的基本功能是串口的开关以及数据的收发,考虑到不好布局以及降低综合难度,并没有把参数配置功能加在程序界面中,而是用一行文字标注。提供的 C 码源中已经实现了这部分功能,请有能力的朋友自行添加到 ets 文件中!
我们将开发板通过USB转TTL模块连接到电脑,打开应用如下图所示,我们打开串口。

打开串口后,点击开始接收,并使用PC端串口助手发送文本 "ShiMeta Pi ,Hello!" 应用程序成功接收到文本并显示在数据接收区。
再通过应用程序发送字符串 "open harmony!" ,串口终端也成功接收到数据,如下图所示:


4-3 测试程序代码介绍
由于涉及的的知识点前面都有介绍,这里贴上测试程序napi_init.cpp代码,供有需要的朋友参考,也可以在资料中自行查看。
#include "napi/native_api.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "hilog/log.h"
const int GLOBAL_RESMGR = 0xFF00;
const char *UART_TAG = "[UART]";
const char *UART_DEVICE = "/dev/ttyS3"; // 固定使用ttyS3串口
// 全局变量
static int uart_fd = -1; // 串口文件描述符
static struct termios old_cfg; // 保存原始配置
static bool uart_opened = false;
// 配置串口参数
static int configure_uart(int fd, int baudrate, int databits, int stopbits, char parity)
{
struct termios cfg;
// 获取当前配置
if (tcgetattr(fd, &cfg) != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, UART_TAG,
"Failed to get uart config: %{public}s", strerror(errno));
return -1;
}
// 保存原始配置
old_cfg = cfg;
// 清除所有标志
cfg.c_cflag &= ~CSIZE;
cfg.c_cflag &= ~CSTOPB;
cfg.c_cflag &= ~PARENB;
cfg.c_cflag &= ~PARODD;
// 设置数据位
switch (databits) {
case 5: cfg.c_cflag |= CS5; break;
case 6: cfg.c_cflag |= CS6; break;
case 7: cfg.c_cflag |= CS7; break;
case 8: cfg.c_cflag |= CS8; break;
default: cfg.c_cflag |= CS8; break;
}
// 设置停止位
if (stopbits == 2) {
cfg.c_cflag |= CSTOPB;
}
// 设置校验位
switch (parity) {
case 'O': case 'o': // 奇校验
cfg.c_cflag |= PARENB;
cfg.c_cflag |= PARODD;
break;
case 'E': case 'e': // 偶校验
cfg.c_cflag |= PARENB;
cfg.c_cflag &= ~PARODD;
break;
case 'N': case 'n': // 无校验
default:
cfg.c_cflag &= ~PARENB;
break;
}
// 设置波特率
speed_t speed;
switch (baudrate) {
case 4800: speed = B4800; break;
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 57600: speed = B57600; break;
case 115200: speed = B115200; break;
case 230400: speed = B230400; break;
case 460800: speed = B460800; break;
case 921600: speed = B921600; break;
case 1500000: speed = B1500000; break;
default: speed = B115200; break;
}
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);
// 设置控制模式
cfg.c_cflag |= CLOCAL | CREAD;
// 设置输入模式
cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
cfg.c_iflag &= ~(INLCR | ICRNL | IGNCR);
// 设置输出模式
cfg.c_oflag &= ~OPOST;
// 设置本地模式
cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 设置读取参数
cfg.c_cc[VTIME] = 0; // 非阻塞读取
cfg.c_cc[VMIN] = 0;
// 应用配置
if (tcsetattr(fd, TCSANOW, &cfg) != 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, UART_TAG,
"Failed to set uart config: %{public}s", strerror(errno));
return -1;
}
// 清空缓冲区
tcflush(fd, TCIOFLUSH);
return 0;
}
// 打开串口
static napi_value Open_UART(napi_env env, napi_callback_info info)
{
napi_value result;
// 检查串口是否已经打开
if (uart_opened) {
OH_LOG_Print(LOG_APP, LOG_WARN, GLOBAL_RESMGR, UART_TAG,
"UART is already opened");
napi_create_string_utf8(env, "UART already opened", NAPI_AUTO_LENGTH, &result);
return result;
}
// 打开串口设备
uart_fd = open(UART_DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (uart_fd < 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, UART_TAG,
"Failed to open %{public}s: %{public}s", UART_DEVICE, strerror(errno));
napi_create_string_utf8(env, "Failed to open UART device", NAPI_AUTO_LENGTH, &result);
return result;
}
// 配置串口参数 (115200, 8N1)
if (configure_uart(uart_fd, 115200, 8, 1, 'N') != 0) {
close(uart_fd);
uart_fd = -1;
napi_create_string_utf8(env, "Failed to configure UART", NAPI_AUTO_LENGTH, &result);
return result;
}
uart_opened = true;
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, UART_TAG,
"UART opened successfully");
napi_create_string_utf8(env, "UART opened successfully", NAPI_AUTO_LENGTH, &result);
return result;
}
// 关闭串口
static napi_value Close_UART(napi_env env, napi_callback_info info)
{
napi_value result;
if (!uart_opened || uart_fd < 0) {
OH_LOG_Print(LOG_APP, LOG_WARN, GLOBAL_RESMGR, UART_TAG,
"UART is not opened");
napi_create_string_utf8(env, "UART is not opened", NAPI_AUTO_LENGTH, &result);
return result;
}
// 恢复原始配置
tcsetattr(uart_fd, TCSANOW, &old_cfg);
// 关闭串口
close(uart_fd);
uart_fd = -1;
uart_opened = false;
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, UART_TAG,
"UART closed successfully");
napi_create_string_utf8(env, "UART closed successfully", NAPI_AUTO_LENGTH, &result);
return result;
}
// 设置串口配置
static napi_value Set_UART_Config(napi_env env, napi_callback_info info)
{
napi_value result;
size_t argc = 4;
napi_value args[4];
// 获取参数
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (argc < 4) {
napi_create_string_utf8(env, "Invalid parameters", NAPI_AUTO_LENGTH, &result);
return result;
}
if (!uart_opened || uart_fd < 0) {
napi_create_string_utf8(env, "UART is not opened", NAPI_AUTO_LENGTH, &result);
return result;
}
// 解析参数
int32_t baudrate, databits, stopbits;
char parity_char;
size_t parity_len;
char parity_str[10];
napi_get_value_int32(env, args[0], &baudrate);
napi_get_value_int32(env, args[1], &databits);
napi_get_value_int32(env, args[2], &stopbits);
napi_get_value_string_utf8(env, args[3], parity_str, sizeof(parity_str), &parity_len);
parity_char = (parity_len > 0) ? parity_str[0] : 'N';
// 重新配置串口
if (configure_uart(uart_fd, baudrate, databits, stopbits, parity_char) != 0) {
napi_create_string_utf8(env, "Failed to configure UART", NAPI_AUTO_LENGTH, &result);
return result;
}
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, UART_TAG,
"UART configured: %{public}d-%{public}d-%{public}d-%{public}c",
baudrate, databits, stopbits, parity_char);
napi_create_string_utf8(env, "UART configured successfully", NAPI_AUTO_LENGTH, &result);
return result;
}
// 发送数据
static napi_value Send_Data(napi_env env, napi_callback_info info)
{
napi_value result;
size_t argc = 1;
napi_value args[1];
// 获取参数
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (argc < 1) {
napi_create_string_utf8(env, "Invalid parameters", NAPI_AUTO_LENGTH, &result);
return result;
}
if (!uart_opened || uart_fd < 0) {
napi_create_string_utf8(env, "UART is not opened", NAPI_AUTO_LENGTH, &result);
return result;
}
// 获取要发送的字符串
size_t str_len;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &str_len);
char *send_data = (char*)malloc(str_len + 1);
if (!send_data) {
napi_create_string_utf8(env, "Memory allocation failed", NAPI_AUTO_LENGTH, &result);
return result;
}
napi_get_value_string_utf8(env, args[0], send_data, str_len + 1, &str_len);
// 发送数据
ssize_t bytes_written = write(uart_fd, send_data, str_len);
if (bytes_written < 0) {
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, UART_TAG,
"Failed to send data: %{public}s", strerror(errno));
free(send_data);
napi_create_string_utf8(env, "Failed to send data", NAPI_AUTO_LENGTH, &result);
return result;
}
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, UART_TAG,
"Sent %{public}zd bytes: %{public}s", bytes_written, send_data);
free(send_data);
char response[100];
snprintf(response, sizeof(response), "Sent %zd bytes successfully", bytes_written);
napi_create_string_utf8(env, response, NAPI_AUTO_LENGTH, &result);
return result;
}
// 接收数据
static napi_value Receive_Data(napi_env env, napi_callback_info info)
{
napi_value result;
if (!uart_opened || uart_fd < 0) {
napi_create_string_utf8(env, "UART is not opened", NAPI_AUTO_LENGTH, &result);
return result;
}
char buffer[1024];
ssize_t bytes_read = read(uart_fd, buffer, sizeof(buffer) - 1);
if (bytes_read < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 非阻塞模式下没有数据可读
napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &result);
return result;
}
OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, UART_TAG,
"Failed to read data: %{public}s", strerror(errno));
napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &result);
return result;
}
if (bytes_read == 0) {
napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &result);
return result;
}
buffer[bytes_read] = '\0';
OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, UART_TAG,
"Received %{public}zd bytes: %{public}s", bytes_read, buffer);
napi_create_string_utf8(env, buffer, NAPI_AUTO_LENGTH, &result);
return result;
}
// 检查串口状态
static napi_value Get_UART_Status(napi_env env, napi_callback_info info)
{
napi_value result;
if (uart_opened && uart_fd >= 0) {
napi_create_string_utf8(env, "opened", NAPI_AUTO_LENGTH, &result);
} else {
napi_create_string_utf8(env, "closed", NAPI_AUTO_LENGTH, &result);
}
return result;
}
// 模块初始化
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{ "Open_UART", nullptr, Open_UART, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "Close_UART", nullptr, Close_UART, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "Set_UART_Config", nullptr, Set_UART_Config, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "Send_Data", nullptr, Send_Data, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "Receive_Data", nullptr, Receive_Data, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "Get_UART_Status", nullptr, Get_UART_Status, nullptr, nullptr, nullptr, napi_default, nullptr }
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}