目录
在实际的项目中,对端设备上电后就一直发送数据,而此时本端串口还处于一个未初始化的默认状态,这种情况就可能会出现各种莫名其妙问题,本文以一例简要说明。
问题现象
出现问题时,数据收发异常,例如本端发送0x55,但是对端却收到一个如下的字节,明显异常了。
然而问题又是偶现的,那么怎么办呢?
故障处理
复现前把准备的都准备,预留示波器的几个测试点,从串口控制器的输出端、到电平转换端、到链接器端,都准备好可测量的,该飞线飞线。
量波形
故障难得复现,复现后,发送55,在示波器量取波形如下:

数据分析
-
数据位:8 bit
-
校验位:无
-
停止位:1 bit (最常见的默认设置)
-
字节数据:0x55
x55 的二进制表示是 0101 0101。
重要提示:串口传输是低位在先(LSB First)的。所以,在传输线上,这个字节的传输顺序是从右向左的。
传输顺序为:1 (LSB) -> 0 -> 1 -> 0 -> 1 -> 0 -> 1 -> 0 (MSB)
一个完整的串口数据帧由以下几部分组成:
-
起始位:总是1个比特时间的逻辑
0(低电平)。 -
数据位:这里是8位,从LSB到MSB。
-
校验位:无,所以这一位省略。
-
停止位:这里是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) 在对端持续发送数据,而本端反复上下电运行业务流程时,是对串口软硬件的一个有效测试。.
2253

被折叠的 条评论
为什么被折叠?



