基于DA转换模块的单片机仿真和C语言开发

基于DA转换模块的单片机仿真和C语言开发
基于DA转换模块的单片机仿真和C语言开发

1.总体设计思路

波形发生器是一种常用的信号源,广泛地应用于电子电路、自动控制系统和教学实验等领域。函数信号发生器是一种能够产生多种波形,如三角波、锯齿波、矩形波(含方波)、正弦波的电路。函数信号发生器在电路实验和设备检测中具有十分广泛的用途。通过对函数波形发生器的原理以及构成分析,可设计一个能变换出三角波、正弦波、方波的函数波形发生器。目前使用的信号发生器大部分是函数信号发生器,且特殊波形发生器的价格昂贵。所以本设计使用的是DAC0832芯片构成的发生器,可产生三角波、方波、正弦波等多种特殊波形和任意波形,波形的频率可用程序控制改变。在单片机上加外围器件距阵式键盘,通过键盘控制波形频率的增减以及波形的选择,并用了LCD显示频率大小。在单片机的输出端口接DAC0832进行D/A转换,再通过运放进行波形调整,最后输出波形接在示波器上显示。本设计具有线路简单、结构紧凑、价格低廉、性能优越等优点。

本设计制作的波形发生器,可以输出多种标准波形,如方波、正弦波、三角波、锯齿波等,还可以输出任意波形,如用鼠标创建的一个周期的非规则波形或用函数描述的波形等,输出的波形的频率、幅度均可调,且能脱机输出。设计的人机界面不但清晰美观,而且操作方便。

设计思路:课设需要各个波形的基本输出。如输出三角波、方波、正弦波。这些波形的实现的具体步骤:锯齿波实现很简单,只需要一开始定义一个初值,然后不断的加1,当溢出后又重初值开始加起,就这样循环下去。三角波的实验过程是先加后减,实现方法是先是开始加1直到溢出后就执行减1操作,就这样不断调用这个循环。方波的实现方法是连续输出一个数,到某个时候就改变一下值,可以把值定义为正极性的,也可以是负极性。正弦波的实现是非常麻烦的。它的实现过程是通过定义一些数据,然后执行时直接输出定义的数据就可以了。

元件选型:单片机AT89C51系统,DAC0832一片,PC机一台,运算放大器。

2. 芯片简介

2.1 DAC0832芯片介绍:

0832采用双缓冲接口方式,其传送控制端接地,输入所存允许断ILE与+5V 电源相连,利用一个地址码进行二次输出操作,完成数据的传送和激动转换,第一次操作室P2.6为高电平,将P0口数据线上的数据锁存于DAC0832的输入寄存器中。第二次操作是写控制信号由效,传送控制端为低电平,将输入寄存器中的内容锁存入0832的DAC寄存器中,D/A转换器便开始对锁存于DAC寄存器的8位数据进行转换,约经过1/2时钟周期后,在输出端(IOUT2、IOUT1)建立稳定的电流输出。运放的作用是将0832输出的模拟电流信号转换为电压波形。DAC0832为一个8位D/A转换器,单电源供电,在+5~+15V范围内均可正常工作。

基准电压的范围为±10V,电流建立时间为1μs,CMOS工艺,低功耗20mW。

DAC0832内部结构资料:芯片内有两级输入寄存器,使DAC0832具备双缓冲、单缓冲和直通三种输入方式,以便适于各种电路的需要(如要求多路D/A异步输入、同步转换等)。D/A转换结果采用电流形式输出。要是需要相应的模拟信号,可通过一个高输入阻抗的线性运算放大器实现这个供功能。运放的反馈电阻可通过RFB端引用片内固有电阻,还可以外接

DI0~DI7:数据输入线,TLL电平。

ILE:数据锁存允许控制信号输入线,高电平有效。

CS:片选信号输入线,低电平有效。

WR1:为输入寄存器的写选通信号。

XFER:数据传送控制信号输入线,低电平有效。

WR2:为DAC寄存器写选通输入线。

Iout1:电流输出线。当输入全为1时Iout1最大。

Iout2: 电流输出线。其值与Iout1之和为一常数。

Rfb:反馈信号输入线,芯片内部有反馈电阻。

Vcc:电源输入线 (+5v~+15v) 。

Vref:基准电压输入线 (-10v~+10v) 。

AGND:模拟地,摸拟信号和基准电源的参考地。

DGND:数字地,两种地线在基准电源处共地比较好。

2.2 AT89C52管脚说明:

图2.1AT89C52引脚图

VCC:供电电压。

GND:接地。

P0口:P0口为一个8位漏级开路双向I/O口,每脚可吸收8TTL门电流。当P1口的管脚第一次写1时,被定义为高阻输入。P0能够用于外部程序数据存储器,它可以被定义为数据/地址的第八位。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须被拉高。

P1口:P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。P1口管脚写入1后,被内部上拉为高,可用作输入,P1口被外部下拉为低电平时,将输出电流,这是由于内部上拉的缘故。在FLASH 编程和校验时,P1口作为第八位地址接收。

P2口:P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4个TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。并因此作为输入时,P2口的管脚被外部拉低,将输出电流。这是由于内部上拉的缘故。P2口当用于外部程序存储器或16位地址外部数据存储器进行存取时,P2口输出地址的高八位。在给出地址“1”时,它利用内部上拉优势,

当对外部八位地址数据存储器进行读写时,P2口输出其特殊功能寄存器的内容。P2口在FLASH编程和校验时接收高八位地址信号和控制信号。

P3口:P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL 门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。

RST:复位输入。当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。

ALE/PROG:当访问外部存储器时,地址锁存允许的输出电平用于锁存地址的地位字节。在FLASH编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,此频率为振荡器频率的1/6。因此它可用作对外部输出的脉冲或用于定时目的。然而要注意的是:每当用作外部数据存储器时,将跳过一个ALE脉冲。如想禁止ALE的输出可在SFR8EH地址上置0。此时, ALE 只有在执行MOVX,MOVC指令是ALE才起作用。另外,该引脚被略微拉高。如果微处理器在外部执行状态ALE禁止,置位无效。

/PSEN:外部程序存储器的选通信号。在由外部程序存储器取指期间,每个机器周期两次/PSEN有效。但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。

/EA/VPP:当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。在FLASH编程期间,此引脚也用于施加12V编程电源(VPP)。

XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入。

XTAL2:来自反向振荡器的输出。

3. 原理图设计及仿真结果

3.1. 总体硬件原理图:

图3.1总体硬件原理图

3.2 方波产生原理及波形

此波形的实现更加简单,只需开始的时候设置一个初值然后直接输出这个值就行了,输出一段时间后,然后再重新置一个数据,然后再输出这个数据一段时间,但是此时的时间一定要等于前面那段时间。这样才是一个方波,如果两个时间不相同,那就相当于一个脉冲波了。

流程图如下图所示:

图3.2 方波产生流程图仿真结果图:

图3.3方波仿真结果

3.3 正弦波产生原理及波形

正弦波的实现则相对比较复杂,因为正弦波的实现是输出各个点的值就行了,可是各个点值则要通过正弦函数来求出。输出的数据刚好是256个数据,这样则可以直接相加就行了

流程图如下图所示:

图3.3 正弦波产生流程图

仿真结果图:

图3.4正弦波仿真结果

3.4三角波产生原理及波形

三角波的实现是设置一个初值,然后进行加数,同样是加到某个数之后再行减数,减到初值之后就再返回到先前的操作,这个操作跟锯齿波的实现是相似的。此程序输入的VREF的电压是+5V,因此该波形输出的最大频率是初值为0和最终值为256,这样输出的波形是最大的。

流程图如下图所示:

图3.5正弦波产生流程图

仿真结果图:

图3.6三角波仿真结果

4.其他模块及拓展部分

4.1 通过开关实现波形的切换及调频

通过开关实现波形的切换比较简单只需通过输出波形后不断返回到检测开关的子程序中,判断是否有别的开关拨动,如果有别的开关拨动则执行别的程序,否则输出原来的波形,不过如果要能够识别别的开关发生变化,必须将此开关关掉否则会识别不了别的键按下。当然开关的调频和调幅的实现也一样,不过首先先输出一个波形,然后再检测开关是否需要调频或者调幅,如果需要则转入到相应的程序中,最后再重新输出波形。

电路模块实现:

图4.1波形的切换及调频电路

4.2 运算放大器模块

大多数常用的D/A转换器的数字输入时二进制或BCD码形式,输出可以是电流也可以使电压,而多数是电流。因此,在多数电路中,D/A转换器的输出需要用运算放大器组成的I/V转换器将电流输出转换成电压输出

4.3 扩展功能简介

电位器RV1用于调整输入的I/V转换系数。

调节可变电阻RV1可以调整电压,电流转化比例。该电压,电流成正比变化,当可变电阻变大时,输出波形随输出电压增大而增大;当可变电阻变小时,输出波形随输出电压变小而变小。通过电阻调节,可显示电压的变化,如图所示:

图4.2波形幅度控制电路

输出波形可随电压值变化而变化

图4.3不同幅值的波形比较

该LED模块可显示1. 波形选择提示 2. 可显示通过按键调节的增大或减小的频率。LED通过接AT89C52的P0口和排阻,实现数据传输和显示功能。

图4.4 LED显示电路

5.心得体会

本次的设计中利用AT89C52和DAC0832以及放大器完成电路的设计,用开关来控制各种波形的发生及转换,用单片机输出后,经过模数转换器生成波形,最终可以通过示波器观察。

在这次的软件设计中,程序设计采用的是C语言。C语言具有速度快,可以直接对硬件进行操作的优点,它可以极好的发挥硬件的功能。并且C语言编写的代码非常容易理解,但存在不好维护,很容易产生 bug,难于调试的缺点。因此,在大型程序的设计中,多采用C语言进行程序编译。C语言简洁高效,是最贴近硬件的高级编程语言,经过多年的发展,现在已成熟为专业水平的高级语言。而且,现在单片机产品推出时纷纷配套了C语言编译器,应用广泛。就本次课程设计来说,C语言完全适用本次课设的。

由于真正意义上的程序设计还不多,因此还不是很得心应手,所以在设计中遇到一些问题和一些难点。比如:在程序设计中如何实现程序结构的最优化,以达到较高的质量。这是以后设计中要注意的问题。

通过这次课程设计,我进一步了解了波形发生器的原理,在实际动手操作过程中,使我接触了许多我以前没接触过的元件,而且重新温习了学了已久的C 语言,使我学得了许多知识,使我获益匪浅。

这次课程设计,使我的动手能力得到了很大的提

高,更使我们懂得理论知识的重要性,没有理论的指导一切实际行动都是盲目的,且实际操作是我们得到的理论知识得到验证,更能增加对理论知识的理解。虽然在这次设计的过程中,困难不少,但是正是在自己的努力,同学们的帮助下,自己能够顺利的完成,确实还是蛮欣慰的。感谢这次课程设计给了自己锻炼的机会,自己在今后的学习和生活中,会更加的努力,争取更大的进步!

6.仿真程序

#include

#define uchar unsigned char

#define uint unsigned int

//#define Fosc 24000000/12000000 //12分频后的频率

#define DAdata P0 //DA数据端口

sbit DA_S1= P2^0; // 控制DAC0832的8位输入寄存器,仅当都为0时,可以输出数据(处于直通状态),否则,输出将被锁存

sbit DA_S2= P2^1; // 控制DAC0832的8位DAC寄存器,仅当都为0时,可以输出数据(处于直通状态),否则,输出将被锁存

sbit key= P3^2;

uchar wavecount; //'抽点'计数

uchar THtemp,TLtemp;//传递频率的中间变量

//uint T_temp;

uchar judge=1; //在方波输出函数中用于简单判别作用

uchar waveform; //当其为0、1、2时,分别代表三种波

uchar code freq_unit[3]={10,50,200}; //三种波的频率单位

uchar idata wavefreq[3]={1,1,1}; //给每种波定义一个数组单元,用于存放单位频率的个数uchar code lcd_hang1[]={"Sine Wave " "Triangle Wave " "Square Wave " "Select Wave: " "press No.1 key! "};

uchar idata lcd_hang2[16]={"f= Hz "};

/*uchar code wave_freq_adjust[]={ //频率调整中间值

0xff,0xb8,0x76,0x56,0x43,0x37,0x2e,0x26,0x20,0x1c, //正弦波频率调整中间值

0xff,0x8e,0x5a,0x41,0x32,0x28,0x20,0x1b,0x17,0x0e, //三角波频率调整中间值

0xff,0x8e,0x5a,0x41,0x32,0x28,0x20,0x1b,0x17,0x0e};

uint code wave_freq_adjust[]={ //频率调整中间值

380,184,118,86,67,55,46,28,38,32,

295,142, 90,65,50,40,32,27,23,14,

295,142, 90,65,50,40,32,27,23,14}; */

/*uchar code waveTH[]={

0xfc,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

0xfc,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

uchar code waveTL[]={

0xf2,0x78,0xfb,0x3c,0x63,0x7d,0x8f,0x9d,0xa8,0xb1,

0x17,0x0b,0xb2,0x05,0x37,0x58,0x70,0x82,0x90,0x9b,

0x4d,0xa7,0xc4,0xd3,0xdc,0xe2,0xe6,0xea,0xec,0xee};*/

/***********这两组数组很重要,需要根据波形来调试,选择合适的值,使输出波形达到频率要求************/

uchar code waveTH[]={

0xfd,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

0xfd,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,

0xec,0xf6,0xf9,0xfb,0xfc,0xfc,0xfd,0xfd,0xfd,0xfe};

uchar code waveTL[]={

0x06,0x8a,0x10,0x4e,0x78,0x93,0xa8,0xb3,0xbe,0xc6, //正弦波频率调整中间值

0xac,0xde,0x48,0x7a,0x99,0xaf,0xbb,0xc8,0xd0,0xde, //三角波频率调整中间值

0x88,0x50,0x90,0x32,0x34,0xbe,0x4a,0xa3,0xe5,0x2c};

/****************************************************************************** *******************/

uchar code triangle_tab[]={ //每隔数字8,采取一次

0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78, 0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8,0xff, 0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80,

0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08,0x00};

uchar code sine_tab[256]={

//输出电压从0到最大值(正弦波1/4部分)

0x80,0x83,0x86,0x89,0x8d,0x90,0x93,0x96,0x99,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,0xb1 ,0xb4,0xb7,0xba,0xbc,

0xbf,0xc2,0xc5,0xc7,0xca,0xcc,0xcf,0xd1,0xd4,0xd6,0xd8,0xda,0xdd,0xdf,0xe1,0xe3,0xe5, 0xe7,0xe9,0xea,0xec,

0xee,0xef,0xf1,0xf2,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfd,0xfe,0xff,0xff,0 xff,0xff,0xff,0xff,

//输出电压从最大值到0(正弦波1/4部分)

0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfd,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf 2,0xf1,0xef,

0xee,0xec,0xea,0xe9,0xe7,0xe5,0xe3,0xe1,0xde,0xdd,0xda,0xd8,0xd6,0xd4,0xd1,0xcf,0xcc, 0xca,0xc7,0xc5,0xc2,

0xbf,0xbc,0xba,0xb7,0xb4,0xb1,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x99 ,0x96,0x93,0x90 ,0x8d,0x89,0x86,0x83,0x80,

//输出电压从0到最小值(正弦波1/4部分)

0x80,0x7c,0x79,0x76,0x72,0x6f,0x6c,0x69,0x66,0x63,0x60,0x5d,0x5a,0x57,0x55,0x51,0x4 e,0x4c,0x48,0x45,0x43,

0x40,0x3d,0x3a,0x38,0x35,0x33,0x30,0x2e,0x2b,0x29,0x27,0x25,0x22,0x20,0x1e,0x1c,0x1 a,0x18,0x16 ,0x15,0x13,

0x11,0x10,0x0e,0x0d,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x02,0x01,0x0 0,0x00,0x00,0x00,0x00,0x00,

//输出电压从最小值到0(正弦波1/4部分)

0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02 ,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x 0a,0x0b,0x0d,0x0e,0x10,

0x11,0x13,0x15 ,0x16,0x18,0x1a,0x1c,0x1e,0x20,0x22,0x25,0x27,0x29,0x2b,0x2e,0x30,0x3 3,0x35,0x38,0x3a,0x3d,

0x40,0x43,0x45,0x48,0x4c,0x4e,0x51,0x55,0x57,0x5a,0x5d,0x60,0x63,0x66 ,0x69,0x6c,0x6 f,0x72,0x76,0x79,0x7c,0x80};

void delay(uchar z)

{

uint x,y;

for(x=z;x>0;x--)

for(y=110;y>0;y--);

}

void triangle_out() //三角波输出

{

DAdata=triangle_tab[wavecount++];

if(wavecount>64) wavecount=0;

DA_S1=0; //打开8位输入寄存器

DA_S1=1; //关闭8位输入寄存器

}

void sine_out() //正弦波输出

{

DAdata=sine_tab[wavecount++];

DA_S1=0; //打开8位输入寄存器

DA_S1=1; //关闭8位输入寄存器

}

void square_out() //方波输出

{

judge=~judge;

if(judge==1) DAdata=0xff;

else DAdata=0x00;

DA_S1=0; //打开8位输入寄存器

DA_S1=1; //关闭8位输入寄存器

}

/************1602液晶的相关函数*************/ #define lcd_ports P1

sbit rs=P2^2;

sbit rw=P2^3;

sbit lcden=P2^4;

void write_com(uchar com)

{

rs=0; //置零,表示写指令

lcden=0;

lcd_ports=com;

delay(5);

lcden=1;

delay(5);

lcden=0;

}

void write_date(uchar date)

{

rs=1; //置1,表示写数据(在指令所指的地方写数据)

lcden=0;

lcd_ports=date;

delay(5);

lcden=1;

delay(5);

lcden=0;

}

void disp_lcd(uchar addr,uchar *temp1)

{

uchar num;

write_com(addr);

delay(1); //延时一会儿???

for(num=0;num<16;num++)

{

write_date(temp1[num]);//或者这样写write_date(*(temp1+num));

delay(1);

}

}

void init_lcd()

{

//uchar num;

lcden=0; //可有可无???

rw=0; //初始化一定要设置为零,表示写数据

write_com(0x38); //使液晶显示点阵,为下面做准备

write_com(0x0c); //初始设置

write_com(0x06); //初始设置

write_com(0x01); //清零

write_com(0x80); //使指针指向第一行第一格

disp_lcd(0x80,&lcd_hang1[3*16]); //在第一行显示

disp_lcd(0xc0,&lcd_hang1[4*16]); //在第二行显示

/*for(num=0;num<16;num++)

{

write_date(table[num]);

delay(5);

}

write_com(0x80+0x40); //给指针重新赋值,使之指向第二行第一格for(num=0;num<16;num++)

{

write_date(table1[num]);

delay(5);

} */

/*TMOD=0x01; //选用定时方式1

TH0=(65536-50000)/256; //赋初值

TL0=(65536-50000)%256; //

EA=1; //开总中断

ET0=1; //开定时器中断

TR0=1; //启动定时器*/

}

/********************1602液晶函数声明结束*********************/

void main()

{

uchar i=0;

DA_S2=0; //使DAC寄存器处于直通状态

DAdata=0;

DA_S1=1; //关闭8位输入寄存器

init_lcd();

waveform=0;

TMOD=0x01; //设置定时器0为16位工作方式

IT0=1; //设置外部中断0为下降沿触发

ET0=1; //开定时器中断

EX0=1;

EA=1;

while(1)

{

//DAout(0xff); //可输出TTL波形

//DAout(0x80);

//T_temp=32;

}

}

void timer0() interrupt 1

{

TH0=THtemp;

TL0=TLtemp;

if(waveform==0) sine_out();

else if(waveform==1) triangle_out();

else if(waveform==2) square_out();

}

void key_int0() interrupt 0

{

uchar keytemp;

uint total_freq; //总频率

EA=0; TR0=0; //关总中断与定时器

delay(5); //延时够吗???

if(key==0) //确实有按键按下而引发中断

{

keytemp=P3&0xf0; //获取P3口高四位的值

switch(keytemp)

{

case 0xe0: //选择波形

waveform++;

if(waveform>2) waveform=0;

break;

case 0xd0: //频率按规定单位依次增加

wavefreq[waveform]++;

if(wavefreq[waveform]>10) wavefreq[waveform]=1; // /*这边要用“>10”,因为它比“=11”可靠

break; // 性更高,使加数有个上限,不会一直加下去*/

case 0xb0: //频率按规定单位依次衰减

wavefreq[waveform]--;

if(wavefreq[waveform]<1) wavefreq[waveform]=10; //这边要用“<1”,因为它比“=0”可靠性更高

break;

case 0x70: //TTL输出

DA_S2=1; //使DAC寄存器关闭

break;

}

THtemp=waveTH[waveform*10+(wavefreq[waveform]-1)]; //方括号中选取第几个数后,并把该值赋给T_temp

TLtemp=waveTL[waveform*10+(wavefreq[waveform]-1)];

total_freq= wavefreq[waveform] * freq_unit[waveform]; //求输出频率(个数*单位)

lcd_hang2[5]=total_freq%10+0x30; //在液晶中显示个位,(0x30 在液晶显示中表示数字0)

total_freq/=10; lcd_hang2[4]=total_freq%10+0x30; //在液晶中显示时十位

total_freq/=10; lcd_hang2[3]=total_freq%10+0x30; //在液晶中显示时百位

total_freq/=10; lcd_hang2[2]=total_freq%10+0x30; //在液晶中显示时千位

disp_lcd(0x80,&lcd_hang1[waveform*16]); //在第一行显示

disp_lcd(0xc0,lcd_hang2); //在第二行显示

}

wavecount=0; //'抽点'计数清零

while(!key);

EA=1; TR0=1; //开启总中断与定时器

}

相关主题
相关文档
最新文档