在嵌入式开发中,串口通信是最基础也最常用的调试手段之一。RK3568作为一款高性能的ARM处理器,广泛应用于各类嵌入式设备。本文将手把手教你如何在RK3568开发板上,用C语言编写一个完整的串口自发自收测试程序,并分享实际开发中的关键技巧和避坑指南。
在开始编码前,确保你的RK3568开发板已正确连接:
常见硬件问题排查:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 无任何输出 | 电源未接通/串口线接反 | 检查电源LED/调换TX/RX线序 |
| 乱码 | 波特率不匹配 | 确认终端和程序使用相同波特率 |
| 间歇性断连 | 接触不良 | 检查连接器是否插紧 |
RK3568采用ARM64架构,需要在x86主机上配置交叉编译环境:
bash复制# 安装官方推荐的工具链
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
tar xvf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
export PATH=$PATH:$(pwd)/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin
验证安装:
bash复制aarch64-none-linux-gnu-gcc --version
现代串口应用通常需要同时处理收发任务,我们采用生产者-消费者模型:
c复制#include <pthread.h>
// 全局共享资源
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int serial_fd; // 串口文件描述符
void* read_thread(void* arg) {
char buffer[256];
while(1) {
int n = read(serial_fd, buffer, sizeof(buffer));
if(n > 0) {
buffer[n] = '\0';
printf("Received: %s", buffer);
}
}
}
void* write_thread(void* arg) {
char input[256];
while(1) {
printf("Enter message: ");
fgets(input, sizeof(input), stdin);
write(serial_fd, input, strlen(input));
}
}
精确配置串口参数是稳定通信的关键:
c复制int setup_serial(int fd, int baudrate) {
struct termios options;
// 获取当前设置
if(tcgetattr(fd, &options) < 0) {
perror("tcgetattr failed");
return -1;
}
// 设置波特率
cfsetispeed(&options, baudrate);
cfsetospeed(&options, baudrate);
// 8N1配置
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8位数据位
options.c_cflag &= ~PARENB; // 无奇偶校验
options.c_cflag &= ~CSTOPB; // 1位停止位
// 原始输入模式
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST; // 原始输出
// 立即应用设置
if(tcsetattr(fd, TCSANOW, &options) < 0) {
perror("tcsetattr failed");
return -1;
}
return 0;
}
波特率对照表:
| 宏定义 | 实际波特率 |
|---|---|
| B115200 | 115200 |
| B57600 | 57600 |
| B38400 | 38400 |
| B19200 | 19200 |
整合各模块的完整实现:
c复制int main(int argc, char *argv[]) {
if(argc != 2) {
fprintf(stderr, "Usage: %s <serial_port>\n", argv[0]);
return 1;
}
// 打开串口设备
int fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);
if(fd < 0) {
perror("open serial port failed");
return 1;
}
// 配置串口参数
if(setup_serial(fd, B115200) < 0) {
close(fd);
return 1;
}
// 创建读写线程
pthread_t tid_read, tid_write;
pthread_create(&tid_read, NULL, read_thread, NULL);
pthread_create(&tid_write, NULL, write_thread, NULL);
// 等待线程结束
pthread_join(tid_read, NULL);
pthread_join(tid_write, NULL);
close(fd);
return 0;
}
使用交叉编译器构建可执行文件:
bash复制aarch64-none-linux-gnu-gcc serial_test.c -o serial_test -lpthread
将程序推送到开发板:
bash复制adb push serial_test /data
adb shell chmod +x /data/serial_test
运行测试:
bash复制adb shell /data/serial_test /dev/ttyS4
问题1:权限不足
bash复制open /dev/ttyS4: Permission denied
解决方案:
bash复制adb shell chmod 666 /dev/ttyS4
问题2:数据丢失
问题3:线程阻塞
c复制// 启用硬件流控示例
options.c_cflag |= CRTSCTS;
在实际项目中,我发现最容易被忽视的是串口接地问题——不良的接地会导致信号干扰,表现为随机数据错误。建议在PCB设计阶段就做好地平面分割,并在调试时使用示波器检查信号质量。