1. IIC总线通信原理与LM75温度传感器实战
IIC(Inter-Integrated Circuit)总线是飞利浦公司开发的一种简单、双向二线制同步串行总线,广泛应用于嵌入式系统中连接低速外设。作为一名嵌入式开发者,我经常使用IIC与各种传感器交互,今天以LM75数字温度传感器为例,分享IIC通信的完整实现过程。
1.1 IIC总线物理层特性
IIC总线仅需两根信号线:
- SDA(Serial Data):双向数据线
- SCL(Serial Clock):时钟信号线
这两根线都需要通过上拉电阻连接到正电源(通常4.7kΩ),采用开漏输出结构,支持多主多从架构。在我的STM32项目中,实测总线电容超过400pF时通信就会不稳定,这时需要降低上拉电阻值或使用IIC缓冲器。
1.2 IIC协议时序详解
起始条件(START):
当SCL为高电平时,SDA从高到低的跳变。用示波器抓取波形时,这个下降沿必须足够陡峭(通常<1μs),否则从机可能无法识别。
c复制// STM32硬件IIC产生起始条件
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
地址帧格式:
7位从机地址 + 1位读写方向(0-写,1-读)。LM75的默认地址是0x48(二进制1001000),但通过地址引脚可配置为0x48-0x4F。实际发送时需左移一位并添加R/W位:
c复制#define LM75_ADDR 0x48
uint8_t write_addr = LM75_ADDR << 1; // 0x90
uint8_t read_addr = (LM75_ADDR << 1) | 1; // 0x91
数据有效性:
SDA必须在SCL高电平期间保持稳定,变化只能发生在SCL低电平时段。我在调试时曾因GPIO速度设置不当导致SDA变化太慢,引发建立时间违规。
停止条件(STOP):
当SCL为高时,SDA从低到高的跳变。特别注意:连续通信时(如写配置后立即读数据),应该使用重复起始条件(Repeated START)而非STOP+START。
1.3 LM75温度读取实战
LM75内部寄存器包括:
- 温度寄存器(0x00):16位补码格式,精度0.125℃
- 配置寄存器(0x01):设置工作模式
- 温限寄存器(0x02/0x03):设定OS输出阈值
读取温度的标准流程:
- 发送START
- 发送写地址(0x90)
- 发送寄存器指针(0x00)
- 发送重复START
- 发送读地址(0x91)
- 接收两个字节数据
- 发送STOP
c复制uint8_t temp_data[2];
I2C_Write(I2C1, LM75_ADDR, 0x00, 1); // 设置指针
I2C_Read(I2C1, LM75_ADDR, temp_data, 2); // 读取数据
// 数据转换
int16_t raw = (temp_data[0] << 8) | temp_data[1];
float temperature = raw / 256.0f;
注意:LM75的IIC时钟最高支持400kHz,但实际使用时建议从100kHz开始调试。我曾遇到PCB走线过长导致400kHz通信失败的情况,降低频率后问题解决。
2. ADC原理与嵌入式应用技巧
模数转换器(ADC)是将模拟信号转换为数字信号的关键器件,理解其工作原理对设计高精度测量系统至关重要。
2.1 ADC核心三阶段解析
采样阶段:
根据奈奎斯特采样定理,采样频率必须至少是信号最高频率的2倍。对于50Hz工频干扰,理论上100Hz采样即可,但实际中我通常使用1kHz以上采样率,并配合硬件RC滤波(如100Ω+100nF组合)。
量化过程:
12位ADC将0-Vref范围分为4096个等级(LSB)。量化误差固有存在,其最大值为±0.5LSB。例如3.3V基准时:
$$ LSB = \frac{3.3V}{4096} ≈ 0.8mV $$
编码输出:
多数MCU的ADC采用二进制补码或直接二进制格式。STM32的ADC数据通常右对齐,读取时需注意寄存器组合:
c复制uint16_t adc_value = ADC1->DR; // 直接读取数据寄存器
2.2 基准电压选择策略
基准电压源质量直接影响ADC精度,常见方案对比:
| 基准类型 | 精度 | 温漂 | 适用场景 |
|---|---|---|---|
| 电源电压(3.3V) | ±5% | 100ppm/℃ | 普通检测 |
| TL431 | ±0.5% | 50ppm/℃ | 中精度测量 |
| REF5025 | ±0.05% | 3ppm/℃ | 高精度仪器 |
在电池供电项目中,我使用低功耗基准源如REF3033(3.3V/50μA),其初始精度±0.2%足够多数应用。
2.3 软件校准技巧
即使硬件相同,不同MCU的ADC性能也有差异。上电时执行这些校准步骤可提升精度:
- 偏移校准:
c复制ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
-
参考电压校准:
通过已知精确电压(如1.25V基准)反推实际Vref:
$$ V_{ref_real} = \frac{V_{known} \times 4095}{ADC_{read}} $$ -
滑动平均滤波:
c复制#define SAMPLE_NUM 16
uint32_t sum = 0;
for(int i=0; i<SAMPLE_NUM; i++){
sum += ADC_Read();
delay_ms(1);
}
uint16_t result = sum / SAMPLE_NUM;
3. 硬件设计避坑指南
3.1 PCB布局要点
-
IIC走线:
保持SCL/SDA等长,远离高频信号线。我的经验法则是:走线长度(cm)< 300/频率(MHz)。对于400kHz IIC,走线最好不超过75cm。 -
ADC信号链:
模拟输入前应添加π型滤波器(如100Ω+100nF+100nF),布局时遵循:
传感器 → 滤波 → 保护电路(如TVS) → ADC输入
3.2 抗干扰设计
-
电源去耦:
每个IC的VDD引脚就近放置100nF+10μF组合电容。对于高精度ADC,额外并联1μF钽电容。 -
接地策略:
混合信号系统采用"分地-单点连接"方案。我的典型做法:
code复制模拟地 → 10Ω电阻 → 数字地
↓
电源地
3.3 典型问题排查
IIC通信失败:
- 用逻辑分析仪确认起始条件波形
- 检查上拉电阻值(4.7kΩ@3.3V,2.2kΩ@5V)
- 测量总线电容(应<400pF)
ADC读数跳动:
- 检查参考电压纹波(最好<10mVpp)
- 确认输入信号阻抗(应<10kΩ)
- 尝试注入延迟:采样时间=周期数×(1/ADC时钟)
4. 进阶应用实例
4.1 多传感器协同采样
通过DMA实现IIC+ADC同步数据采集:
c复制// 配置IIC DMA(LM75)
I2C_DMACmd(I2C1, I2C_DMAReq_Rx, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&I2C1->DR;
// 配置ADC DMA
ADC_DMACmd(ADC1, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
// 触发同步
EXTI_TriggerCmd(EXTI_Line0, ENABLE);
4.2 低功耗设计
动态调整外设时钟:当温度变化<0.5℃/分钟时,将LM75采样率从1Hz降至0.1Hz,ADC从连续模式切为单次模式。实测可使系统功耗降低60%。
c复制void Enter_LowPowerMode(void){
// 降低IIC速度
I2C_InitStructure.I2C_ClockSpeed = 10000; // 10kHz
I2C_Init(I2C1, &I2C_InitStructure);
// 配置ADC间断模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
}
在最近的一个物联网项目中,这套方案使CR2032纽扣电池的续航从3个月延长到了8个月。关键点是合理设置状态检测阈值,避免频繁唤醒。
