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针对传感器测量的一大亮点。它可以从AIN1或AIN2引脚输出最高1mA的可编程恒流源。对于电阻式传感器(如RTD热电阻),直接用它驱动,就能把电阻变化转换成电压变化进行测量,省了外部恒流源电路。我的驱动初始化代码里IoReg写0x03,就是开启从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 Reg、Mode Reg、Config 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)都能正常通信,不必在过程中切换。关键在于时序必须满足芯片要求。
看数据手册,有两个关键时间参数:t1和t2。t1是CS下降沿到第一个SCLK上升沿的时间,AD7793要求至少500ns。t2是字节传输之间CS保持低电平的时间,至少也要500ns。这就是为什么我的Ad7793Write和Ad7793Read函数里,在拉低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’),确保足够。操作步骤:
- 启动SPI模块(模式0,MSB先行)。
- 拉低片选CS(
PRT1DR的Bit7对应我硬件连接的CS引脚)。 - 循环发送5次0xFF。这里用了
while循环等待发送缓冲区空,是标准的查询式发送,避免数据覆盖。 - 拉高CS,完成复位。
- 短暂延时
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 }读函数是驱动中最容易出问题的地方。
- 命令构造:
i1 = iReg1 | 0x40;将寄存器地址与0x40(即R/W位为1)或操作,形成正确的读命令字节。 - 模式切换:
SPIM_Start(SPIM_SPIM_MODE_3 ...);这一行在我的原始上下文中可能是个隐患。它试图将SPI从模式0切换到模式3。目的是为了在读取数据时,确保SCLK的相位正确。然而,并非所有SPI外设都支持运行时动态改变模式,有些可能需要重新初始化。更通用的做法是:全程使用一种SPI模式(通常用模式0),并确保MCU的SPI时钟极性、相位配置与AD7793期望的完全一致。数据手册显示,在CPHA=0时,数据在SCLK的上升沿被采样,下降沿更新。只要MCU配置与此匹配,就不需要切换模式。 - 读取操作:SPI是全双工,读数据的同时必须写数据以产生时钟。这里写入
0x00(dummy byte)来驱动SCLK,从机的数据(AD7793的输出)会同时移入MCU的接收缓冲区。这是一个标准操作。 - 字节顺序: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; }这个初始化函数配置了一个典型的桥式传感器测量场景:
- IO寄存器 (地址0x02):写入
0x03。二进制0000 0011。IOEN=1(使能IO控制),IEXCDIR[1:0]=01(电流从IOUT1引脚流出),IEXCEN[1:0]=01(使能1mA激励电流)。这样,AD7793就能为传感器电桥提供稳定的1mA激励了。 - 模式寄存器 (地址0x00):写入
0x000A。这是一个16位寄存器。MD1:MD0 = 00:连续转换模式。芯片会不停地进行AD转换。G2:G0 = 000:这里增益为1?等等,这里似乎有矛盾。0x000A的二进制是0000 0000 0000 1010。低8位是0x0A。根据数据手册,模式寄存器的低8位是FS11-FS4,高8位的低4位是FS3-FS0和G2-G0。0x0A写入低字节,它对应输出数据速率设置的一部分。而增益位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)。这里需要根据你的实际传感器信号幅度来修正增益值。
- 配置寄存器 (地址0x01):写入
0x3007。iTx[0]=0x30(高字节):0011 0000。BUF=1(内部缓冲器使能,允许高阻抗信号源),REFDET=0(未使用),BAL=0(未使用),CH2-CH0=000(选择AIN1(+)和AIN2(-)作为输入通道)。iTx[1]=0x07(低字节):0000 0111。BURN=0(不开启烧毁电流),UNIPOLAR=1(单极性输入模式,输入范围0到Vref),GAIN2-GAIN0=111(增益128)。注意:这里低字节的增益设置111(增益128)与前面模式寄存器中可能设置的增益冲突。实际上,增益是由配置寄存器(ConfigReg)的GAIN2-GAIN0位决定的,模式寄存器里的G2-G0位是保留位,应写0。所以我的模式寄存器写入0x000A是正确的(增益位为0),而增益128是在配置寄存器里设置的0x07。这样就对了!
PRT1GS=0x0c;:这一行是PSoC1特有的端口全局选择寄存器配置,与AD7793芯片本身无关,可能是用来配置某个引脚为特殊功能(比如UART)。在纯粹的AD7793驱动中,这行应该去掉。
初始化完成后,AD7793就会按照设定(通道AIN1/AIN2,增益128,单极性,内部基准,16.7Hz速率,连续转换)开始工作。数据转换完成后,会更新数据寄存器,并通过DOUT/RDY引脚输出低电平(如果使能了RDY功能)或通过SPI读取。
4. 驱动移植与系统集成实战
4.1 移植到其他MCU平台的要点
我的驱动是基于PSoC1的,但核心逻辑通用。移植到其他平台,你需要重写底层硬件相关部分:
- SPI初始化:配置你的MCU SPI为主机模式、模式0(或3)、MSB先行、时钟频率(建议在1MHz以下,初期调试可更低如100kHz)。注意数据大小通常为8位。
- GPIO初始化:配置CS引脚为普通推挽输出。如果使用
DOUT/RDY引脚来查询转换是否完成(推荐方式),则将其配置为上拉输入。 - 实现基础函数:
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)(精确微秒延时)
- 重构核心函数:用你实现的上述函数,替换掉我代码中
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 数据读取与转换:从码值到物理量
初始化并启动转换后,如何获取有用的电压或传感器值?
等待数据就绪:有两种方式。
- 查询
DOUT/RDY引脚(推荐):将此引脚配置为MCU输入。当转换完成时,该引脚会变低电平。你的主循环可以不断查询此引脚,变低后再去读取数据,效率最高。 - 查询状态寄存器:通过SPI读取状态寄存器(地址0x00),检查
RDY位(bit7)是否为0。但每次查询都需要一次SPI通信,会增加总线负担和功耗。
- 查询
读取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)。需要将其转换为long或int32_t类型再进行移位,避免溢出。码值转换为电压:
- 单极性模式(0V到Vref):
电压 = (adc_value / 2^24) * Vref例如,Vref = 2.5V,adc_value = 8388608(即0x800000的一半),则电压 = (8388608 / 16777216) * 2.5 = 1.25V。 - 双极性模式(-Vref到+Vref):
电压 = ((adc_value / 2^23) - 1) * Vref此时adc_value是24位有符号补码。
- 单极性模式(0V到Vref):
转换为传感器物理量:这需要结合你的传感器灵敏度、激励电压和电路增益。
- 假设你使用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。
- 假设你使用1mA激励电流(
4.3 低功耗与噪声优化配置心得
AD7793的一大优势是低功耗,在电池供电设备中很有用。
功耗模式:模式寄存器(ModeReg)的
MD1:MD0位可以设置为10(单次转换模式)或11(掉电模式)。在不需要连续测量的场合,使用单次转换模式,测量一次后芯片自动进入待机,功耗可低至1μA。我的代码用了连续模式,适合需要实时监控的场景。输出数据速率与噪声:速率越低,内置数字滤波器的截止频率越低,对噪声的抑制越好,但响应速度也慢。下表是不同速率下的噪声性能(典型值,增益=128,内部基准):
输出数据速率 (Hz) 有效分辨率 (Bits, 无噪声) 典型噪声 (μV rms) 4.17 23.5 0.55 16.7 22.5 1.1 470 20 12 选择原则:在满足系统响应速度的前提下,尽量选择低的输出数据速率。我的项目选16.7Hz,是在响应速度(约60ms更新一次)和噪声性能之间取的平衡。
基准电压选择:我使用了内部2.5V基准,它简单方便,但温漂典型值10ppm/°C。如果对长期稳定性要求极高,应使用外部低漂移基准源(如REF5025),并在配置寄存器中设置
REFDET=0(使用外部基准)。缓冲器与输入阻抗:配置寄存器中
BUF=1使能了内部缓冲器。这允许信号源有较高的输出阻抗(兆欧级),但会引入额外的噪声(约50nV/√Hz)并限制输入共模电压范围。如果信号源阻抗很低(如小于10kΩ),且共模电压在合适范围内,可以关闭缓冲器(BUF=0)以获得更低的噪声。
5. 调试过程中遇到的典型问题与解决方案
开发这套驱动时,我踩遍了几乎所有能踩的坑。这里把常见问题和解决办法列出来,希望能让你一路绿灯。
5.1 问题一:SPI通信完全无响应,读回数据全是0xFF或0x00
现象:调用读写函数后,读取任何寄存器返回的值都是固定的0xFF或0x00,或者状态寄存器的RDY位永远为1(不更新)。
排查步骤:
- 硬件三连查:
- 电源和地:用万用表测量AD7793的
AVDD和DVDD引脚电压是否正确(通常3.3V或5V),地线连接是否牢固。 - 基准电压:测量
REFIN(+)和REFIN(-)之间的电压,内部基准时应为2.5V左右。 - 时钟:AD7793需要外部晶振或时钟输入。检查晶振是否起振(用示波器测CLK引脚),频率是否正确(典型值4.9152MHz或2.4576MHz)。
- 电源和地:用万用表测量AD7793的
- SPI信号抓取:使用逻辑分析仪连接
CS、SCLK、DIN、DOUT四条线。- 看CS:是否在传输每个字节前拉低,之后拉高?脉宽是否太短?
- 看时序:CS拉低后,是否等待了足够时间(>500ns)才出现第一个SCLK上升沿?字节间CS是否保持低电平足够时间?
- 看数据:发送的命令字节是否正确?读操作时,在SCLK的对应边沿,DOUT线上是否有数据输出?
- 软件配置核对:
- SPI模式是否匹配(CPOL, CPHA)?强烈建议先用模式0(CPOL=0, CPHA=0)尝试。
- MCU的SPI时钟频率是否过高?初期调试建议降到100kHz以下。
- GPIO初始化是否正确?CS引脚是否配置为输出?DOUT/RDY是否配置为输入(如果使用)?
解决方案:根据逻辑分析仪波形调整延时参数,修正SPI模式配置,降低时钟频率。确保硬件连接无误。
5.2 问题二:可以读写寄存器,但转换数据不正确或不稳定
现象:能正确读写配置、模式寄存器,但读取的数据寄存器值跳动很大,或者换算出的电压值与预期相差甚远。
排查步骤:
- 检查模拟前端:
- 输入引脚
AIN+和AIN-是否接对?是否有虚焊? - 传感器激励是否正常?用万用表测量电桥或信号源的输出差分电压是否在预期范围内,是否在AD7793的允许输入范围内(与增益和基准电压有关)。
- PGA增益设置是否合适?输入信号过小会导致分辨率低,过大则会饱和,输出为全0或全1。
- 输入引脚
- 检查基准电压:基准电压的噪声和稳定性直接决定ADC精度。测量基准电压引脚,看是否稳定在标称值(如2.5V),纹波是否过大。
- 检查配置寄存器:
BUF位设置是否正确?高阻抗源必须使能缓冲。UNIPOLAR位设置是否正确?你的输入信号是单极性(0-Vref)还是双极性(-Vref/2 到 +Vref/2)?- 输入通道
CH2-CH0选择是否正确?是否选中了你实际接线的那个通道对?
- 检查模式寄存器:是否已正确启动转换?
MD1:MD0不能是11(掉电模式)。在连续转换模式下,需要等待第一次转换完成(RDY变低)后再读取数据。 - 数据读取与处理:
- 是否读取了完整的3个字节?字节顺序(MSB first)是否正确?
- 码值转换为电压的公式是否正确?特别是双极性模式下的偏移二进制转补码计算。
解决方案:使用一个已知的、稳定的直流电压源(如分压得到的1.0V)作为输入,配置为合适的增益(使输入电压接近但不超过满量程),测试ADC读数。如果读数稳定且准确,说明驱动和ADC本身没问题,问题出在传感器或前端电路。如果读数依然不准,则需检查基准源和配置。
5.3 问题三:DOUT/RDY引脚功能异常
现象:希望用DOUT/RDY引脚来中断或查询转换完成,但该引脚一直为高或一直为低。
排查步骤:
- 引脚配置:
DOUT/RDY是复用引脚。它的功能由IO寄存器的IOEN位控制。只有当IOEN=0时,该引脚才作为DOUT/RDY输出。我的初始化代码中IOEN=1,这实际上将该引脚配置为了电流源输出(IOUT1)!这是一个严重的配置错误。如果你想使用RDY功能,必须设置IOEN=0。 - 上拉电阻:
DOUT/RDY是开漏输出,需要外部上拉电阻(通常10kΩ)到DVDD。检查硬件上是否有这个上拉电阻。 - 软件查询:如果配置正确且有上拉,转换未完成时引脚为高电平,完成后为低电平。在主循环中查询该引脚状态,应在转换周期内看到高低变化。
解决方案:如果需要使用RDY功能,修改IO寄存器的配置,将iTx[0]从0x03改为0x00(IOEN=0,禁用IO控制,使能DOUT/RDY)。同时确保硬件上有上拉电阻。
5.4 问题速查表
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 读回数据全为0xFF | 1. SPI通信失败 2. 芯片未上电或复位 3. CS时序错误 | 1. 逻辑分析仪抓波形 2. 查电源、地、复位 3. 检查CS延时 t1 |
| 读回数据全为0x00 | 1. 输入信号负饱和或为0 2. 基准电压为0 3. 配置错误(如掉电模式) | 1. 测量输入电压 2. 测量基准电压 3. 读取模式寄存器确认 |
| 数据跳动大(噪声) | 1. 电源纹波大 2. 基准噪声大 3. 输入信号受干扰 4. 输出数据速率太高 | 1. 电源加滤波电容 2. 使用更干净的基准源 3. 检查布线,屏蔽信号线 4. 降低输出数据速率 |
| RDY引脚一直高电平 | 1.IOEN位被误设为12. 无外部上拉电阻 3. 未启动转换 | 1. 检查IO寄存器配置 2. 硬件添加上拉电阻 3. 检查模式寄存器 |
| 转换值线性度差 | 1. 输入信号超出允许范围(饱和) 2. PGA增益设置不当 3. 缓冲器未使能导致输入阻抗不匹配 | 1. 确保VIN < Vref / Gain2. 调整增益使信号接近满量程 3. 高阻抗源需设置 BUF=1 |
最后,分享一个调试小技巧:善用状态寄存器(Status Reg)。上电后,先别急着配置,尝试读取状态寄存器(地址0x00)。如果通信正常,你应该能读到一个值,其中RDY位很可能是1(表示未就绪),ERR位为0(表示无错误)。这是一个快速验证SPI通信是否建立的好方法。如果连状态寄存器都读不回正确的RDY位状态,那问题肯定出在最基础的电源、时钟或SPI通信上。