Linux串口驱动之五工程常见问题

       

     

目录

问题现象

   故障处理

     量波形

数据分析

波特率分析

   正常时波形

波特率获取

分频因子

串口收发统计

问题解决及启示


      在实际的项目中,对端设备上电后就一直发送数据,而此时本端串口还处于一个未初始化的默认状态,这种情况就可能会出现各种莫名其妙问题,本文以一例简要说明。

问题现象

    出现问题时,数据收发异常,例如本端发送0x55,但是对端却收到一个如下的字节,明显异常了。

    然而问题又是偶现的,那么怎么办呢?

   故障处理

     复现前把准备的都准备,预留示波器的几个测试点,从串口控制器的输出端、到电平转换端、到链接器端,都准备好可测量的,该飞线飞线。

     量波形

      故障难得复现,复现后,发送55,在示波器量取波形如下:

      

数据分析

  • 数据位:8 bit

  • 校验位:无

  • 停止位:1 bit (最常见的默认设置)

  • 字节数据:0x55

x55 的二进制表示是 0101 0101

重要提示:串口传输是低位在先(LSB First)的。所以,在传输线上,这个字节的传输顺序是从右向左的。

传输顺序为:1 (LSB) -> 0 -> 1 -> 0 -> 1 -> 0 -> 1 -> 0 (MSB)

一个完整的串口数据帧由以下几部分组成:

  1. 起始位:总是1个比特时间的逻辑0(低电平)。

  2. 数据位:这里是8位,从LSB到MSB。

  3. 校验位:无,所以这一位省略。

  4. 停止位:这里是1位,逻辑1(高电平)。

所以,0x55的完整帧结构(按传输顺序)为:

起始位	Bit 0 (LSB)	Bit 1	Bit 2	Bit 3	Bit 4	Bit 5	Bit 6	Bit 7 (MSB)	停止位
0	      1	           0	  1	      0	      1	      0	      1     	0        1

与上面量取的波形完全一致,说明数据没有错误,即数据本身没有错误。

波特率分析

时基 10us,每个大格子10us,每个小格子2us,每bit占用大概8.5个us,与115200的波特率比较接近。

   正常时波形

     

很明显两者的波特率不一样的了。

时基 5us,每个大格子5us,每个小格子1us,每bit占用大概2.2个us

波特率获取

    很多人可能看到问题现象就猜测到了波特率不一致导致的,当然这只是其中原因之一。毕竟我们在日常测试中并没有出现问题。线缆等的问题也可能是原因。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define _GNU_SOURCE
#include <sys/ioctl.h>
#include <asm/termbits.h>

#define TARGET_PORT "/dev/ttyAMA1"

// 标准波特率对照表
struct baud_rate {
    speed_t speed;
    const char *name;
    int value;
};

struct baud_rate standard_bauds[] = {
    {B0, "B0", 0},
    {B50, "B50", 50},
    {B75, "B75", 75},
    {B110, "B110", 110},
    {B134, "B134", 134},
    {B150, "B150", 150},
    {B200, "B200", 200},
    {B300, "B300", 300},
    {B600, "B600", 600},
    {B1200, "B1200", 1200},
    {B1800, "B1800", 1800},
    {B2400, "B2400", 2400},
    {B4800, "B4800", 4800},
    {B9600, "B9600", 9600},
    {B19200, "B19200", 19200},
    {B38400, "B38400", 38400},
    {B57600, "B57600", 57600},
    {B115200, "B115200", 115200},
    {B230400, "B230400", 230400},
    {B460800, "B460800", 460800},
    {B500000, "B500000", 500000},
    {B576000, "B576000", 576000},
    {B921600, "B921600", 921600},
    {B1000000, "B1000000", 1000000},
    {B1152000, "B1152000", 1152000},
    {B1500000, "B1500000", 1500000},
    {B2000000, "B2000000", 2000000},
    {0, NULL, 0}  // 结束标记
};

const char* get_standard_baud_name(speed_t speed) {
    for (int i = 0; standard_bauds[i].name != NULL; i++) {
        if (standard_bauds[i].speed == speed) {
            return standard_bauds[i].name;
        }
    }
    return "CUSTOM";
}

int get_standard_baud_value(speed_t speed) {
    for (int i = 0; standard_bauds[i].name != NULL; i++) {
        if (standard_bauds[i].speed == speed) {
            return standard_bauds[i].value;
        }
    }
    return -1;
}

const char* get_baud_type(speed_t speed) {
    for (int i = 0; standard_bauds[i].name != NULL; i++) {
        if (standard_bauds[i].speed == speed) {
            return "STANDARD";
        }
    }
    return "CUSTOM";
}

int main() {
    int fd;
    struct termios2 tty;
    
    printf("=== Querying Baud Rate for %s ===\n", TARGET_PORT);
    
    // 检查串口是否存在
    if (access(TARGET_PORT, F_OK) != 0) {
        printf("Error: Serial port %s does not exist\n", TARGET_PORT);
        printf("Please check if the port is correct or if the device is connected\n");
        return 1;
    }
    
    // 检查读取权限
    if (access(TARGET_PORT, R_OK) != 0) {
        printf("Warning: No read permission on %s\n", TARGET_PORT);
        printf("Trying to open with current permissions...\n");
    }
    
    // 尝试以只读方式打开串口
    fd = open(TARGET_PORT, O_RDONLY | O_NOCTTY);
    if (fd < 0) {
        printf("Error: Cannot open %s - %s (errno: %d)\n", 
               TARGET_PORT, strerror(errno), errno);
        
        if (errno == EACCES) {
            printf("Try running with sudo: sudo %s\n", "/proc/self/exe");
        }
        return 1;
    }
    
    printf("✓ Successfully opened %s\n", TARGET_PORT);
    
    // 使用 TCGETS2 获取串口属性
    if (ioctl(fd, TCGETS2, &tty) != 0) {
        printf("Error: TCGETS2 ioctl failed - %s (errno: %d)\n", 
               strerror(errno), errno);
        close(fd);
        return 1;
    }
    
    printf("✓ Successfully retrieved serial attributes via TCGETS2\n\n");
    
    // 显示详细的波特率信息
    printf("BAUD RATE INFORMATION:\n");
    printf("──────────────────────\n");
    printf("Input Speed:  %8d baud", tty.c_ispeed);
    printf("  [%s - %s]\n", get_baud_type(tty.c_ispeed), get_standard_baud_name(tty.c_ispeed));
    
    printf("Output Speed: %8d baud", tty.c_ospeed);
    printf("  [%s - %s]\n", get_baud_type(tty.c_ospeed), get_standard_baud_name(tty.c_ospeed));
    
    // 检查波特率是否一致
    if (tty.c_ispeed == tty.c_ospeed) {
        printf("✓ Input and output speeds match\n");
    } else {
        printf("⚠ Warning: Input and output speeds differ!\n");
    }
    
    // 显示串口配置详情
    printf("\nSERIAL PORT CONFIGURATION:\n");
    printf("──────────────────────────\n");
    
    // 数据位
    int data_bits = 0;
    if ((tty.c_cflag & CSIZE) == CS5) data_bits = 5;
    else if ((tty.c_cflag & CSIZE) == CS6) data_bits = 6;
    else if ((tty.c_cflag & CSIZE) == CS7) data_bits = 7;
    else if ((tty.c_cflag & CSIZE) == CS8) data_bits = 8;
    
    printf("Data Bits:    %d\n", data_bits);
    printf("Stop Bits:    %s\n", (tty.c_cflag & CSTOPB) ? "2" : "1");
    
    // 奇偶校验
    if (tty.c_cflag & PARENB) {
        printf("Parity:       %s\n", (tty.c_cflag & PARODD) ? "Odd" : "Even");
    } else {
        printf("Parity:       None\n");
    }
    
    // 流控制
    if (tty.c_cflag & CRTSCTS) {
        printf("Flow Control: RTS/CTS (Hardware)\n");
    } else if (tty.c_iflag & (IXON | IXOFF)) {
        printf("Flow Control: XON/XOFF (Software)\n");
    } else {
        printf("Flow Control: None\n");
    }
    
    // 工作模式
    printf("Mode:         %s\n", (tty.c_lflag & ICANON) ? "Canonical" : "Raw");
    
    // 显示控制标志
    printf("\nCONTROL FLAGS (c_cflag): 0x%08x\n", tty.c_cflag);
    printf("──────────────────────────\n");
    printf("BOTHER:    %s\n", (tty.c_cflag & BOTHER) ? "Yes (Custom baud)" : "No");
    printf("CBAUD:     0x%08x\n", tty.c_cflag & CBAUD);
    printf("CREAD:     %s\n", (tty.c_cflag & CREAD) ? "Yes" : "No");
    printf("CLOCAL:    %s\n", (tty.c_cflag & CLOCAL) ? "Yes" : "No");
    
    close(fd);
    
    printf("\n✓ Query completed successfully\n");
    
    return 0;
}

正常时与异常时获取到的波特率都如下,与期望值相符。是不是又迷茫了?!

分频因子

  • IBRD (Integer Baud Rate Divisor) = 0x00000006 = 6

  • FBRD (Fractional Baud Rate Divisor) = 0x00000021 = 33

  • 系统时钟频率 = 48 MHz

  

波特率 = 系统时钟频率 / (16 × (IBRD + FBRD/64))

波特率 = 48,000,000 / (16 × 6.515625)
       = 48,000,000 / 104.25
       ≈ 460,431.655 Hz

与期望也一致。

至此,你又迷茫了,分频因子出去的波特率与期望的也一致,但是示波器量出来的缺不对。

串口收发统计

cat /proc/tty/driver/ttyAMA

cat /proc/tty/driver/ttyAMA
serinfo:1.0 driver revision:
0: uart:mmio:0x28001000 irq:5 tx:52 rx:0 RTS|CTS|DTR|DSR
1: uart:mmio:0x28000000 irq:6 tx:9548 rx:2839036 fe:2805403 oe:1 RTS|CTS|DTR|DSR|CD|RI

可以看到统计有大量FE,即帧错误。

  顺便提及一句:这里的OE只有1个,即上层软件没有及时读取数据,导致前述接收流程中的两级buffer都满了导致。

问题解决及启示

    大量实验现象证明,该芯片的串口控制器存在某些异常,通过外围电路的管控,可以将此问题解决。

  1) 假如在设计时,我们没有设计合理的外围电路,仅仅依赖此串口控制器,则问题解决起来成本更高。

 2) 在对端持续发送数据,而本端反复上下电运行业务流程时,是对串口软硬件的一个有效测试。.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

proware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值