第二讲:物理层接口与驱动开发

各位同学,今天咱们聊聊物理层。说白了,就是POS机和外界通信的那根“线”怎么干活。

我做了十几年嵌入式通信,最深的体会是:物理层搞不定,上层协议再漂亮也是白搭。你想想看,数据发不出去,或者发出去全是乱码,应用层写得再好有什么用?

2.1 串口通信:RS232 vs RS485

串口是POS机最常用的物理接口。但RS232和RS485,很多人傻傻分不清。

2.1.1 RS232:点对点的老将

RS232是单端信号,一根信号线对地。电压范围是±3V到±15V。逻辑1是负电压,逻辑0是正电压——这个和TTL电平刚好相反,要注意。

我在项目中遇到过一个问题:某款POS机接打印机,死活通信不上。查了半天,发现是电平转换芯片坏了,输出只有±2V,接收端不认。嗯,RS232的电压余量一定要留够。

参数RS232RS485
信号方式单端差分
最大距离约15米约1200米
节点数1对1最多32个节点
电压范围±3V ~ ±15V±1.5V ~ ±6V

2.1.2 RS485:长距离多节点

RS485用差分信号,抗干扰能力强。POS机在超市收银台,旁边就是大功率电器,这时候RS485就比RS232靠谱得多。

我建议:如果距离超过10米,或者环境有电磁干扰,果断选RS485。别为了省一个电平转换芯片的钱,后面调试到崩溃。

注意:RS485需要加终端电阻,一般是120Ω。不加电阻,长线反射会把信号搞成一团浆糊。我曾经在项目里忘了加,波形图看起来像心电图,折腾了两天才发现。

2.2 GPIO模拟UART:没有串口怎么办?

有些低成本MCU,硬件UART数量不够。或者你用的引脚被其他功能占用了。怎么办?用GPIO模拟。

说白了,就是用两个普通IO口,一个当TX,一个当RX,靠软件定时器来模拟波特率。

2.2.1 基本原理

UART一帧数据包括:起始位(低电平)、8个数据位(LSB first)、可选的校验位、停止位(高电平)。

模拟发送很简单:拉低TX引脚(起始位),然后按位发送数据,最后拉高(停止位)。关键是时序要准。

// GPIO模拟UART发送一个字节
void gpio_uart_send_byte(uint8_t data) {
    // 起始位:拉低
    GPIO_WriteLow(TX_PIN);
    delay_us(BIT_TIME);
    
    // 发送8个数据位,LSB first
    for (int i = 0; i < 8; i++) {
        if (data & 0x01) {
            GPIO_WriteHigh(TX_PIN);
        } else {
            GPIO_WriteLow(TX_PIN);
        }
        data >>= 1;
        delay_us(BIT_TIME);
    }
    
    // 停止位:拉高
    GPIO_WriteHigh(TX_PIN);
    delay_us(BIT_TIME);
}
经验之谈:模拟UART的波特率误差要控制在±2%以内。我习惯用定时器中断来做精确延时,而不是纯软件循环。因为中断不会被其他代码打断,时序更稳。

2.2.2 接收的坑

接收比发送难。因为你要检测起始位的下降沿,然后精确采样每个数据位的中点。

我曾经犯过一个错误:采样点选在了数据位的边缘。结果电平刚好在跳变,读到的数据全是错的。正确做法是:在每位的中点采样,也就是延时半个位时间后再读。

2.3 SPI接口调试:高速通信的利器

SPI在POS机里常用于连接安全模块、显示屏、SD卡等。速度比UART快得多,但调试起来也麻烦一些。

2.3.1 四根线搞定

SCK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。

注意:SPI有四种模式,由时钟极性(CPOL)和时钟相位(CPHA)决定。我见过太多人栽在这个上面。

模式CPOLCPHA数据采样边沿
模式000上升沿采样
模式101下降沿采样
模式210下降沿采样
模式311上升沿采样

怎么确定用哪种模式?看从设备的数据手册。如果手册说“数据在时钟上升沿锁存”,那就是模式0或模式3。具体看空闲时时钟电平。

调试技巧:先用逻辑分析仪抓波形,看CS拉低后,时钟和数据的关系对不对。我习惯先发一个0x55(二进制01010101),这样每个位都跳变,容易看出时序问题。

2.4 物理层波形分析:示波器是照妖镜

做物理层开发,示波器就是你的眼睛。没有它,你就是在黑暗中摸索。

2.4.1 看什么?

  • 电压幅值:信号有没有达到接收端要求的电平?RS232要±5V以上,TTL要3.3V或5V。
  • 上升/下降时间:太慢会导致位宽失真,太快会产生过冲和振铃。
  • 时序关系:时钟和数据之间的建立时间、保持时间够不够?
  • 噪声:有没有毛刺?电源纹波会不会耦合到信号线上?

2.4.2 实战案例

有一次,POS机读卡器偶尔读不到卡。用示波器一看,SPI时钟线上有个窄毛刺,刚好在数据采样时刻出现。原因是PCB走线太长,信号反射造成的。

解决办法:在时钟线靠近源端串一个22Ω电阻,吸收反射。毛刺消失了,问题解决。

切记:示波器的探头地线要尽量短。长地线会引入噪声,你看到的波形可能不是真实的信号。我习惯用弹簧地线夹,直接夹在探头尖端附近的地线上。

2.5 驱动开发要点总结

写物理层驱动,我总结了三条原则:

  1. 先看波形,再写代码。用示波器确认硬件连接没问题,再动手写驱动。否则你都不知道是硬件问题还是软件问题。
  2. 分层测试。先测发送,再测接收。发送用示波器看波形对不对,接收用串口助手或逻辑分析仪看数据对不对。
  3. 异常处理不能省。超时、帧错误、溢出错误,都要有对应的处理逻辑。我见过太多驱动,正常通信没问题,一遇到干扰就死机。

好了,物理层就讲到这里。下一讲咱们聊聊数据链路层,看看怎么把物理层传过来的比特流,组装成完整的报文帧。

课后练习:用GPIO模拟一个9600波特率的UART发送,用示波器抓波形,验证每个位的宽度是否正确。误差超过±2%的话,调整你的延时函数。

公众号:蓝海资料掘金营,微信deep3321