基于C语言的串口数据收发故障排查(termios+Atmega169P)
串口通信故障排查:Atmega169P与Cygwin模拟端数据收发失败
我正在开发一套C语言实现的串口通信系统,核心是通过/dev/ttyS2串口文件,结合termios库与Atmega169P微控制器实现数据收发。系统分为两端:
- 模拟端:在Cygwin终端运行,通过termios配置操作
/dev/ttyS2收发数据,用带转换器的物理串口线连接笔记本与控制器 - 控制器端:通过USART寄存器UDR0读取数据,完成初始化与数据处理
测试场景:在控制器端添加逻辑,当收到模拟端发送的0b0001时,调用已验证正常的writeChar()在LCD位置1显示"1";同时在位置5固定显示测试"1"。但实际测试中,仅位置5的测试"1"正常显示,位置1无内容,说明串口通信未正常工作。
模拟端原代码
#include <termios.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> void sendChar(int fd){ char input = getchar(); if (input == 'n') { uint8_t out = 0b0001; write(fd, &out, 1); } if (input == 's') { uint8_t out = 0b0100; write(fd, &out, 1); } if (input == 'e') { printf("You have exited the simulation"); } close(fd); } void main() { int fd; fd = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY); if (fd==-1) {printf("Couldn't open specified file");} struct termios tty; tcgetattr(fd, &tty); cfsetospeed(&tty, B9600); cfsetispeed(&tty, B9600); tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~PARENB; tty.c_cflag &= ~CSTOPB; tcsetattr(fd, TCSANOW, &tty); sendChar(fd); }
控制器端原代码
#include <avr/io.h> #include <stdio.h> #define BAUD 9600 #define F_CPU 8000000UL #define UBRR_VALUE (F_CPU / (BAUD * 16UL) ) void USART_init(void) { // Set baud rate prescaler UBRR0H = (UBRR_VALUE >> 8); UBRR0L = UBRR_VALUE; // Enable receiver and transmitter UCSR0B |= (1 << RXEN0) | (1 << TXEN0); // Set frame format: 8 data bits, no parity, 1 stop bit UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); } unsigned char USART_Receive(void) { // Wait for data to be received while(!(UCSR0A & (1<<RXC0))); // Get and return received data from buffer volatile uint8_t data = UDR0; if (UDR0 == 0b0001) { writeChar('1',1); } } int main(){ USART_init(); LCD_init(); USART_Receive(); writeChar('1',5); }
问题排查与修复建议
1. 模拟端代码修复
原代码存在串口配置不完整、逻辑错误、缺少错误校验的问题,修改后代码如下:
#include <termios.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdint.h> void sendChar(int fd){ char input = getchar(); ssize_t write_ret; if (input == 'n') { uint8_t out = 0b0001; write_ret = write(fd, &out, 1); if(write_ret != 1){ printf("Failed to send data\n"); } tcdrain(fd); // 等待数据发送完成 } else if (input == 's') { uint8_t out = 0b0100; write_ret = write(fd, &out, 1); if(write_ret != 1){ printf("Failed to send data\n"); } tcdrain(fd); } else if (input == 'e') { printf("You have exited the simulation\n"); close(fd); return; } close(fd); } int main() { int fd; fd = open("/dev/ttyS2", O_RDWR | O_NOCTTY); if (fd == -1) { perror("Couldn't open specified file"); return 1; } struct termios tty; if(tcgetattr(fd, &tty) != 0){ perror("tcgetattr failed"); close(fd); return 1; } // 配置串口参数 cfsetospeed(&tty, B9600); cfsetispeed(&tty, B9600); tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~PARENB; tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CRTSCTS; // 禁用硬件流控 tty.c_cflag |= CREAD | CLOCAL; // 启用接收器与本地模式 tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 禁用规范模式与回显 tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控 tty.c_oflag &= ~OPOST; // 原始输出模式 // 设置超时 tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = 10; if(tcsetattr(fd, TCSANOW, &tty) != 0){ perror("tcsetattr failed"); close(fd); return 1; } sendChar(fd); return 0; }
2. 控制器端代码修复
原代码存在UDR0重复读取、缺少返回值的问题,修改后代码如下:
#include <avr/io.h> #include <stdio.h> #define BAUD 9600 #define F_CPU 8000000UL #define UBRR_VALUE (F_CPU / (BAUD * 16UL)) // 假设已实现的LCD操作函数 void LCD_init(void); void writeChar(char c, uint8_t pos); void USART_init(void) { UBRR0H = (UBRR_VALUE >> 8); UBRR0L = UBRR_VALUE; UCSR0B |= (1 << RXEN0) | (1 << TXEN0); UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); } unsigned char USART_Receive(void) { while(!(UCSR0A & (1<<RXC0))); uint8_t data = UDR0; if (data == 0b0001) { writeChar('1', 1); } return data; // 补充返回值 } int main(){ USART_init(); LCD_init(); USART_Receive(); writeChar('1',5); while(1); // 死循环保持程序运行 }
3. 硬件与连接排查
- 确认串口转换器的TX/RX引脚与Atmega169P的USART引脚交叉连接(模拟端TX接控制器RX,模拟端RX接控制器TX)
- 检查串口转换器与Atmega169P的供电是否稳定
- 用minicom等串口工具测试模拟端串口是否能正常发送数据,排除Cygwin环境配置问题
- 用逻辑分析仪检测串口引脚的电平信号,确认数据是否实际传输
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

