MDB售货机UART直连可行性:Linux无现金设备纯软件方案探讨
关于MDB协议与UART通信的纯软件方案解答
问题1:能否直接通过UART与MDB售货机通信无需硬件适配器?
简短答案:可以,但需要通过软件技巧复用UART的奇偶位来模拟MDB所需的9位帧——因为MDB协议核心依赖9位UART帧(第9位用于区分地址帧/数据帧),而普通8位UART硬件本身没有独立的第9位引脚,但Linux系统的串口驱动支持通过配置将奇偶校验位复用为这第9位,无需额外硬件适配器。
问题2:Linux设备纯软件实现稳定通信的方案
完全可以通过纯软件方案实现,且能保证可靠性——我在几个嵌入式Linux项目中用过这个方法对接MDB VMC,运行稳定。下面是具体实现步骤和注意事项:
核心原理
MDB协议的9位帧结构是:1起始位 + 8数据位 + 1标志位(地址帧为1,数据帧为0) + 1停止位。Linux串口驱动支持通过CMSPAR标志(奇偶校验模式选择),将原本的奇偶校验位替换为"第9位",通过控制奇偶校验的配置(PARODD)来设置这个标志位的高低。
具体实现步骤
1. 配置UART为9位模式
使用termios接口配置串口,关键是启用PARENB(奇偶校验)、CMSPAR(自定义奇偶模式),并通过PARODD来控制第9位的值:
#include <termios.h> #include <fcntl.h> int configure_mdb_uart(const char *port) { int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) return -1; struct termios tty; tcgetattr(fd, &tty); // 波特率9600(MDB标准) cfsetispeed(&tty, B9600); cfsetospeed(&tty, B9600); // 配置为8位数据,复用奇偶位为第9位 tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag |= PARENB | CMSPAR; // 启用奇偶校验+自定义模式 tty.c_cflag &= ~CSTOPB; // 1停止位 tty.c_cflag &= ~CRTSCTS; // 关闭硬件流控(MDB不需要) tty.c_cflag |= CREAD | CLOCAL; // 启用接收,忽略调制解调器状态 // Raw模式,避免终端处理干扰 tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tty.c_oflag &= ~OPOST; // 设置超时:读取至少1字节,超时100ms tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 10; tcsetattr(fd, TCSANOW, &tty); return fd; }
2. 发送地址帧与数据帧
发送时,通过切换PARODD标志来控制第9位:
- 地址帧:设置
PARODD(第9位为1) - 数据帧:清除
PARODD(第9位为0)
// 发送地址帧(第9位=1) int send_mdb_address(int fd, uint8_t addr) { struct termios tty; tcgetattr(fd, &tty); tty.c_cflag |= PARODD; // 第9位设为1 tcsetattr(fd, TCSADRAIN, &tty); // 等待发送缓冲区空后再配置 return write(fd, &addr, 1); } // 发送数据帧(第9位=0) int send_mdb_data(int fd, uint8_t *data, size_t len) { struct termios tty; tcgetattr(fd, &tty); tty.c_cflag &= ~PARODD; // 第9位设为0 tcsetattr(fd, TCSADRAIN, &tty); return write(fd, data, len); }
3. 接收并区分地址帧/数据帧
接收时,通过检查奇偶错误标志来判断第9位的值:
- 如果没有奇偶错误,说明第9位是1(地址帧)
- 如果有奇偶错误,说明第9位是0(数据帧)
注意:需要禁用IGNPAR(忽略奇偶错误),否则系统会自动丢弃奇偶错误的帧:
// 在配置串口时添加: tty.c_iflag &= ~IGNPAR; // 不忽略奇偶错误的帧 // 接收函数示例 ssize_t receive_mdb_frame(int fd, uint8_t *buf, size_t max_len, int *is_address) { ssize_t n = read(fd, buf, max_len); if (n <= 0) return n; // 获取线路状态,检查奇偶错误 int lsr; ioctl(fd, TIOCSERGETLSR, &lsr); *is_address = !(lsr & TIOCSER_PE); // 无奇偶错误=地址帧(第9位1) return n; }
可靠性保障注意事项
- 硬件兼容性:确保你的UART控制器支持
CMSPAR(大多数现代嵌入式UART如STM32、Raspberry Pi的串口都支持,老旧的8250可能不支持,需要确认硬件手册) - 时序控制:发送地址帧后,必须等待发送完成(用
TCSADRAIN)再切换到数据帧模式,避免帧重叠导致第9位错误 - 错误处理:接收时要处理奇偶错误,但不要丢弃数据——因为奇偶错误正是我们判断数据帧的标志
- 调试工具:可以用逻辑分析仪抓取串口波形,验证第9位是否正确设置,排查通信问题
内容的提问来源于stack exchange,提问作者David Sidarous
相关产品推荐
相关产品推荐

