You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:23:10