ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于51单片机与DAC0832的波形发生器设计及汇编实现详解

基于51单片机与DAC0832的波形发生器设计及汇编实现详解 简介针对51单片机波形发生器课程设计需求这份资料提供基于DAC0832数模转换芯片的完整汇编语言程序与Proteus仿真工程适合电子类、自动化类专业学生及单片机入门开发者参考学习。系统支持产生正弦波、方波、锯齿波、三角波四种常用波形输出频率可在100赫兹至100千赫兹范围内调节其中正弦波信号的电压峰峰值可在0到5伏之间连续可调能够满足基础信号源实验和大部分教学演示场景。整个压缩包共22个文件大小127KB主要包含汇编源码、Keil工程文件、Proteus仿真文件以及可直接烧录的十六进制文件另有列表与编译中间文件便于核对构建过程文件组织比较清晰方便按需查阅。目前已有5410人学习下载说明该方案具有一定参考价值。借助这套资料读者可以快速打开仿真工程观察四种波形输出理解数模转换、按键控制与频率调节的实现思路也可以在此基础上修改程序参数扩展更多信号类型或幅值调节方式为课程答辩和二次开发提供实际支撑。 记得那年做课程设计老师指定用51单片机加DAC0832实现一个信号波形发生器还特别要求汇编语言。当时第一反应是这年头还有人用汇编写这种项目后来跑通之后才明白老师不是守旧而是这个组合把单片机最核心的几个知识点全串起来了——I/O控制、定时中断、查表算法、数模转换每一环都有存在感。这篇文章就把整个项目的硬件电路、汇编编写思路、Proteus仿真过程和踩坑记录完整复盘一遍给准备做类似课程设计或者想彻底搞懂DAC0832怎么用的同学一个可直接参考的模板。1. 为什么51单片机DAC0832这个老组合至今还是波形发生器入门的首选1.1 这个项目真正让你学到的东西先说说这套组合能做什么通过AT89C51的P0口向DAC0832发送8位数字量DAC0832将数字量转换成对应的模拟电流经过运放转成电压信号最终在示波器上输出方波、锯齿波、三角波和正弦波。听起来不复杂但整个过程涵盖了单片机课程设计最常见的几个坑位。第一是I/O口的操作。P0口是准双向口输出高电平时驱动能力很弱实际硬件必须外接上拉电阻否则你写MOV P0,#0FFH引脚上也未必能出来稳定的高电平第二是定时器的应用波形频率就是靠T0定时中断控制的每个中断周期输出一个采样点频率的精度直接由定时器初值的计算决定第三是查表法的思想尤其正弦波不能靠单片机实时去算sin函数只能提前把采样值算好存进ROM运行时逐个查表输出。这些点单独拎出来都不难但合在一起就是一个完整的数字信号转模拟信号闭环。做完之后你会对MOVC A,ADPTR这条查表指令的理解比背十遍书都深刻。1.2 和DDS方案比这个方案的价值在哪现在做波形发生器很多人第一反应是AD9833或者AD9850这种DDS芯片频率高、精度好、外围电路也简单。但回到课程设计的场景DDS芯片恰恰掩盖了太多底层细节——你只是往SPI寄存器里写几个字节波形就出来了至于波形到底是怎么逐点构成的、定时周期和采样点数如何影响输出频率完全没有体感。DAC0832就完全相反。它没有那么多寄存器8根数据线接P0口数据到了就输出控制逻辑只有ILE、CS、WR1、WR2、XFER这几个引脚。你可以清楚地看到每一个数字量对应一个输出电压台阶波形频率等于采样点数和输出间隔时间的倒数。这个看得见摸得着的过程才是这个项目最值钱的地方。所以我的结论是如果纯粹做仪器别用51加DAC0832性能确实不够如果是为了学原理、交作业、为后续做更复杂的信号处理打基础这套方案依然是性价比最高的入门路径。2. DAC0832与AT89C51的电路连接直通方式最省事2.1 DAC0832内部结构与三种工作方式的区别DAC0832内部实际是两个寄存器加一个R-2R电阻网络的D/A转换器8位数码从DI0~DI7进入经过输入寄存器和DAC寄存器最终转成一对互补电流输出Iout1和Iout2。为什么有两个寄存器因为这样可以实现一种叫双缓冲的用法——多路DAC可以先把数据分别锁存到输入寄存器然后同时触发XFER让所有DAC在同一时刻更新输出避免各路波形不同步。三种工作方式的核心区别在于这两个寄存器的控制方式工作方式ILECSWR1WR2XFER使用场景直通方式VCCGNDGNDGNDGND单路DAC数据实时输出单缓冲方式VCC单片机地址线或GND接单片机的WRGNDGND单路受控写入双缓冲方式VCC地址译码器输出接单片机的WR接地址译码器单片机控制多路DAC同步更新这个项目只输出一路波形完全没必要用双缓冲。我直接采用直通方式把ILE接高电平CS、WR1、WR2、XFER全部接地这样单片机往P0口写什么DAC0832就立刻输出对应的模拟量不需要任何额外的写锁存时序。2.2 P0口与数据线的连接以及为什么不需要74LS373连接方式非常直接AT89C51的P0.0~P0.7依次接DAC0832的DI0~DI7。注意顺序别接反我之前就干过把P0.0接到DI7的蠢事结果波形乱成一团。这里有个初学者容易纠结的问题P0口既当数据总线又当低8位地址总线是不是得加74LS373锁存器分情况看。如果电路里只有DAC0832一个外设单片机不需要寻址外部存储器或外部I/O口那么P0口就纯粹当普通I/O用直接接DAC即可不需要锁存器。如果同时接外部RAM或者多个外设才需要74LS373先锁定地址再通过WR信号控制DAC写数据。本项目的电路就是最简单的那种8根数据线直连没有地址线也不必区分地址周期和数据周期。实际硬件制作时要注意一点P0口内部没有上拉电阻输出高电平时是弱上拉驱动能力很弱DAC0832的输入可能识别不了。所以在真实电路板上P0口各线对VCC接一个10kΩ排阻这是硬件制作时最容易被忽略的细节。Proteus仿真中这个问题不明显但实际焊板子时必须加上。2.3 电流输出转电压运放接线与VREF极性DAC0832的输出是电流而不是电压Iout1和Iout2是一对互补输出两者之和为常数。要让示波器看到电压波形必须把电流经过运放转换。典型接法是用一片LM358作为I-V转换电路运放反相输入端接Iout1运放同相输入端接地内部反馈电阻Rfb接运放输出端。这时输出电压大约满足Vout -VREF × D / 256其中D是单片机写入的8位数字量范围0~255。从这个公式能直接看出两个重要结论DAC0832的模拟输出量和VREF直接成正比所以VREF必须接稳定的参考电压源不能直接用VCC了事否则输出波形会跟着电源抖动。Vout的极性取决于VREF的极性。VREF接5V输出范围大约0到-4.98V示波器上看到的是向下延伸的波形想要0到5V的正向波形有两个办法一是把VREF接到-5V二是后面再加一级反相放大电路。我第一次仿真时把VREF接到5V示波器上看到的锯齿是往下走的还以为是程序问题排查半天才反应过来是参考电压极性没搞对。如果你在Proteus里想让波形看起来正常最简单粗暴的方式就是给VREF接一个-5V直流源。运放供电建议用±12V双电源或者至少-5V否则运放输出会被电源电压钳位波形削顶。3. 汇编查表法波形其实是逐点画出来的3.1 8位DAC的输出分辨率与台阶效应先建立一个直观概念DAC0832是8位的意味着数字量一共256级满量程电压范围如果设计成0~5V那么每级的电压步进大约是5V/256≈0.0195V也就是19.5mV左右。单片机不断刷新DAC的输入数据DAC输出端就形成一个一个的电压台阶这些台阶连起来就是波形。所以波形发生器的本质是用足够快的速度循环往DAC里塞数据。塞得快台阶密波形就平滑塞得慢台阶稀肉眼就能看到阶梯状。这也就解释了为什么输出频率升高后波形会变差——采样点数是固定的输出频率越高每个采样点持续的时间越短波形周期内台阶数量越少失真自然越明显。3.2 正弦波采样表怎么来正弦波用汇编实时计算是不可能的单片机没有浮点运算单元就算把角度换算成整数查三角函数表代码量和执行时间也受不了。标准的做法是查表提前计算出一个完整周期内256个点的采样值按顺序存到ROM里程序运行时逐个取出送到P0口循环往复。采样值的计算公式很简单value 128 127 × sin(2π × i / 256)其中i从0取到255。加上128是为了把sin函数的输出范围从-1~1平移到0~255之间让DAC可以输出单极性正电压。这个公式在Excel里一拉就能生成256个数据然后用汇编的DB伪指令存到代码段里即可。手工计算不现实而且容易算错建议用工具生成节省时间还能保证精度。完整周期的正弦表如下所示只贴前一小段示例SIN_TAB: DB 128, 131, 134, 137, 140, 144, 147, 150 DB 153, 156, 159, 162, 165, 168, 171, 174 ; 按Excel公式生成后补齐256个字节查表时用MOVC A,ADPTR指令A寄存器存放采样点索引DPTR指向SIN_TAB表首地址。这条指令会从ROM中取数据不需要额外声明前提是表不能超过当前代码段一般几个KB的程序完全没问题。3.3 采样点数、定时周期与输出频率的关系所有波形之所以能统一成一套框架关键在于频率公式f 1 / (采样点数 × 单点输出间隔时间)以256点的正弦表为例如果定时器每隔T秒触发一次中断中断里输出一个采样点那么完整输出一个周期需要256个点波形频率f 1 / (256 × T)。这个公式是所有波形频率控制的基石。举个具体例子AT89C51晶振12MHz机器周期是1us。想输出大约100Hz的正弦波每个采样点间隔T 1 / (100 × 256) ≈ 39us。定时器T0工作在方式1即16位定时器初值计算初值 65536 - 39 65497 0xFFD9也就是TH00xFFTL00xD9。反过来如果你把初值改成0xFFEC20us波形频率就变成1/(256×20us)≈195Hz。改波形频率的本质就是改定时初值这个思路对后面扩展按键调频非常关键。4. 汇编代码实现定时中断框架加四种波形逻辑4.1 定时器初始化和统一中断框架为了让四种波形都能用同一套定时机制最好把所有波形都设计成256个采样点一个周期。这样中断服务程序只负责两件事按当前索引查表输出然后把索引加1。先看定时器初始化代码ORG 0000H LJMP MAIN ORG 000BH LJMP T0_ISR MAIN: MOV TMOD, #01H ; T0方式116位定时器 MOV TH0, #0FFH ; 100Hz输出示例 MOV TL0, #0D9H MOV DPTR, #SIN_TAB ; 默认输出正弦波 MOV 30H, #00H ; 采样点索引清零 SETB ET0 SETB EA SETB TR0 LOOP: SJMP LOOP ; 主程序空转实际工作都在中断里这里我把采样点索引放在内部RAM的30H单元而不是R寄存器原因是中断服务程序里如果用了R0等寄存器主程序大量使用时会冲突除非你每次进出都保护所有相关寄存器。放到RAM单元后就省心很多中断和主程序各自用各自的存储空间。4.2 四种波形的核心数据与输出逻辑方波的256点表最简单前128个点全为0xFF后128个点全为0x00。用汇编生成表SQUARE_TAB: ; 前128个0xFF DB 0FFH,0FFH,0FFH,0FFH,0FFH,0FFH,0FFH,0FFH ; ... 补齐128个 ; 后128个0x00 DB 00H,00H,00H,00H,00H,00H,00H,00H ; ... 补齐128个锯齿波更直观从0递增到255可以直接在中断里用代码生成不用开表T0_ISR_SAW: MOV A, 30H ; 取当前索引 MOV P0, A ; 索引值本身就是数字量 INC 30H RETI因为8位计数器从255再加1会自动溢出变成0正好构成锯齿波的循环。三角波可以用查表也可以实时生成实时生成的代码稍微复杂一点要区分上升段和下降段。这里我倾向于把三角波也做成256点的表表生成规则索引0~127时值为索引×2索引128~255时值为(255-索引)×2。这样峰值大约254波形对称且周期正好256点。正弦波使用前文提到的SIN_TAB查表不需要判断边界因为索引自然回绕。中断服务程序的通用框架如下T0_ISR: PUSH PSW PUSH ACC PUSH DPH PUSH DPL MOV A, 30H MOVC A, ADPTR ; 查表取出数字量 MOV P0, A ; 送给DAC0832 INC 30H ; 索引加1自动回绕 POP DPL POP DPH POP ACC POP PSW RETI切换波形时主程序只需要修改DPTR的指向改成SQUARE_TAB、SAW_TAB、TRI_TAB或者SIN_TAB无需改动中断逻辑。这种数据驱动的结构放到后来学习更复杂的嵌入式系统思路也是通用的。4.3 中断现场保护的细节为什么中断服务程序里要PUSH和POP这几个寄存器因为中断随时可能打断主程序的执行CPU自动保存的只有当前PC指针PSW、ACC、DPTR这些寄存器主程序可能正在用。如果在中断里把ACC改了返回主程序后主程序拿到的ACC就不是原来那个值了程序行为就会莫名其妙地错乱。在Keil的C语言中编译器会帮你处理这些事情但汇编里必须手动处理。如果中断程序用了R0~R7对应的寄存器组如果和主程序不同可以通过PSW的RS0、RS1切换寄存器组但最简单可靠的办法还是用多少就保护多少。我见过太多初学者写汇编中断不保护现场结果波形偶尔乱跳、定时不准查半天找不到原因。5. Proteus仿真从搭建电路到调试核心要点5.1 仿真电路搭建具体步骤Proteus建工程的步骤不复杂但有几个细节直接决定仿真能不能通过。我是这样搭的新建Proteus工程选取AT89C51芯片放置到原理图编辑区。双击AT89C51芯片在属性编辑窗口中设置Program File即加载编译好的HEX文件同时把Clock Frequency设置为12MHz仿真中可以不画晶振电路和复位电路直接靠这个属性驱动。从库中放置DAC0832和LM358。提示一下不同版本的Proteus库中DAC0832的搜索名可能略有差异但一般直接输入DAC0832都能找到如果找不到检查一下库过滤器或者用DAC0808代替练手但引脚定义不同电路需相应调整。按照第2节的接线方式连线。P0.0~P0.7接DI0~DI7ILE接VCCCS、WR1、WR2、XFER全部接GNDVREF接-5V直流源Iout1接LM358反相输入端Iout2接地Rfb接LM358输出端。放置虚拟示波器A通道连接到LM358输出端GND接公共地。点击仿真运行按钮应该就能看到波形。5.2 汇编源码如何变成能在Proteus里跑的HEX文件Proteus本身不能编译汇编代码需要先用Keil生成HEX文件。我用的是Keil uVision具体流程新建Keil工程设备选择Atmel下的AT89C51。新建一个文本文件保存成.asm后缀把汇编代码写进去。在工程左侧的Source Group上右键把.asm文件添加进工程。点击Options for Target在Output选项卡里勾选Create HEX File。点击Build按钮F7快捷键编译没有报错的话在工程目录的Objects文件夹里生成.hex文件。回到Proteus双击AT89C51在Program File里选择这个.hex文件。这里有一个初学者经常栽的坑编译之前忘了勾选Create HEX File导致编译很顺利却找不到HEX文件。每次编译前先检查一下这个选项不然白折腾。5.3 仿真时最常遇到的几个问题排查**问题一示波器是一条直线完全没波形。**先看AT89C51是否真的加载了HEX文件这是最高频的失误。然后在单片机引脚上接一个LED让主程序循环翻转LED确认程序在跑。如果程序在跑但DAC没输出检查ILE是不是接了高电平、CS/WR1/WR2/XFER是不是都接地了。直通方式下这四个脚任何一个接错数据都进不到DAC内部。**问题二波形有但是反的或者幅度不对。**这个优先查VREF极性和运放供电。VREF接5V输出就是负向波形接-5V输出正向波形。LM358如果只接了单电源输出接近0V的部分会被砍掉波形底部削平。把运放供电改成±12V通常能解决。**问题三波形看起来有明显的台阶感。**这是8位DAC的正常现象不是bug。输出频率越高台阶越明显因为周期内采样点数相对变少。课程设计答辩时如果被老师问到这个能解释清楚这是量化误差和采样点数限制导致的固有失真就够了。**问题四实际硬件输出高电平不够稳。**P0口必须接上拉电阻这是仿真本文还有配套的精品资源点击获取
返回列表