AD7705与STC89C52的串口数据采集实战:从SPI配置到调试
2026/8/31 18:42:27 网站建设 项目流程

简介:本资源是一套基于AD7705高精度ADC与STC89C52单片机协同工作的嵌入式数据采集系统完整工程,面向电子类专业学生、嵌入式初学者及工业测控项目开发者,解决模拟信号高分辨率采集、串行通信驱动与单片机底层编程实践等核心问题。压缩包共41个文件,涵盖5个C源文件(含main.c与底层驱动)、4个头文件(定义寄存器与通信协议)、3个HEX可执行文件(支持直接烧录)、2个PNG运行截图(含串口工具实测波形与结果)、2个批处理脚本(用于自动打包与Hex复制)以及Keil工程文件(uvproj/uvopt)和编译中间文件(obj/lst/m51),整体大小仅207KB,轻量易上手。已有197人学习下载,资源结构清晰,包含电路设计说明、例程说明文档及反面案例分析图,特别提供IO口误用导致通信异常的实测波形对比,有助于快速定位SPI时序与硬件连接常见问题,是掌握Σ-Δ型ADC与51单片机接口开发的典型实战范例。 做采集项目这么多年,AD7705这颗芯片我前前后后用在了好几个称重和压力采集的方案里,搭配STC89C52做数据读取、再从串口丢到电脑上位机,属于非常经典也特别适合新手入门的组合。这篇就围绕“AD7705 + STC89C52 + 串口输出”这个项目,把选型思路、硬件接法、寄存器配置、代码流程和调试踩坑一次性讲透。

如果你手上正好有AD7705或者国产兼容的TM7705模块,想用51单片机把它跑起来,这篇文章可以直接照着抄。即便你用的是STM32或者其他MCU,只要搞懂了AD7705的SPI通信和寄存器操作逻辑,迁移过去也就是改个底层接口的事。

1. 项目整体设计与选型考量

1.1 为什么选STC89C52加AD7705这个组合

很多入门做数据采集的朋友,第一个想到的ADC往往是PCF8591这种8位芯片,便宜是便宜,但测个电池电压还行,真要放在称重、流量计、压力变送器这种对精度有要求的场景里,8位分辨率完全不够看。STC89C52本身没有内置ADC,外挂一颗高精度ADC就成了最直接的方案。

AD7705这颗芯片在工业仪表里非常常见,16位分辨率,内置PGA可编程增益放大器,支持双通道差分输入,内部还有数字低通滤波器,可以直接把传感器出来的毫伏级信号放大后再转换,省掉了一堆外部运放电路。TM7705是国产兼容型号,引脚定义、寄存器结构和通信时序基本一致,价格更便宜,市面上大量模块用的就是TM7705,拿来替代AD7705完全没问题。

选择STC89C52来做主控,不是因为它的性能有多强,而是因为它足够简单、足够经典。51单片机的IO模拟SPI非常直观,寄存器少,代码逻辑一眼能看穿,特别适合用来理解AD7705的通信机制。如果你一上来就用带硬件SPI的STM32,反而会因为硬件外设的配置复杂度搞不清到底是芯片的问题还是SPI配置的问题。

1.2 系统架构与数据流向

整个系统的数据流是这样一个链条:传感器输出的模拟电压信号进入AD7705的差分输入端,经过芯片内部的PGA放大和Σ-Δ转换后,变成16位数字量。STC89C52通过模拟SPI总线去读这个数字量,然后通过UART串口把数据发送给电脑端的串口调试助手。上位机收到的是可以直接解析的原始AD值或换算后的物理量。

这里有个容易混淆的点:AD7705的接口是SPI,不是串口。标题里说的“串口”,指的是MCU和PC之间那条UART链路。很多刚接触的朋友以为AD7705可以直接接串口调试助手,这是不对的。正确理解是两条独立的总线:MCU和ADC之间走SPI,MCU和PC之间走UART。搞清楚这一点,后面调试的时候就不会晕。

另外一个需要提前明确的是供电问题。STC89C52的标准工作电压是5V,而AD7705的数据手册上写了两种供电范围:2.7V到3.3V,或者4.75V到5.25V。也就是说,AD7705在5V供电下是可以正常工作的,这给51方案带来了很大便利,不需要额外做电平转换。但我建议模块上的AVDD和DVDD都用5V供电,然后留出单独的参考电压引脚接一个稳定的2.5V基准源。后面会详细说为什么参考电压这么关键。

2. AD7705核心细节与硬件要点

2.1 AD7705到底是一颗什么样的ADC

先把这个芯片的基本规格说清楚。AD7705是16位输出的Σ-Δ型ADC,支持两路差分输入通道,内部集成可编程增益放大器,增益倍数是1、2、4、8、16、32、64、128可选。芯片内部还有一个数字低通滤波器,可以通过配置时钟寄存器的三个低两位来选择输出数据更新速率,在工频50Hz环境下选择50Hz或60Hz的输出速率,可以起到很好的工频干扰抑制效果。

这里要特别说明一个规格上的误区。很多资料把AD7705说成“24位ADC”,严格来说是不对的。AD7705数据手册上标称的是16位分辨率,但因为它内部有数字滤波和过采样机制,在低输出速率下有效分辨率可以做到超过16位,比如在50Hz更新速率下能到18位甚至20位的有效位数。这就是为什么市面上有人把它往“24位”上靠,其实严谨的表述是:16位无失码,低速率下有效分辨率可以做到20位左右。TM7705作为国产兼容芯片,电性能指标和寄存器定义基本对应,实际使用中按AD7705的文档来操作即可。

还有一个隐藏得分点需要知道:AD7705内置了自校准和系统校准功能。芯片可以在上电后对失调和增益误差做校准,校准结果存放在偏移寄存器和增益寄存器中。虽然对于一般应用不做校准也能用,但既然芯片提供了这个功能,最好还是用起来,精度有明显的提升。

2.2 引脚连接与硬件设计注意事项

我实际用的接法如下表,这套连接在STC89C52的最小系统板上直接就能工作。

AD7705引脚功能连接去向
SCLKSPI时钟P1.5
DINSPI数据输入P1.6
DOUTSPI数据输出P1.7
CS片选,低有效P1.4
DRDY数据就绪,低有效P1.3
RESET复位P1.2
AVDD模拟电源5V
DVDD数字电源5V
GND公共地
VREF参考电压输入2.5V基准源
AIN1(+) / AIN1(-)通道1差分输入传感器信号
AIN2(+) / AIN2(-)通道2差分输入传感器信号

硬件上有几个细节非常关键。第一个是DRDY引脚必须接。很多开发板的AD7705模块把DRDY引出来了,但有的人图省事不接,直接靠延时去读数据。这在低速场景下勉强能工作,但一旦时序稍有偏差,读到的可能就是上一次转换的旧数据,或者读到一半数据寄存器被更新导致数据错位。我的建议是DRDY必须接,读数据前先查询这个引脚,等到低电平再读,这才是可靠的。

第二个细节是RESET引脚的处理。AD7705的RESET引脚低电平有效,上电后会有一个内部复位过程。为了保证复位可靠,建议在RESET引脚上并联一个0.1uF电容到地,同时用单片机IO口控制它,初始化时先拉低再拉高。如果模块上已经接了RC复位电路,那单片机上的这个IO口配置成普通输出口就行,不影响。

第三个细节是参考电压。AD7705的转换结果直接和参考电压挂钩,计算公式是:转换结果 = 输入电压 × 2的15次方 / 参考电压。如果参考电压不稳定,转换结果就会出现同比例波动。所以千万不要直接把VDD当作VREF用,最好用TL431或者专门的基准芯片搭一个2.5V参考源。实际测试中,直接接5V电源做基准和用独立2.5V基准相比,数据跳动的幅度能差好几倍。

2.3 供电与去耦:效果差一个数量级的细节

AD7705是Σ-Δ型ADC,对电源噪声非常敏感。这类芯片内部虽然有大把的数字滤波,但模拟前端如果供电不干净,噪声照样会串进去。我的经验是:AVDD和DVDD分开走线,分别用10uF钽电容加0.1uF陶瓷电容去耦,电容尽量靠近芯片的电源引脚。STC89C52的5V电源如果是从USB取的,最好在电源入口加一个磁珠或者电感做隔离,否则PC的开关电源噪声会直接灌进ADC的供电。

还有一个容易被忽略的点:AD7705需要外部时钟输入,通常接一个2.4576MHz的晶振,或者用有源晶振从CLKIN引脚输入时钟。这个时钟的频率直接决定了内部滤波器的截止频率和输出数据速率。如果你买的模块上已经焊了晶振,那就不用操心,但如果模块上没有晶振,DRDY引脚就永远不会拉低,芯片压根不会工作。这是新手经常卡住的地方。

我建议在调试时先把通道1的AIN1(+)和AIN1(-)直接接到已知电压上,比如把AIN1(+)接一个精密电位器的中间抽头,AIN1(-)接地,然后用万用表实测输入电压,再对照AD读数换算结果,这样能快速确认硬件链路是否正常。

3. 寄存器配置与SPI时序实现

3.1 通信寄存器机制与SPI时序要点

AD7705的控制核心是它的通信寄存器。这个寄存器本身不是用来存数据的,而是用来告诉芯片“下一步你要操作哪个寄存器,是读还是写”。每次和AD7705通信,都要先往通信寄存器写一个命令字节,然后再进行实际的数据读写。

通信寄存器的8位定义如下:

  • bit7:必须为0,这是通信寄存器的标志位
  • bit6:R/W,1表示下一次操作是读,0表示写
  • bit5到bit3:寄存器选择位,指向时钟寄存器、设置寄存器、数据寄存器、偏移寄存器、增益寄存器
  • bit2到bit1:通道选择位,00为通道1,01为通道2
  • bit0:必须为0

常用的几个命令字节很关键,记住它们能省去一半的查手册时间:

  • 写时钟寄存器:0x00
  • 写设置寄存器:0x08
  • 读数据寄存器(通道1):0x50
  • 读数据寄存器(通道2):0x52

SPI时序方面,AD7705的SCLK空闲时为低电平。数据在DIN引脚上于SCLK上升沿被锁存,DOUT引脚上的数据在SCLK下降沿更新。用51单片机的GPIO模拟SPI时,只需要按照“先准备数据,再产生时钟沿”的顺序来操作即可。对于低速的51单片机来说,不需要额外加延时,IO翻转的速度本身就在AD7705允许的SPI时钟范围之内。

3.2 初始化配置流程与代码

初始化的完整流程是:复位AD7705,等待DRDY拉低,向通信寄存器写命令选择时钟寄存器,写入时钟寄存器配置,再向通信寄存器写命令选择设置寄存器,写入设置寄存器配置,最后等待DRDY拉低表示第一次转换完成。

下面这段初始化代码是我实际项目里精简出来的,可以直接用在STC89C52上,晶振假设是11.0592MHz,AD7705外部时钟为2.4576MHz:

#include <reg52.h> sbit AD_SCLK = P1^5; sbit AD_DIN = P1^6; sbit AD_DOUT = P1^7; sbit AD_CS = P1^4; sbit AD_DRDY = P1^3; sbit AD_RST = P1^2; void AD7705_WriteByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { AD_SCLK = 0; if (dat & 0x80) AD_DIN = 1; else AD_DIN = 0; AD_SCLK = 1; dat <<= 1; } } unsigned char AD7705_ReadByte(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { AD_SCLK = 0; AD_SCLK = 1; dat <<= 1; if (AD_DOUT) dat |= 0x01; } return dat; } void AD7705_WriteReg(unsigned char reg_cmd, unsigned char dat) { AD_CS = 0; AD7705_WriteByte(reg_cmd); AD7705_WriteByte(dat); AD_CS = 1; } void AD7705_Reset(void) { AD_RST = 0; // 复位低电平保持时间可以适当拉长 AD_RST = 1; } void AD7705_Init(void) { AD7705_Reset(); // 写时钟寄存器:选择通道1,更新速率约50Hz(2.4576MHz时钟) AD7705_WriteReg(0x00, 0x0C); // 写设置寄存器:自校准,PGA=1,双极性,无缓冲,正常工作 AD7705_WriteReg(0x08, 0x40); while (AD_DRDY); // 等待校准完成,DRDY拉低表示有新的转换数据 }

时钟寄存器写入的0x0C,是在2.4576MHz主时钟下对应约50Hz输出更新速率的配置。这个值要根据你自己的晶振频率查数据手册确认,不同的晶振频率对应的配置码不一样,直接照抄前一定要确认你模块上的晶振是2.4576MHz。

设置寄存器写0x40的含义是:MD1MD0为01(自校准),G2G1G0为000(增益1倍),B/U为0(双极性输入),BUF为0(无缓冲),FSYNC为0(正常转换)。这里选自校准是因为芯片会在写配置后自动完成一次零点校准,对消除芯片本身的失调误差很有帮助。如果你的传感器是单极性输出,比如0到2.5V,那设置寄存器应该写0x44,也就是把B/U位改成1。

3.3 读取转换结果的完整流程

AD7705的数据是16位有符号数,双极性模式下,满量程输入对应0x7FFF和0x8000。读取过程是:拉低CS,发送0x50命令到通信寄存器,告诉芯片接下来要读通道1的数据寄存器,然后连续读两个字节,先读高字节,再读低字节,最后拉高CS。

读取代码:

unsigned int AD7705_ReadData(void) { unsigned char h, l; unsigned int value; while (AD_DRDY); // 等待新数据就绪 AD_CS = 0; AD7705_WriteByte(0x50); // 选择数据寄存器,通道1,读操作 h = AD7705_ReadByte(); l = AD7705_ReadByte(); AD_CS = 1; value = (unsigned int)h << 8 | l; return value; }

主程序里循环读取,每隔一段时间读一次,然后把AD值通过串口发到电脑。

这里有一个特别重要的细节:发送0x50命令字节时,通信寄存器已经通过bit6位指定了“下次操作是读”。但我的代码是利用CS来控制整个读写事务的,在CS拉低期间先发命令再读数据。如果CS一直保持低电平,命令字节和数据字节会在同一条SPI线上连续传输,AD7705是能区分开的,因为第一个字节被通信寄存器截获,第二个和第三个字节被数据寄存器截获。不过为了保险和逻辑清晰,还是建议按我代码里这种方式:拉低CS,发送命令,读取数据,拉高CS。

另一个细节是DRDY和CS的时序关系。DRDY拉低表示数据寄存器里有新数据,但只要你开始读取数据寄存器,DRDY就会在读取过程中自动变高。所以主循环里先查DRDY再发送读命令,这个顺序不能反过来。如果先发送了读命令再去查DRDY,有可能在读取过程中DRDY刚好变高,你会误认为数据还没准备好。

4. 串口输出与上位机联调

4.1 串口初始化与数据发送

STC89C52的串口配置比较固定。我习惯用11.0592MHz晶振,定时器1工作在方式2,波特率9600。这个配置下波特率误差几乎为零,串口助手收到的数据不会乱码。

串口初始化代码:

void UART_Init(void) { SCON = 0x50; // 模式1,8位UART,允许接收 TMOD &= 0x0F; TMOD |= 0x20; // 定时器1,方式2 TH1 = 0xFD; // 9600波特率重载值,11.0592MHz晶振 TL1 = 0xFD; TR1 = 1; } void UART_SendByte(unsigned char dat) { SBUF = dat; while (!TI); TI = 0; } void UART_SendString(unsigned char *str) { while (*str) { UART_SendByte(*str++); } }

数据发送时,我推荐按三字节一帧的格式发:帧头、AD值高字节、AD值低字节。这样上位机解析起来非常方便。比如可以定义帧头为0xAA,然后把16位AD值拆成高低两个字节发出去。不推荐直接把数值转成十进制字符串再发,虽然看着直观,但解析和存储的代价都更高,而且传输效率低。

下面这个发送函数可以作为参考:

void Send_AD_Value(unsigned int val) { UART_SendByte(0xAA); // 帧头 UART_SendByte(val >> 8); // 高字节 UART_SendByte(val & 0xFF); // 低字节 }

主循环里加上适当的延时,比如每200ms读一次并发送一次,就可以在串口助手里看到稳定的数据流了。

4.2 串口调试助手与CH340驱动的那些事

电脑端推荐用SSCOM或者XCOM这类轻量级串口助手,接收区设置成HEX显示,这样能直接看到帧头和原始AD值。如果是用CH340这种USB转串口模块,第一次插上电脑后需要装驱动,Windows系统偶尔会出现“CH340驱动预安装成功但设备管理器找不到COM口”的情况,这种情况多半是驱动版本太老,或者USB线质量问题导致枚举失败。换个USB口、换根带屏蔽的USB线、重新安装最新版驱动,基本能解决。

串口参数必须是:波特率9600,数据位8,停止位1,无校验。这些参数要和单片机程序里配置的完全一致。如果串口助手里收到的是乱码,先说结论:百分之九十是波特率不对。最常见的原因是单片机的晶振不是11.0592MHz,而是用了12MHz。12MHz晶振在9600波特率下的误差达到2.1%左右,短帧数据勉强能看,但数据一多就乱码。用11.0592MHz晶振才是标准答案。

5. 常见问题与排查实录

5.1 问题速查表

我把实际调试中遇到的高频问题整理成了一张表,每一项都是自己踩过的坑:

现象可能原因排查方向
DRDY始终为高,没有任何反应AD7705没有时钟输入,模块上晶振没焊检查CLKIN引脚是否有2.4576MHz时钟
DRDY能拉低,但读数全是0xFFFF输入信号超出量程或参考电压没接测量VREF引脚电压,缩小输入信号范围
读数始终为0x0000通道接反、PGA配置不对、输入悬空检查通道选择寄存器,给AIN输入一个已知电压
数据跳动好几千个数输出速率太高,数字滤波不够;供电去耦不良降低输出速率到50Hz,检查电源去耦电容
切换通道后读数不变通信寄存器通道位没写对,或者没有等待DRDY确认命令字节的CH位,切换后等待新数据
串口乱码晶振频率和波特率不匹配确认晶振是11.0592MHz,检查TH1重载值

5.2 我自己踩过的三个坑

第一个坑是AD7705的复位时序。刚开始做的时候,我没有把RESET引脚接到单片机,而是在模块上直接焊了一个RC复位电路。结果芯片偶尔上电后DRDY电平死活不对,要重新断电几次才能正常。后来查了数据手册,发现AD7705在上电后会执行内部复位,但这个过程需要时间。如果电源上升沿比较慢,复位可能不完整。解决办法就是把RESET引脚接到单片机IO口上,初始化流程里先拉低再拉高,给它一个明确的复位脉冲。自那以后再也没出现过上电不工作的问题。

第二个坑是读取数据时忘记检查DRDY。早期偷懒,直接延时100ms后读数据,结果发现读出来的数值总是有一个固定偏差。原因是当数据寄存器读取和内部转换更新发生竞争时,得到的是一个不完整的数据。后来老老实实把读取流程改成了“先查DRDY,再发命令,再读数据”,问题立刻消失。对于Σ-Δ型ADC,这个检查是不能省的,尤其输出速率高的时候。

第三个坑和TM7705有关。我买过一批TM7705模块,和AD7705的寄存器定义几乎一致,但在增益寄存器的默认值上有点差异。程序里如果不对增益寄存器做显式配置,AD7705默认是16倍增益,而TM7705某些批次的默认值不同。结果同样一个输入电压,两块板子读出的数值差了好几倍。解决办法很简单:初始化时不要依赖默认值,直接往增益寄存器写入0x01,明确把增益配置成1倍,再写一次增益校准寄存器触发增益校准。用显式配置代替默认依赖,这是兼容国产芯片最重要的经验。

5.3 关于校准和精度的进一步建议

如果只是做入门验证,直接用初始化代码里的自校准就够了。但如果你要做的设备是需要批量标定的,比如称重仪表,那就要在系统层面做两点校准:零点和满量程。具体做法是:先让传感器处于空载状态,读取AD值记为零偏;再施加一个已知标准砝码,读取AD值记为满偏。上位机或单片机程序里用这两点做线性插值,把AD值转换成实际物理量。

AD7705内部也提供了系统校准功能,可以配合外部标准源来做。系统校准时,设置寄存器的MD位配置成系统零标定或系统满标定,然后把标准电压加到输入端,芯片会自动把测量到的失调和增益误差写入对应的校准寄存器。这个功能对小信号放大场景特别有用,可以消除输入通道的增益误差。

我个人在这些项目里的体会是:AD7705加STC89C52这套组合,最大的价值不是性能,而是它把ADC的底层机制完全暴露出来了。你手动模拟SPI去操作寄存器,比调库不知道清晰多少倍。等你把这一套跑通了,再去用STM32的硬件SPI、DMA、长串口发送,会非常顺手。如果后续你想在这个项目上继续扩展,建议从两个方向入手:一个是在单片机端加平均值滤波或者滑动滤波,把AD7705的数字滤波和软件滤波叠加,数据会更平滑;另一个是改成STM32加硬件SPI,把输出速率提上去,用DMA自动搬运数据,再配合蓝牙或者WiFi模块做无线数据回传。都是在现在这套基础上很自然的延伸方向。

本文还有配套的精品资源,点击获取

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

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

立即咨询