51单片机+DAC0832数模转换的Proteus仿真与波形实现
2026/9/16 16:04:46 网站建设 项目流程

简介:面向51单片机数模(D/A)转换的Proteus仿真实验包,以DAC0832芯片为核心,完整覆盖从电路搭建、C语言编程到虚拟仿真的全流程,适合正在学习单片机接口技术、数模转换原理或Proteus仿真的电子类专业学生与开发者。实验包共21个文件、638KB,其中既有C语言源文件和A51启动代码,也有hex编译结果、uvproj/pdsprj工程文件及备份、仿真截图,还包含lst/obj/m51等编译过程文件,便于直接打开工程复现并二次修改。已有5064人学习下载,通过对照源码、工程配置与截图,可以深入理解51单片机如何通过I/O口向DAC0832发送数字量并输出对应模拟电压,掌握单缓冲/双缓冲工作模式的选择与配置。结合Proteus虚拟调试,还可有效排查常见接线与时序问题,减少硬件损耗,为后续嵌入式系统设计打下实践基础。

1. Proteus 仿真 51 单片机数模转换:锯波直接上示波器先于焊板

51 单片机内部没有片内 DAC,输出真正的模拟电压只能外挂转换芯片或用 PWM 加滤波近似。本实验选择 DAC0832 这个 8 位并行 D/A 芯片,在 Proteus 里搭完整链路,用一根示波器线就能看到数字编码到模拟电压的对应关系。工程包里包含 Keil 的 C 源码2.c、启动文件STARTUP.A51、编译产物2.hex以及 Proteus 原理图dac0832A2.pdsprj,属于标准联调结构,打开就能复现全部现象。适合两类人:第一次接触总线式 DAC、分不清单缓冲和双缓冲区别的新手;想快速确认 DAC0832 三种工作模式在时序上到底差在哪的熟手。下面从芯片内部结构开始讲,再做接线和代码,最后落到波形验证上。

2. DAC0832 双缓冲结构与时序选型

2.1 两级锁存器:CS、WR1、XFER、WR2 各管什么

DAC0832 是 20 引脚、8 位并行输入的数模转换芯片,内部有两级锁存结构:输入寄存器和 DAC 寄存器。数据先被第一级锁存,再送入第二级,最终驱动内部的 R-2R 电阻网络输出电流。两级结构意味着写一个数据至少需要两个动作:先让数据通过输入锁存,再让数据进入 DAC 锁存,区别在于这两个动作分别由哪一组控制线触发。

控制线的分工是理解整个芯片的关键,我直接按项目接线给出一张常用表:

引脚方向有效电平本实验接法
DI0 ~ DI7输入高有效接 51 单片机 P1.0 ~ P1.7
ILE输入高电平接 +5V
CS输入低电平接 GND
WR1输入低电平有效,上升沿锁存接 P2.0,软件控制
XFER输入低电平接 GND
WR2输入低电平有效,上升沿锁存接 GND
Iout1输出电流输出接运放反相输入端
Iout2输出电流输出接运放同相输入端,同相端再接 GND
Rfb输入反馈电阻接运放输出,构成 I/V 转换
Vref输入参考电压接 +5V
Vcc电源接 +5V
DGND电源接 GND

这里最容易搞混的是 WR1 和 WR2。WR1 配合 CS 锁存输入寄存器,WR2 配合 XFER 锁存 DAC 寄存器。CS 等于 0 且 WR1 从低变高时,P1 口上的数据被锁进输入寄存器;XFER 等于 0 且 WR2 从低变高时,输入寄存器的数据进入 DAC 寄存器并立刻影响模拟输出。在仿真里用软件模拟一个上升沿很简单,但很多初学者直接把这几个引脚全部接地,导致数据是直通的,写不写时序都没区别,看起来也能出波形,实际上绕过了对芯片工作机制的理解。

2.2 三种工作模式的电平配置与适用场景

DAC0832 的三种模式本质上是把两级锁存器置于不同透明或锁存状态。直通模式让两级完全透明,数据线上是什么,输出就是什么;单缓冲模式让其中一级透明,另一级由控制线决定锁存时机;双缓冲模式则两级都受控,适合多片 DAC 同步输出。给出一张对照表:

模式ILECSWR1XFERWR2适用场景
直通接高接低接低接低接低连续波形输出,数据随时更新
单缓冲接高接低由 MCU 控制接低接低单通道 D/A,最常用
双缓冲接高由 MCU 控制由 MCU 控制由 MCU 控制由 MCU 控制多片 DAC 同时更新输出

在 Proteus 仿真里,直通模式不用写任何 WR 时序代码,P1 = d一句就能实时看到电压变化,适合验证接线和观察 R-2R 网络输出;单缓冲模式需要软件模拟一次完整的上升沿写入时序,和真实硬件行为一致,是最推荐学习的配置;双缓冲模式在单通道实验里表现不出优势,反而容易因为忘了操作 WR2 导致数据卡在第一级锁存器出不来。

2.3 为什么仿真项目选择单缓冲方式

这个实验最终目的是看数模转换的电压变化过程,同时保留对写时序的控制权,所以采用单缓冲模式最合理。我把直通条件放在初始化里做对比,便于查看两种模式的输出差异:

sbit CS_0832 = P2^0; sbit WR1_0832 = P2^1; void dac_write(unsigned char val) { CS_0832 = 0; /* 片选有效,选中 DAC0832 */ WR1_0832 = 0; /* 准备锁存 */ P1 = val; /* 数据放到总线,注意这里先给数据再拉 WR */ WR1_0832 = 1; /* 上升沿,把数据锁入输入寄存器 */ }

这段代码的逻辑顺序很关键:先置 WR1 为低,再放数据,最后拉高 WR1。上升沿锁存的是此刻总线上的电平,如果先拉高 WR1 再改 P1 口,锁进去的是旧数据。CS_0832在初始化后一直保持低电平,所以整个写入过程实际只受 WR1 控制。要切换到直通模式,只需把dac_write里的时序操作删掉,直接P1 = val,再让 WR1 固定接地即可。单缓冲模式的优点在于,写数据的动作完全由软件掌握,波形生成的每个台阶都可以精确计时,这在后面输出三角波和正弦波时特别有用。

3. Proteus 工程搭建与 Keil 联调:从 pdsprj 到 hex

3.1 Proteus 元件清单与引脚连接

新建 Proteus 工程时,元件搜索关键词用AT89C51DAC0832LM358RESPACK-8,这些在 Proteus 8 Professional 的标准库中都能找到,不需要额外安装第三方库。这个实验用不到复杂外设,电源直接用左侧终端模式里的 POWER 和 GROUND。搭建电路时,我习惯先放单片机,再放 DAC0832,最后放运放和虚拟仪器,原理图连线时尽量让数据线走同一方向,方便对照检查。

按网络分组给出一份接线清单:

网络标号AT89C51DAC0832LM358其他
DB0 ~ DB7P1.0 ~ P1.7DI0 ~ DI7
DAC_CSP2.0CS
DAC_WRP2.1WR1
ANA_OUTIout1反相输入 2 脚虚拟示波器 A 通道
RFBRfb输出 1 脚
AGNDIout2同相输入 3 脚再接地
VREFVref+5V

运放接法上有一个高频踩坑点:DAC0832 的 Iout1 和 Iout2 是两个方向相反的电流输出,Iout2 接运放同相端时,同相端还要再并联一个到地的电阻,否则仿真里同相端处于高阻悬浮状态,输出波形会叠加上不明偏置。LM358 的供电我用 +5V 和 -5V 双电源,在 Proteus 里分别放置+5V-5V电源符号即可。如果只给单电源,输出接近 0V 附近的台阶会被截平,看不出完整的 256 级阶梯。

3.2 Keil 工程设置:生成 2.hex 的关键开关

2.cSTARTUP.A512.uvproj2.uvopt放在一起,是典型的 Keil C51 工程结构。STARTUP.A51是编译器自动加入的启动文件,负责清零片内 RAM、初始化堆栈指针,一般不需要手动修改。2.uvopt保存的是窗口布局、断点等用户环境配置,是否加入版本库都行,不影响编译结果。要让工程生成2.hex,需要检查几项设置。

在 Keil 的 Options for Target 对话框里,Output 选项卡勾选 Create HEX File,这是必须项,不勾选的话 Proteus 加载不了程序。在 Device 选项卡选择 AT89C51 或 AT89C52,这两个型号在指令集上完全兼容,本实验用 AT89C51 即可。仿真的晶振频率在 Proteus 芯片属性里设置,Keil 侧的 Xtal 值只是给模拟器用的,不影响 hex 内容。命令行批处理构建也可以,这样不用打开 GUI:

C:\Keil_v5\UV4\UV4.exe -b 2.uvproj -j0 -o build_log.txt

-b表示后台编译,-j0指定使用 0 个 GUI 窗口,-o把编译输出写入build_log.txt。编译结束后查看该文件,出现0 Error(s)即可。工程里已有的2.build_log.htm就是这种构建方式留下的日志,内容里能看到本次编译的警告和错误数量。配合Listings目录下的2.m51映射文件,还能查每个变量落在哪个存储区,调试时很有用。

3.3 在 Proteus 中加载 hex 并配置仿真参数

双击原理图中的 AT89C51 芯片,在 Program File 一栏选择编译生成的2.hex,这是 Proteus 与 Keil 联调的桥接点。Crystal Frequency 设置为 12MHz,12MHz 下每个机器周期 1 微秒,软件延时的估算方便。仿真动画速度默认是实时,但 Proteus 的实时仿真往往跑不满真实速度,如果波形周期明显偏长,可以适当调低工具菜单里的 Animation Speed 选项,或者在代码里减小延时值。

工程根目录下既有dac0832A2.pdsprj,又有两份.pdsbak备份文件。pdsbak是 Proteus 系统在每次仿真前自动保留的上一次可恢复状态,文件名带Last Loaded的是最近一次打开的布局。如果当前原理图连线被改乱,直接打开Backup Of dac0832a2.pdsbak就能回到一个已知可运行的状态。这个习惯在复杂项目中很实用,我一般每完成一个阶段的接线就手动复制一份带日期后缀的 pdsbak。

3.4 电源、地线与数字模拟共地问题

Proteus 仿真里最容易产生迷之波形的原因之一,是多个电源网络混用。数字部分和模拟部分的电源在实验板上要分开滤波,但最终共地。DAC0832 的 Vcc 和运放的 Vcc 都接 +5V,DGND 和运放的地都接到同一个 GROUND 终端,虚拟示波器的地线也接这里。Vref 不要悬空,也不要直接接 Vcc 就完事,在仿真里接一个 5V 电源符号即可;如果想观察 Vref 对满量程电压的影响,可以改成电位器,转动时输出台阶高度会随之变化,这个测试比改代码更直观。

4. 单片机 C 程序实现:P1 总线驱动 DAC0832 输出波形

4.1 引脚定义与数据通道设计

数据通道使用 P1 口作为 8 位并行输出。51 单片机的 P1 口内部带上拉,复位后默认输出高电平,这正好对应 DAC0832 输入为 0xFF 的状态。单片机运行用户程序后,随时改写 P1 口就能改变 DA 输入编码。注意不要用 P0 口做这个实验,P0 口是开漏结构,驱动 DAC0832 时如果忘了接上拉电阻,数据高位会漂移。

控制引脚定义保持和 Proteus 原理图一致,否则仿真里波形完全不动:

#include <REG51.H> sbit CS_0832 = P2^0; /* DAC0832 片选,低有效 */ sbit WR1_0832 = P2^1; /* 输入寄存器写控制,上升沿锁存 */ sbit WR2_0832 = P2^2; /* DAC 寄存器写控制,实验中固定接低 */ sbit XFER_0832 = P2^3; /* 传输控制,实验中固定接低 */

WR2_0832XFER_0832虽然定义为 P2 口引脚,但在单缓冲模式下它们保持高阻或低电平都无所谓,因为原理图上这两个引脚直接接地。保留定义是为了以后切到双缓冲模式时,只需改动代码和接线,不用重新命名变量。

4.2 锯齿波与三角波生成程序

完整输出锯齿波的2.c程序如下,直接覆盖工程里的同名文件即可:

#include <REG51.H> sbit CS_0832 = P2^0; sbit WR1_0832 = P2^1; void delay(unsigned int t) { unsigned int i; while (t--) { for (i = 0; i < 120; i++); } } void dac_write(unsigned char val) { CS_0832 = 0; WR1_0832 = 0; /* WR1 拉低,准备写入 */ P1 = val; /* 数据送入 DI0~DI7 */ WR1_0832 = 1; /* 上升沿锁存,内部模拟输出更新 */ } void main(void) { unsigned char d = 0; CS_0832 = 0; while (1) { dac_write(d); /* 输出当前编码对应的模拟电压 */ delay(5); d++; } }

dac_write函数是这个程序的核心,参数val从 0 累加到 255,每写一次,DAC0832 输出一档新的模拟电压。delay 的参数 t 控制每级台阶停留时间,t 越大波形周期越长。我给出的for (i = 0; i < 120; i++);内循环,在 12MHz 晶振下经过编译优化后大约耗时 0.5ms,锯齿波一个完整周期约 128ms。这个延时值不是固定的,如果你在 Proteus 里观察到波形爬升太快,把delay(5)改成delay(10)即可。

三角波的改动更小,在主循环里先递增到 254 再递减到 1,两个方向各走 255 步,形成对称三角波:

while (1) { for (d = 0; d < 255; d++) { dac_write(d); delay(5); } for (d = 254; d > 1; d--) { dac_write(d); delay(5); } }

递增方向电压从 0 爬到接近满量程,递减方向从满量程落回 0,波形斜率由同一个 delay 控制,因此三角波两个边完全对称。这段代码放入2.c后重新编译并加载 hex,虚拟示波器上应该能看到标准等腰三角波,每个边包含 255 个小台阶。

4.3 正弦波查表实现与表格参数

正弦波不能用循环递增实现,需要预先计算 256 个点的编码值,运行时查表输出。生成表格最直接的方式是 Python 脚本,我一般这样算:

import math N = 256 table = [int(127.5 + 127.5 * math.sin(2 * math.pi * i / N)) for i in range(N)] print(", ".join(str(x) for x in table))

为什么取127.5 + 127.5 * sin()?因为 DAC0832 是 8 位,编码范围 0 到 255,正弦值要映射到这个区间。127.5是零电平对应的编码,幅度为 127.5 保证峰值在 255、谷值在 0,不截顶。生成后放到 C 程序里:

code unsigned char sin_table[256] = { 128, 131, 134, 137, 140, 143, 147, 150, 153, 156, 159, 162, /* 此处省略中间数据,由脚本生成后完整粘贴 */ };

code关键字把表放在程序存储区,不占片内 RAM。主循环里按索引逐点输出,索引溢出时清零,就能得到连续正弦波。256 个点的表有 256 个台阶,肉眼在示波器上已经看不出阶梯感,接近理想正弦。如果觉得波形频率太高,把延时加大或减小查表步进即可,例如索引每次加 2,频率翻倍但点数减半。

4.4 波形上的毛刺从哪来

仿真波形上偶尔能看到台阶边缘出现窄尖峰,这不是 Proteus 渲染错误,而是真实 DAC 的毛刺现象。DAC0832 内部的模拟开关切换时,不同位的导通时间有微小差异,导致输出出现短暂过冲。在单缓冲模式下,WR1 上升沿锁存数据的同时,内部开关组开始重新组合,这个瞬间最容易产生毛刺。毛刺宽度一般只有几十纳秒,但仿真步长较小时会被捕捉到。消除毛刺的常见做法是加输出滤波电容,在运放输出端对地并联一个 0.1uF 电容;实验里也可以直接忽略,因为毛刺不改变台阶的平均电平。

5. 用虚拟示波器验证输出精度与故障排查

5.1 虚拟示波器接法与触发电平设置

Proteus 左侧工具栏选择 Virtual Instruments Mode,放置 Oscilloscope。通道 A 接运放输出端,也就是ANA_OUT网络,地线接 GROUND。示波器显示 D/A 转换结果比用虚拟电压表直观得多,但电压表在验证单点精度时更方便。双击示波器,把 Time/Div 调成 10ms 或 20ms,Channel A 的 Volts/Div 设 1V 或 2V,触发方式设为 Auto,触发电平设为 1.25V 附近,这样锯齿波回扫时能稳定触发,波形不再左右乱跑。

如果是三角波,触发电平设在零电平附近更容易获得对称显示。不要直接用默认的 5V 触发电平,DAC0832 输出经运放反相后振幅约 -5V 到 0V,默认触发条件可能一直得不到满足,示波器会像没接信号一样静止不动。

5.2 理想输出电压计算与实测比对

DAC0832 运放输出的理想电压可以精确计算。8 位编码 d,参考电压 Vref 取 5V,反馈电阻等于内部 R-2R 网络的基准电阻,输出电压:

Vout = -Vref * d / 256

当 d 等于 255 时,输出约 -4.98V;d 等于 128 时,输出约 -2.5V。注意负号来自运放反相放大结构。仿真中接好电压表后,把程序改成输出固定值,比如让 d 恒为 128,实测电压应与理论值相差不超过 0.02V。如果偏差达到 0.1V 以上,优先检查 Vref 是否真的是 5V,再用电压表确认运放供电端电压。我给客户项目做这类验证时,习惯先把最高编码、中间编码、最低编码三点测准,三点校准能快速暴露接线问题。

5.3 波形异常原因对照表

仿真中遇到波形问题不要急于改代码,先按现象定位。列出一份排查对照表,覆盖这个工程最常见的几类异常:

现象可能原因处理方法
输出恒为 0Vhex 未正确加载重新选择 hex 文件并重启仿真
波形呈反相Iout1 与 Iout2 接反对调两个引脚与运放的连接
高电平端截平运放单电源供电改为 +5V / -5V 双电源
台阶数不足 256P1 口某位虚接检查数据线网络标号
波形周期极短代码中 delay 被优化将 delay 函数改为 volatile 参数
示波器静止触发条件不符改用 Auto 触发并下调触发电平

最后一个排查技巧直接决定效率:Proteus 仿真失败时,先暂停仿真,点选芯片引脚看左侧状态栏的颜色提示,蓝色表示低电平,红色表示高电平,灰色是浮空。DAC0832 的 CS、WR1 固定接地时应一直是蓝色,如果显示红色,说明网络连接断开,找线比改代码优先。完成波形验证后把示波器探头换成电压表,再复核一次零点和满量程两个端点,就能确认整个链路从数字到模拟的精度余量。

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

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

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

立即咨询