Kreos

导航

ADS1248高精度ADC軟件開發指南

ADS1248/1247(TI) 24位ADC详细配置说明

本文详细介绍STM32L433单片机如何与ADS1248/1247 ADC进行交互,涉及寄存器配置、数据读写、线性度校验和应用电路,包括MUX、SYS、FSC等关键寄存器的详解及注意事项。

ADS1248是TI的一款 24位delta-sigma(ΔΣ) 、2KSPS、8通道(4通道差分)ADC芯片,通讯协议为SPI。可编程数据速率高达2ksps。低噪声PGA:48nVRMS在PGA=128。低漂移内部2.048-V参考值:10ppm/°C(最大值)。模拟电源:单极(2.7V至5.25V)和双极(±2.5V)工作。


ADS1248/1247寄存器讲解

提示:在配置寄存器前,默认已经配置好了硬件SPI,SPI的配置为主模式、全双工、数据位8位、CPOL = 0、CPHA为数据线的第一个变化沿、软件控制NSS、256分频、最高位先发送、TIMODE模式关闭、CRC关闭。作者使用的是STM32L433单片机,使用HAL库来编写,本文将详细阐述ADS1248各个寄存器以及注意事项。

ADS1248/1247一共有15个寄存器,寄存器列表如下:

image

 


提示:图上可以看到,寄存器的地址为0x00 - 0x0e,每个寄存器都是一个字节八位分别去配置ADC的功能。

  1. MUX0寄存器:此寄存器的[7:6]位为BSC位,此位用于配置输入电流的限位值,手册上说是烧毁检测电流源的设置,一般设置为00,[5:3]位为MUX_SP位,此位用于设置ADC的输入正通道,000-111分别对应AIN0-AIN7,默认为AIN0。[2:0]位为输入的负通道,此位也是000-111对应AIN0-AIN7通道。
  2. VBIAS寄存器:此寄存器用于配置ADC在通道上施加的偏置电压,偏置电压计算是 (AVDD + AVSS) / 2,默认此位不做配置
  3. MUX1寄存器:7位为CLKSTAT位,此位为只读,用于读取当前ADC使用的是外部还是内部振荡器,0为内部,1为外部。[6:5]此位用于是否打开内部参考,如果使用内部基准,此位需要打开设置为01.[4:3]此位用于选择内部引用还是外部引用,我们用的是内部,所以设置为10.[2:0]此位为一个监视器,用于将输入端在内部直接连接内部基准源或者温度测量单元上,我们没有使用监视器,设置为000.
  4. SYS0寄存器(此位非常重要!):[7]此位必须始终设置为0,手册上这样说,[6:4]此位用于设置内部增益PGA,000-111分别对应放大倍数1、2、4、8、16、32、64、128倍。[3:0]此位用于设置ADC的输出速率,0000-1000分别对应5、10、20、40、80、160、320、640、1000SPS,1001-1111对应2000SPS,一般我们如果要输出速率为2000SPS时设置为1111,当然1001也是可以的。
  5. OFC1/OFC1/OFC2寄存器:三个寄存器组成了ADC24位偏移校准字。24位字是两种补体格式,内部向左移以与ADC24位转换结果对齐。ADC在全比例操作前从转换结果中减去寄存器值。默认此位不做配置
  6. FSC0/FSC1/FSC2寄存器:三个寄存器组成了ADC24位全标度校准字。这个24位的字是直接的二进制字。ADC将寄存器值除以FSC寄存器400000h,得到校准的比例因子。在偏移校准后,ADC将比例因子乘以转换结果。当PGA设置改变时,经过后的FSC重置值自动加载。此位需要配置为0x400000,此位需要注意,要记得配置
  7. IDAC0寄存器:[7:4]此位只读,TI编程的位,用于版本标识。[3]此位用于设置DRDY/DOUT仅作为输出功能或时作为数据准备和数据输出功能。[2:0]此位用于使能两个励磁电流源(IDACs),可用于传感器激励,默认此位不做配置
  8. IDAC1寄存器:[7:4]此位为选择了第一励磁电流源的输出引脚,[3:0]此位为选择了第二励磁电流源的输出引脚,默认此位不做配置
  9. GPIOCFG/GPIODIR/GPIODAT寄存器:用于IO口扩展,因为此器件时SPI传输,可以通过单片机配置寄存器直接操作ADC上的IO口,用作IO口的扩展,默认此位不做配置

ADS1248/1247程序编写

image

 

写寄存器:

在这里插入图片描述
提示:手册说的很清楚,第一个命令字节:0100 rrrr,其中rrrr是要写入的第一个寄存器的地址。第二个命令字节:0000 nnnn,其中nnnn是要写入的字节数-1。字节(s):要写入寄存器的数据。
所以我们按照手册的方法来写:

static void ADS1248_WriteReg(uint8_t RegAddr,uint8_t *Buffer,uint8_t Length)
{   
    uint8_t Cmd[2];  
    ADS1248_CLR_CS;
    ADS1248_SET_START;
    HAL_Delay(20);
    /*ADS1248芯片手册规定的发送数据格式*/
    /*First Command Byte: 0100 rrrr
      where rrrr is the address of the first register to be written.*/
    Cmd[0] = ADS1248_CMD_WREG | (RegAddr & 0x0F);  
    /*Second Command Byte: 0000 nnnn, 
      where nnnn is the number of bytes to be written – 1*/
    Cmd[1] = (Length - 1) & 0x0F;
    /*指定向指定寄存器写入指定字节数据*/
    HAL_SPI_Transmit(&hspi3, Cmd, 2,HAL_MAX_DELAY);
    /*Byte(s): data to be written to the registers.*/
    HAL_SPI_Transmit(&hspi3, Buffer, Length,HAL_MAX_DELAY);
    HAL_Delay(20);
    ADS1248_SET_CS;
    ADS1248_CLR_START;
}

 c运行

我们先定义一个cmd的数组,然后将CS片选拉低,START置高,然后进行数据的拼接,在第一个字节里发送0x04然后将要写的寄存器进行或操作,这样就完成了手册中说的前四个位是写命令,后四个位是要写的寄存器,后面跟着要给这个寄存器写的数据。

读寄存器:

在这里插入图片描述
提示:还是按照手册来,先进行数据拼接然后读出要的数据,这里TI给出的是:在读取寄存器数据时,不可能使用串行接口的全双工特性。例如,在读取VBIAS和MUX1数据时,不能发出SYNC命令,如图84所示。在读出寄存器数据期间发送的任何命令都将被忽略。因此,TI建议在读出寄存器数据时通过DIN发送nop。

void ADS1248_ReadReg(uint8_t RegAddr,uint8_t *Buffer,uint8_t Length)
{
    uint8_t Cmd[2];
    ADS1248_CLR_CS;
    ADS1248_SET_START;
    /*ADS1248芯片手册规定的发送数据格式*/
    /*First Command Byte: 0010 rrrr, 
      where rrrr is the address of the first register to read.*/
    Cmd[0] = ADS1248_CMD_RREG | (RegAddr & 0x0f);  
    /*Second Command Byte: 0000 nnnn, 
      where nnnn is the number of bytes to read –1*/
    Cmd[1] = (Length - 1) & 0x0f;
    /*发送读取指令*/
    HAL_SPI_Transmit(&hspi3,Cmd,2,HAL_MAX_DELAY);    
    /*接收数据*/    
    HAL_SPI_Receive(&hspi3, Buffer, Length, HAL_MAX_DELAY);        
    Cmd[0] = ADS1248_CMD_NOP;  
    /*接受数据时不能使用SPI的全双工,要发送nop,此为手册原句*/
    /*It is not possible to use the full-duplex nature of the 
      serial interface when reading out the register data. For
      example, a SYNC command cannot be issued when reading out the 
      VBIAS and MUX1 data, as shown in
      Figure 84. Any command sent during the readout of the register
      data is ignored. Thus, TI recommends sending
      NOPs through DIN when reading out the register data.*/
    HAL_SPI_Transmit(&hspi3, Cmd,1,HAL_MAX_DELAY); 
    HAL_Delay(20);
    ADS1248_SET_CS;
}

 

c运行

还是和写寄存器一样,先进行指令的拼接,然后通过SPI直接将数据读出来,读数据的时候持续发送nop,因为TI说了在读数据的时候不能进行写操作,否则命令将会被忽略。

配置ADS1248/1247寄存器:

    uint8_t Cmd = 0; 
    /*对ADC进行复位*/
    ADS1248_Reset();                                                                           
    HAL_Delay(20);          
    /*配置MUX0寄存器AIN0为正输入,AIN1为负输入*/
    Cmd = 0x08;/*0000 0001*/                    
    ADS1248_WriteReg(ADS1248_MUX0,&Cmd,1);      
    /*配置MUX1使用内部2.048基准、打开内部基准源*/
    Cmd=0x30 ;/*0 01 00 000*/
    ADS1248_WriteReg(ADS1248_MUX1,&Cmd,1);                                                                                                            
    /*配置SYS0寄存器PGA为放大64倍,输出速率为20SPS*/
    Cmd=0x62;/*0 110 0010*/
    ADS1248_WriteReg(ADS1248_SYS0,&Cmd,1); 
    /*配置IDAC0不使能DRDY、关闭激励*/
    Cmd=0x00 ;/*0000 0000*/
    ADS1248_WriteReg(ADS1248_IDAC0,&Cmd,1);                   
    /*配置IDAC1寄存器不选择励磁电流源引脚、不使能引脚*/
    Cmd=0xFF;/*1111 1111*/    
    ADS1248_WriteReg(ADS1248_IDAC1,&Cmd,1);           
    /*配置FSC0为00*/
    Cmd=0x00;/*0010 0000*/        
    ADS1248_WriteReg(ADS1248_FSC0,&Cmd,1); 
    /*配置FSC1为00*/
    Cmd=0x00;/*0000 0000*/        
    ADS1248_WriteReg(ADS1248_FSC1,&Cmd,1); 
    /*配置FSC2为40,在公式中需要除以常数0x400000*/
    Cmd=0x40;/*0000 0100*/        
    ADS1248_WriteReg(ADS1248_FSC2,&Cmd,1);     
    HAL_Delay(20);     
    /*拉高START准备ADC转换*/ 
    ADS1248_CLR_START;

 

c运行

上面的代码是上电对ADS1248/1247进行一个寄存器的配置,配置完之后选择持续转换,这样就可以一直获取到数据了。

读数据:

提示:因为此ADC是24位的ADC,最高位是符号位,所以有23位的分辨率,最大值是8388608,24位的数据我们用数据进行移位拼接,将最高位左移16位,第二位左移8位,最后一个直接获取,之后将数据相加即可得到我们要的最终数据,手册上写了内置滤波器,所以我们不需要在用软件滤波,直接读取数字量即可。

int32_t ADS1248_Read(void)
{
    /*定义发送的读取指令,后面三个是空操作等待ADC相应*/
    uint8_t  Cmd[4]={ADS1248_CMD_RDATAC,ADS1248_CMD_NOP,ADS1248_CMD_NOP,ADS1248_CMD_NOP};
    int32_t  i32Data = 0;
    int32_t  i32Data0 = 0;
    int32_t  i32Data1 = 0;
    int32_t  i32Data2 = 0;
    uint8_t  Buf[4] = {0};
    /*开启转换*/    
    ADS1248_SET_START;
    /*等待转换结束*/
    if(ADS1248_Wait() == 0)
    {
        ADS1248_CLR_CS;
        /*读取ADC数据保存在BUF中*/
        HAL_SPI_TransmitReceive(&hspi3,Cmd,Buf,4,HAL_MAX_DELAY);         
        ADS1248_SET_CS;        
        ADS1248_CLR_START;    
        /*24位ADC数据进行拼接*/  
        i32Data0 = Buf[0];
        i32Data0 = i32Data0 << 16;
        i32Data1 = Buf[1];
        i32Data1 = i32Data1 << 8;        
        i32Data2 = Buf[2];     
        i32Data = i32Data0 + i32Data1 + i32Data2;
     //i32Data=Buf[0]<<16|Buf[1]<<8|Buf[2]; //不直觀
        return i32Data;        
    }
    else
    {
        return 0;
    }
}

 

c运行

开启转换

开启转换很简单,只需要将START引脚拉高即可,关闭转换也只需要把START引脚拉低即可。

GPIO初始化

这部分不用说了,凡是看这篇文章的肯定都会,只是配置IO推挽输出和输入,DRDY为输入,CS和START为推挽输出,可以根据需要修改IO的宏定义。

void Ads1248Init(void)
{
    /*初始化各个引脚*/
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = ADS1248_CS_PIN | ADS1248_START_PIN | ADS1248_RST_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    

    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = ADS1248_DRDY_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); 
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}
    }

 

c运行

H文件的定义如下:

/*RST复位引脚*/
#define ADS1248_RST_PIN         GPIO_PIN_9
#define ADS1248_RST_GPIO        GPIOA
#define ADS1248_SET_RESET        HAL_GPIO_WritePin(ADS1248_RST_GPIO, ADS1248_RST_PIN, GPIO_PIN_SET)
#define ADS1248_CLR_RESET        HAL_GPIO_WritePin(ADS1248_RST_GPIO, ADS1248_RST_PIN, GPIO_PIN_RESET)
/*开始使能引脚*/
#define ADS1248_START_PIN       GPIO_PIN_10
#define ADS1248_START_GPIO      GPIOA
#define ADS1248_SET_START        HAL_GPIO_WritePin(ADS1248_START_GPIO, ADS1248_START_PIN, GPIO_PIN_SET)
#define ADS1248_CLR_START        HAL_GPIO_WritePin(ADS1248_START_GPIO, ADS1248_START_PIN, GPIO_PIN_RESET)
/*DRDY引脚*/
#define ADS1248_DRDY_PIN        GPIO_PIN_11
#define ADS1248_DRDY_GPIO       GPIOA
#define ADS1248_DRDY_READY        HAL_GPIO_ReadPin(ADS1248_DRDY_GPIO,ADS1248_DRDY_PIN) 
/*CS片选引脚*/
#define ADS1248_CS_PIN          GPIO_PIN_15
#define ADS1248_CS_GPIO         GPIOA
#define ADS1248_SET_CS            HAL_GPIO_WritePin(ADS1248_CS_GPIO, ADS1248_CS_PIN, GPIO_PIN_SET)
#define ADS1248_CLR_CS            HAL_GPIO_WritePin(ADS1248_CS_GPIO, ADS1248_CS_PIN, GPIO_PIN_RESET)

 

c运行

注意事项

在这里插入图片描述
手册写的很清楚,差分输入的电压正负都需要计算,差模输入,意味着负输入端不可直接接地,接地会引发不可预知的错误。我遇到的错误是AD值不准,PGA = 16以上会引发线性度畸变。

下面贴出实测的线性度和数据

在这里插入图片描述

应用电路

在这里插入图片描述

 

image

 


 


基于SPI的ADS1248高精度ADC驱动程序设计与实现

menu-r.4af5f7ec.gif

简介:ADS1248是一款高精度、低功耗的16位模数转换器,广泛应用于传感器接口、工业自动化和医疗设备等领域。本项目提供了一套完整的C语言驱动程序(包含.c和.h文件),实现了通过SPI通信协议对ADS1248的全面控制。驱动涵盖初始化、读写操作、PGA增益设置、数字滤波器配置及ADC转换控制等核心功能,便于快速集成到嵌入式系统中,显著提升开发效率。该驱动适用于各类需要高精度模拟信号采集的应用场景。
ADS1248-Drivers-master_spi_ads1248_

1. ADS1248芯片功能与应用场景介绍

核心架构与功能特性

ADS1248是一款高精度、低噪声的24位Δ-Σ ADC,集成可编程增益放大器(PGA)、多路复用器(MUX)、数字滤波器及内部基准源,支持8通道差分输入。其有效分辨率可达21位以上,典型噪声密度为7 nV/√Hz,适用于微伏级信号采集。

典型应用场景

广泛应用于RTD测温、热电偶冷端补偿、称重传感器信号调理等工业精密测量领域。例如,在PLC模拟输入模块中,ADS1248通过高共模抑制比(>100 dB)和内置PGA实现对桥式传感器mV级输出的稳定放大与转换。

工作模式与性能权衡

芯片支持多种数据速率(2.5 SPS 至 4 kSPS),不同模式下噪声与功耗动态变化。以50 Hz/60 Hz陷波滤波为例,可同时抑制工频干扰,提升现场环境下的信号完整性。

  1. // 示例:配置为单次转换模式,启用PGA增益128,使用内部参考
    write_register(CONFIG0_REG, 0x01); // AVDD as ref
    write_register(CONFIG1_REG, 0x90); // PGA enabled, gain=128
    write_register(CONFIG2_REG, 0x20); // Single-shot mode

     

  2.  
c运行

该配置使满量程输入范围缩至±15.625 mV,显著提升小信号分辨率,适合热电偶类应用。

2. SPI通信协议原理及其在ADS1248中的应用

SPI(Serial Peripheral Interface)是一种广泛应用于嵌入式系统中的高速、全双工同步串行通信协议。由于其结构简单、传输效率高,SPI成为连接微控制器(MCU)与各类外围设备(如ADC、DAC、传感器、存储器等)的首选接口之一。在高精度模数转换器ADS1248中,SPI不仅承担着配置寄存器写入、状态查询和数据读取的核心功能,还直接影响系统的实时性、稳定性和抗干扰能力。深入理解SPI协议的工作机制,并结合ADS1248的数据手册进行精准时序控制,是实现可靠数据采集的前提。

本章将从SPI通信的基本原理出发,逐步解析其在ADS1248上的具体实现方式,涵盖电气连接、时序要求、命令帧格式以及驱动层设计策略。通过理论分析与代码实践相结合的方式,揭示如何在复杂工业环境中构建高效、稳定的SPI通信链路。

2.1 SPI通信协议基本原理

SPI采用主从架构,通常由一个主设备(Master)和一个或多个从设备(Slave)组成,通信基于四根信号线完成:SCLK(Serial Clock)、MOSI(Master Out Slave In)、MISO(Master In Slave Out)和CS/SS(Chip Select)。这种四线制结构支持全双工通信,即主从设备可以同时发送和接收数据,极大提升了数据吞吐率。

2.1.1 同步串行通信机制与四线制结构

SPI属于同步通信协议,意味着所有数据传输都依赖于主设备提供的时钟信号SCLK。该时钟决定了每一位数据的采样时刻,确保了收发双方的时间基准一致。以下是四个核心引脚的功能说明:

引脚名称方向功能描述
SCLK 输出(主) 主设备产生串行时钟,用于同步数据传输
MOSI 输出(主)→ 输入(从) 主设备向从设备发送数据
MISO 输入(主)← 输出(从) 从设备向主设备返回数据
CS/SS 输出(主) 片选信号,低电平有效,用于选中特定从设备

当CS为低时,从设备被激活并准备接收或响应数据;当CS为高时,从设备进入高阻态,不参与总线活动。这一机制允许多个从设备共享同一组SCLK、MOSI和MISO线路,仅通过独立的CS线进行区分。

  1. // 示例:手动控制片选引脚(GPIO模拟)
    void select_ads1248() {
    HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); // 拉低CS,选中设备
    }
     
    void deselect_ads1248() {
    HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); // 拉高CS,释放设备
    }

     

  2.  
c运行

逻辑分析 
HAL_GPIO_WritePin 是STM32 HAL库函数,用于设置指定GPIO引脚电平。
- 在SPI操作前必须调用 select_ads1248() ,否则ADS1248不会响应任何指令。
- 每次SPI事务结束后应立即释放CS,防止误触发或总线冲突。

该机制特别适用于多传感器系统,例如在一个工业测温节点中集成多个ADS1248以监测不同位置的RTD信号。每个ADS1248拥有独立的CS引脚,MCU可通过依次拉低各CS线实现分时访问,从而节省MCU资源并简化布线。

2.1.2 主从模式下数据传输时序关系

SPI通信的本质是在SCLK上升沿或下降沿进行数据采样与输出。典型的SPI传输以字节为单位,每周期传输8位数据,但也可扩展至16位或更多。主设备发起SCLK脉冲,同时推动MOSI线上数据变化,并在相应边沿捕获MISO线上的反馈。

整个过程遵循以下流程:
1. 主设备拉低目标从设备的CS引脚;
2. 主设备在每个SCLK周期输出一位到MOSI,同时从MISO读入一位;
3. 经过8个时钟周期后,完成一个字节的双向交换;
4. 若需继续传输,则保持CS为低,进入下一字节;
5. 所有数据传输完成后,主设备拉高CS结束通信。

下图展示了SPI一次完整字节交换的时序关系(CPOL=0, CPHA=0):

sequenceDiagram
    participant Master
    participant Slave
    Master->>Slave: CS = L
    loop 8 cycles
        Master->>Slave: SCLK ↑ (采样MISO)
        Slave-->>Master: Data[MISO]
        Master->>Slave: SCLK ↓ (更新MOSI)
        Note right of Master: MOSI更新下一位
    end
    Master->>Slave: CS = H

 

mermaid

流程图说明 
- CS先置低,启动通信;
- SCLK上升沿用于采样MISO数据,保证建立时间;
- SCLK下降沿允许MOSI更新,避免毛刺影响;
- 循环执行8次完成一字节;
- 最后释放CS,结束帧。

此模型体现了SPI“边沿采样、对称传输”的特点,非常适合对时序敏感的应用场景,如ADS1248这类需要精确寄存器访问的ADC芯片。

2.1.3 CPOL与CPHA极性和相位组合解析

SPI协议定义了四种工作模式,由两个参数决定:CPOL(Clock Polarity)和CPHA(Clock Phase),它们共同决定了SCLK空闲状态和数据采样的时机。

模式CPOLCPHA空闲电平采样边沿更新边沿
0 0 0 上升沿 下降沿
1 0 1 下降沿 上升沿
2 1 0 下降沿 上升沿
3 1 1 上升沿 下降沿

ADS1248支持SPI模式1(CPOL=0, CPHA=1)和模式3(CPOL=1, CPHA=1),默认推荐使用模式1。这意味着:
- SCLK空闲时为低电平;
- 数据在SCLK的 下降沿采样 
- 发送端在上升沿改变数据。

为了验证这一点,可参考TI官方数据手册中关于Timing Requirements的部分,其中明确指出 t_DIS(Data Invalid to SCLK Falling Edge)最小值为20ns,表明器件在SCLK下降前已准备好数据,且在此边沿被采样。

  1. // STM32 HAL配置示例:设置SPI为模式1
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL = 0
    hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // CPHA = 1 → Mode 1
    hspi1.Init.NSS = SPI_NSS_SOFT; // 软件控制CS
    if (HAL_SPI_Init(&hspi1) != HAL_OK) {
    Error_Handler();
    }

     

  2.  
c运行

参数说明 
CLKPolarity = LOW :设定SCLK空闲状态为低;
CLKPhase = 2EDGE :表示在第二个边沿(即下降沿)采样数据;
NSS = SOFT :关闭硬件片选,由软件控制CS引脚更灵活;
- 此配置符合ADS1248推荐的SPI模式1要求。

若配置错误,例如误设为模式0(CPHA=0),则主设备将在SCLK上升沿采样,而ADS1248尚未稳定输出数据,导致读取值异常甚至通信失败。因此,在初始化阶段务必核对SPI模式是否匹配。

此外,考虑到长距离传输或噪声环境下的信号完整性,建议在SCLK线上添加适当的串联电阻(如22Ω~33Ω)以抑制反射,并尽量缩短走线长度。对于更高可靠性需求,可在PCB布局中采用差分时钟缓冲方案,但这会增加成本,一般仅用于极端工况。

综上所述,SPI的四线制结构、主从同步机制及四种工作模式的选择,构成了其灵活性与高性能的基础。针对ADS1248这一精密ADC器件,正确理解和配置SPI通信参数,是保障后续寄存器操作与数据采集准确性的关键前提。

2.2 ADS1248中SPI接口电气连接与引脚功能

ADS1248的SPI接口严格按照标准四线制设计,具备完整的MOSI、MISO、SCLK和CS引脚,并额外提供DRDY(Data Ready)中断输出用于异步通知。合理的电气连接不仅是物理连通的保障,更是确保信号质量、降低误码率的重要环节。

2.2.1 CS、SCK、MOSI、MISO引脚作用详解

引脚名称I/O描述
10 DIN Input 串行数据输入,即MOSI,接收来自主机的命令和数据
11 DOUT Output 串行数据输出,即MISO,向主机返回寄存器值或转换结果
12 SCLK Input 串行时钟输入,由主机提供,控制数据传输节奏
13 CSn Input 片选输入,低电平有效,用于启动SPI事务

这些引脚均工作在3.3V CMOS电平下,最大时钟频率可达4MHz(典型值),满足大多数MCU外设SPI模块的输出能力。

值得注意的是,DIN和DOUT为单向引脚,不可互换。DIN仅用于接收主机指令(如写寄存器命令),而DOUT仅在读操作期间输出数据。两者在非活动期间呈现高阻态,避免总线争抢。

在多设备共用SPI总线时,所有设备的SCLK、MOSI应并联连接至同一主控输出端,而每个设备的CSn必须单独连接至不同的GPIO引脚,以实现独立寻址。

2.2.2 上拉/下拉电阻配置建议与时钟速率限制

尽管ADS1248内部未集成强上拉电阻,但在高噪声环境下仍建议对外部SPI信号线采取保护措施:

信号线是否需要外部电阻推荐值原因
CSn 是(弱上拉) 10kΩ 防止意外选中,提高抗干扰能力
SCLK 可选(串联阻尼) 22–33Ω 抑制高频振铃
DIN 可选 10kΩ下拉 防止浮空误触发
DOUT 开漏输出,已有内部驱动
graph LR
    A[MCU_SCLK] -->|串联22R| B(ADS1248_SCLK)
    C[MCU_MOSI] --> D(ADS1248_DIN)
    E(ADS1248_CS) -->|10k上拉| F[VCC]
    F --> G[MCU_CS1]
    H[MCU_MISO] <-- DOUT --- I(ADS1248_DOUT)

 

mermaid

电路说明 
- CSn通过10kΩ电阻上拉至3.3V,确保未选中时保持高电平;
- SCLK串联小电阻用于阻抗匹配,减少反射;
- DIN可加10kΩ下拉以防MCU复位期间输出不确定电平;
- DOUT为开漏结构,通常无需外部电阻,但若总线较长可考虑加弱上拉。

关于时钟速率,ADS1248规定SCLK最高频率为4MHz(f_SCLK),最低无限制。然而实际可用速率受限于以下几个因素:
- MCU SPI外设最大输出频率;
- PCB走线引起的延迟与失真;
- 寄存器访问所需建立/保持时间。

例如,在读取24位ADC结果时,需连续传输3个字节,耗时至少 $3 \times 8 / 4\text{MHz} = 6\mu s$,加上CS建立时间(t_CSS ≥ 50ns)和数据延迟(t_DRDY_to_DATA ≤ 1μs),整体响应时间可控。

2.2.3 多设备共用总线时的片选管理策略

在大型数据采集系统中,常需接入多个ADS1248以扩展通道数。此时可采用两种片选策略:

  1. 独立片选法 :每个ADS1248分配一个专用CS引脚;
  2. 译码片选法 :使用地址译码器(如74HC138)生成片选信号,节省GPIO资源。
  1. // 独立片选示例:选择第2个ADS1248
    void select_ads1248_device(uint8_t dev_num) {
    switch(dev_num) {
    case 0: HAL_GPIO_WritePin(CS1_GPIO_Port, CS1_Pin, RESET); break;
    case 1: HAL_GPIO_WritePin(CS2_GPIO_Port, CS2_Pin, RESET); break;
    case 2: HAL_GPIO_WritePin(CS3_GPIO_Port, CS3_Pin, RESET); break;
    default: return;
    }
    }

    c运行

优势分析 
- 独立片选:编程简单,响应快,适合≤4个设备;
- 译码片选:节省MCU引脚,适合大规模阵列部署。

无论哪种方式,都必须保证任一时刻只有一个CS为低,避免总线冲突。此外,建议在每次CS切换前后插入微秒级延时(如 us_delay(1) ),以满足ADS1248的t_CSD(CS Disable Time)和t_CSS要求。

表格总结如下:

策略GPIO占用扩展性实现难度适用规模
独立CS N个设备需N个引脚 中等 小型系统(<8)
译码CS 3~4个地址线 大型系统(≥8)

综上,合理规划SPI电气连接与片选机制,不仅能提升通信稳定性,还能为未来系统升级预留空间。

2.3 SPI通信时序控制与寄存器访问机制

ADS1248的SPI通信严格依赖精确的时序控制。任何超出规格书允许范围的操作都可能导致寄存器写入失败或数据损坏。

2.3.1 命令帧格式:写寄存器、读寄存器操作码定义

ADS1248使用专用命令字进行寄存器访问,主要包括:
WRITE_REG 0x40 | reg_addr ,后跟1~n字节数据;
READ_REG 0x20 | reg_addr ,后跟dummy字节以触发输出;
START/RDY 0x08 ,启动转换;
RESET 0x06 ,软复位。

例如,向CONFIG0寄存器(地址0x00)写入0x80:

  1. uint8_t cmd[2] = {0x40, 0x80}; // WRITE_REG + data
    select_ads1248();
    HAL_SPI_Transmit(&hspi1, cmd, 2, 10);
    deselect_ads1248();

    c运行

读取同一寄存器:

uint8_t tx_buf[3] = {0x20, 0x00, 0xFF}; // READ_REG + addr + dummy
uint8_t rx_buf[3];
select_ads1248();
HAL_SPI_TransmitReceive(&hspi1, tx_buf, rx_buf, 3, 10);
deselect_ads1248();
uint8_t config0_value = rx_buf[2]; // 实际数据在第三个字节

c运行

注意 :ADS1248在接收到READ_REG命令后,会在下一个SCLK周期开始输出目标寄存器值,因此需要发送dummy字节来“推动”时钟。

2.3.2 数据采样窗口与时钟边沿同步要求

根据数据手册,ADS1248要求SCLK下降沿采样数据(CPHA=1),因此主设备必须确保:
- MOSI数据在SCLK上升沿更新;
- MISO数据在SCLK下降沿稳定。

这要求MCU SPI模块必须配置为Mode 1或Mode 3。

2.3.3 最小建立/保持时间分析与硬件延迟补偿

关键时序参数包括:
- t_DIS ≥ 20ns(DIN无效到SCLK下降沿)
- t_DV ≥ 30ns(DOUT有效到SCLK下降沿)

若MCU运行在高速下(如72MHz APB2),可能需插入NOP或使用DMA来规避时序违规。

使用DMA可彻底消除CPU干预带来的抖动,显著提升定时精度。

3. ADS1248初始化配置流程实现

在高精度模数转换系统中,ADS1248的初始化是确保后续数据采集可靠、准确的关键环节。该过程不仅涉及电源上电后的状态恢复,还包括对内部多个功能模块的有序配置与验证。由于ADS1248集成了可编程增益放大器(PGA)、多路复用输入通道、数字滤波器和参考源选择机制,其寄存器体系结构复杂且存在严格的依赖关系,因此必须按照特定顺序执行初始化操作。一个不完整或错误的配置可能导致ADC输出异常、信噪比下降甚至通信失败。

本章将深入剖析ADS1248从硬件上电到进入稳定工作模式的全流程控制逻辑。首先分析芯片上电复位(POR)行为及其对内部状态机的影响;随后详细解读关键配置寄存器的功能字段,并结合实际应用场景说明如何设置输入通道、参考电压源及增益参数;接着探讨寄存器写入顺序中的依赖性问题,提出基于读回验证的容错机制;最后通过完整的C语言代码示例展示一个健壮的初始化函数设计,包含超时重试、错误码返回和调试日志支持等工业级特性。整个流程强调“先底层后高层”、“先使能再配置”的工程原则,为构建高可靠性测量系统提供坚实基础。

3.1 芯片上电复位与状态机启动过程

ADS1248作为一款高集成度的Σ-Δ型ADC,在上电过程中需经历一系列自动化的硬件自检与状态迁移步骤,以确保所有内部模块处于已知且安全的状态。这一阶段的核心目标是完成电源稳定检测、内部寄存器清零以及SPI接口使能,从而为后续软件配置建立前提条件。理解此过程对于避免因时序不当导致的通信失败至关重要。

3.1.1 POR(Power-On Reset)行为与时序要求

当VDD引脚电压从0上升至工作范围(典型值为2.7V~5.5V)时,ADS1248会触发内置的上电复位电路(Power-On Reset, POR)。该机制通过监测供电电压是否达到阈值来决定是否释放内部复位信号。一旦电压满足要求,POR将自动清除所有寄存器内容并强制芯片进入默认待机模式。

根据TI官方数据手册(SLAS653),POR完成所需时间取决于外部去耦电容大小和电源爬升速率。推荐最小延迟时间为 50ms ,在此期间不应进行任何SPI通信。若过早访问器件,可能造成命令丢失或总线冲突。以下为典型上电时序图:

sequenceDiagram
    participant MCU
    participant ADS1248
    Note over MCU,ADS1248: 上电开始
    ADS1248->>ADS1248: VDD上升至2.7V+
    ADS1248-->>ADS1248: POR激活,内部复位置位
    delay 50ms
    ADS1248-->>ADS1248: POR结束,寄存器初始化完成
    MCU->>ADS1248: 拉低CS,发送RESET命令(0x06)
    ADS1248-->>MCU: 完成软复位,准备接收配置

 

mermaid

说明 :尽管POR会自动完成大部分初始化,但建议在应用层仍主动发送一次 RESET 命令以确保状态一致性,特别是在冷启动或电源波动频繁的环境中。

3.1.2 RESET引脚使用规范与软复位命令触发

除了POR外,ADS1248还提供了外部硬复位引脚 RESET (低有效),可用于手动重启芯片。该引脚应连接至微控制器GPIO或专用复位IC,以便在运行中强制恢复初始状态。

引脚名称方向功能描述
RESET 输入 低电平有效,持续≥50ns即可触发复位

此外,也可通过SPI发送 软复位命令 0x06 实现相同效果,无需物理拉低引脚。该方式更适用于远程诊断或固件升级场景。

void ads1248_soft_reset(void) {
digitalWrite(CS_PIN, LOW); // 选中设备
spi_write_byte(0x06); // 发送软复位指令
delayMicroseconds(10); // 等待命令执行
digitalWrite(CS_PIN, HIGH); // 释放总线
delay(50); // 等待内部重置完成
}

 

c运行

逐行解析 
- 第1行:函数封装便于调用;
- 第2行:拉低片选使能SPI通信;
- 第3行:发送十六进制命令 0x06 ,对应“Reset”操作;
- 第4行:短暂延时保证命令被锁存;
- 第5行:释放CS,结束传输;
- 第6行:等待至少50ms让内部逻辑重建。

参数说明 : CS_PIN 为用户定义的片选GPIO编号; spi_write_byte() 为底层SPI写函数,假设已正确配置SCK、MOSI引脚。

3.1.3 初始化前的状态查询与就绪判断

在正式开始寄存器配置之前,必须确认ADS1248已完成复位并准备好响应命令。可通过轮询 DRDY (Data Ready)引脚状态来判断。该引脚在空闲状态下保持高电平,每当有新转换结果可用时变低。但在初始化阶段,若未启动连续转换,则 DRDY 应在高电平状态。

int ads1248_wait_ready(uint32_t timeout_ms) {
uint32_t start = millis();
while (digitalRead(DRDY_PIN) == LOW) {
if ((millis() - start) > timeout_ms) {
return -1; // 超时错误
}
delay(1);
}
return 0; // 就绪
}

 

c运行

逻辑分析 
- 使用非阻塞式轮询防止死循环;
- 设定最大等待时间为 timeout_ms (推荐100ms);
- 返回值 表示成功, -1 表示超时。

扩展建议 :可结合中断方式监听 DRDY 下降沿,提升实时性。

3.2 关键配置寄存器功能解析

ADS1248共设有24个8位寄存器,其中前四个(CONFIG0~CONFIG3)为核心配置寄存器,决定了ADC的工作模式、输入通道、增益、滤波器类型等关键参数。这些寄存器的正确设置直接关系到系统的动态范围、噪声性能和采样速率。

3.2.1 配置寄存器0~3(CONFIG0~3)字段含义

下表列出了各配置寄存器的主要位域及其功能:

寄存器地址主要功能
CONFIG0 0x00 数据速率、滤波器模式、斩波使能
CONFIG1 0x01 PGA使能、增益设置、缓冲使能
CONFIG2 0x02 参考源选择、IDAC电流路径、校准控制
CONFIG3 0x03 输入多路复用器正负端选择

以 CONFIG1 为例,其位分配如下:

  1. Bit[7:5]: PGAGAIN[2:0] — 增益设置(000=1x, 111=128x)
  2. Bit[4] : PGAEN — 1=启用PGA,0=旁路
  3. Bit[3:0]: BUFGAIN[3:0]— 缓冲器增益(通常设为0)

例如,欲设置PGA增益为16倍,需将 PGAGAIN 设为 100 (即十进制4),同时使能PGA:

  1. uint8_t config1 = (1 << 4) | (4 << 5); // PGAEN=1, PGAGAIN=4
  2. write_register(CONFIG1_REG, config1);
c运行

参数说明 
(1 << 4) 表示第4位置1,启用PGA;
(4 << 5) 将增益值左移至[7:5]位;
write_register() 为封装好的SPI写函数。

3.2.2 输入多路复用器MUX设置与通道选择

ADS1248支持8个差分或16个单端输入通道,由 CONFIG3 和 MUX_EN 寄存器联合控制。每个通道的正负输入可通过写入 MUXP 和 MUXN 字段指定。

例如,选择AIN0为正端、AIN1为负端构成差分输入:

// 设置 MUXP = AIN0 (0b000), MUXN = AIN1 (0b001)
uint8_t mux_config = (0 << 4) | (1 << 0);
write_register(CONFIG3_REG, mux_config);

 

c运行

位域解释 
- 高4位 [7:4] 对应 MUXP 
- 低4位 [3:0] 对应 MUXN 
- 支持组合如 AIN2-AIN3 AIN4-AIN5 等。

注意事项 :切换通道后需等待至少一个滤波器建立周期才能获取有效数据。

3.2.3 参考源选择、缓冲使能与PGA旁路控制

参考电压稳定性直接影响ADC转换精度。ADS1248支持三种参考源:
- 内部2.048V基准(出厂校准)
- 外部差分参考(REFP/REFN引脚)
- 模拟电源AVDD作为参考

通过 CONFIG2 寄存器的 REF_SOURCE 位进行选择:

// 使用内部参考源
uint8_t config2 = read_register(CONFIG2_REG);
config2 &= ~(1 << 6); // 清除REF_SOURCE位
write_register(CONFIG2_REG, config2);

 

c运行

逻辑说明 
REF_SOURCE=0 → 内部参考;
REF_SOURCE=1 → 外部参考;
- 若使用外部参考,需确保REFP > 1.2V且具备足够驱动能力。

同时,可通过 BUFBOT 和 BUF_TOP 位使能输入缓冲,降低对信号源的负载影响。

3.3 寄存器写入顺序与依赖关系管理

ADS1248的寄存器并非独立存在,某些配置项之间存在严格的先后依赖关系。错误的写入顺序可能导致功能失效或不可预测的行为。

3.3.1 必须优先配置的寄存器项及其逻辑依据

合理的写入顺序应遵循以下原则:

  1. 先配置参考源(CONFIG2)
    因为PGA和ADC核心依赖参考电压,若未先设定,增益计算将出错。

  2. 再配置PGA与增益(CONFIG1)
    PGA依赖参考源和输入结构,应在MUX前设置。

  3. 然后设置通道选择(CONFIG3)
    输入通路确定后才能开始转换。

  4. 最后设置采样率与滤波器(CONFIG0)
    滤波器响应时间受前面配置影响。

    // 正确顺序示例
    write_register(CONFIG2_REG, ref_source_cfg); // Step 1
    write_register(CONFIG1_REG, pga_gain_cfg); // Step 2
    write_register(CONFIG3_REG, mux_select_cfg); // Step 3
    write_register(CONFIG0_REG, filter_rate_cfg); // Step 4

    c运行

反例警示 :若先写 CONFIG0 设定高速模式,但后续才启用PGA,可能导致瞬态电流冲击。

3.3.2 写操作确认机制与错误反馈处理

由于SPI通信可能存在干扰或时钟偏差,不能假定每次写入都成功。建议采用“写后读回”机制进行验证:

  1. int write_with_verify(uint8_t reg, uint8_t value) {
  2. write_register(reg, value);
  3. uint8_t readback = read_register(reg);
  4. if (readback != value) {
  5. return -1; // 写入失败
  6. }
  7. return 0;
  8. }
c运行

增强策略 :引入CRC校验或增加重试次数(最多3次)。

3.3.3 利用读回验证确保配置一致性

完整的配置验证流程如下:

graph TD
    A[开始初始化] --> B[发送RESET]
    B --> C[等待DRDY高电平]
    C --> D[依次写入CONFIG2→CONFIG1→CONFIG3→CONFIG0]
    D --> E{每步是否读回一致?}
    E -- 是 --> F[继续下一步]
    E -- 否 --> G[记录错误码,尝试重试]
    G --> H{超过最大重试次数?}
    H -- 是 --> I[返回初始化失败]
    H -- 否 --> D
    F --> J[初始化成功]
mermaid

优势 :显著提升系统鲁棒性,尤其适用于工业现场恶劣电磁环境。

3.4 完整初始化代码流程设计

为了实现模块化、可维护性强的驱动开发,应将初始化过程封装为独立函数,并提供清晰的错误码接口。

3.4.1 封装init_ads1248()函数接口与返回码定义

  1. typedef enum {
  2. INIT_SUCCESS = 0,
  3. INIT_TIMEOUT_DRDY,
  4. INIT_WRITE_FAIL,
  5. INIT_RESET_FAIL,
  6. INIT_UNKNOWN_ERROR
  7. } ads1248_init_status_t;
  8.  
  9. ads1248_init_status_t init_ads1248(void) {
  10. // 1. 硬件复位或软复位
  11. ads1248_soft_reset();
  12.  
  13. // 2. 等待就绪
  14. if (ads1248_wait_ready(100) != 0) {
  15. return INIT_TIMEOUT_DRDY;
  16. }
  17.  
  18. // 3. 配置参考源(内部)
  19. if (write_with_verify(CONFIG2_REG, 0x00) != 0) {
  20. return INIT_WRITE_FAIL;
  21. }
  22.  
  23. // 4. 设置PGA增益16x
  24. if (write_with_verify(CONFIG1_REG, 0xA0) != 0) { // 1010 0000
  25. return INIT_WRITE_FAIL;
  26. }
  27.  
  28. // 5. 选择通道 AIN0-AIN1
  29. if (write_with_verify(CONFIG3_REG, 0x01) != 0) {
  30. return INIT_WRITE_FAIL;
  31. }
  32.  
  33. // 6. 设置滤波器:Sinc4,10SPS
  34. if (write_with_verify(CONFIG0_REG, 0x23) != 0) {
  35. return INIT_WRITE_FAIL;
  36. }
  37.  
  38. return INIT_SUCCESS;
  39. }
c运行

参数说明 
0xA0 1010 0000 → PGAEN=1, PGAGAIN=5(32x?注意修正!)
- 实际应为 0x90 (1001 0000)表示16x增益。

3.4.2 错误重试机制与超时保护策略

改进版加入重试逻辑:

  1. #define MAX_RETRIES 3
  2.  
  3. ads1248_init_status_t init_ads1248_with_retry(void) {
  4. for (int i = 0; i < MAX_RETRIES; i++) {
  5. ads1248_init_status_t status = init_ads1248();
  6. if (status == INIT_SUCCESS) {
  7. printf("ADS1248 初始化成功\n");
  8. return INIT_SUCCESS;
  9. }
  10. delay(100);
  11. }
  12. printf("错误:多次初始化失败,状态码:%d\n", status);
  13. return status;
  14. }
c运行

优点 :适应电源不稳定或瞬时干扰场景。

3.4.3 日志输出辅助调试信息以支持现场排查

添加宏开关用于调试:

  1. #define DEBUG_ADS1248_INIT
  2.  
  3. #ifdef DEBUG_ADS1248_INIT
  4. #define ADS_LOG(fmt, ...) printf("[ADS1248] " fmt "\n", ##__VA_ARGS__)
  5. #else
  6. #define ADS_LOG(...)
  7. #endif
  8.  
  9. // 在init函数中插入:
  10. ADS_LOG("正在写入CONFIG1: 0x%02X", config1_val);
c运行

价值 :极大简化现场故障定位,尤其适合嵌入式长期运行系统。

4. 基于C语言的ADS1248读写函数设计

在嵌入式系统开发中,对高精度模数转换器(ADC)如ADS1248的控制必须依赖于高效、稳定且可移植的底层驱动代码。本章聚焦于如何使用标准C语言构建一套完整的ADS1248寄存器级读写接口体系,重点围绕SPI通信抽象层的设计、专用寄存器访问机制、ADC数据获取流程以及模块化组织结构展开深入探讨。通过合理的函数封装和分层架构设计,确保驱动既满足实时性要求,又具备良好的可维护性和跨平台适应能力。

4.1 底层SPI读写抽象层封装

为实现与硬件解耦的软件设计目标,首先需建立一个独立于具体微控制器平台的SPI通信抽象层。该层负责屏蔽底层外设差异,提供统一的数据传输接口,是整个ADS1248驱动的基础支撑模块。其核心任务包括字节级发送/接收、片选信号管理、多字节连续传输优化等。

4.1.1 spi_write_byte()与spi_read_byte()函数实现

最基础的操作单元是单字节的SPI读写操作。以下为典型 spi_write_byte() 和 spi_read_byte() 函数的C语言实现示例:

  1. /**
  2. * @brief 向SPI总线写入一个字节并返回接收到的数据(全双工)
  3. * @param data 待发送的字节
  4. * @return 从MISO线上读取的响应字节
  5. */
  6. uint8_t spi_write_byte(uint8_t data) {
  7. while (!(SPI1->SR & SPI_SR_TXE)); // 等待发送缓冲区空
  8. *((volatile uint8_t*)&SPI1->DR) = data; // 写入数据寄存器(低8位)
  9. while (!(SPI1->SR & SPI_SR_RXNE)); // 等待接收完成
  10. return *((volatile uint8_t*)&SPI1->DR); // 读取接收到的数据
  11. }
  12.  
  13. /**
  14. * @brief 仅读取一个字节(通常用于响应阶段)
  15. * @return 接收到的字节
  16. */
  17. uint8_t spi_read_byte(void) {
  18. return spi_write_byte(0xFF); // 发送哑元数据以触发时钟
  19. }
c运行
逻辑分析与参数说明
  • SPI1->SR 是状态寄存器,检查 TXE (Transmit Buffer Empty)标志位以避免覆盖未发送数据。
  • 使用 volatile 关键字防止编译器优化掉必要的内存访问。
  • 强制类型转换 (volatile uint8_t*) 确保只操作8位数据,避免STM32等平台因自动宽度扩展导致异常。
  • spi_read_byte() 实际上仍需发起一次全双工传输,因此调用 spi_write_byte(0xFF) 是标准做法——发送无意义数据以生成SCK时钟,从而驱动从设备输出数据。

此设计适用于大多数支持8位模式的硬件SPI外设,具有高度通用性。

4.1.2 多字节连续传输优化与CS自动控制

对于需要批量读写的场景(如读取24位ADC结果或配置多个寄存器),应提供高效的多字节传输函数,并集成片选(CS)自动管理机制:

  1. /**
  2. * @brief 连续写入多个字节并可选择是否保持CS拉低
  3. * @param pBuf 数据缓冲区指针
  4. * @param len 数据长度
  5. * @param keep_cs_high 是否在结束后释放CS
  6. */
  7. void spi_write_bytes(const uint8_t *pBuf, uint32_t len, bool keep_cs_high) {
  8. ADS1248_CS_LOW(); // 拉低片选
  9. for (uint32_t i = 0; i < len; i++) {
  10. spi_write_byte(pBuf[i]);
  11. }
  12. if (!keep_cs_high) {
  13. ADS1248_CS_HIGH(); // 自动释放CS
  14. }
  15. }
  16.  
  17. /**
  18. * @brief 读取指定长度的字节流
  19. * @param pBuf 存储读取数据的缓冲区
  20. * @param len 要读取的字节数
  21. */
  22. void spi_read_bytes(uint8_t *pBuf, uint32_t len) {
  23. ADS1248_CS_LOW();
  24. for (uint32_t i = 0; i < len; i++) {
  25. pBuf[i] = spi_read_byte();
  26. }
  27. ADS1248_CS_HIGH();
  28. }
c运行
参数类型说明
pBuf const uint8_t* uint8_t* 输入/输出数据缓冲区地址
len uint32_t 传输字节数,决定循环次数
keep_cs_high bool 若为true,则不释放CS,用于后续连续操作

上述函数通过宏定义 ADS1248_CS_LOW() 和 ADS1248_CS_HIGH() 控制GPIO引脚状态,提高了代码可读性与可配置性。

sequenceDiagram
    participant MCU
    participant ADS1248
    MCU->>ADS1248: CS = LOW
    loop 每个字节
        MCU->>ADS1248: SCK 上升沿发送 MOSI 数据
        ADS1248-->>MCU: SCK 下降沿输出 MISO 数据
    end
    MCU->>ADS1248: CS = HIGH (可选)
mermaid

该流程图展示了典型的多字节SPI事务过程,强调了片选信号在整个传输期间的有效性维持原则。

4.1.3 平台无关性设计原则与移植接口定义

为了增强驱动的可移植性,建议将SPI底层操作抽象为接口函数集合,并通过条件编译适配不同MCU平台。例如定义如下头文件 spi_platform.h 

  1. #ifndef SPI_PLATFORM_H
  2. #define SPI_PLATFORM_H
  3.  
  4. #include <stdint.h>
  5. #include <stdbool.h>
  6.  
  7. // 抽象SPI操作接口
  8. uint8_t platform_spi_write_byte(uint8_t data);
  9. void platform_spi_write_bytes(const uint8_t *buf, uint32_t len, bool hold_cs);
  10. void platform_spi_read_bytes(uint8_t *buf, uint32_t len);
  11.  
  12. // 片选操作由平台实现
  13. #define ADS1248_CS_INIT() platform_cs_init()
  14. #define ADS1248_CS_LOW() platform_cs_low()
  15. #define ADS1248_CS_HIGH() platform_cs_high()
  16.  
  17. #endif
c运行

这样,在更换MCU时只需重写 platform_*.c 文件中的具体实现,而上层ADS1248驱动无需修改。这种分层策略极大提升了代码复用率与项目可维护性。

4.2 ADS1248专用寄存器读写接口开发

ADS1248的所有功能均通过内部寄存器进行配置与查询。因此,必须构建专用的寄存器级读写接口,准确映射TI官方数据手册中定义的命令格式。

4.2.1 write_register(uint8_t reg, uint8_t value) 实现

根据ADS1248协议,写寄存器操作需先发送写命令字(0x40 | reg_addr),再发送数据字节:

  1. #define ADS1248_CMD_WRITE_REG(base_reg) (0x40 | (base_reg))
  2.  
  3. void write_register(uint8_t reg, uint8_t value) {
  4. uint8_t cmd[2];
  5. cmd[0] = ADS1248_CMD_WRITE_REG(reg);
  6. cmd[1] = value;
  7.  
  8. spi_write_bytes(cmd, 2, false); // 写入两字节后释放CS
  9. }
c运行
执行逻辑逐行解析
  1. 宏 ADS1248_CMD_WRITE_REG 将寄存器地址编码为SPI命令字(最高位为1表示写操作);
  2. 构造包含命令和数据的数组 cmd[2] 
  3. 调用 spi_write_bytes 执行完整传输并自动释放CS。

此方法简洁高效,符合ADS1248规范。

4.2.2 read_register(uint8_t reg) 返回值解析

读寄存器操作稍复杂:需先发送读命令(0x20 | reg_addr),然后忽略第一个返回字节(伪字节),接着读取真实数据:

  1. #define ADS1248_CMD_READ_REG(base_reg) (0x20 | (base_reg))
  2.  
  3. uint8_t read_register(uint8_t reg) {
  4. uint8_t cmd = ADS1248_CMD_READ_REG(reg);
  5. uint8_t dummy, data;
  6.  
  7. ADS1248_CS_LOW();
  8. spi_write_byte(cmd); // 发送读命令
  9. dummy = spi_read_byte(); // 忽略第一个返回字节
  10. data = spi_read_byte(); // 获取实际寄存器值
  11. ADS1248_CS_HIGH();
  12.  
  13. return data;
  14. }
c运行
关键点说明
  • 第一个返回字节是“dummy byte”,对应命令传输期间的回传数据,应丢弃;
  • 第二个字节才是目标寄存器内容;
  • 必须手动控制CS信号以保证命令—响应序列完整性。

4.2.3 批量寄存器读取用于诊断与监控

在调试或故障排查时,常需一次性读取多个连续寄存器内容。为此可扩展批量读取函数:

  1. void read_registers(uint8_t start_reg, uint8_t *values, uint8_t count) {
  2. uint8_t cmd = ADS1248_CMD_READ_REG(start_reg);
  3. ADS1248_CS_LOW();
  4. spi_write_byte(cmd);
  5. spi_read_byte(); // Dummy read
  6. spi_read_bytes(values, count); // 连续读出多个寄存器值
  7. ADS1248_CS_HIGH();
  8. }
c运行
寄存器地址功能描述典型用途
0x00 CONFIG0 增益、数据速率设置
0x01 CONFIG1 PGA使能、参考源选择
0x02 CONFIG2 滤波器模式、斩波启用
0x03 CONFIG3 多路复用器输入配置
0x04 IDACCTRL 恒流源配置
0x05 CHANNMAP 通道映射表

通过调用 read_registers(0x00, buf, 6) 可快速抓取全部关键配置,便于日志记录与远程诊断。

graph TD
    A[开始读寄存器] --> B[发送READ命令]
    B --> C[接收Dummy Byte]
    C --> D[连续读取N个有效数据]
    D --> E[释放CS]
    E --> F[返回数据数组]
mermaid

该流程图清晰表达了多寄存器读取的状态流转关系。

4.3 ADC数据获取与转换结果解析

完成初始化后,用户最关心的是如何正确获取并解释ADC转换结果。这涉及启动转换、等待就绪、读取原始码值及物理量换算等多个环节。

4.3.1 发起单次转换命令并等待DRDY下降沿

ADS1248支持多种转换模式,其中单次转换可通过写入 SYNC 命令触发:

  1. #define ADS1248_CMD_SYNC 0x08
  2. #define ADS1248_PIN_DRDY GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4)
  3.  
  4. void start_single_conversion(void) {
  5. spi_write_byte(ADS1248_CMD_SYNC); // 触发同步转换
  6. }
  7.  
  8. bool wait_for_drdy_low(uint32_t timeout_ms) {
  9. uint32_t start = get_tick_ms();
  10. while (ADS1248_PIN_DRDY == Bit_SET) {
  11. if ((get_tick_ms() - start) > timeout_ms) {
  12. return false; // 超时失败
  13. }
  14. }
  15. return true;
  16. }
c运行

注: get_tick_ms() 需由系统滴答定时器提供毫秒级计时。

行为分析
  • SYNC 命令通知ADS1248立即开始一次A/D转换;
  • DRDY引脚在转换完成前保持高电平,完成后拉低;
  • 使用轮询方式检测DRDY下降沿是最简单可靠的方法,也可改用外部中断提升效率。

4.3.2 读取24位转换结果并进行符号扩展处理

转换完成后,从 DATA 寄存器(地址0x00)顺序读取3个字节:

  1. int32_t read_adc_result(void) {
  2. uint8_t data[3];
  3. int32_t result;
  4.  
  5. // 自动读取3字节数据(内部包含命令+dummy)
  6. spi_write_byte(0x20); // READ命令
  7. spi_read_byte(); // Dummy
  8. spi_read_bytes(data, 3); // MSB -> LSB
  9.  
  10. // 组合24位有符号整数
  11. result = (int32_t)((data[0] << 16) | (data[1] << 8) | data[2]);
  12.  
  13. // 符号扩展至32位
  14. if (result & 0x800000) {
  15. result |= 0xFF000000;
  16. }
  17.  
  18. return result;
  19. }
c运行
详细解析
  • 数据按 大端序 排列:第1字节为MSB;
  • 左移组合后判断最高位(bit23)是否为1,若是则补全高位为1(负数);
  • 最终得到一个范围为 [-8,388,608, +8,388,607] 的有符号32位整数。

4.3.3 将原始码值转换为物理电压量的数学模型

最终目标是将数字码值还原为实际输入电压。计算公式如下:

V_{in} = \frac{Code \times V_{ref}}{Gain \times 2^{23}}

其中:
- $ Code $:24位补码表示的ADC输出;
- $ V_{ref} $:所选参考电压(如2.5V);
- $ Gain $:PGA增益倍数(1~128);

  1. float convert_to_voltage(int32_t code, float vref, uint8_t gain) {
  2. float resolution = vref / (gain * 8388608.0f); // 2^23
  3. return code * resolution;
  4. }
c运行
参数单位示例值
code -4194304 (负满量程)
vref V 2.5
gain × 64
输出电压 V -0.125

该模型可用于热电偶冷端补偿、RTD电阻计算等高级应用。

4.4 驱动模块化组织与API对外暴露

良好的模块化设计是专业级驱动的重要标志。应合理划分 .c 与 .h 文件职责,最小化全局变量暴露。

4.4.1 .h头文件中声明公共函数与宏常量

ads1248.h 提供统一接口入口:

  1. #ifndef ADS1248_H
  2. #define ADS1248_H
  3.  
  4. #include <stdint.h>
  5. #include <stdbool.h>
  6.  
  7. // 命令定义
  8. #define ADS1248_CMD_RESET 0x06
  9. #define ADS1248_CMD_SYNC 0x08
  10. #define ADS1248_CMD_WAKEUP 0x02
  11.  
  12. // 函数声明
  13. void init_ads1248(void);
  14. void write_register(uint8_t reg, uint8_t value);
  15. uint8_t read_register(uint8_t reg);
  16. int32_t read_adc_result(void);
  17. float convert_to_voltage(int32_t code, float vref, uint8_t gain);
  18. bool wait_for_drdy_low(uint32_t timeout_ms);
  19.  
  20. #endif
c运行

4.4.2 全局变量最小化与静态函数封装

所有内部辅助函数应声明为 static ,防止命名冲突:

  1. // ads1248.c 局部函数
  2. static void local_delay_us(uint32_t us) {
  3. // 微秒级延时
  4. }
c运行

仅暴露必要API,提升封装性。

4.4.3 提供用户级调用示例程序框架

  1. int main(void) {
  2. system_init();
  3. spi_init();
  4. init_ads1248();
  5.  
  6. while (1) {
  7. start_single_conversion();
  8. if (wait_for_drdy_low(100)) {
  9. int32_t code = read_adc_result();
  10. float volt = convert_to_voltage(code, 2.5f, 64);
  11. printf("Voltage: %.6f V\r\n", volt);
  12. }
  13. delay_ms(500);
  14. }
  15. }
c运行

该示例展示了完整的采集—转换—输出流程,适合快速验证与产品原型开发。

5. 可编程增益放大器(PGA)增益设置方法

在高精度模数转换系统中,信号幅值往往极其微弱,尤其是在传感器输出端如热电偶、RTD或称重桥路等场景下,原始电压信号可能仅有几十至几百微伏。若直接送入ADC进行量化处理,其有效分辨率将严重受限,信噪比(SNR)显著下降。为此,ADS1248集成了内置的可编程增益放大器(Programmable Gain Amplifier, PGA),用于对输入信号进行前置放大,从而充分利用ADC的有效动态范围,提升测量精度与系统灵敏度。本章深入剖析PGA在ADS1248中的实现机制、寄存器配置方式、动态调节策略以及实际工程应用中的优化选型逻辑。

5.1 PGA在ADS1248中的作用与增益范围

5.1.1 放大微弱信号以提高信噪比的必要性

在精密测量系统中,噪声是影响测量准确性的核心因素之一。当传感器输出信号非常微小时,若不经过前置放大,直接进入24位Δ-Σ ADC,尽管理论上具有高达140dB以上的动态范围,但实际有效位数(ENOB)会因量化噪声和前端电路噪声而大幅降低。例如,一个输出为±10mV的称重传感器,在未使用PGA的情况下接入ADS1248,默认满量程为±2.5V(参考电压为2.5V且增益为1),此时ADC仅利用了其总输入范围的0.4%,相当于损失了近8位有效分辨率。

通过启用PGA并设置合适的增益倍数(如64或128),可将输入信号放大至接近ADC满量程水平,使得量化步长相对于信号幅度更小,从而显著提升信噪比(SNR)。这一过程本质上是对“信号—噪声—量化误差”三者关系的再平衡。更重要的是,ADS1248的PGA位于调制器之前,属于Δ-Σ架构中的模拟前端部分,因此其增益操作不会引入额外的数字噪声,反而有助于抑制后续级联电路中的相对噪声贡献。

此外,PGA还能配合参考电压的选择灵活调整系统整体输入范围。例如,在采用内部2.5V基准时,若PGA增益设为32,则差分输入满量程变为 ±(2.5V / 32) = ±78.125mV,这恰好匹配多数RTD激励电路在标准电流下的输出范围,极大提升了系统的适配能力。

5.1.2 支持1~128倍增益档位及其对应满量程输入

ADS1248支持七级可编程增益设置,具体包括:1、2、4、8、16、32、64 和 128 倍增益(共8种选择)。这些增益值通过CONFIG1寄存器中的 PGAGAIN[2:0] 字段进行编码控制,如下表所示:

PGAGAIN[2:0]增益倍数满量程输入范围(@ VREF = 2.5V)
000 1 ±2.5 V
001 2 ±1.25 V
010 4 ±625 mV
011 8 ±312.5 mV
100 16 ±156.25 mV
101 32 ±78.125 mV
110 64 ±39.0625 mV
111 128 ±19.53125 mV

注:满量程输入 = ±(VREF / Gain)

该表格揭示了一个关键设计原则——随着增益增加,允许的最大输入信号幅值线性减小。这意味着用户必须在“灵敏度”与“抗过载能力”之间做出权衡。例如,在检测低电平生物电信号时,选用128倍增益可以获得最高分辨率;但在存在较大共模电压或瞬态干扰的工业现场,则应谨慎选择过高增益,以防输入超出线性区导致饱和失真。

值得注意的是,ADS1248的PGA具备极低的输入偏置电流(典型值<1 pA)和高输入阻抗(>1 GΩ),特别适合驱动高阻源(如pH探头、光电二极管跨阻放大器输出等)。同时,其增益误差在整个温度范围内保持稳定(典型±0.5%),非线性度低于0.002% FSR,确保了长期运行下的测量一致性。

5.1.3 增益误差与非线性度对测量精度的影响

尽管ADS1248的PGA在设计上力求理想化,但在极端条件下仍存在一定的增益误差与非线性效应,这些参数直接影响最终测量结果的准确性。

增益误差主要来源于运放内部晶体管的工艺偏差及反馈网络的匹配程度。TI官方数据手册标明,在常温下PGA的整体增益误差最大不超过±0.5%,而在整个工作温度范围(-40°C 至 +125°C)内可能扩展至±1%。对于要求0.1%以内精度的应用(如医疗仪器或实验室标准设备),这一误差不可忽略,需通过校准手段予以补偿。

非线性度则表现为增益随输入信号幅度变化而轻微波动的现象,通常用积分非线性(INL)表示。ADS1248的PGA INL 典型值为 ±2 ppm FSR,属于极高水平,意味着即使在满量程附近也能维持良好的线性响应。然而,在极高增益模式下(如×128),任何微小的失调电压漂移都会被放大,进而引发零点偏移问题。

为应对上述挑战,推荐采取以下措施:
- 在出厂阶段执行多点增益标定,建立查找表或拟合多项式;
- 利用片内自检功能读取短路输入下的输出码值,计算并存储零点偏移;
- 结合软件滤波与平均技术削弱随机噪声对校准结果的干扰。

graph TD
    A[传感器微弱信号] --> B{是否小于100μV?}
    B -- 是 --> C[启用PGA ×64 或 ×128]
    B -- 否 --> D[选择 ×1 ~ ×16 增益]
    C --> E[检查是否接近满量程]
    D --> E
    E --> F{是否会受共模干扰?}
    F -- 是 --> G[启用差分输入 + 屏蔽走线]
    F -- 否 --> H[单端输入简化布线]
    G --> I[确认增益后启动ADC转换]
    H --> I
mermaid

此流程图展示了基于输入信号特征选择PGA增益的基本决策路径,体现了从物理信号到电气参数再到系统配置的完整映射逻辑。

5.2 增益配置寄存器位域操作实践

5.2.1 CONFIG1寄存器中PGAEN与PGAGAIN位设置

ADS1248的增益配置主要由 CONFIG1 寄存器(地址0x01)完成,其格式如下:

Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0
PGAEN - - - - PGAGAIN[2] PGAGAIN[1] PGAGAIN[0]

其中:
PGAEN (Bit 7) :PGA使能位。
:启用PGA(推荐用于所有需要放大的场景)
:绕过PGA,信号直通至ADC调制器(适用于大信号输入)
PGAGAIN[2:0] (Bits 2–0):定义增益倍数,编码规则见前述表格。

示例:若希望启用64倍增益,则需设置 PGAEN=1 PGAGAIN=110 (即二进制6),故CONFIG1寄存器值为:

uint8_t config1_value = (1 << 7) | (6 << 0); // 即 0b10000110 = 0x86
c运行

随后通过SPI写入寄存器:

  1. // 写寄存器函数原型已在第四章定义
  2. write_register(CONFIG1_REG_ADDR, config1_value);
c运行
参数说明与代码解析:
  • (1 << 7) :将第7位置1,激活PGA功能。
  • (6 << 0) :将十进制6(即二进制 110 )写入低三位,表示×64增益。
  • config1_value 最终为 0x86 ,符合TI文档规定。
  • write_register() 函数封装了SPI命令帧发送逻辑,包含起始字节(写操作标志+寄存器地址)、数据字节传输及CS控制。

执行该语句后,ADS1248将在下次转换周期中应用新的增益设置。需要注意的是,更改增益会影响输入满量程,因此必须同步更新后续的数据解析公式中使用的比例因子。

5.2.2 不同增益下参考电压与输入动态范围匹配

为了最大化ADC利用率,必须使预期最大信号幅值接近所选增益下的满量程输入。假设某RTD测温电路在4mA激励下产生最大100mV差分输出,目标是将其精确数字化。

增益满量程输入是否适用
1 ±2.5 V ❌ 过大,分辨率浪费严重
8 ±312.5 mV ✅ 可接受,保留足够裕量
16 ±156.25 mV ✅ 更优,接近满量程
32 ±78.125 mV ⚠️ 接近上限,存在饱和风险

综合考虑安全裕量(建议留出10%-20%余量),最佳选择为 增益×8 或 ×16 。若选用×16,则满量程为±156.25mV,100mV信号约占全范围的64%,能够充分发挥24位ADC的优势。

进一步地,结合参考电压选择(可通过CONFIG2设置使用内部2.5V或外部REF+引脚电压),可构建更灵活的量程体系。例如,若外接1.25V精密基准,则在×16增益下满量程仅为 ±78.125mV,更适合极低输出传感器。

5.2.3 避免输入过载导致饱和的预防措施

当输入信号超过当前增益对应的满量程范围时,PGA将进入饱和状态,输出恒定最大/最小码值,造成严重测量错误。为防止此类情况发生,建议采取以下措施:

  1. 硬件限幅保护 :在模拟输入端加入钳位二极管(如BAT54S),连接至AVDD与GND,限制瞬态电压尖峰;
  2. 软件监测DRDY与状态寄存器 :定期读取 STATUS 寄存器(地址0x00)中 WDT (Watchdog Timeout)和 FAULT 位,判断是否发生超限;
  3. 自动增益切换机制 :首次采集后分析码值大小,若接近±2^23,则自动降低增益重新采样。

以下是一个典型的防饱和检测代码段:

  1. int32_t raw_code = read_adc_data(); // 获取24位补码输出
  2. if (raw_code > 0x7FFFFF || raw_code < -0x800000) {
  3. // 超出正常范围,可能是饱和
  4. uint8_t current_gain = get_current_pga_gain(); // 查询当前增益
  5. if (current_gain > 1) {
  6. reduce_pga_gain(); // 自动降增益
  7. delay_ms(10);
  8. raw_code = read_adc_data(); // 重新采样
  9. }
  10. }
c运行

该机制可在初始化或连续扫描模式中集成,形成闭环保护。

防护措施实现难度成本有效性
输入RC滤波 ★☆☆ 中等
硬件钳位 ★★☆
软件检测+重试 ★★★ 极低
自适应增益调整 ★★★★ 极高

5.3 动态增益切换策略与自适应调节算法

5.3.1 根据输入信号幅值自动调整增益等级

在多通道或多工况测量系统中,各通道信号幅值差异显著。例如,一路连接热电偶(μV级),另一路连接压力变送器(mV级)。若统一使用固定增益,必然导致某些通道信噪比低下或溢出。

为此,可设计一种 自适应增益调节算法 ,流程如下:

  1. 初始化所有通道为中等增益(如×8);
  2. 对每个通道执行一次快速转换;
  3. 分析输出码值占满量程的比例;
  4. 若 |code| < 10% FSR → 提升增益;
    若 |code| > 90% FSR → 降低增益;
  5. 重复步骤2-4直至稳定。
  1. void auto_adjust_gain(uint8_t channel) {
  2. select_channel(channel); // 切换MUX
  3. set_pga_gain(GAIN_8); // 初始增益
  4. int32_t code = sample_once(); // 单次采样
  5. double ratio = fabs(code) / (double)(1<<23);
  6.  
  7. if (ratio < 0.1 && get_current_gain() < MAX_GAIN) {
  8. increase_pga_gain();
  9. } else if (ratio > 0.9 && get_current_gain() > MIN_GAIN) {
  10. decrease_pga_gain();
  11. }
  12. }
c运行

此函数可根据实时信号强度动态优化增益,提升整体系统适应性。

5.3.2 多次尝试机制避免初始误判造成死锁

由于噪声或瞬态干扰,首次采样结果可能出现异常峰值,误导增益调整方向。为此,应引入多次采样取平均或中值滤波机制:

  1. int32_t robust_sample(uint8_t ch) {
  2. int32_t samples[5];
  3. for (int i = 0; i < 5; i++) {
  4. samples[i] = read_single_conversion();
  5. delay_ms(2);
  6. }
  7. return median_filter(samples, 5); // 返回中值
  8. }
c运行

中值滤波能有效剔除毛刺,提升判断可靠性。

5.3.3 在多通道扫描中独立配置各通道增益

ADS1248虽共享同一套PGA配置,但可通过在每次通道切换后 重新写入CONFIG1寄存器 的方式实现“伪独立增益”。例如:

  1. struct channel_config {
  2. uint8_t pos_input;
  3. uint8_t neg_input;
  4. uint8_t pga_gain;
  5. } ch_cfg[4] = {
  6. {0, 1, GAIN_64}, // 热电偶
  7. {2, 3, GAIN_8}, // 压力传感器
  8. {4, 5, GAIN_32}, // RTD
  9. {6, 7, GAIN_1} // 大信号校准源
  10. };
  11.  
  12. void scan_all_channels() {
  13. for (int i = 0; i < 4; i++) {
  14. write_register(MUX_CONFIG_REG, encode_mux(ch_cfg[i].pos_input, ch_cfg[i].neg_input));
  15. write_register(CONFIG1_REG, build_config1(1, ch_cfg[i].pga_gain));
  16. delay_ms(5); // 稳定时间
  17. int32_t result = read_adc_data();
  18. process_result(i, result);
  19. }
  20. }
c运行

虽然频繁修改寄存器会增加通信开销,但对于非高速应用场景(≤10Hz更新率),完全可行。

stateDiagram-v2
    [*] --> Idle
    Idle --> Channel_Selection : start_scan()
    Channel_Selection --> PGA_Config_Update : set MUX
    PGA_Config_Update --> Conversion_Start : update CONFIG1
    Conversion_Start --> Data_Read : wait DRDY↓
    Data_Read --> Result_Processing : read 24-bit code
    Result_Processing --> Next_Channel : store & log
    Next_Channel --> Channel_Selection : i++
    Next_Channel --> [*] : done
mermaid

状态机清晰展示了每通道独立增益配置的执行流程。

5.4 实际应用中的增益选型指导

5.4.1 称重传感器桥路输出与增益匹配实例

典型称重传感器为六线制惠斯通电桥,激励电压5V,灵敏度2mV/V。在满载时输出为 2mV/V × 5V = 10mV。

假设使用ADS1248内部2.5V参考,目标是让10mV信号接近满量程。计算所需增益:

\text{Gain} = \frac{V_{REF}}{V_{in(max)}} = \frac{2.5}{0.01} = 250

但ADS1248最大增益为128,无法完全覆盖。此时有两种方案:

  • 方案一 :使用外部参考电压1.25V,则 ×128 增益下满量程为 ±9.76mV,略低于10mV → 存在轻微截断;
  • 方案二 :保持2.5V参考,使用 ×128 增益,满量程±19.53mV,10mV约占51% FSR → 可接受。

推荐采用方案二,并在软件中实施线性插值校正。

5.4.2 RTD测温电路中恒流源配合增益设定

PT100在0°C时电阻为100Ω,采用4mA恒流激励时输出电压为 4mA × 100Ω = 0.4V。在800°C时约为400Ω → 1.6V。

显然,此信号已较大,无需高增益。建议设置PGA增益为1或2,避免饱和。若使用三线制补偿,则差分输入电压仍较小,可适当提升增益。

5.4.3 高共模电压环境下差分输入与增益协同优化

在电机电流检测等场合,共模电压可达数十伏。此时必须使用隔离放大器或差分探头先行调理,再接入ADS1248。在此基础上,根据差分输出幅值选择增益,优先保证信噪比与线性度。

综上所述,PGA不仅是信号放大的工具,更是实现高精度测量的关键环节。合理配置不仅能提升性能,还可增强系统鲁棒性。

6. 数字滤波器配置与抗干扰优化策略

6.1 ADS1248内置数字滤波器类型与特性

ADS1248集成了高度可配置的数字滤波器模块,用于在模数转换后对输出数据进行噪声抑制和频率选择性处理。其核心滤波架构基于FIR(有限冲激响应)结构,支持多种工作模式,能够灵活应对工业环境中常见的工频干扰(50Hz/60Hz)和其他宽带噪声。

6.1.1 FIR滤波器结构与陷波频率设置(50Hz/60Hz抑制)

ADS1248采用多级同步滤波器设计,其主滤波器为Sinc^4型低通滤波器,在特定输出数据速率下可实现对50Hz或60Hz的天然陷波。例如:

  • 当输出数据速率为 20 SPS 时,Sinc4滤波器的第一零点恰好位于50Hz;
  • 当数据速率为 25 SPS 时,零点落在60Hz;
  • 若使用“同时抑制50Hz与60Hz”模式,则需启用特殊滤波配置(如双陷波模式),通过调整调制时钟分频比实现。

该特性使得ADS1248无需额外软件滤波即可有效消除电力系统引入的共模干扰。

6.1.2 滤波器响应时间与输出数据速率权衡

不同滤波模式下的建立时间(settling time)直接影响系统的动态响应能力。以下是典型配置对比表:

输出数据速率 (SPS)滤波器类型建立周期数总建立时间 (ms)50Hz抑制能力
20 Sinc4 4 200
25 Sinc4 4 160
60 Normal 2 33.3
120 Chopping 3 25
3.75 Sinc4 4 1066.7 极强
480 Fast Sinc 1 2.08
960 Bypass 1 1.04

注:建立周期指从新通道切换或增益变更后,需要等待若干个输出周期才能获得稳定有效数据。

6.1.3 不同滤波模式(Normal、Sinc4、Chopping)比较

模式特性描述应用场景建议
Sinc4 高分辨率,强噪声抑制,长延迟 精密称重、RTD测温
Normal 平衡性能,适中响应速度 一般传感器采集
Chopping 内部斩波技术降低偏移漂移 微小信号长期监测
Fast Sinc 快速响应,较低ENOB 动态过程监控
Bypass 无滤波,原始输出 自定义外部滤波算法

可通过CONFIG0寄存器中的 FILT[2:0] 字段设置滤波模式,具体编码如下:

  1. #define FILT_SINC4 0x00
  2. #define FILT_NORMAL 0x01
  3. #define FILT_CHOP 0x03
  4. #define FILT_FAST_SINC 0x05
  5. #define FILT_BYPASS 0x07
  6.  
  7. // 示例:配置为Sinc4模式
  8. write_register(CONFIG0_REG, (read_register(CONFIG0_REG) & 0xF8) | FILT_SINC4);
c运行

6.2 滤波器参数配置与寄存器设置方法

6.2.1 设置数据速率与对应的滤波器截止频率

ADS1248的数据速率由内部调制器时钟(fMOD)分频决定,通过 DRATE[2:0] 位域控制。fMOD通常为4.9152MHz(内部振荡器)或外部MCLK提供。

DRATE编码数据速率(SPS)滤波器截止频率(Hz)推荐用途
0b000 3.75 ~1.5 超高精度静态测量
0b001 7.5 ~3 高稳定性温度采集
0b010 15 ~6 工业仪表通用
0b011 25 ~10 抗60Hz干扰
0b100 60 ~24 快速扫描多通道
0b101 240 ~96 动态信号捕捉
0b110 480 ~192 实时反馈控制
0b111 960 ~384 高速调试模式

设置示例代码:

  1. void set_data_rate(uint8_t drate_val) {
  2. uint8_t config0 = read_register(CONFIG0_REG);
  3. config0 = (config0 & 0x1F) | ((drate_val & 0x07) << 5); // DRATE占bit7~5
  4. write_register(CONFIG0_REG, config0);
  5. }
c运行

6.2.2 修改FILT[2:0]字段选择滤波模式

FILT位位于CONFIG0寄存器bit[4:2],需保留其他位不变进行掩码操作:

  1. void set_filter_mode(uint8_t filt_mode) {
  2. uint8_t reg = read_register(CONFIG0_REG);
  3. reg = (reg & 0xE3) | (filt_mode << 2); // 清除bit4~2并写入
  4. write_register(CONFIG0_REG, reg);
  5. }
c运行

6.2.3 计算建立周期数量以确保稳定输出

当发生以下事件时,必须等待足够建立周期:
- 切换输入通道
- 更改PGA增益
- 改变参考源
- 启动新的连续转换

建立周期数由滤波器类型决定,例如Sinc4需4个周期。若当前数据速率为15SPS(周期≈66.7ms),则总建立时间为:

$$ T_{settle} = 4 \times 66.7ms = 266.8ms $$

推荐在代码中加入延时或状态轮询机制:

  1. void wait_for_data_stable(uint8_t drate, uint8_t filt_mode) {
  2. const float cycle_time_ms = 1000.0f / drate_table[drate];
  3. uint8_t settle_cycles = get_settle_cycles(filt_mode); // 查表获取
  4. delay_ms((uint32_t)(cycle_time_ms * settle_cycles));
  5. }
c运行

6.3 外部电磁干扰抑制与PCB布局建议

6.3.1 模拟地与数字地分离与单点连接

为防止数字开关噪声耦合至敏感模拟前端,应将AGND与DGND物理分割,并通过磁珠或0Ω电阻在靠近ADC处实现单点连接。电源部分采用π型滤波(LC+电容)进一步隔离。

graph TD
    A[模拟电源 AVDD] --> B[LDO稳压器]
    B --> C[10μF陶瓷电容]
    C --> D[ADS1248_AVDD]
    E[数字电源 DVDD] --> F[独立LDO或经磁珠接入]
    F --> G[0.1μF + 1μF去耦]
    G --> H[ADS1248_DVDD]
    I[AGND] -- "单点连接" --> J[DGND]
    K[输入信号] --> L[差分走线]
    L --> M[RC低通滤波]
    M --> N[ADS1248_INP/INM]
mermaid

6.3.2 输入端RC低通滤波器设计与元件选型

在每路输入前增加RC滤波网络,典型参数:
- R = 1kΩ(金属膜电阻,低温漂)
- C = 10nF(C0G/NP0材质,高压贴片电容)

截止频率:
$$ f_c = \frac{1}{2\pi RC} ≈ 15.9kHz $$

可有效衰减射频干扰,同时避免影响有用信号带宽。

6.3.3 屏蔽电缆与差分走线降低噪声耦合

对于远距离传感器连接(如PT100),应使用屏蔽双绞线(STP),屏蔽层仅在一端接地(通常为系统大地),避免地环路电流。PCB上差分走线保持等长、间距恒定,远离高频数字线路。

6.4 综合抗干扰策略在工业环境中的应用

6.4.1 结合软件均值滤波与硬件滤波双重降噪

即使启用了Sinc4滤波,仍可在MCU侧实施移动平均或滑动窗口中值滤波:

  1. #define FILTER_WINDOW_SIZE 8
  2. int32_t window[FILTER_WINDOW_SIZE] = {0};
  3. uint8_t idx = 0;
  4.  
  5. int32_t apply_moving_average(int32_t new_sample) {
  6. window[idx++] = new_sample;
  7. if (idx >= FILTER_WINDOW_SIZE) idx = 0;
  8.  
  9. int64_t sum = 0;
  10. for (int i = 0; i < FILTER_WINDOW_SIZE; i++) {
  11. sum += window[i];
  12. }
  13. return (int32_t)(sum / FILTER_WINDOW_SIZE);
  14. }
c运行

6.4.2 DRDY中断驱动采集避免轮询引入抖动

利用DRDY引脚下降沿触发外部中断,确保采样时刻精确同步滤波器输出周期:

  1. void EXTI_IRQHandler(void) {
  2. if (EXTI_GetITStatus(DRDY_EXTI_LINE)) {
  3. int32_t raw = read_conversion_result(); // 读取24位结果
  4. processed_volts = raw_to_voltage(raw, VREF, GAIN);
  5. xQueueSendFromISR(data_queue, &processed_volts, NULL);
  6. EXTI_ClearITPendingBit(DRDY_EXTI_LINE);
  7. }
  8. }
c运行

此方式可显著降低CPU负载并提升时间确定性。

6.4.3 长期运行下的漂移校正与定期自检机制

部署自动归零(Zero Calibration)与满量程校准流程,建议每8小时执行一次内部短路自检:

  1. void run_offset_calibration(void) {
  2. // 将输入MUX设为短路(AINP=AINN=GND)
  3. write_register(MUX0_REG, 0x08); // INP=AVSS, INM=AVSS
  4. delay_ms(100);
  5. send_command(CALIB_ZERO_INTERNAL);
  6. delay_ms(50);
  7. // 读取校准寄存器验证结果
  8. }
c运行

结合EEPROM存储校准系数,实现全生命周期精度保障。

menu-r.4af5f7ec.gif

简介:ADS1248是一款高精度、低功耗的16位模数转换器,广泛应用于传感器接口、工业自动化和医疗设备等领域。本项目提供了一套完整的C语言驱动程序(包含.c和.h文件),实现了通过SPI通信协议对ADS1248的全面控制。驱动涵盖初始化、读写操作、PGA增益设置、数字滤波器配置及ADC转换控制等核心功能,便于快速集成到嵌入式系统中,显著提升开发效率。该驱动适用于各类需要高精度模拟信号采集的应用场景。

menu-r.4af5f7ec.gif

 

 

 
 

posted on 2025-12-10 10:24  前沿风暴  阅读(1)  评论(0)    收藏  举报