串口通讯
通用异步收发器(Universal Asynchronous Receiver/Transmitter),简称UART,是一种串行、异步、全双工的通信协议,在嵌入式领域应用的非常广泛,如下为20pin扩展接口的串口部分,在使能时需要注意复用的影响。本章节以串口UART5_TX_M0/UART5_RX_M0为例,验证串口通讯部分。
Net name | NUM | NUM | Net name | ||
---|---|---|---|---|---|
UART6_RX_M1 | GPIO1_A0_3V3 | 1 | 2 | GPIO4_D5_3V3 | UART5_TX_M0 |
UART6_TX_M1 | GPIO1_A1_3V3 | 3 | 4 | GPIO4_D4_3V3 | UART5_RX_M0 |
UART6_RTSN_M1 | GPIO1_A2_3V3 | 5 | 6 | GPIO4_D3_3V3 | UART5_RTSN_M0 |
UART6_CTSN_M1 | GPIO1_A3_3V3 | 7 | 8 | GPIO4_D2_3V3 | UART5_CTSN_M0 |
UART7_RX_M2 | GPIO1_B4_3V3 | 9 | 10 | GPIO1_C6_1V8 | UART0_RTSN |
GND | 11 | 12 | GND | ||
UART4_TX_M2 | GPIO1_B3_3V3 | 13 | 14 | SARADC_VIN2 | 1V8 SARADC in |
UART4_RX_M2 | GPIO1_B2_3V3 | 15 | 16 | VCC3V3_SYS | |
GPIO1_B1_3V3 | 17 | 18 | VCC12V_DCIN | ||
VCC5V0_SYS | 19 | 20 | VCC12V_DCIN |
DTS修改与编译
# 修改rk3588-toybrick-x0.dtsi文件
&uart5 {
status = "okay";
pinctrl-0 = <&uart5m0_xfer &uart5m0_ctsn>;
};
重新编译kernel(./edge build -k
)并更新烧录boot_linux.img至开发板,通过命令 ls /dev/ttyS*
可以查看到ttyS5设备(即为UART5_M0)
</left>
串口链接
通过串口线或USB转串口线,把开发板与电脑连接起来:
- 开发板 ------ 电脑
- TXD ------ RXD
- RXD ------ TXD
- GND ----- GND
串口相关命令
# 查询其通信参数命令
stty -F /dev/ttyS5
# 设置通讯速率,其中 ispeed 为输入速率, ospeed 为输出速率
stty -F /dev/ttyS5 ispeed 115200 ospeed 115200
# 默认串口是开启回显的可以使用以下命令关闭回显
stty -F /dev/ttyS5 -echo
# 关闭RTS/CTS流控
stty -F /dev/ttyS5 -crtscts
Windows主机通讯
配置好串口调试助手后,尝试使用如下命令测试发送/读取数据
# 使用 echo 命令向终端设备文件写入字符串
echo Hello! > /dev/ttyS5
echo "Toybrick funny" > /dev/ttyS5
# 使用 cat 命令读取终端设备文件
cat /dev/ttyS5
执行结果
设备测试用例
- Python
- C++
以下是使用Python进行串口通信的示例代码:
import serial
import time
import sys
class UARTTest:
def __init__(self, port="/dev/ttyS5", baudrate=115200):
self.ser = serial.Serial(
port=port,
baudrate=baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1
)
def send_command(self, cmd):
"""发送命令到串口"""
self.ser.write(cmd.encode() + b'\n')
time.sleep(0.1) # 等待数据发送完成
def receive_data(self):
"""从串口接收数据"""
if self.ser.in_waiting:
return self.ser.readline().decode().strip()
return None
def transfer_file(self, filename, is_send=True):
"""传输文件
is_send=True: 发送文件
is_send=False: 接收文件
"""
if is_send:
with open(filename, 'rb') as f:
data = f.read()
self.ser.write(data)
else:
with open(filename, 'wb') as f:
while True:
if self.ser.in_waiting:
data = self.ser.read(self.ser.in_waiting)
if not data:
break
f.write(data)
def close(self):
"""关闭串口"""
self.ser.close()
# 使用示例
if __name__ == "__main__":
uart = UARTTest()
# 发送 命令示例
uart.send_command("Hello RK3588!")
# 接收数据示例
while True:
data = uart.receive_data()
if data:
print(f"Received: {data}")
# 文件传输示例
# uart.transfer_file("test.txt", True) # 发送文件
# uart.transfer_file("received.txt", False) # 接收文件
uart.close()
使用前需要安装pyserial库:
pip3 install pyserial
以下是使用C++进行串口通信的示例代码:
#include <iostream>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <fstream>
class UARTTest {
private:
int uart_fd;
struct termios uart_config;
public:
UARTTest(const char* port = "/dev/ttyS5", int baudrate = B115200) {
// 打开串口
uart_fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
if (uart_fd == -1) {
std::cerr << "Error opening port" << std::endl;
return;
}
// 配置串口参数
tcgetattr(uart_fd, &uart_config);
uart_config.c_cflag = baudrate | CS8 | CLOCAL | CREAD;
uart_config.c_iflag = IGNPAR;
uart_config.c_oflag = 0;
uart_config.c_lflag = 0;
tcflush(uart_fd, TCIFLUSH);
tcsetattr(uart_fd, TCSANOW, &uart_config);
}
// 发送命令
void sendCommand(const char* cmd) {
write(uart_fd, cmd, strlen(cmd));
write(uart_fd, "\n", 1);
}
// 接收数据
std::string receiveData() {
char buffer[1024];
int n = read(uart_fd, buffer, sizeof(buffer));
if (n > 0) {
buffer[n] = 0;
return std::string(buffer);
}
return "";
}
// 传输文件
void transferFile(const char* filename, bool is_send) {
if (is_send) {
std::ifstream file(filename, std::ios::binary);
if (file.is_open()) {
char buffer[1024];
while (!file.eof()) {
file.read(buffer, sizeof(buffer));
write(uart_fd, buffer, file.gcount());
}
file.close();
}
} else {
std::ofstream file(filename, std::ios::binary);
if (file.is_open()) {
char buffer[1024];
while (true) {
int n = read(uart_fd, buffer, sizeof(buffer));
if (n <= 0) break;
file.write(buffer, n);
}
file.close();
}
}
}
~UARTTest() {
close(uart_fd);
}
};
int main(int argc, char* argv[]) {
UARTTest uart;
if (argc == 1) {
// 无参数时,进入接收模式
std::cout << "Entering receive mode. Press Ctrl+C to exit." << std::endl;
while (true) {
std::string received = uart.receiveData();
if (!received.empty()) {
std::cout << "Received: " << received << std::endl;
}
}
}
else if (argc == 2) {
// 一个参数时,检查是否为文件
std::ifstream file(argv[1]);
if (file.good()) {
// 是文件,进行文件传输
std::cout << "Sending file: " << argv[1] << std::endl;
uart.transferFile(argv[1], true);
std::cout << "File transfer completed." << std::endl;
}
else {
// 不是文件,将参数作为字符串发送
std::cout << "Sending message: " << argv[1] << std::endl;
uart.sendCommand(argv[1]);
}
}
else {
// 将所有参数组合成一个字符串发送
std::string message;
for (int i = 1; i < argc; i++) {
message += argv[i];
if (i < argc - 1) message += " ";
}
std::cout << "Sending message: " << message << std::endl;
uart.sendCommand(message.c_str());
}
return 0;
}
编译命令:
g++ uart_test.cpp -o uart_test
备注
在运行程序前,请确保已经正确配置了串口权限,可能需要使用sudo运行或将当前用户添加到dialout组:
sudo usermod -a -G dialout $USER