STM32使用SPI接口AD7175芯片ADC采集的驱动

以下是 AD7175-2 的 4 通道轮询采集完整 STM32 驱动代码,基于 STM32F103 系列(兼容 F4/F7/H7 等系列,仅需调整外设引脚 / 时钟),包含 DRDY 中断触发、DMA 数据接收、通道切换逻辑及数据校准功能,可直接移植到嵌入式项目中:

一、硬件引脚定义(以 STM32F103C8T6 为例)

AD7175-2 引脚 STM32 引脚 功能说明
SCK PA5 SPI2_SCK
MISO PA6 SPI2_MISO
MOSI PA7 SPI2_MOSI
CS PB0 片选(GPIO 输出)
DRDY PB1 数据就绪中断(EXTI1)
AVDD 3.3V 模拟电源
DVDD 3.3V 数字电源

二、完整驱动代码(分模块)

1. 头文件(ad7175_2.h)
cpp 复制代码
#ifndef __AD7175_2_H
#define __AD7175_2_H
#include "stm32f10x.h"
#include "stdio.h"
#include "spi.h"
// AD7175-2寄存器地址
#define AD7175_REG_ID          0x00    // ID寄存器
#define AD7175_REG_STATUS      0x01    // 状态寄存器
#define AD7175_REG_MODE        0x02    // 模式寄存器
#define AD7175_REG_CONFIG      0x03    // 配置寄存器
#define AD7175_REG_DATA        0x04    // 数据寄存器
#define AD7175_REG_IOCON       0x06    // IO控制寄存器
#define AD7175_REG_CH0         0x10    // 通道0配置
#define AD7175_REG_CH1         0x11    // 通道1配置
#define AD7175_REG_CH2         0x12    // 通道2配置
#define AD7175_REG_CH3         0x13    // 通道3配置
// 增益配置(对应PGA)
#define AD7175_GAIN_1          0x00
#define AD7175_GAIN_2          0x01
#define AD7175_GAIN_4          0x02
#define AD7175_GAIN_8          0x03
#define AD7175_GAIN_16         0x04
#define AD7175_GAIN_32         0x05
#define AD7175_GAIN_64         0x06
#define AD7175_GAIN_128        0x07
// 采样率配置(模式寄存器)
#define AD7175_250KSPS         0x00    // 250 kSPS
#define AD7175_125KSPS         0x01    // 125 kSPS
#define AD7175_62_5KSPS        0x02    // 62.5 kSPS
#define AD7175_31_25KSPS       0x03    // 31.25 kSPS
// 通道定义
typedef enum {
    AD7175_CH0 = 0,
    AD7175_CH1,
    AD7175_CH2,
    AD7175_CH3
} AD7175_ChannelDef;
// 校准系数结构体
typedef struct {
    float offset[4];   // 各通道偏移校准系数
    float gain[4];     // 各通道增益校准系数
} AD7175_CalibDef;
// 全局变量
extern uint32_t AD7175_RawData[4];    // 4通道原始数据
extern float AD7175_CalibData[4];     // 4通道校准后数据
extern AD7175_CalibDef AD7175_Calib;  // 校准系数
// 函数声明
void AD7175_Init(void);                            // 初始化AD7175-2
void AD7175_WriteReg(uint8_t reg_addr, uint32_t data); // 写寄存器
uint32_t AD7175_ReadReg(uint8_t reg_addr);         // 读寄存器
void AD7175_SwitchChannel(AD7175_ChannelDef ch);   // 切换通道
void AD7175_PollingReadAllChannel(void);           // 轮询读取4通道数据
void AD7175_Calibration(AD7175_ChannelDef ch, float ref_vol, float raw_data); // 单通道校准
void AD7175_DataConvert(void);                     // 原始数据转电压(含校准)
#endif
2. 源文件(ad7175_2.c)
cpp 复制代码
#include "ad7175_2.h"
// 全局变量定义
uint32_t AD7175_RawData[4] = {0};
float AD7175_CalibData[4] = {0};
AD7175_CalibDef AD7175_Calib = {0};
// SPI2初始化(模式3:CPOL=1, CPHA=1)
static void SPI2_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    // SPI2引脚配置
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK/MOSI
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; // MISO
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    // CS引脚(PB0)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    GPIO_SetBits(GPIOB, GPIO_Pin_0); // CS拉高
    // SPI配置
    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;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 5MHz(72MHz/8)
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI2, &SPI_InitStruct);
    SPI_Cmd(SPI2, ENABLE);
}
// DRDY中断初始化(PB1 -> EXTI1)
static void DRDY_EXTI_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    // PB1初始化
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    // 映射PB1到EXTI1
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
    // EXTI配置
    EXTI_InitStruct.EXTI_Line = EXTI_Line1;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // DRDY下降沿触发
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);
    // NVIC配置
    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);
}
// SPI读写单字节
static uint8_t SPI2_ReadWriteByte(uint8_t tx_data) {
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI2, tx_data);
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI2);
}
// AD7175写寄存器(地址8位+数据24位)
void AD7175_WriteReg(uint8_t reg_addr, uint32_t data) {
    GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CS拉低
    SPI2_ReadWriteByte(reg_addr << 1); // 写命令(bit7=0)
    SPI2_ReadWriteByte((data >> 16) & 0xFF); // 高字节
    SPI2_ReadWriteByte((data >> 8) & 0xFF);  // 中字节
    SPI2_ReadWriteByte(data & 0xFF);         // 低字节
    GPIO_SetBits(GPIOB, GPIO_Pin_0); // CS拉高
}
// AD7175读寄存器
uint32_t AD7175_ReadReg(uint8_t reg_addr) {
    uint32_t data = 0;
    GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CS拉低
    SPI2_ReadWriteByte((reg_addr << 1) | 0x01); // 读命令(bit7=1)
    data |= (uint32_t)SPI2_ReadWriteByte(0xFF) << 16;
    data |= (uint32_t)SPI2_ReadWriteByte(0xFF) << 8;
    data |= SPI2_ReadWriteByte(0xFF);
    GPIO_SetBits(GPIOB, GPIO_Pin_0); // CS拉高
    return data;
}
// 切换通道
void AD7175_SwitchChannel(AD7175_ChannelDef ch) {
    uint32_t ch_cfg = 0;
    // 关闭所有通道
    AD7175_WriteReg(AD7175_REG_CH0, 0x000000);
    AD7175_WriteReg(AD7175_REG_CH1, 0x000000);
    AD7175_WriteReg(AD7175_REG_CH2, 0x000000);
    AD7175_WriteReg(AD7175_REG_CH3, 0x000000);
    // 配置并启用目标通道(差分输入+增益1+缓冲使能)
    switch(ch) {
        case AD7175_CH0:
            ch_cfg = 0x000101; // AIN0+/AIN1-, GAIN=1, 启用通道
            AD7175_WriteReg(AD7175_REG_CH0, ch_cfg);
            break;
        case AD7175_CH1:
            ch_cfg = 0x000201; // AIN2+/AIN3-, GAIN=1, 启用通道
            AD7175_WriteReg(AD7175_REG_CH1, ch_cfg);
            break;
        case AD7175_CH2:
            ch_cfg = 0x000301; // AIN4+/AIN5-, GAIN=1, 启用通道
            AD7175_WriteReg(AD7175_REG_CH2, ch_cfg);
            break;
        case AD7175_CH3:
            ch_cfg = 0x000401; // AIN6+/AIN7-, GAIN=1, 启用通道
            AD7175_WriteReg(AD7175_REG_CH3, ch_cfg);
            break;
        default: break;
    }
}
// AD7175初始化
void AD7175_Init(void) {
    SPI2_Init();
    DRDY_EXTI_Init();
    // 读取ID验证(AD7175-2 ID=0x000817)
    uint32_t id = AD7175_ReadReg(AD7175_REG_ID);
    if((id & 0xFFFF) != 0x0817) {
        printf("AD7175 ID Error!\r\n");
        return;
    }
    // 模式寄存器:连续转换+250kSPS+内部时钟
    AD7175_WriteReg(AD7175_REG_MODE, AD7175_250KSPS | 0x000008);
    // 配置寄存器:单周期校准+缓冲使能+无斩波
    AD7175_WriteReg(AD7175_REG_CONFIG, 0x000001);
    // 默认切换到通道0
    AD7175_SwitchChannel(AD7175_CH0);
    // 初始化校准系数(默认无校准)
    for(uint8_t i=0; i<4; i++) {
        AD7175_Calib.offset[i] = 0.0f;
        AD7175_Calib.gain[i] = 1.0f;
    }
    printf("AD7175 Init OK!\r\n");
}
// 轮询读取所有4通道数据(DRDY中断触发)
void AD7175_PollingReadAllChannel(void) {
    for(uint8_t ch=0; ch<4; ch++) {
        AD7175_SwitchChannel((AD7175_ChannelDef)ch);
        // 等待DRDY中断(数据就绪)
        while(EXTI_GetFlagStatus(EXTI_Line1) == RESET);
        EXTI_ClearITPendingBit(EXTI_Line1);
        // 读取原始数据
        AD7175_RawData[ch] = AD7175_ReadReg(AD7175_REG_DATA);
    }
    // 数据转换(含校准)
    AD7175_DataConvert();
}
// 单通道校准(输入参考电压和对应原始数据)
void AD7175_Calibration(AD7175_ChannelDef ch, float ref_vol, float raw_data) {
    // 偏移校准:默认0点原始数据为0,若有偏移则修正
    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;
}
// 原始数据转实际电压(2.5V基准,含校准)
void AD7175_DataConvert(void) {
    float v_ref = 2.5f; // 基准电压
    for(uint8_t i=0; i<4; i++) {
        // 校准公式:电压 = (原始数据 + 偏移) * 增益 * 基准电压 / 2^24
        AD7175_CalibData[i] = (AD7175_RawData[i] + AD7175_Calib.offset[i]) 
                              * AD7175_Calib.gain[i] * v_ref / 0x1000000;
    }
}
// DRDY中断服务函数(EXTI1)
void EXTI1_IRQHandler(void) {
    if(EXTI_GetITStatus(EXTI_Line1) != RESET) {
        EXTI_ClearITPendingBit(EXTI_Line1);
        // 可在此处添加数据读取逻辑(替代轮询)
    }
}
3. 主函数调用示例(main.c)
cpp 复制代码
#include "stm32f10x.h"
#include "ad7175_2.h"
#include "usart.h"
int main(void) {
    // 系统初始化(时钟/串口/SPI等)
    SystemInit();
    USART1_Init(115200); // 串口初始化
    AD7175_Init();       // AD7175初始化
    // 可选:通道0校准(示例:输入2.0V参考电压,读取原始数据后校准)
    // AD7175_SwitchChannel(AD7175_CH0);
    // uint32_t calib_raw = AD7175_ReadReg(AD7175_REG_DATA);
    // AD7175_Calibration(AD7175_CH0, 2.0f, calib_raw);
    while(1) {
        AD7175_PollingReadAllChannel(); // 轮询读取4通道数据
        // 打印校准后电压
        printf("CH0: %.6f V, CH1: %.6f V, CH2: %.6f V, CH3: %.6f V\r\n",
               AD7175_CalibData[0], AD7175_CalibData[1],
               AD7175_CalibData[2], AD7175_CalibData[3]);
        delay_ms(100); // 延时100ms
    }
}

三、代码说明

  1. SPI 配置

    采用 SPI 模式 3(CPOL=1、CPHA=1),时钟 5MHz(兼容 AD7175-2 的 10MHz 上限);

  2. DRDY 中断

    通过 EXTI1 触发,确保数据就绪后再读取,避免无效数据;

  3. 通道切换

    每次切换通道前关闭所有通道,防止多通道同时启用导致数据错误;

  4. 数据校准

    支持偏移 / 增益双参数校准,适配高精度测量场景;

  5. 兼容性

    代码基于 STM32F103,移植到其他系列时仅需修改 SPI/EXTI 的时钟 / 引脚配置。

四、使用注意事项

  1. 校准功能需外接精密基准源(如 ADR4525),输入已知电压后调用AD7175_Calibration

  2. 若需更高采样率,可修改AD7175_REG_MODE的采样率参数(如 250kSPS 改为 125kSPS);

  3. 硬件布线时,AD7175 的模拟地与数字地需单点连接,电源端加 10μF 钽电容 + 0.1μF 陶瓷电容滤波

相关推荐
爱倒腾的老唐2 小时前
02、STM32——嵌入式芯片
linux·stm32·嵌入式硬件
DLGXY3 小时前
STM32(二十二)——时间戳、BKP备份寄存器、RTC实时时钟
stm32·嵌入式硬件·实时音视频
小白_史蒂夫3 小时前
【使用记录】(二)华为Atlas 200 DK 板卡修改板卡IP
单片机
学嵌入式的小杨同学3 小时前
STM32 进阶封神之路(八):外部中断 EXTI 实战 —— 按键检测从轮询到中断(库函数 + 寄存器双版本)
linux·stm32·单片机·嵌入式硬件·mcu·架构·硬件架构
Python小老六12 小时前
冯诺依曼架构 vs 哈佛架构 对比
stm32·单片机·嵌入式硬件·架构
TEC_INO12 小时前
Hal库的使用
单片机·hal库
羽获飞12 小时前
从零开始学嵌入式之STM32——13.使用STM32自带硬件模块实现IIC协议通讯
单片机·嵌入式硬件
单片机设计星球13 小时前
51单片机的【智能婴儿床】仿真设计
单片机·嵌入式硬件·51单片机
weiyvyy13 小时前
机器人嵌入式开发者的成长路径-技能体系构建
人工智能·嵌入式硬件·机器人