3. 硬件接口与协议:GPIO、SPI、I2C、UART

各位同学,咱们今天聊聊提花机里那些看不见、摸不着,但至关重要的“通信语言”。说白了,就是主控芯片怎么跟传感器、执行器打招呼的。我做了这么多年纺织自动化,发现很多现场问题,最后都出在这些接口上。不是信号没发出来,就是时序没对上。

这一节,我带你把这四种最常用的接口吃透。你想想看,提花机里几百个电磁阀,要是每个都单独拉一根线,那线束比手臂还粗。所以,必须靠协议来“复用”线路。

核心观点:选对接口,比写对代码更重要。接口选错了,后面全是坑。

3.1 GPIO:最基础的“开关”

GPIO,全称通用输入输出口。说白了,就是芯片的“手指头”。你可以让它输出高电平(3.3V或5V),或者低电平(0V)。也可以读取外部是高还是低。

在提花机里,GPIO最常见的用途就是控制电磁阀的使能信号。我记得有一次,现场一台机器老是跳闸。排查了半天,发现是GPIO配置成了“开漏输出”,驱动能力不够,导致电磁阀线圈半吸合,电流过大。嗯,这里要注意:GPIO的驱动电流通常只有几毫安到二十毫安,驱动继电器或电磁阀,必须加三极管或MOS管做功率放大。

我个人习惯,在初始化GPIO时,一定先确认默认电平。很多芯片上电瞬间,GPIO是浮空或高阻态,这时候如果直接连到电磁阀,可能会误动作。我的做法是:先拉低,再配置方向。

避坑指南:我曾经在调试一块新板子时,发现GPIO输出波形有毛刺。后来用示波器一看,是代码里连续写了两次输出值,中间没有加延时。GPIO的电气特性决定了它需要几个纳秒的稳定时间。所以,写代码时,gpio_set_level() 之后,最好加一个 nop() 空操作。

3.2 SPI:高速的“流水线”

SPI,串行外设接口。它像一条流水线:主设备(Master)负责发时钟,从设备(Slave)跟着时钟走。SPI有四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。

提花机里,SPI常用于读取高速编码器的位置数据,或者配置AD转换器的参数。为什么用SPI?因为它快。标准SPI能跑到几十兆赫兹,一帧数据几微秒就传完了。

但SPI有个坑:时钟极性和相位必须匹配。也就是CPOL和CPHA这两个参数。我见过一个案例,工程师把SPI模式设成了Mode 0,但传感器芯片要求Mode 3。结果数据读出来全是乱的,像天书一样。后来我让他用逻辑分析仪抓波形,一对比就发现了问题。

// 以STM32为例,配置SPI为Mode 0
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;   // 时钟空闲为低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第一个边沿采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);

警告:SPI的从设备片选(CS)信号,必须在传输开始前拉低,传输结束后拉高。如果CS信号有毛刺或抖动,从设备可能会误判为新的传输开始。我建议在CS线上加一个10kΩ的上拉电阻,确保默认状态为高。

3.3 I2C:多设备的“总线”

I2C,集成电路总线。它只有两根线:SCL(时钟)和SDA(数据)。所有设备都挂在这两根线上,靠地址来区分。I2C是半双工的,也就是说,同一时刻只能一个设备说话。

提花机里,I2C常用于读取温度传感器、EEPROM存储参数,或者配置IO扩展芯片。为什么用I2C?因为它节省引脚。一个I2C总线可以挂几十个设备,每个设备只需要一个地址。

但I2C也有脾气。它的两根线都是开漏输出,必须加上拉电阻。电阻值选多大?我一般选4.7kΩ。如果总线长度超过1米,或者设备数量多,可以换成2.2kΩ。电阻太小,功耗大;电阻太大,信号上升沿变缓,容易出错。

我记得有一次,客户反映机器运行一段时间后,温度数据突然变成0xFF。我怀疑是I2C总线被干扰了。后来发现,是电机启动时的大电流导致电源电压跌落,I2C从设备瞬间掉电复位,但主设备不知道。解决方案很简单:在I2C从设备的VCC引脚上加一个100μF的电解电容,扛住电压跌落。

关键点:I2C通信的起始条件(SCL高时,SDA从高变低)和停止条件(SCL高时,SDA从低变高),必须严格满足时序要求。很多初学者写I2C驱动时,忽略了这两个条件的建立时间和保持时间,导致通信不稳定。

3.4 UART:最通用的“对话”

UART,通用异步收发传输器。它只需要两根线:TX(发送)和RX(接收)。UART是异步的,也就是说,没有单独的时钟线。收发双方必须约定好相同的波特率(比如115200bps)。

提花机里,UART常用于和触摸屏、上位机或者无线模块通信。为什么用UART?因为它简单,几乎所有的单片机都有UART外设,而且调试方便。我调试程序时,最喜欢用UART打印日志。串口助手一开,数据一目了然。

但UART有个天生的弱点:没有时钟同步,抗干扰能力相对较弱。如果波特率偏差超过2%,就可能出现乱码。我曾经遇到过,一个工程师把晶振选成了12MHz,但计算波特率时用了11.0592MHz的公式,结果波特率误差达到了3.5%,通信时好时坏。

// 以ESP32为例,配置UART
uart_config_t uart_config = {
    .baud_rate = 115200,
    .data_bits = UART_DATA_8_BITS,
    .parity = UART_PARITY_DISABLE,
    .stop_bits = UART_STOP_BITS_1,
    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM_1, &uart_config);
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0);

实用技巧:UART通信时,建议在数据帧中加入校验和(Checksum)。虽然UART硬件有奇偶校验位,但那只能检测单比特错误。对于工业现场,电磁干扰可能导致多比特翻转。我习惯在每帧数据的末尾加一个字节的异或校验和,接收方收到后重新计算,不一致就丢弃重发。

3.5 四种接口的对比与选择

好了,四种接口都讲完了。你可能会问:那我到底该用哪个?别急,我整理了一张表,你一看就明白。

接口 线数 速度 通信方式 典型应用
GPIO 1根/信号 取决于软件 单线、电平 使能信号、状态指示
SPI 4根 最高几十MHz 全双工、同步 高速传感器、AD/DA
I2C 2根 最高3.4MHz 半双工、同步 温度传感器、EEPROM
UART 2根 最高几Mbps 全双工、异步 人机界面、无线模块

我个人选型的原则很简单:

  • 如果只需要控制一个开关,用GPIO,别折腾协议。
  • 如果数据量大、速度要求高,比如读取编码器位置,用SPI。
  • 如果设备多、引脚有限,比如挂一堆传感器,用I2C。
  • 如果需要和电脑或屏幕通信,用UART,调试也方便。

最后说一句:接口只是工具,理解它的脾气秉性,才能用好它。我在现场见过太多因为接口配置不当导致的“软故障”——时好时坏,最难排查。所以,设计阶段多花十分钟确认接口参数,能省下现场调试的十个小时。

提花机信号采集接口选型逻辑 主控芯片 GPIO 电磁阀使能 SPI 高速编码器 I2C 温度/EEPROM UART 触摸屏/上位机 根据设备类型和速度需求,选择合适的接口

专注资料整理