3、通信协议入门:UART、I2C、SPI基础,在能耗监测中的应用场景
各位同学,咱们今天聊点实在的。做嵌入式能耗监测,说白了就是跟各种传感器、芯片打交道。怎么让它们听话、怎么把数据拿回来?靠的就是通信协议。UART、I2C、SPI,这三个是基本功,就像练武要先扎马步一样。
我个人习惯,不管项目多急,先把这三兄弟搞清楚再动手。不然你想想看,焊好板子发现数据死活读不出来,那才叫一个头大。好,咱们一个一个来。
3.1 UART:最老实的串口通信
UART,全称通用异步收发传输器。名字挺长,其实原理很简单——就是一根线发、一根线收,外加一根地线。我最早接触单片机就是从UART开始的,那时候觉得这东西真神奇,两根线就能传数据。
核心要点:
- 异步通信:没有时钟线,靠双方约定好波特率(比如9600、115200)来同步。
- 数据格式:起始位(1位)+ 数据位(通常8位)+ 校验位(可选)+ 停止位(1位或2位)。
- 全双工:可以同时收发,互不干扰。
能耗监测中的应用场景:
UART最常见的用法就是接那些“老派”但可靠的传感器,比如某些电流互感器模块、电表模块。它们输出就是串口数据,你直接用MCU的UART引脚接上就行。
我在项目中遇到过一个问题:用UART读取一个电表数据,波特率设成9600,结果数据全是乱码。后来发现是电表用的不是标准8位数据,而是7位数据位+偶校验。嗯,这里要注意,有些工业设备喜欢玩这种“非标”配置。
避坑指南:
我曾经在调试一个能耗采集器时,UART通信时好时坏。查了半天,发现是地线没接好。UART虽然简单,但地线必须共地,否则电平参考点不一样,数据肯定出错。记住:TX接RX,RX接TX,GND接GND,别搞反了。
// UART初始化示例(STM32 HAL库)
void UART_Init(void) {
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200; // 波特率
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据
huart1.Init.StopBits = UART_STOPBITS_1; // 1位停止位
huart1.Init.Parity = UART_PARITY_NONE; // 无校验
huart1.Init.Mode = UART_MODE_TX_RX; // 收发模式
HAL_UART_Init(&huart1);
}
// 发送数据
HAL_UART_Transmit(&huart1, (uint8_t *)"Hello", 5, 1000);
3.2 I2C:两线制,多设备共享总线
I2C,Inter-Integrated Circuit,飞利浦发明的。它只用两根线:SCL(时钟线)和SDA(数据线)。但厉害的是,一根总线上可以挂几十个设备,每个设备有自己的地址。
核心要点:
- 同步通信:有时钟线,主设备控制时钟。
- 半双工:同一时刻只能一个方向传输数据。
- 设备地址:每个从设备有7位或10位地址,主设备通过地址选择通信对象。
- 上拉电阻:SCL和SDA必须接上拉电阻(通常4.7kΩ),否则无法工作。
能耗监测中的应用场景:
I2C在能耗监测里简直是“万金油”。温度传感器(如LM75)、湿度传感器、光照传感器、甚至某些专用的功率监测芯片(如INA219),清一色都是I2C接口。你想想看,一个MCU的I2C引脚,接上五六个传感器,省引脚又省布线,多爽。
我记得有一次做智能插座项目,里面用了INA219监测电流和电压。INA219就是I2C接口,地址是0x40。我只需要两根线就能读到功率数据,代码写起来也简单。
警告:
I2C总线长度不能太长,一般不超过1米。如果线太长,信号会衰减,通信就会失败。我曾经在工厂里遇到一个情况,传感器离MCU有2米远,I2C死活不通。后来换成RS485才解决。所以,远距离传输别用I2C。
// I2C读取INA219功率数据示例
uint16_t INA219_ReadPower(void) {
uint8_t data[2];
uint16_t power_raw;
// 发送寄存器地址(0x03是功率寄存器)
uint8_t reg_addr = 0x03;
HAL_I2C_Master_Transmit(&hi2c1, 0x40 << 1, ®_addr, 1, 100);
// 读取2字节数据
HAL_I2C_Master_Receive(&hi2c1, 0x40 << 1, data, 2, 100);
power_raw = (data[0] << 8) | data[1];
return power_raw;
}
3.3 SPI:高速四线制,适合大数据量
SPI,Serial Peripheral Interface,摩托罗拉发明的。它比I2C快得多,但用的线也多:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。每个从设备需要一根独立的CS线。
核心要点:
- 同步通信:主设备提供时钟。
- 全双工:可以同时收发数据。
- 速度高:典型速度几MHz到几十MHz,远快于I2C。
- 片选信号:每个从设备一根CS线,低电平有效。
能耗监测中的应用场景:
SPI适合那些需要高速、大数据量传输的场景。比如高精度ADC(模数转换器),采样率很高,数据量很大,用I2C就太慢了。还有某些专用的电能计量芯片(如ADE7758),也是SPI接口。你想想看,要实时监测三相电的电压、电流、功率因数,数据量可不小,SPI正好派上用场。
我个人习惯,如果传感器数据量超过几百字节每秒,我就优先考虑SPI。I2C虽然方便,但速度上限摆在那里(标准模式100kHz,快速模式400kHz),扛不住大数据量。
避坑指南:
我曾经用SPI接一个ADC芯片,数据读出来总是跳变。查了半天,发现是时钟极性(CPOL)和时钟相位(CPHA)没配置对。SPI有四种模式,主设备和从设备必须匹配。很多芯片手册里会写“Mode 0”或“Mode 3”,你照着配就行。别想当然,一定要看数据手册。
// SPI读取ADC数据示例
uint16_t SPI_ReadADC(void) {
uint8_t tx_data = 0x00; // 发送任意数据,触发从设备发送
uint8_t rx_data;
// CS拉低,选中设备
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
// 收发数据
HAL_SPI_TransmitReceive(&hspi1, &tx_data, &rx_data, 1, 100);
// CS拉高,释放设备
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
return rx_data;
}
3.4 三种协议对比与选型建议
好,咱们把三个协议放在一起对比一下,方便你选型时参考。
| 特性 | UART | I2C | SPI |
|---|---|---|---|
| 线数 | 2(TX、RX) | 2(SCL、SDA) | 4(SCLK、MOSI、MISO、CS) |
| 通信方式 | 异步 | 同步 | 同步 |
| 传输方向 | 全双工 | 半双工 | 全双工 |
| 速度 | 慢(典型115200bps) | 中(典型400kHz) | 快(典型10MHz+) |
| 多设备支持 | 点对点 | 多设备(地址区分) | 多设备(片选区分) |
| 抗干扰能力 | 一般 | 较弱 | 较强 |
| 典型能耗监测应用 | 电表模块、串口传感器 | 温湿度传感器、功率监测芯片 | 高精度ADC、电能计量芯片 |
选型建议:
- 设备少、速度要求不高:用UART,简单可靠。
- 需要挂多个传感器、引脚有限:用I2C,两线制搞定一切。
- 需要高速、大数据量:用SPI,别犹豫。
说白了,没有最好的协议,只有最合适的。我在实际项目中,经常是UART、I2C、SPI混着用。比如一个能耗监测终端,用UART接电表,用I2C接温湿度传感器,用SPI接高精度ADC。各司其职,互不干扰。
3.5 小结
嗯,今天咱们把UART、I2C、SPI这三个通信协议的基础讲了一遍。记住它们的核心区别:UART是异步、两线、点对点;I2C是同步、两线、多设备;SPI是同步、四线、高速。在能耗监测中,选对协议能省不少事。
下一章咱们会深入讲讲这些协议在低功耗设计中的注意事项,比如怎么在休眠模式下保持通信、怎么降低通信功耗。到时候再聊。
课后小练习:
找一个你手头的传感器,看看它的数据手册,判断它用的是UART、I2C还是SPI。然后试着用MCU读取它的数据。相信我,动手做一遍比看十遍书都管用。