10 UART Communication
1 UART Introduction
For basic concepts of UART communication, please refer to: https://zhuanlan.zhihu.com/p/657771076 Rockchip UART (Universal Asynchronous Receiver/Transmitter) is based on the 16550A serial standard, with the complete module supporting the following features:
- Supports 5, 6, 7, 8 bits data length.
- Supports 1, 1.5, 2 bits stop bits.
- Supports odd parity and even parity, does not support mark parity and space parity.
- Supports receive FIFO and transmit FIFO, generally 32 bytes or 64 bytes.
- Supports up to 4M baud rate, actual supported baud rate needs to coordinate with chip clock division strategy.
- Supports interrupt transfer mode and DMA transfer mode.
- Supports hardware automatic flow control, RTS+CTS.
2 UART Board Interface

3 UART Usage - Command Line Method
3.1 Device Tree Parsing
Tips
The file path below: out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip/ requires compiling the source code first.
Basic definition layer (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";
};Let's parse some basic properties:
compatible: Specifies compatibility, supports RK3568 UART and standard DW APB UARTreg: Register address range (0xfe670000-0xfe6700ff)interrupts: Interrupt number 119, high-level triggerclocks: Baud rate clock (SCLK_UART3) and APB clock (PCLK_UART3)dmas: DMA channels 6 (TX) and 7 (RX)pinctrl-0: Uses uart3m0_xfer pin group by defaultstatus: Disabled by default
Pin configuration layer (rk3568-pinctrl.dtsi), where UART3 provides 2 pin configuration modes:
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: Pin group 1, uses PA0 as RX, PA1 as TXuart3m1_xfer: Pin group 2, uses PC0 as RX, PB7 as TX
Board-level configuration layer (rk3568-toybrick-x0.dtsi):
&uart3 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart3m1_xfer>;
};&uart3: References the uart3 node in the basic definitionstatus = "okay": Enables the UART3 controllerpinctrl-0: Selects M1 mode pins (GPIO3_C0/GPIO3_B7)
3.2 UART Testing Method at Application Layer
In the past, when developing and debugging microcontrollers, we often used USB to serial modules from Qin Heng Electronics (CH340 series). In addition, there are common FT232RL USB to serial chips. The test this time uses a USB to serial module equipped with the FT232RL chip. There is no difference in usage between them. In actual use, we only need to connect the module's TX and RX to the board's RX and TX.

In this experiment, we still choose this method to communicate with the board via USB for UART.
As before, the driver files are placed in the /dev directory. Execute the command:
ls /dev/tty*You can view all terminal devices as follows:

Among them, the tty prefix is for virtual terminals, and the ttyS prefix is for the serial terminal that this section needs to study.
(ttyS3 is UART3, ttyS8 is UART8 (this serial port is occupied by the Bluetooth module))
Busybox is a single executable that integrates hundreds of common Linux commands, among which stty is the abbreviation for "set tty" and is a command specifically used to change and print terminal line settings.
Common commands are as follows:
View Port
busybox stty -F /dev/ttySx //ttyS is the specific port to viewSet Baud Rate
busybox stty -F /dev/ttyS3 baudrateSet Baud Rate to 9600, 8 Data Bits, 1 Stop Bit, No Parity
busybox stty -F /dev/ttyS3 9600 cs8 -cstopb -parenbDisable Hardware Flow Control
busybox stty -F /dev/ttyS3 -crtsctsmicrocom is a component of BusyBox, whose core function is to open a specified serial device and implement data reception.
Run Serial Port at Baud Rate 115200
microcom -s 115200 /dev/ttyS33.2 Specific Function Demonstration
Use the ssty tool to query the board serial port UART3 parameters:
busybox stty -F /dev/ttyS3
Use the ssty tool to modify the serial port baud rate to 115200, where ispeed is the input rate and ospeed is the output rate:
busybox stty -F /dev/ttyS3 ispeed 115200 ospeed 115200
(Note: Each time the device restarts, the baud rate needs to be set again. After restart, it defaults to baud rate 9600)
Note
Serial tool download link and path: https://pan.baidu.com/s/1ZUn2BNg-Sb6M-fWhDqAFMw?pwd=smcc Extraction code: smcc ShimetaPi OpenHarmony Resources>02-Software Tools>Rockchip>OpenHarmony>Serial Tools>sscom5.13.1.exe
After configuring the serial debugging assistant according to the above settings, use the following commands on the board side to test whether serial data sending is successful:
# Execute the following commands on the board terminal
# Use echo command to write strings "Hello!" and "OpenHarmony!" to the terminal device file
echo Hello! > /dev/ttyS3
echo "OpenHarmony" > /dev/ttyS3
# The serial debugging assistant on PC will receive the content
The PC side successfully received the data as shown in the figure. The board sending data is normal.
Next, test whether the board's serial port can receive data normally by sending data from the PC side.
Use the microcom tool mentioned above, execute the following commands on the board terminal to connect to the serial device ttyS3 and perform bidirectional communication. At this time, the microcom command will wait for serial data and display the received data on the terminal:
microcom -s 115200 /dev/ttyS3
The board terminal successfully displays the received data. The PC side sends data, the board successfully receives the data, and the board receives data normally.
4. UART Usage - NAPI Method
Resource Path
HAP package: \05-Development Resources\01-OpenHarmony Development Resources\Peripheral Test APP\HAP\UART_TEST.hap
Project source code: \05-Development Resources\01-OpenHarmony Development Resources\Peripheral Test APP\SRC\UART_TEST
Here we use reading and writing to the system node /dev/ttyS3 to build NAPI.
4-1 Test Environment Preparation
First, set permissions for the /dev/ttyS3 node:
chmod 777 /dev/ttyS3 //path4-2 Test Program Usage Introduction
Below is our serial port test program. The basic functions implemented are serial port on/off and data sending/receiving. Considering the difficulty of layout and to reduce complexity, we did not add parameter configuration functions to the program interface, but used a line of text to mark it. The C source code provided has already implemented this part of the function. Friends with ability can add it to the ets file by themselves!
Connect the development board to the computer through the USB to TTL module. Open the application as shown in the figure below. We open the serial port.

After opening the serial port, click "Start Receiving", and use the PC serial assistant to send the text "ShiMeta Pi, Hello!". The application successfully receives the text and displays it in the data receiving area.
Then send the string "open harmony!" through the application. The serial terminal also successfully receives the data, as shown in the following figures:


4-3 Test Program Code Introduction
Since the knowledge points involved have been introduced earlier, the test program napi_init.cpp code is attached below for friends in need to reference. You can also view it in the resources.
#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"; // Fixed use of ttyS3 serial port
// Global variables
static int uart_fd = -1; // Serial file descriptor
static struct termios old_cfg; // Save original configuration
static bool uart_opened = false;
// Configure serial port parameters
static int configure_uart(int fd, int baudrate, int databits, int stopbits, char parity)
{
struct termios cfg;
// Get current configuration
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;
}
// Save original configuration
old_cfg = cfg;
// Clear all flags
cfg.c_cflag &= ~CSIZE;
cfg.c_cflag &= ~CSTOPB;
cfg.c_cflag &= ~PARENB;
cfg.c_cflag &= ~PARODD;
// Set data bits
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;
}
// Set stop bits
if (stopbits == 2) {
cfg.c_cflag |= CSTOPB;
}
// Set parity bits
switch (parity) {
case 'O': case 'o': // Odd parity
cfg.c_cflag |= PARENB;
cfg.c_cflag |= PARODD;
break;
case 'E': case 'e': // Even parity
cfg.c_cflag |= PARENB;
cfg.c_cflag &= ~PARODD;
break;
case 'N': case 'n': // No parity
default:
cfg.c_cflag &= ~PARENB;
break;
}
// Set baud rate
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);
// Set control mode
cfg.c_cflag |= CLOCAL | CREAD;
// Set input mode
cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
cfg.c_iflag &= ~(INLCR | ICRNL | IGNCR);
// Set output mode
cfg.c_oflag &= ~OPOST;
// Set local mode
cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// Set read parameters
cfg.c_cc[VTIME] = 0; // Non-blocking read
cfg.c_cc[VMIN] = 0;
// Apply configuration
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;
}
// Flush buffer
tcflush(fd, TCIOFLUSH);
return 0;
}
// Open serial port
static napi_value Open_UART(napi_env env, napi_callback_info info)
{
napi_value result;
// Check if serial port is already opened
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;
}
// Open serial device
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;
}
// Configure serial port parameters (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;
}
// Close serial port
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;
}
// Restore original configuration
tcsetattr(uart_fd, TCSANOW, &old_cfg);
// Close serial port
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;
}
// Set serial port configuration
static napi_value Set_UART_Config(napi_env env, napi_callback_info info)
{
napi_value result;
size_t argc = 4;
napi_value args[4];
// Get parameters
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;
}
// Parse parameters
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';
// Reconfigure serial port
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;
}
// Send data
static napi_value Send_Data(napi_env env, napi_callback_info info)
{
napi_value result;
size_t argc = 1;
napi_value args[1];
// Get parameters
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;
}
// Get string to send
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);
// Send data
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;
}
// Receive data
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) {
// No data available in non-blocking mode
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;
}
// Check serial port status
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;
}
// Module initialization
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);
}