3、UART接口详解:UART协议帧格式、波特率设置与误差分析、在红外探测器中的应用实例
UART,也就是通用异步收发传输器。说实话,这玩意儿在嵌入式领域太常见了。我最早接触红外探测器时,第一件事就是看它支不支持UART。为什么?因为简单、可靠、调试方便。你拿个USB转串口的小板子,接上三根线,数据就能跑了。
这一节,咱们就把UART协议掰开揉碎了讲清楚。我会结合MLX90614这个经典的红外测温传感器,带你看看实际项目中怎么用。
3.1 UART协议帧格式
UART是异步通信,没有时钟线。收发双方靠什么同步?靠约定的波特率和帧格式。说白了,就是大家提前说好:什么时候开始、什么时候结束、数据怎么放。
一个标准的UART帧,包含这几部分:
- 起始位:1位,逻辑0。告诉接收方“我要发数据了”。
- 数据位:5~9位,常见的是8位。从最低位开始传输。
- 校验位:可选,奇校验或偶校验。用于简单的错误检测。
- 停止位:1位、1.5位或2位,逻辑1。表示帧结束。
核心要点:起始位是下降沿,停止位是高电平。空闲时总线保持高电平。这个特性很重要,后面讲红外探测器通信时会用到。
我画了一张帧结构图,你看一眼就明白了:
这张图里,我特意标出了LSB和MSB的位置。很多新手会搞反,以为高位先发。其实UART规定的是低位先发。我在一个项目里就吃过这个亏,调试了两天才发现是字节顺序搞反了。
3.2 波特率设置与误差分析
波特率,就是每秒传输的符号数。对于UART来说,一个符号就是一bit。所以波特率9600,意思就是每秒传9600个bit。
但这里有个坑:波特率不是随便设的。收发双方的误差必须控制在可接受范围内。我个人的经验是,误差超过2%就容易丢数据了。
注意:波特率误差主要来自时钟源。MCU内部RC振荡器精度通常只有±2%~±5%,而外部晶振可以做到±50ppm甚至更高。如果你用内部RC做UART通信,一定要实测一下实际波特率。
咱们来算一笔账。假设你设置波特率115200,时钟源是16MHz的晶振。MCU内部通常用波特率发生器分频,公式是:
BRR = 时钟频率 / (16 × 目标波特率)
= 16000000 / (16 × 115200)
= 8.68
分频系数不能是小数,只能取整。取8的话,实际波特率是:
实际波特率 = 16000000 / (16 × 8) = 125000
误差 = (125000 - 115200) / 115200 ≈ 8.5%。这个误差太大了,通信肯定不稳定。
那怎么办?换一个能整除的波特率,或者换晶振。比如用14.7456MHz的晶振,分频系数就是8,刚好整除。
小技巧:我习惯在项目初期就把常用波特率对应的分频系数算好,列个表。调试时直接查表,省得每次重新算。
下面是我整理的一个常用波特率对照表:
| 目标波特率 | 16MHz晶振分频系数 | 实际波特率 | 误差 |
|---|---|---|---|
| 9600 | 104.17 → 104 | 9615 | 0.16% |
| 19200 | 52.08 → 52 | 19231 | 0.16% |
| 38400 | 26.04 → 26 | 38462 | 0.16% |
| 57600 | 17.36 → 17 | 58824 | 2.12% |
| 115200 | 8.68 → 9 | 111111 | 3.55% |
你看,16MHz下115200的误差高达3.55%,这在实际项目中基本不可用。我曾经在一个产品里用了内部RC振荡器,结果批量生产时发现部分芯片通信不稳定,最后全部换成外部晶振才解决。嗯,这个教训挺深刻的。
3.3 UART在红外探测器中的应用实例:MLX90614
MLX90614是Melexis公司的一款红外测温传感器。它内部集成了红外热电堆和信号处理芯片,可以直接输出物体温度。它支持两种通信接口:SMBus(兼容I2C)和PWM。但很多人不知道,它其实也可以通过UART方式读取数据——只要用对方法。
MLX90614的默认地址是0x5A,7位地址。它内部有多个寄存器,比如:
- 0x07:T环境温度(环境温度)
- 0x06:T物体1温度(红外测温结果)
- 0x05:T物体2温度(部分型号支持)
读取温度时,需要先发送设备地址+写命令,再发送寄存器地址,然后重新发送设备地址+读命令,最后读取数据。这个过程其实和I2C一样,但如果你用UART转I2C的模块,或者直接用MCU的UART模拟I2C时序,就能实现。
我分享一个实际项目中用过的代码片段。这是用STM32的UART模拟I2C读取MLX90614温度的示例:
// MLX90614读取温度函数
// 使用UART模拟I2C协议
float MLX90614_ReadTemp(uint8_t regAddr)
{
uint8_t txBuf[3];
uint8_t rxBuf[2];
uint16_t rawData;
float temperature;
// 1. 发送起始条件 + 设备地址(写) + 寄存器地址
txBuf[0] = 0xB4; // 0x5A << 1 | 0 (写)
txBuf[1] = regAddr;
HAL_UART_Transmit(&huart1, txBuf, 2, 100);
// 2. 发送重复起始条件 + 设备地址(读)
txBuf[0] = 0xB5; // 0x5A << 1 | 1 (读)
HAL_UART_Transmit(&huart1, txBuf, 1, 100);
// 3. 读取2字节数据
HAL_UART_Receive(&huart1, rxBuf, 2, 100);
// 4. 组合数据并计算温度
rawData = (rxBuf[1] << 8) | rxBuf[0];
temperature = rawData * 0.02 - 273.15;
return temperature;
}
关键点:MLX90614返回的温度数据是16位有符号数,单位是0.02℃。所以计算公式是:温度 = 原始值 × 0.02 - 273.15。这个公式我每次用都要确认一遍,因为很容易把符号搞错。
实际应用中,我建议你注意以下几点:
- 上电延迟:MLX90614上电后需要至少10ms才能稳定工作。我习惯等50ms再开始通信。
- 通信速率:SMBus模式下最大速率100kHz。用UART模拟时,波特率建议设在9600~38400之间。
- 噪声抑制:红外传感器对电源噪声敏感。我一般在VDD和GND之间加一个10μF电解电容和0.1μF陶瓷电容。
- 多次采样取平均:单次读取可能有±0.5℃的波动。我通常连续读10次,去掉最大最小值,再取平均。
避坑指南:我曾经在一个项目中直接用UART的TX/RX引脚去连MLX90614的SDA/SCL,结果发现电平不匹配。MLX90614是3.3V电平,而我的MCU是5V。后来加了电平转换芯片才搞定。所以,接口电平匹配是第一步,千万别忽略。
最后,我总结一下UART在红外探测器中的典型应用场景:
- 调试接口:用UART输出温度数据到PC,方便开发和验证。
- 主控通信:通过UART与主MCU交换数据,比如把温度值传给显示模块或无线模块。
- 固件升级:很多红外探测器支持通过UART进行固件升级,省去拆机烧录的麻烦。
- 多传感器组网:用UART配合RS485,可以实现多个红外探测器挂在同一总线上。
嗯,这一节的内容就到这里。UART看似简单,但用好了能解决很多实际问题。下一节咱们聊聊I2C接口,它在红外探测器中的应用更广泛,也更灵活。
公众号:蓝海资料掘金营,微信deep3321