1. STM32与AD7175-2的SPI接口驱动设计
在嵌入式数据采集系统中,高精度ADC是不可或缺的核心部件。AD7175-2作为ADI公司推出的24位Σ-Δ型ADC,具有最高250kSPS采样率和±0.0015%的非线性误差,非常适合工业测量应用。本文将详细介绍基于STM32的完整驱动实现方案。
1.1 硬件架构设计要点
AD7175-2与STM32的硬件连接需要特别注意信号完整性问题。在电路设计阶段,我们采用以下方案:
- 电源部分:模拟电源(AVDD)和数字电源(DVDD)分别采用独立的LDO供电,并在靠近芯片引脚处放置10μF钽电容与0.1μF陶瓷电容并联,有效抑制高频噪声
- 信号走线:SPI时钟线(SCK)与数据线(MISO/MOSI)保持等长走线,长度差控制在5mm以内,避免时序偏移
- 接地策略:采用星型接地拓扑,AD7175的AGND和DGND在芯片下方单点连接,再汇入系统接地点
关键提示:DRDY信号线需配置上拉电阻(典型值4.7kΩ),避免浮空状态导致误触发。在STM32端应设置为上拉输入模式。
1.2 SPI接口配置细节
AD7175-2支持SPI模式0和模式3,本方案选择模式3(CPOL=1, CPHA=1)以匹配芯片的时序特性。具体配置参数如下:
| 参数项 | 配置值 | 技术依据 |
|---|---|---|
| 时钟极性(CPOL) | 1(空闲时为高电平) | AD7175数据手册图29时序要求 |
| 时钟相位(CPHA) | 1(第二个边沿采样) | 确保建立保持时间满足要求 |
| 时钟频率 | ≤10MHz | AD7175最大SCK频率限制 |
| 数据位序 | MSB优先 | 芯片寄存器默认配置 |
| NSS模式 | 软件控制 | 灵活控制片选时序 |
在STM32F103上,通过SPI_InitTypeDef结构体实现上述配置:
c复制SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // CPOL=1
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // CPHA=1
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 9MHz@72MHz
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
2. 寄存器配置与通道管理
2.1 关键寄存器设置解析
AD7175-2通过寄存器配置实现工作模式控制,主要寄存器包括:
模式寄存器(0x02)配置:
c复制#define AD7175_REG_MODE 0x02
#define AD7175_250KSPS 0x00
AD7175_WriteReg(AD7175_REG_MODE, AD7175_250KSPS | 0x000008);
- 位[2:0]:采样率选择,000对应250kSPS
- 位3:单周期转换使能
- 位4:内部时钟模式
- 位[23:5]:保留位需保持默认值
配置寄存器(0x03)参数:
c复制AD7175_WriteReg(AD7175_REG_CONFIG, 0x000001);
- 位0:REFEN使能内部基准缓冲
- 位1:BUFEN使能输入缓冲器
- 位[4:2]:PGA增益设置(000=增益1)
2.2 多通道切换策略
AD7175-2支持4路差分输入,通道切换需要遵循特定时序:
- 禁用当前通道:向通道寄存器写入0x000000
- 配置新通道:设置AIN+和AIN-输入对及增益
- 等待建立时间:至少需要3个转换周期(12μs@250kSPS)
c复制void AD7175_SwitchChannel(AD7175_ChannelDef ch) {
// 禁用所有通道
AD7175_WriteReg(AD7175_REG_CH0, 0x000000);
AD7175_WriteReg(AD7175_REG_CH1, 0x000000);
AD7175_WriteReg(AD7175_REG_CH2, 0x000000);
AD7175_WriteReg(AD7175_REG_CH3, 0x000000);
// 配置目标通道
uint32_t ch_cfg = 0x000101; // AINx+/AINy-, 增益=1, 启用
switch(ch) {
case AD7175_CH0: ch_cfg = 0x000101; break; // AIN0+/AIN1-
case AD7175_CH1: ch_cfg = 0x000201; break; // AIN2+/AIN3-
case AD7175_CH2: ch_cfg = 0x000301; break; // AIN4+/AIN5-
case AD7175_CH3: ch_cfg = 0x000401; break; // AIN6+/AIN7-
}
AD7175_WriteReg(AD7175_REG_CH0 + ch, ch_cfg);
// 添加延时确保建立时间
delay_us(15); // 大于12μs的最小要求
}
3. 数据采集与校准实现
3.1 DRDY中断触发机制
AD7175-2通过DRDY引脚指示数据就绪状态,本设计采用外部中断方式检测:
c复制// EXTI1中断初始化
void DRDY_EXTI_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
EXTI_InitStruct.EXTI_Line = EXTI_Line1;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
3.2 双参数校准算法
为实现高精度测量,采用偏移(offset)和增益(gain)双参数校准:
c复制void AD7175_Calibration(AD7175_ChannelDef ch, float ref_vol, float raw_data) {
// 偏移校准:零点修正
AD7175_Calib.offset[ch] = - (raw_data - (ref_vol / 2.5 * 0xFFFFFF));
// 增益校准:满量程修正
AD7175_Calib.gain[ch] = ref_vol / (raw_data + AD7175_Calib.offset[ch])
* 0xFFFFFF / 2.5;
}
void AD7175_DataConvert(void) {
float v_ref = 2.5f; // 内部基准电压
for(uint8_t i=0; i<4; i++) {
AD7175_CalibData[i] = (AD7175_RawData[i] + AD7175_Calib.offset[i])
* AD7175_Calib.gain[i] * v_ref / 0x1000000;
}
}
校准步骤:
- 短接输入引脚,读取零点原始值
- 输入已知基准电压(如2.0V),读取满量程原始值
- 调用AD7175_Calibration计算校准系数
- 后续采集时自动应用校准参数
4. 系统优化与问题排查
4.1 常见问题解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 读取ID不正确 | SPI时序不匹配 | 检查CPOL/CPHA设置 |
| DRDY无中断触发 | 上拉电阻未启用 | 配置GPIO为上拉输入模式 |
| 数据跳动大 | 电源噪声干扰 | 增加电源滤波电容 |
| 通道切换后数据异常 | 建立时间不足 | 增加切换后的延时(>12μs) |
| 线性度差 | 未校准或基准源不准 | 使用高精度基准源重新校准 |
4.2 性能优化技巧
- DMA传输优化:
c复制// 配置SPI DMA接收
DMA_InitTypeDef DMA_InitStruct;
DMA_DeInit(DMA1_Channel4);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = 3; // 24位数据
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStruct);
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
- 低噪声布线原则:
- 模拟信号走线远离数字信号线
- 采用地平面分割技术
- 敏感信号使用屏蔽线缆
- 软件滤波算法:
c复制#define FILTER_LEN 8
float moving_avg_filter(float new_val) {
static float buf[FILTER_LEN] = {0};
static uint8_t idx = 0;
float sum = 0;
buf[idx++] = new_val;
if(idx >= FILTER_LEN) idx = 0;
for(uint8_t i=0; i<FILTER_LEN; i++) {
sum += buf[i];
}
return sum / FILTER_LEN;
}
在实际项目中,我发现AD7175-2的温度漂移问题需要特别关注。建议在环境温度变化较大的场合,每隔2小时重新校准一次零点,或者采用温度传感器进行实时补偿。另外,当采样率低于10kSPS时,可以启用芯片的内部斩波功能,能有效降低1/f噪声的影响。