资讯动态

51单片机汇编驱动ADC0809与DAC0832:从时序控制到闭环系统实践

发布时间:2026/8/29 15:12:12 来源:尧图企业网站定制
1. 项目缘起从模拟到数字的桥梁搭建最近在整理实验室的旧项目资料翻到了当年用51单片机做的一个经典实验——A/D和D/A转换。这个实验可以说是单片机从“数字世界”迈向“模拟世界”的第一步也是很多同学从点亮LED、驱动数码管这类纯数字控制转向处理真实物理信号比如温度、压力、声音的必经之路。我记得当时为了搞懂ADC0809和DAC0832这两个芯片的时序没少在示波器前蹲着调通的那一刻看到电压表指针随着电位器的转动而平稳变化那种成就感至今难忘。这个实验的核心就是教会我们如何用51单片机去“感知”和“控制”模拟量。A/D转换模数转换好比是单片机的“耳朵”和“眼睛”它能将外部连续变化的模拟信号比如0-5V的电压转换成单片机能够理解和处理的数字量比如0-255。而D/A转换数模转换则是单片机的“嘴巴”和“手”它能把单片机内部的数字指令转换成连续的模拟电压输出去驱动电机、控制亮度或者发出声音。ADC0809和DAC0832就是实现这两个功能非常经典且教学常用的芯片。对于正在学习51单片机汇编语言的朋友来说这个实验的价值在于它迫使你跳出纯逻辑的编程思维去理解和操控硬件时序。你需要精确地控制几个控制引脚的高低电平顺序这就是时序才能让芯片正确地开始转换、读取数据或输出模拟量。这个过程是理解单片机作为“系统控制器”角色的关键一步。下面我就结合当年的笔记和代码把这个实验从头到尾捋一遍把那些容易踩坑的细节和调试心得也分享出来。2. 实验核心器件与电路原理剖析在动手写代码之前我们必须先搞清楚要用的两个“主角”芯片是怎么工作的以及它们如何与我们的51单片机这里以经典的AT89C51为例连接。一知半解地接上线代码大概率是跑不起来的。2.1 ADC0809八路模拟输入的“采样官”ADC0809是一个8位分辨率、逐次逼近型的A/D转换器。所谓8位意味着它能把一个模拟电压假设量程是0-5V分成2的8次方也就是256个等级输出的数字量范围是00H到FFH0-255。逐次逼近型是一种常见的转换原理速度中等精度满足一般教学和工控需求。它的引脚很多但我们需要重点关注的也就那么几个IN0-IN78路模拟信号输入通道。你可以把它想象成一个8选1的开关一次只能选一路信号进行转换。ADDA, ADDB, ADDC通道选择地址线。这三位二进制编码000到111就决定了当前选中哪一路模拟输入IN0到IN7。例如CBA000就选中IN0通道。ALE地址锁存允许信号。这是一个上升沿有效的引脚。当ALE从低电平跳到高电平时它会将ADDA、ADDB、ADDC引脚上的通道地址“锁存”进芯片内部的地址锁存器。简单说就是告诉0809“记住我现在要转换的是这个通道的信号”。START转换启动信号。这是一个下降沿有效的引脚。给START一个从高到低的跳变一个脉冲ADC0809就会开始对当前选中的通道进行模数转换。EOC转换结束信号。这是一个输出引脚。当ADC开始转换时EOC会变低当转换完成数据准备好后EOC会自动变回高电平。这个信号非常关键我们可以通过查询EOC是否变高来判断转换是否完成从而去读取数据。OE输出允许信号。这是一个输入引脚高电平有效。只有当OE被单片机置为高电平时ADC0809才会把转换好的8位数字量放到数据总线D0-D7上否则数据总线是高阻态相当于断开。所以读数据前必须先给OE一个高电平。CLK时钟信号。ADC0809工作需要外部提供一个时钟典型频率是640kHz范围在10kHz到1280kHz。通常我们用单片机的ALE信号频率是晶振的1/6经过分频后提供。与51单片机的典型连接思路数据线D0-D7直接连接到单片机的P0口需外加上拉电阻因为P0口是开漏输出。地址线ADDA-C、ALE、START、OE这些控制信号可以连接到单片机的其他I/O口如P2或P3的某些位来单独控制。更常见的做法是利用单片机系统的地址总线进行统一编址将ADC0809当作一个外部RAM单元来访问这样可以用MOVX指令来统一产生控制时序代码更简洁。本实验代码采用的就是这种方法。2.2 DAC0832将数字命令变为模拟输出的“执行者”DAC0832是一个8位分辨率的电流输出型数模转换器。它接收8位数字量输出一个与数字量成比例的模拟电流。要得到电压还需要外接一个运算放大器构成电流-电压转换电路。它的关键引脚DI0-DI78位数字量输入线。CS片选信号低电平有效。WR1输入寄存器写选通信号低电平有效。ILE输入寄存器锁存允许信号高电平有效。通常直接接高电平VCC。XFER数据传送控制信号低电平有效。WR2DAC寄存器写选通信号低电平有效。 DAC0832内部有两级锁存器这种结构允许它在保持当前模拟输出的同时接收下一个要输出的数字量适用于需要多路D/A同步输出的场合。在我们的单路输出实验中通常将两级锁存器同时直通即把WR2和XFER接地一直有效CS和WR1由单片机控制。这样当CS和WR1同时有效时数字量就直接进入DAC寄存器并开始转换。输出电路DAC0832输出的是电流Iout1和Iout2。我们需要一个运算放大器如常用的LM358来构成一个反相比例放大器将电流转换成电压。典型的接法是Iout1接运放的反相输入端Iout2和运放的同相输入端接地反相输入端和输出端之间接一个反馈电阻Rfb内部通常已集成一个15kΩ的也可以外接。这样输出电压 Vout - (数字量/256) * Vref。其中Vref是参考电压决定了输出电压的范围。例如Vref接-5V则输出就是0到5V。2.3 整体电路框架与调试要点将ADC0809和DAC0832接入51单片机最小系统就构成了一个完整的“感知-处理-控制”闭环。典型的实验场景是用一个电位器产生一个可调的模拟电压0-5V接到ADC0809的IN0通道。单片机读取这个电压值数字量然后直接将这个数字量送给DAC0832输出。用万用表或示波器测量DAC的输出电压理论上它应该跟随电位器调整的输入电压变化。注意在面包板或实验板上搭建这个电路时电源去耦电容必不可少。务必在ADC0809和DAC0832的VCC和GND引脚附近并联一个0.1uF的瓷片电容和一个10uF的电解电容用于滤除电源噪声这对保证转换精度和稳定性至关重要尤其是DAC的输出电源纹波会直接反映在模拟输出上。另一个调试关键是共地。单片机的地、ADC0809的地、DAC0832的地、运放的地以及模拟信号源的地必须全部连接在一起建立一个统一的“零电位”参考点。否则测量会出现巨大误差甚至无法工作。3. 汇编代码逐行解读与核心时序实现理解了硬件我们来看软件如何驱动。汇编语言的优势在这里体现得淋漓尽致——你可以精确控制每一个引脚电平变化的时刻这正是ADC/DAC芯片时序所要求的。下面我提供一套经过测试的参考代码并附上详细注释。假设我们的硬件连接如下采用外部RAM编址方式ADC0809的片选CS连接地址线A15P2.7因此其通道0的地址为7FF8HA150且低三位地址A2A1A0000选择IN0。DAC0832的片选CS连接地址线A14P2.6因此其地址为BFFFHA140其他无关地址位取1。ADC0809的EOC连接单片机P3.2INT0用于查询转换状态。; ; 文件名AD_DA.ASM ; 功能 循环读取ADC0809通道0的模拟电压 ; 并将读取的数字量直接输出给DAC0832 ; 实现电压跟随器功能。 ; 硬件连接见上文描述 ; ADC_ADDR EQU 7FF8H ; ADC0809 通道0的地址 (A150, A2A1A0000) DAC_ADDR EQU 0BFFFH ; DAC0832 的地址 (A140) ORG 0000H LJMP MAIN ORG 0100H MAIN: MOV SP, #60H ; 设置堆栈指针 LOOP: LCALL READ_ADC ; 调用子程序读取ADC值 LCALL WRITE_DAC ; 调用子程序将值写入DAC SJMP LOOP ; 无限循环 ; ; 子程序READ_ADC ; 功能 启动ADC0809通道0转换并等待转换完成 ; 然后读取转换结果存入累加器A ; 使用 A, DPTR ; READ_ADC: MOV DPTR, #ADC_ADDR ; DPTR指向ADC地址 MOVX DPTR, A ; 关键操作1向该地址写任意数据 ; 这个写操作会产生一个WR*信号。 ; 对于ADC0809的典型接法WR*信号会同时连接到 ; ALE和START引脚。因此一次写操作会依次产生 ; 1. ALE上升沿锁存通道地址由地址总线低三位A2A1A0提供我们已固定为000。 ; 2. START下降沿启动转换。 ; 等待转换完成 (查询EOC引脚这里假设EOC接P3.2) WAIT_EOC_HIGH: JB P3.2, WAIT_EOC_HIGH ; 如果EOC为低说明正在转换继续等待 WAIT_EOC_LOW: JNB P3.2, WAIT_EOC_LOW ; 如果EOC为高说明转换完成跳出循环 ; 注意这里等待了一个从低到高的跳变更严谨。 MOVX A, DPTR ; 关键操作2从同一地址读取数据 ; 这个读操作会产生一个RD*信号。 ; RD*信号通常连接到ADC0809的OE引脚。 ; OE变高ADC将转换结果输出到数据总线单片机通过P0口读入A。 RET ; ; 子程序WRITE_DAC ; 功能 将累加器A中的数字量写入DAC0832 ; 使用 A, DPTR ; WRITE_DAC: MOV DPTR, #DAC_ADDR ; DPTR指向DAC地址 MOVX DPTR, A ; 向该地址写数据。这个写操作会产生WR*信号 ; 连接到DAC0832的CS和WR1假设WR2和XFER已接地直通。 ; 当CS和WR1同时有效低电平数据从单片机数据总线 ; 写入DAC0832的输入寄存器并立即进入DAC寄存器开始转换。 RET END代码核心逻辑与时序分析启动ADC转换 (MOVX DPTR, A)这行代码是精髓。当执行MOVX DPTR, A写外部RAM时51单片机会在P3.6WR引脚产生一个低电平有效的写脉冲。我们的电路设计将这个WR信号同时接到了ADC0809的ALE和START上。于是这个脉冲的上升沿WR从低变高使ALE有效锁存地址紧接着的下降沿实际上WR脉冲结束时从低变高但START是下降沿触发需要一点小技巧通常用WR经过非门或与地址信号组合来产生START但很多教材为了简化假设连接正确启动转换。代码中向一个固定地址写数据地址总线的低三位对应ADDA-C由硬件连接决定从而选择了通道。等待转换完成查询法代码中通过查询P3.2EOC的状态来等待。JB P3.2, WAIT_EOC_HIGH是在等EOC变低转换开始JNB P3.2, WAIT_EOC_LOW是在等EOC再变高转换结束。这是一个完整的“低-高”跳变检测比只检测高电平更可靠避免了芯片上电初始状态可能带来的误判。读取ADC结果 (MOVX A, DPTR)执行此指令时单片机在P3.7RD引脚产生读脉冲。我们将RD连接到OE。OE变高ADC0808内部的输出三态门打开转换好的数据出现在数据总线P0口上被读入累加器A。写入DAC输出 (MOVX DPTR, A)这个过程相对简单。再次使用MOVX DPTR, A写指令向DAC的地址写入数据。WR脉冲使DAC0832的CS和WR1有效数据被锁存并立即转换输出电压随之改变。重要心得很多同学在这里会迷惑为什么读和写用的是同一个地址因为对于ADC我们关心的是对它的“操作”写操作启动转换读操作读取结果而不是地址本身的数据。我们利用的是执行MOVX指令时自动产生的控制信号WR或RD来驱动ADC的START、OE等引脚。地址线只是用来选中这个外设芯片而已。这是一种非常典型的“总线式”扩展I/O口的方法。4. 调试过程中常见的坑与解决方案即使代码和电路看起来都对第一次做这个实验也大概率不会一帆风顺。下面是我和同学们常遇到的几个问题及排查思路。4.1 ADC转换结果不准或不变化现象调节电位器但单片机读回来的数字量不变或者变化没有规律。排查步骤检查模拟输入用万用表直接测量接到ADC0809 IN0引脚的电压确认它是否随电位器平滑变化0-5V。检查参考电压ADC0809需要稳定的参考电压Vref和Vref-。通常Vref接5VVref-接地0V。确保这两个引脚电压准确。参考电压的精度直接决定转换精度。检查时钟信号用示波器测量ADC0809的CLK引脚看是否有约640kHz的方波。如果没有时钟芯片根本无法工作。检查时钟电路通常是一个与非门或D触发器构成的振荡电路或者由单片机ALE分频而来。检查控制时序这是最复杂的一步。需要用双通道示波器同时抓取WR或START、EOC和RD或OE的波形。触发一次转换后应该先看到WRSTART有一个负脉冲。随后EOC引脚应该立刻变低持续约100us取决于时钟频率。大约100us后EOC自动变高表示转换结束。此时当你执行读指令时应该能看到RDOE上产生一个负脉冲。 如果时序不对比如OE脉冲在EOC变高之前就出现了那读到的就是无效数据。重点检查WR、RD与ALE、START、OE之间的硬件连接逻辑是否正确。检查数据线确保P0口的上拉电阻通常用10kΩ排阻已正确连接且接触良好。P0口在不接上拉时无法输出高电平。4.2 DAC输出不对或没有输出现象万用表测量运放输出端电压为0或者始终为某个固定值如电源电压或负电源电压不随输入数字量变化。排查步骤检查运放电路这是最容易出错的地方。首先确认运放如LM358的正负电源如12V和-12V或单电源5V已正确接入。检查反相输入端、同相输入端、输出端以及反馈电阻的连接是否正确无误。一个快速判断方法是将DAC的数字输入DI0-DI7全部接地输入00H理论上输出应为0V。如果输出不是0V多半是运放电路问题。检查DAC基准电压测量DAC0832的Vref引脚电压。如果你期望输出0-5VVref通常接-5V因为运放是反相接法。确保这个-5V电源稳定。检查控制信号确保DAC0832的CS和WR1信号能被正确的写脉冲触发。可以用示波器观察当执行MOVX DPTR, A向DAC地址写数据时对应的片选和写引脚上应该有负脉冲。检查电流输出端如果运放电路复杂可以先断开Iout1与运放的连接在Iout1和地之间接一个精密电阻如5kΩ测量电阻两端的电压。根据公式 Iout1 (数字量/256) * (Vref / Rfb内部)。假设Vref-5V数字量FFH则Iout1 ≈ (255/256)* (5V/15kΩ) ≈ 0.332mA。在5kΩ电阻上产生的电压约为1.66V。用万用表测量这个电压是否随数字量线性变化可以快速判断DAC芯片本身是否工作正常。4.3 系统干扰与稳定性问题现象数字量读取或模拟量输出值在小范围内跳动抖动不稳定。解决方案电源滤波重申一遍在每个芯片的电源引脚附近紧贴芯片焊接或安装0.1uF的瓷片电容。地线布局尽量使用粗短的导线连接地线避免形成地线环路。模拟地ADC、DAC、运放、电位器的地和数字地单片机、逻辑芯片的地可以在一点汇合俗称“单点接地”。软件滤波对于ADC读取的抖动可以在软件中采用多次采样取平均的方法。例如连续读取10次ADC值去掉最大最小值后求平均再将这个平均值送给DAC。这能有效抑制随机干扰。; 简单的软件平均滤波示例读取5次取平均 READ_ADC_AVG: MOV R2, #5 ; 采样5次 MOV R3, #0 ; 和的高字节清零 MOV R4, #0 ; 和的低字节清零 SAMPLING_LOOP: LCALL READ_ADC ; 调用之前的单次读取子程序 ADD A, R4 ; 将本次结果加到和的低字节 MOV R4, A CLR A ADDC A, R3 ; 处理进位 MOV R3, A DJNZ R2, SAMPLING_LOOP ; 循环5次 ; 现在R3R4中是5次采样的和16位 ; 除以5右移2位近似除以4再处理余数这里简化处理 MOV A, R4 SWAP A ANL A, #0FH ; 取高四位相当于除以16 ... ; 更精确的除法需要编写除法子程序此处略 RET5. 实验的进阶玩法与思路拓展当你成功实现了基本的电压跟随器后这个实验平台还可以玩出很多花样深化对单片机系统的理解。5.1 实现一个简单的数字电压表将读取的ADC数字量0-255转换成实际的电压值0.00-5.00V并显示在数码管或LCD上。这涉及到标度变换。 公式很简单电压值 (数字量 / 255) * 5.0 V。 但在汇编里做浮点除法很麻烦。一个实用的技巧是电压值 (单位mV) ≈ 数字量 * 5000 / 255。 可以先计算数字量 * 500016位乘法再除以255。或者更取巧因为5000/255 ≈ 19.6078可以近似为电压值 (mV) ≈ 数字量 * 20然后通过校准修正误差。显示部分就需要调用数码管动态扫描或LCD写字符串的子程序了。5.2 生成简单的波形让DAC输出不再简单地跟随输入而是由程序控制输出特定的数字序列从而生成波形。锯齿波让输出数字量从0递增到255再回到0不断循环。OUTPUT_SAWTOOTH: MOV A, #0 SAW_LOOP: LCALL WRITE_DAC ; 输出当前值 LCALL DELAY ; 调用一个微小的延时控制波形频率 INC A ; 值加1 CJNE A, #0, SAW_LOOP ; 加到256溢出为0则循环 SJMP OUTPUT_SAWTOOTH方波先输出255高电平一段时间再输出0低电平一段时间循环。正弦波需要预先计算好一个正弦函数表0-255范围内的值存储在程序存储器中然后循环查表输出。这是学习查表法和DDS直接数字频率合成概念的好例子。5.3 引入中断机制优化程序上述代码用的是查询方式等待EOC单片机在等待期间一直在空循环效率很低。我们可以将EOC信号连接到单片机的外部中断引脚如INT0利用中断方式来读取ADC结果。优点启动转换后单片机可以继续执行其他任务如刷新显示、处理数据等ADC转换完成产生中断时再进入中断服务程序读取数据。大大提高了CPU利用率。修改要点初始化时设置外部中断0为下降沿或低电平触发。在中断服务程序INT0_ISR中读取ADC数据并存入一个全局变量。主循环中只需启动转换然后就可以去做其他事情需要用到ADC数据时直接读取那个全局变量即可。从查询到中断的升级是单片机编程能力提升的一个重要标志。这个实验虽然基础但涵盖了硬件连接、时序控制、软件编程、调试排错等多个核心技能点吃透它你对单片机系统的理解会上一个坚实的台阶。希望这份详细的回顾和解读能帮你顺利跨过这道坎。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价