AD7793高精度ADC裸机驱动开发:从SPI时序到传感器测量的完整指南
2026/6/6 18:29:31 网站建设 项目流程

1. 项目概述:从零开始构建AD7793的嵌入式驱动程序

最近在做一个高精度称重传感器的项目,核心的模拟前端选用了ADI的AD7793这款24位Σ-Δ ADC。网上找了一圈,要么是Arduino库,要么是STM32 HAL库的封装,真正贴近底层寄存器操作、能在资源受限的MCU上跑的裸机驱动少之又少。没办法,只能自己动手丰衣足食。折腾了几天,总算把读写时序、配置流程和常见坑点都摸清楚了,代码也稳定跑起来了。这篇笔记就把整个AD7793的驱动开发过程,从芯片理解到代码实现,再到调试避坑,完整地梳理一遍。如果你也在用这颗芯片,或者正在学习如何为类似的SPI接口精密ADC编写底层驱动,希望这篇“踩坑实录”能帮你省下不少时间。

AD7793是一款低功耗、高精度的24位模数转换器,内置PGA和基准电压源,特别适合桥式传感器(比如应变片、压力传感器)和热电偶的测量。它的接口是标准的4线SPI,看起来简单,但时序和配置寄存器有些细节需要特别注意,否则读数不是飘就是根本读不出来。我这次是在Cypress的PSoC 1系列MCU上实现的,但驱动逻辑是通用的,你移植到任何带有SPI外设的MCU(像STM32、GD32、ESP32等)思路都一样。

2. AD7793核心工作机制与驱动设计思路

2.1 芯片功能模块深度解析

要写好驱动,不能只当个“寄存器配置工”,得先明白AD7793内部是怎么干活的。它不是一个简单的ADC,而是一个完整的信号链解决方案。

首先是可编程增益放大器(PGA)。AD7793的PGA增益可以从1到128以2的幂次方调节。这个功能太有用了。比如我的称重传感器是2mV/V的灵敏度,在5V激励下满量程输出才10mV。如果直接用ADC的2.5V基准去量,大部分码值都浪费了,分辨率极差。这时把PGA设置到128倍,10mV的信号就被放大到1.28V,几乎占满了基准电压的一半,有效位数(ENOB)一下子就上来了。驱动里配置ConfigReg时,G2-G0这三位就是干这个的。

其次是Σ-Δ调制器和数字滤波器。这是高精度和低功耗的秘诀。Σ-Δ架构通过过采样和噪声整形,把量化噪声推到高频,然后被后面的数字滤波器狠狠滤掉。AD7793的输出数据速率(ODR)从4.17 Hz到470 Hz可调,速率越低,滤波器陷波越深,噪声越低,精度越高,但响应也越慢。我的称重系统不需要快,稳定更重要,所以选择了16.7 Hz。这个配置在ModeReg里完成,通过设置FS11-FS0位来实现。

最后是内置激励电流源。这是AD7793针对传感器测量的一大亮点。它可以从AIN1AIN2引脚输出最高1mA的可编程恒流源。对于电阻式传感器(如RTD热电阻),直接用它驱动,就能把电阻变化转换成电压变化进行测量,省了外部恒流源电路。我的驱动初始化代码里IoReg0x03,就是开启从IOUT1引脚输出1mA电流。

2.2 驱动架构设计:分层与封装思想

虽然最终呈现的驱动代码看起来是一系列函数,但写的时候我心里是有清晰架构的。一个好的驱动应该层次分明,方便移植和调试。

底层硬件抽象层(HAL):这部分与MCU硬件强相关,主要包括SPI的初始化、收发字节函数,以及片选(CS)、数据就绪(DOUT/RDY)等GPIO的控制。在我的代码里,SPIM_Start(),SPIM_SendTxData(),SPIM_bReadStatus()这些函数都是PSoC Creator环境提供的API,封装了底层SPI模块的操作。如果你用STM32,对应的就是HAL_SPI_TransmitReceive()之类的函数。这一层的目标是:把MCU特定的操作封装成几个统一的接口(如spi_write_byte(),spi_read_byte(),cs_set(),cs_clear())。

核心设备驱动层:这一层只关心AD7793,不关心用什么MCU。它基于HAL层提供的接口,实现AD7793的复位、寄存器读写等基本操作。这就是我代码里的Ad7793Reset(),Ad7793Write(),Ad7793Read()三个核心函数。它们严格遵循AD7793数据手册的SPI时序图。

应用配置层:这是最上层,根据具体的应用需求(测量什么信号、要多大增益、多快速度)来配置AD7793的各个寄存器。函数InisAd7793()就是这个角色。它调用驱动层的写函数,给IO RegMode RegConfig Reg写入特定的值,让芯片按照我的意图工作。

注意:在资源允许的情况下,可以在驱动层和应用层之间再抽象一个“管理层”,用结构体来保存芯片的当前配置(增益、速率、通道等),并提供ad7793_set_gain(),ad7793_set_rate()这样的函数。这样应用代码更清晰。但对于初期调试或资源紧张的MCU,直接像我的代码一样在初始化函数里写死配置,也是最直接有效的方式。

2.3 SPI通信时序的魔鬼细节

AD7793的SPI模式0(CPOL=0, CPHA=0)或模式3(CPOL=1, CPHA=1)都支持。我的驱动里用了点“小花招”:写操作和读命令帧用模式0,而读取数据帧时切换到了模式3。这是因为在PSoC1的SPI Master模块上,这样操作能更可靠地处理时钟极性。其实绝大多数MCU的SPI模块,固定用一种模式(通常是模式0或模式3)都能正常通信,不必在过程中切换。关键在于时序必须满足芯片要求

看数据手册,有两个关键时间参数:t1t2t1是CS下降沿到第一个SCLK上升沿的时间,AD7793要求至少500ns。t2是字节传输之间CS保持低电平的时间,至少也要500ns。这就是为什么我的Ad7793WriteAd7793Read函数里,在拉低CS后和传输数据前后,都调用了Dly(500)(这个延时函数大概在微秒级)。如果你的MCU主频很高(比如100MHz),一个nop指令才10ns,那么简单的for循环延时可能不准,最好用硬件定时器或系统滴答定时器来实现微秒延时。

另一个细节是读写命令码。AD7793的通信帧由1个命令字节和若干数据字节组成。命令字节的最高位(MSB)WEN(写使能)必须为0,次高位R/W决定读写(0写,1读),低6位是寄存器地址。所以:

  • 写寄存器命令:0x00 | (寄存器地址 & 0x3F)
  • 读寄存器命令:0x40 | (寄存器地址 & 0x3F)

我的代码里Ad7793Read函数中i1 = iReg1 | 0x40;就是在构造读命令。

3. 驱动函数逐行详解与避坑指南

3.1 基础延时与复位函数

void Dly(unsigned int di) { unsigned int di1; for(di1=0; di1<di; di1++); }

这是一个非常简单的软件延时函数。di参数是一个无符号整数,循环di次。这里有个大坑:这个延时的具体时间严重依赖于MCU的指令周期和编译器的优化等级。在PSoC1上,我通过示波器大概校准过,Dly(500)能产生大约几十微秒的延时,勉强满足t1/t2的要求。但在其他MCU上,这个函数可能完全不准。强烈建议将其替换为基于系统时钟的精确延时函数,例如STM32的HAL_Delay()(毫秒级)或自己用SysTick写的delay_us()函数。

void Ad7793Reset(void) { unsigned char i; SPIM_Start(SPIM_SPIM_MODE_0|SPIM_SPIM_MSB_FIRST); PRT1DR &= ~0x80; //cs=0 for(i=0; i<5; i++) { while( ! (SPIM_bReadStatus() & SPIM_SPIM_TX_BUFFER_EMPTY ) ); SPIM_SendTxData(0xff); } PRT1DR |= 0x80; //cs=1 Dly(10); }

复位函数是通过向AD7793的DIN引脚连续写入至少32个高电平“1”(即4个字节的0xFF)来实现的。我写了5个字节(40个‘1’),确保足够。操作步骤:

  1. 启动SPI模块(模式0,MSB先行)。
  2. 拉低片选CS(PRT1DR的Bit7对应我硬件连接的CS引脚)。
  3. 循环发送5次0xFF。这里用了while循环等待发送缓冲区空,是标准的查询式发送,避免数据覆盖。
  4. 拉高CS,完成复位。
  5. 短暂延时Dly(10),给芯片一点时间完成内部复位。

避坑提示:复位后,AD7793的所有寄存器都会恢复为默认值。但有一个例外:通信寄存器(Communications Register)是不受复位影响的。这意味着如果复位前SPI通信已经混乱(比如错位了),复位后你可能依然无法正确读写其他寄存器。最稳妥的办法是:上电后,先尝试多次复位操作(比如调用两次Ad7793Reset()),然后再进行初始化。

3.2 寄存器读写函数:时序与模式的把握

void Ad7793Write(unsigned char iReg, unsigned char *iTxbuff, unsigned char iLen) { unsigned char i; SPIM_Start(SPIM_SPIM_MODE_0|SPIM_SPIM_MSB_FIRST); PRT1DR &= ~0x80; //cs=0 Dly(500); // 满足t1 while( ! (SPIM_bReadStatus() & SPIM_SPIM_TX_BUFFER_EMPTY ) ); SPIM_SendTxData(iReg); // 发送写命令+寄存器地址 Dly(100); for(i=0; i<iLen; i++) { while( ! (SPIM_bReadStatus() & SPIM_SPIM_TX_BUFFER_EMPTY ) ); SPIM_SendTxData(iTxbuff[i]); // 发送配置数据 } Dly(500); // 满足t2 PRT1DR |= 0x80; //cs=1 }

写函数是驱动的基础。要点如下:

  • iReg:已经包含了写命令位(0)和寄存器地址。调用时直接传入目标寄存器地址即可,因为写命令位(0)在寄存器地址值中已经隐含(地址值都小于0x40)。
  • Dly(500):在CS拉低后和第一个SCLK之前延时,确保满足t1
  • 发送命令字节后有一个Dly(100),这不是数据手册要求的,但是我为了保险加上的,避免芯片内部处理来不及。
  • 循环发送iLen个数据字节。
  • 最后一个Dly(500)在拉高CS前,确保字节间延时t2
  • 关键检查点:AD7793的寄存器有只读、只写、可读写之分,并且写入某些寄存器(如模式、配置寄存器)后,芯片需要一定时间更新内部状态。写完后立即读可能读到旧值。
void Ad7793Read(unsigned char iReg1, unsigned char *iRxbuff, unsigned char iLen1) { unsigned char i; unsigned char i1; i1 = iReg1 | 0x40; // 构造读命令 Dly(500); SPIM_Start(SPIM_SPIM_MODE_0|SPIM_SPIM_MSB_FIRST); PRT1DR &= ~0x80; //cs=0 while( ! (SPIM_bReadStatus() & SPIM_SPIM_TX_BUFFER_EMPTY ) ); SPIM_SendTxData(i1); // 发送读命令 Dly(100); SPIM_Start(SPIM_SPIM_MODE_3 | SPIM_SPIM_MSB_FIRST); // 切换SPI模式! for(i=0; i<iLen1; i++) { while( ! (SPIM_bReadStatus() & SPIM_SPIM_TX_BUFFER_EMPTY ) ); SPIM_SendTxData(0); // 发送dummy字节以产生SCLK读取数据 while( ! (SPIM_bReadStatus() & SPIM_SPIM_RX_BUFFER_FULL ) ); iRxbuff[i] = SPIM_bReadRxData(); // 读取数据 } Dly(100); PRT1DR |= 0x80; //cs=1 }

读函数是驱动中最容易出问题的地方。

  1. 命令构造i1 = iReg1 | 0x40;将寄存器地址与0x40(即R/W位为1)或操作,形成正确的读命令字节。
  2. 模式切换SPIM_Start(SPIM_SPIM_MODE_3 ...);这一行在我的原始上下文中可能是个隐患。它试图将SPI从模式0切换到模式3。目的是为了在读取数据时,确保SCLK的相位正确。然而,并非所有SPI外设都支持运行时动态改变模式,有些可能需要重新初始化。更通用的做法是:全程使用一种SPI模式(通常用模式0),并确保MCU的SPI时钟极性、相位配置与AD7793期望的完全一致。数据手册显示,在CPHA=0时,数据在SCLK的上升沿被采样,下降沿更新。只要MCU配置与此匹配,就不需要切换模式。
  3. 读取操作:SPI是全双工,读数据的同时必须写数据以产生时钟。这里写入0x00(dummy byte)来驱动SCLK,从机的数据(AD7793的输出)会同时移入MCU的接收缓冲区。这是一个标准操作。
  4. 字节顺序:AD7793的数据寄存器是24位(3字节),传输顺序是MSB先行。我的函数按顺序读取字节到数组iRxbuff中,iRxbuff[0]就是最高字节。

重要经验:如果你发现读回来的数据全是0xFF或0x00,或者完全对不上,第一件事就是用逻辑分析仪抓SPI波形。对照数据手册的时序图,逐位检查:CS时序对不对?命令字节对不对?时钟极性和相位对不对?DOUT线上有没有数据输出?90%的SPI通信问题都能通过波形分析定位。

3.3 初始化函数:配置一个完整的测量链

void InisAd7793(void) { unsigned char iTx[3]; // 1. 配置IO寄存器,开启1mA激励电流输出 iTx[0] = 0x03; Ad7793Write(IoReg, iTx, 1); // 2. 配置模式寄存器,连续转换模式,输出数据速率16.7Hz iTx[0] = 0x00; iTx[1] = 0x0A; Ad7793Write(ModReg, iTx, 2); // 3. 配置配置寄存器:增益128,单极性,缓冲使能,基准电压选择内部 iTx[0] = 0x30; // 0011 0000 iTx[1] = 0x07; // 0000 0111 Ad7793Write(ConfigReg, iTx, 2); // 4. 配置PSoC的某个端口?(此行与AD7793无关,是硬件特定设置) PRT1GS = 0x0c; }

这个初始化函数配置了一个典型的桥式传感器测量场景:

  1. IO寄存器 (地址0x02):写入0x03。二进制0000 0011IOEN=1(使能IO控制),IEXCDIR[1:0]=01(电流从IOUT1引脚流出),IEXCEN[1:0]=01(使能1mA激励电流)。这样,AD7793就能为传感器电桥提供稳定的1mA激励了。
  2. 模式寄存器 (地址0x00):写入0x000A。这是一个16位寄存器。
    • MD1:MD0 = 00:连续转换模式。芯片会不停地进行AD转换。
    • G2:G0 = 000:这里增益为1?等等,这里似乎有矛盾0x000A的二进制是0000 0000 0000 1010。低8位是0x0A。根据数据手册,模式寄存器的低8位是FS11-FS4,高8位的低4位是FS3-FS0G2-G00x0A写入低字节,它对应输出数据速率设置的一部分。而增益位G2-G0是在高字节。我代码里iTx[0]=0x00, iTx[1]=0x0A,实际上高字节全是0,意味着增益G2-G0=000(增益1)。这可能是一个笔误或未完成的代码。通常配置速率16.7Hz和增益128,需要写入类似0x080A这样的值(G2-G0=001对应增益2,若要增益128需要G2-G0=111,即高字节为0x38,整体可能是0x380A)。这里需要根据你的实际传感器信号幅度来修正增益值
  3. 配置寄存器 (地址0x01):写入0x3007
    • iTx[0]=0x30(高字节):0011 0000BUF=1(内部缓冲器使能,允许高阻抗信号源),REFDET=0(未使用),BAL=0(未使用),CH2-CH0=000(选择AIN1(+)和AIN2(-)作为输入通道)。
    • iTx[1]=0x07(低字节):0000 0111BURN=0(不开启烧毁电流),UNIPOLAR=1(单极性输入模式,输入范围0到Vref),GAIN2-GAIN0=111(增益128)。注意:这里低字节的增益设置111(增益128)与前面模式寄存器中可能设置的增益冲突。实际上,增益是由配置寄存器(ConfigReg)的GAIN2-GAIN0位决定的,模式寄存器里的G2-G0位是保留位,应写0。所以我的模式寄存器写入0x000A是正确的(增益位为0),而增益128是在配置寄存器里设置的0x07。这样就对了!
  4. PRT1GS=0x0c;:这一行是PSoC1特有的端口全局选择寄存器配置,与AD7793芯片本身无关,可能是用来配置某个引脚为特殊功能(比如UART)。在纯粹的AD7793驱动中,这行应该去掉。

初始化完成后,AD7793就会按照设定(通道AIN1/AIN2,增益128,单极性,内部基准,16.7Hz速率,连续转换)开始工作。数据转换完成后,会更新数据寄存器,并通过DOUT/RDY引脚输出低电平(如果使能了RDY功能)或通过SPI读取。

4. 驱动移植与系统集成实战

4.1 移植到其他MCU平台的要点

我的驱动是基于PSoC1的,但核心逻辑通用。移植到其他平台,你需要重写底层硬件相关部分:

  1. SPI初始化:配置你的MCU SPI为主机模式、模式0(或3)、MSB先行、时钟频率(建议在1MHz以下,初期调试可更低如100kHz)。注意数据大小通常为8位。
  2. GPIO初始化:配置CS引脚为普通推挽输出。如果使用DOUT/RDY引脚来查询转换是否完成(推荐方式),则将其配置为上拉输入。
  3. 实现基础函数
    • void spi_write_byte(uint8_t data)
    • uint8_t spi_read_byte(void)(通常通过同时读写实现)
    • void cs_low(void)void cs_high(void)
    • void delay_us(uint32_t us)(精确微秒延时)
  4. 重构核心函数:用你实现的上述函数,替换掉我代码中SPIM_*PRT1DR相关的部分,以及不精确的Dly()函数。注意保持原有的时序逻辑和延时。

一个STM32 HAL库版本的Ad7793Write函数骨架可能长这样:

void AD7793_WriteReg(uint8_t reg_addr, uint8_t *data, uint8_t len) { uint8_t cmd = reg_addr & 0x3F; // 写命令,WEN=0, R/W=0 cs_low(); delay_us(10); // 满足t1,可根据实际时钟调整 HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); delay_us(2); // 短延时 HAL_SPI_Transmit(&hspi1, data, len, HAL_MAX_DELAY); delay_us(10); // 满足t2 cs_high(); }

4.2 数据读取与转换:从码值到物理量

初始化并启动转换后,如何获取有用的电压或传感器值?

  1. 等待数据就绪:有两种方式。

    • 查询DOUT/RDY引脚(推荐):将此引脚配置为MCU输入。当转换完成时,该引脚会变低电平。你的主循环可以不断查询此引脚,变低后再去读取数据,效率最高。
    • 查询状态寄存器:通过SPI读取状态寄存器(地址0x00),检查RDY位(bit7)是否为0。但每次查询都需要一次SPI通信,会增加总线负担和功耗。
  2. 读取24位数据:数据寄存器(地址0x02)是24位的,需要读3个字节。

    unsigned char rx_data[3]; long adc_value = 0; Ad7793Read(DataReg, rx_data, 3); // DataReg 应为 0x02 adc_value = ((long)rx_data[0] << 16) | ((long)rx_data[1] << 8) | (long)rx_data[2];

    注意:adc_value是24位有符号数(在单极性模式下,它实际上是无符号的,但符号位为0)。需要将其转换为longint32_t类型再进行移位,避免溢出。

  3. 码值转换为电压

    • 单极性模式(0V到Vref)电压 = (adc_value / 2^24) * Vref例如,Vref = 2.5Vadc_value = 8388608(即0x800000的一半),则电压 = (8388608 / 16777216) * 2.5 = 1.25V。
    • 双极性模式(-Vref到+Vref)电压 = ((adc_value / 2^23) - 1) * Vref此时adc_value是24位有符号补码。
  4. 转换为传感器物理量:这需要结合你的传感器灵敏度、激励电压和电路增益。

    • 假设你使用1mA激励电流(Iex)驱动一个350Ω的应变片(Rg)。
    • 激励电压Vex = Iex * Rg = 1mA * 350Ω = 0.35V
    • 应变片受力后电阻变化ΔR,产生差分电压Vdiff = Iex * ΔR
    • 该差分电压经过AD7793的PGA放大Gain倍后,送入ADC。
    • 所以,ΔR = (电压读数 / Gain) / Iex
    • 再根据应变片的灵敏系数K,计算出应变值ε = (ΔR / Rg) / K

4.3 低功耗与噪声优化配置心得

AD7793的一大优势是低功耗,在电池供电设备中很有用。

  1. 功耗模式:模式寄存器(ModeReg)的MD1:MD0位可以设置为10(单次转换模式)或11(掉电模式)。在不需要连续测量的场合,使用单次转换模式,测量一次后芯片自动进入待机,功耗可低至1μA。我的代码用了连续模式,适合需要实时监控的场景。

  2. 输出数据速率与噪声:速率越低,内置数字滤波器的截止频率越低,对噪声的抑制越好,但响应速度也慢。下表是不同速率下的噪声性能(典型值,增益=128,内部基准):

    输出数据速率 (Hz)有效分辨率 (Bits, 无噪声)典型噪声 (μV rms)
    4.1723.50.55
    16.722.51.1
    4702012

    选择原则:在满足系统响应速度的前提下,尽量选择低的输出数据速率。我的项目选16.7Hz,是在响应速度(约60ms更新一次)和噪声性能之间取的平衡。

  3. 基准电压选择:我使用了内部2.5V基准,它简单方便,但温漂典型值10ppm/°C。如果对长期稳定性要求极高,应使用外部低漂移基准源(如REF5025),并在配置寄存器中设置REFDET=0(使用外部基准)。

  4. 缓冲器与输入阻抗:配置寄存器中BUF=1使能了内部缓冲器。这允许信号源有较高的输出阻抗(兆欧级),但会引入额外的噪声(约50nV/√Hz)并限制输入共模电压范围。如果信号源阻抗很低(如小于10kΩ),且共模电压在合适范围内,可以关闭缓冲器(BUF=0)以获得更低的噪声。

5. 调试过程中遇到的典型问题与解决方案

开发这套驱动时,我踩遍了几乎所有能踩的坑。这里把常见问题和解决办法列出来,希望能让你一路绿灯。

5.1 问题一:SPI通信完全无响应,读回数据全是0xFF或0x00

现象:调用读写函数后,读取任何寄存器返回的值都是固定的0xFF或0x00,或者状态寄存器的RDY位永远为1(不更新)。

排查步骤

  1. 硬件三连查
    • 电源和地:用万用表测量AD7793的AVDDDVDD引脚电压是否正确(通常3.3V或5V),地线连接是否牢固。
    • 基准电压:测量REFIN(+)REFIN(-)之间的电压,内部基准时应为2.5V左右。
    • 时钟:AD7793需要外部晶振或时钟输入。检查晶振是否起振(用示波器测CLK引脚),频率是否正确(典型值4.9152MHz或2.4576MHz)。
  2. SPI信号抓取:使用逻辑分析仪连接CSSCLKDINDOUT四条线。
    • 看CS:是否在传输每个字节前拉低,之后拉高?脉宽是否太短?
    • 看时序:CS拉低后,是否等待了足够时间(>500ns)才出现第一个SCLK上升沿?字节间CS是否保持低电平足够时间?
    • 看数据:发送的命令字节是否正确?读操作时,在SCLK的对应边沿,DOUT线上是否有数据输出?
  3. 软件配置核对
    • SPI模式是否匹配(CPOL, CPHA)?强烈建议先用模式0(CPOL=0, CPHA=0)尝试
    • MCU的SPI时钟频率是否过高?初期调试建议降到100kHz以下。
    • GPIO初始化是否正确?CS引脚是否配置为输出?DOUT/RDY是否配置为输入(如果使用)?

解决方案:根据逻辑分析仪波形调整延时参数,修正SPI模式配置,降低时钟频率。确保硬件连接无误。

5.2 问题二:可以读写寄存器,但转换数据不正确或不稳定

现象:能正确读写配置、模式寄存器,但读取的数据寄存器值跳动很大,或者换算出的电压值与预期相差甚远。

排查步骤

  1. 检查模拟前端
    • 输入引脚AIN+AIN-是否接对?是否有虚焊?
    • 传感器激励是否正常?用万用表测量电桥或信号源的输出差分电压是否在预期范围内,是否在AD7793的允许输入范围内(与增益和基准电压有关)。
    • PGA增益设置是否合适?输入信号过小会导致分辨率低,过大则会饱和,输出为全0或全1。
  2. 检查基准电压:基准电压的噪声和稳定性直接决定ADC精度。测量基准电压引脚,看是否稳定在标称值(如2.5V),纹波是否过大。
  3. 检查配置寄存器
    • BUF位设置是否正确?高阻抗源必须使能缓冲。
    • UNIPOLAR位设置是否正确?你的输入信号是单极性(0-Vref)还是双极性(-Vref/2 到 +Vref/2)?
    • 输入通道CH2-CH0选择是否正确?是否选中了你实际接线的那个通道对?
  4. 检查模式寄存器:是否已正确启动转换?MD1:MD0不能是11(掉电模式)。在连续转换模式下,需要等待第一次转换完成(RDY变低)后再读取数据。
  5. 数据读取与处理
    • 是否读取了完整的3个字节?字节顺序(MSB first)是否正确?
    • 码值转换为电压的公式是否正确?特别是双极性模式下的偏移二进制转补码计算。

解决方案:使用一个已知的、稳定的直流电压源(如分压得到的1.0V)作为输入,配置为合适的增益(使输入电压接近但不超过满量程),测试ADC读数。如果读数稳定且准确,说明驱动和ADC本身没问题,问题出在传感器或前端电路。如果读数依然不准,则需检查基准源和配置。

5.3 问题三:DOUT/RDY引脚功能异常

现象:希望用DOUT/RDY引脚来中断或查询转换完成,但该引脚一直为高或一直为低。

排查步骤

  1. 引脚配置DOUT/RDY是复用引脚。它的功能由IO寄存器IOEN位控制。只有当IOEN=0时,该引脚才作为DOUT/RDY输出。我的初始化代码中IOEN=1,这实际上将该引脚配置为了电流源输出(IOUT1)!这是一个严重的配置错误。如果你想使用RDY功能,必须设置IOEN=0
  2. 上拉电阻DOUT/RDY是开漏输出,需要外部上拉电阻(通常10kΩ)到DVDD。检查硬件上是否有这个上拉电阻。
  3. 软件查询:如果配置正确且有上拉,转换未完成时引脚为高电平,完成后为低电平。在主循环中查询该引脚状态,应在转换周期内看到高低变化。

解决方案:如果需要使用RDY功能,修改IO寄存器的配置,将iTx[0]0x03改为0x00IOEN=0,禁用IO控制,使能DOUT/RDY)。同时确保硬件上有上拉电阻。

5.4 问题速查表

问题现象可能原因排查方法
读回数据全为0xFF1. SPI通信失败
2. 芯片未上电或复位
3. CS时序错误
1. 逻辑分析仪抓波形
2. 查电源、地、复位
3. 检查CS延时t1
读回数据全为0x001. 输入信号负饱和或为0
2. 基准电压为0
3. 配置错误(如掉电模式)
1. 测量输入电压
2. 测量基准电压
3. 读取模式寄存器确认
数据跳动大(噪声)1. 电源纹波大
2. 基准噪声大
3. 输入信号受干扰
4. 输出数据速率太高
1. 电源加滤波电容
2. 使用更干净的基准源
3. 检查布线,屏蔽信号线
4. 降低输出数据速率
RDY引脚一直高电平1.IOEN位被误设为1
2. 无外部上拉电阻
3. 未启动转换
1. 检查IO寄存器配置
2. 硬件添加上拉电阻
3. 检查模式寄存器
转换值线性度差1. 输入信号超出允许范围(饱和)
2. PGA增益设置不当
3. 缓冲器未使能导致输入阻抗不匹配
1. 确保VIN < Vref / Gain
2. 调整增益使信号接近满量程
3. 高阻抗源需设置BUF=1

最后,分享一个调试小技巧:善用状态寄存器(Status Reg)。上电后,先别急着配置,尝试读取状态寄存器(地址0x00)。如果通信正常,你应该能读到一个值,其中RDY位很可能是1(表示未就绪),ERR位为0(表示无错误)。这是一个快速验证SPI通信是否建立的好方法。如果连状态寄存器都读不回正确的RDY位状态,那问题肯定出在最基础的电源、时钟或SPI通信上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询