简易函数信号发生器设计_毕业论文
简易函数信号发生器设计
摘要:信号发生器又称信号源或振荡器,在生产实践和科技领域中有着广泛的应用。这次的设计分为五个模块:单片机控制及显示模块、数模转换模块、波形产生模块、输出显示模块、电源模块。使用AT89C52作为主控台结合芯片DAC0832产生1HZ-300HZ频率可调的五种信号波(正弦波、三角波和方波)。这几种波形有几个开关控制,可以随意进行切换,十分方便。另外,波形的频率和振幅也可以通过开关进行更改。可以说这次的设计操作简单,内容丰富,而且电路快捷明了。在编程语言上,我们选择自身比较熟悉的 C语言,这样在后期波形的调试及与硬件衔接方面更容易发挥出自身优势。经过设计及后期长时间的调试,设计的所有功能均已实现。
关键字:信号发生器、频率、幅度、AT98C52、DAC0832
1、设计要求
1)以单片机为核心,经过D/A转换和放大电路的处理,最后输出信号;
2)要求能输出正弦波、三角波和方波四种信号;
3)输出信号可以通过按键来改变;
4)频率可变,范围在1---300hz;
5)幅度可调0---10v;
6)可实现四路A/D电压采样;
2、总体设计
2.1 系统组成及工作原理
该函数信号发生器可以输出四种波形,有正弦波、三角波和方波。在此基础上进一步实现对波形频率和占空比的调节,并用液晶屏分两行显示波形名称和波形频率。
函数信号发生器的设计总体框图如图1所示,主要有单片机AT89C52,电源,键盘模块,LCD1602显示模块构成。
按案件模块:由5个复位开关与74LS21组成的系统通过对单片机传输中断信号来实现波形切换及频率和占空比的调节。
显示模块:用LCD1602,分行显示波形类型和波形频率的显示。
图1 系统总体框图
2.2测量原理
我们这个系统可实现四路A/D电压采样,使用的算法是:V=5*N/256,取五个点电压进行测量,并将测量完的十进制数据转化为二进制数据。
AD采样数据
电压源输入0.10 0.20 0.30 0.40 0.45
1.01
2.00
3.02
4.01 4.51
电压表测得
(放大后)
采集系统测得 1.01 2.00 3.02 4.02 4.52 3、硬件设计
3.1硬件组成
3.1.1 资源分配
晶振采用12MHZ。P1口的P1.0-P1.4分别与四个按键连接,分别控制波形切换、频率加、频率减,占空比加,占空比减。
P2口与DAC0832的D0-D7数据输入端相连。
P3口用来控制DAC0832的输入寄存器选择信号CS。
3.1.2 D/A转换模块部件
DAC0832芯片原理
管脚功能介绍(如图2所示)
V c c
20
Iout111lsbDI07Iout212DI16DI25Rfb 9DI34DI416Vref
8
DI515DI6
14msbDI713ILE 19WR218CS 1WR1
2
Xfer 17
U2
DAC0832
图2 DAC0832管脚图
DI7~DI0:8位的数据输入端,DI7为最高位。 ILE :数据锁存允许控制信号输入线,高电平有效。 CS :选片信号输入线(选通数据锁存器),低电平有效。
WR1:数据锁存器写选选通输入线,负脉冲有效,由ILE 、CS 、WR1的逻辑组合产生LE1,当LE1为高电平时,数据锁存器状态随输入数据线变化,LE1的负跳变时将输入数据锁存。
XFER :数据传输控制信号输入线,低电平有效,负脉冲有效。
WR2:DAC 寄存器选通输入线,负脉冲有效,由WR2、XFER 的逻辑组合产生LE2,当LE2为高电平时,DAC 寄存器的输出随寄存器的输入而变化,LE2的负跳变时将数据锁存器的内容打入DAC 寄存器并开始D/A 转换。
IOUT1:模拟电流输出端1,当DAC 寄存器中数据全为1时,输出电流最大,当 DAC 寄存器中数据全为0时,输出电流为0。
IOUT2:模拟电流输出端2, IOUT2与IOUT1的和为一个常数,即IOUT1+IOUT2=常数。
RFB :反馈电阻引出端,DAC0832内部已经有反馈电阻,所以 RFB 端可以直接接到外部运算放大器的输出端,这样相当于将一个反馈电阻接在运算放大器的输出端和输入端之间。
VREF :参考电压输入端,此端可接一个正电压,也可接一个负电压,它决定0至255的数字量转化出来的模拟量电压值的幅度,VREF 范围为(+10~-10)V 。VREF 端与D/A 内部T 形电阻网络相连。
Vcc :芯片供电电压,范围为(+5~+15)V 。 AGND :模拟量地,即模拟电路接地端。
DGND :数字量地。 3.1.3放大部分
放大部分采用双极运算放大电路,LM358 内部包括有两个独立的、高增益、
内部频率补偿的双运算放大器,适合于电源电压范围很宽的单电源使用,也适用于双电源工作模式,在推荐的工作条件下,电源电流与电源电压无关。它的使用范围包括传感放大器、直流增益模块和其他所有可用单电源供电的使用运算放大器的场合。
图3 lm358
3.2 单片机核心模块
3.2.1 AT89C52单片机及其说明
AT89C52为8 位通用微处理器:
图4 PDIP封装的AT89C52引脚图
采用工业标准的C51内核,在内部功能及管脚排布上与通用的8xc52 相同,
其主要用于会聚调整时的功能控制。功能包括对会聚主IC 内部寄存器、数据RAM 及外部接口等功能部件的初始化,会聚调整控制,会聚测试图控制,红外遥控信号IR的接收解码及与主板CPU通信等。主要管脚有:XTAL1(19 脚)和XTAL2(18 脚)为振荡器输入输出端口,外接12MHz 晶振。RST/Vpd(9 脚)为复位输入端口,外接电阻电容组成的复位电路。VCC(40 脚)和VSS(20 脚)为供电端口,分别接+5V电源的正负端。P0~P3 为可编程通用I/O 脚,其功能用途由软件定义,在本设计中,P0 端口(32~39 脚)被定义为N1 功能控制端口,分别与N1的相应功能管脚相连接,13 脚定义为IR输入端,10 脚和11脚定义为I2C总线控制端口,分别连接N1的SDAS(18脚)和SCLS(19脚)端口,12 脚、27 脚及28 脚定义为握手信号功能端口,连接主板CPU 的相应功能端,用于当前制式的检测及会聚调整状态进入的控制功能。
P0 口
P0 口是一组8 位漏极开路型双向I/O 口,也即地址/数据总线复用口。作为输出口用时,每位能吸收电流的
方式驱动8 个TTL逻辑门电路,对端口P0 写“1”时,可作为高阻抗输入端用。
在访问外部数据存储器或程序存储器时,这组口线分时转换地址(低8 位)和数据总线复用,在访问期间激活内部上拉电阻。
在Flash 编程时,P0 口接收指令字节,而在程序校验时,输出指令字节,校验时,要求外接上拉电阻。
P1 口
P1 是一个带内部上拉电阻的8 位双向I/O 口, P1 的输出缓冲级可驱动(吸收或输出电流)4 个TTL 逻辑
门电路。对端口写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口。作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(IIL)。
P2 口
P2 是一个带有内部上拉电阻的8 位双向I/O 口,P2 的输出缓冲级可驱动(吸收或输出电流)4 个TTL 逻辑
门电路。对端口P2 写“1”,通过内部的上拉电阻把端口拉到高电平,此时可作输入口,作输入口使用时,因为内部存在上拉电阻,某个引脚被外部信号拉低时会输出一个电流(IIL)。
在访问外部程序存储器或16 位地址的外部数据存储器(例如执行MOVX @DPTR 指令)时,P2 口送出高8 位地址数据。在访问8 位地址的外部数据存储器(如执行MOVX @RI 指令)时,P2 口输出P2 锁存器的内容。
Flash 编程或校验时,P2亦接收高位地址和一些控制信号。
P3 口
P3 口是一组带有内部上拉电阻的8 位双向I/O 口。P3 口输出缓冲级可驱动(吸收或输出电流)4 个TTL 逻
辑门电路。对P3 口写入“1”时,它们被内部上拉电阻拉高并可作为输入端口。此时,被外部拉低的P3 口将用上拉电阻输出电流(IIL)。
P3 口除了作为一般的I/O 口线外,更重要的用途是它的第二功能
P3 口还接收一些用于Flash 闪速存储器编程和程序校验的控制信号。
RST
复位输入。当振荡器工作时,RST引脚出现两个机器周期以上高电平将使单片机复位。
ALE/PROG
当访问外部程序存储器或数据存储器时,ALE(地址锁存允许)输出脉冲用于锁存地址的低8 位字节。一般情况下,ALE 仍以时钟振荡频率的1/6 输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。要注意的是:每当访问外部数据存储器时将跳过一个ALE 脉冲。
对Flash 存储器编程期间,该引脚还用于输入编程脉冲(PROG)。
如有必要,可通过对特殊功能寄存器(SFR)区中的8EH 单元的D0 位置位,可禁止ALE 操作。该位置位后,只有一条 MOVX 和MOVC指令才能将ALE 激活。此外,该引脚会被微弱拉高,单片机执行外部程序时,应设置ALE 禁止位无效。
PSEN
程序储存允许(PSEN)输出是外部程序存储器的读选通信号,当AT89C52 由外部程序存储器取指令(或数据)时,每个机器周期两次PSEN 有效,即输出两个脉冲。在此期间,当访问外部数据存储器,将跳过两次PSEN信号。
EA/VPP
外部访问允许。欲使CPU 仅访问外部程序存储器(地址为0000H—FFFFH),EA 端必须保持低电平(接地)。需注意的是:如果加密位LB1 被编程,复位时内部会锁存EA端状态。如EA端为高电平(接Vcc端),CPU 则执行内部程序存储器中的指令。 Flash 存储器编程时,该引脚加上+12V 的编程允许电源Vpp,当然这必须是该器件是使用12V 编程电压Vpp。
XTAL1
振荡器反相放大器的及内部时钟发生器的输入端。
XTAL2
振荡器反相放大器的输出端。
特殊功能寄存器
在AT89C52 片内存储器中,80H-FFH 共128 个单元为特殊功能寄存器(SFE),SFR 的地址空间映象如表2 所示。
并非所有的地址都被定义,从80H—FFH 共128 个字节只有一部分被定义,还有相当一部分没有定义。对没有定义的单元读写将是无效的,读出的数值将不确定,而写入的数据也将丢失。不应将数据“1”写入未定义的单元,由于这些单元在将来的产品中可能赋予新的功能,在这种情况下,复位后这些单元数值总是“0”。 AT89C52除了与AT89C51所有的定时/计数器0 和定时/计数器1 外,还增加了一个定时/计数器2。定时/计数器2 的控制和状态位位于T2CON (参见表3)T2MOD(参见表4),寄存器对(RCAO2H、RCAP2L)是定时器2 在16 位捕获方式或16 位自动重装载方式下的捕获/自动重装载寄存器。
数据存储器
AT89C52 有256 个字节的内部RAM,80H-FFH 高128 个字节与特殊功能寄存器(SFR)地址是重叠的,也就是高128 字节的RAM 和特殊功能寄存器的地址是相同的,但物理上它们是分开的。当一条指令访问7FH 以上的内部地址单元时,指令中使用的寻址方式是不同的,也即寻址方式决定是访问高128 字节RAM 还是访问特殊功能寄存器。如果指令是直接寻址方式则为访问特殊功能寄存器。例如,下面的直接寻址指令访问特殊功能寄存器0A0H(即P2 口)地址单元。
MOV 0A0H,#data
间接寻址指令访问高128 字节RAM,例如,下面的间接寻址指令中,R0 的内容为0A0H,则访问数据字节地址为0A0H,
而不是P2 口(0A0H)。
MOV @R0,#data
堆栈操作也是间接寻址方式,所以,高128 位数据RAM 亦可作为堆栈区使用。·定时器0和定时器1:
3.2.2单片机最小系统版模块
图 5 3.3两级运放电路
图 6
芯片特性:
内部频率补偿
直流电压增益高(约100dB)
单位增益频带宽(约1MHz)
电源电压范围宽:单电源(3—30V)
双电源(±1.5 一±15V)
低功耗电流,适合于电池供电
· 低输入偏流
低输入失调电压和失调电流
共模输入电压范围宽,包括接地
差模输入电压范围宽,等于电源电压范围
输出电压摆幅大(0 至Vcc-1.5V)
3.4显示电路
图7
LCD1602
主要功能 A、 40通道点阵LCD 驱动; B、可选择当作行驱动或列驱动; C、输入/输出信号:输出,能产生20×2个LCD驱动波形;输入,接受控制器送出的串行数据和控制信号,偏压(V1∽V6); D、通过单片机控制将所测的频率信号读数显示出来
4 、程序设计
4.1 主程序设计
图 8
4.2 子程序设计
4.2.1三角波程序流程图
三角波的实现是设置一个初值,然后进行加数,同样是加到某个数之后再进行减数,减到初值之后就再返回到先前的操作,这个操作跟锯齿波的实现是相似的。此程序输入的VREF的电压是+5V,因此该波形输出的最大频率是初值为00H 和最终值为0FFH,且步数为1,这样输出的波形是最大的。
程序流程图如下图所示:
图9
4.2.2 方波程序流程图
此波形的实现更加简单,只需开始的时候设置一个初值然后直接输出这个值
就行了,输出一段时间后,然后再重新置一个数据,然后再输出这个数据一段时间,但是此时的时间一定要等于前面那段时间。这样才是一个方波,如果两个时间不相同,那就相当于一个脉冲波了。流程图如下图所示:
置初值
输出信号
num++,步数
判断是否已满?
否
是
Num ——,步数
判断是否等于初值?
否/是
开 始
开始
给A设置初值
输出信号
设置输出的时间
再设置一个初值
输出信号
输出一段时间
图10
4.2.3 正弦波程序流程图
正弦波的实现则相对比较复杂,因为正弦波的实现是输出各个点的值就行了,可是各个点值则要通过正弦函数来求出,不过这些值直接去网上下载下来使用就可以了。输出的数据刚好是256个数据,这样则可以直接相加就行了。
流程图如下图所示:
图11 程序设计
#include
typedef unsigned char uchar; unsigned char i,j=0,sqar_num=128; //最大值100,默认值50
unsigned char cho=0; //0:正弦波。1:方波。2:三角波。3:锯齿波。
unsigned char num=0;
unsigned char TIME0_H=0xff,TIME0_L=0xd9; //定时器0的初值设置;全局变量.对应正弦波,锯齿波50HZ
sbit chg= P1^0;
//三角波100Hz.
sbit freq_u=P1^1; sbit freq_d=P1^2;
sbit duty_u=P1^3;
sbit duty_d=P1^4;
sbit cs =P3^7;
bit flag=0;
unsigned int FREQ=150;//初始化频率,50HZ
sbit rw=P3^6;
sbit rs=P3^5;
sbit lcde=P3^0;
unsigned char TempBuffer[7]; unsigned char value1[]={"Frequency:"};
void delay(unsigned int ms)
{
unsigned int i,j;
for (j=0;j for (i=0;i<120;i++); } unsigned char code sin_num[]={ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18,18, 19, 20, 21, 22, 23, 24, 25, 25, 26, 27, 28, 29, 30, 31, 32,34, 35, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 49, 50, 51,52, 54, 55, 56, 57, 59, 60, 61, 63, 64, 66, 67, 68, 70, 71, 73, 74, 75, 77, 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93, 95, 96, 98, 99, 101,102, 104,106,107,109,110,112,113,1 15,116,118,120,121,123,124,126,1 28, 129,131,132,134,135,137,139,1 40,142,143,145,146,148,149,151,1 53, 154,156,157,159,160,162,163,1 65,166,168,169,171,172,174,175,1 77, 178,180,181,182,184,185,187,1 88,189,191,192,194,195,196,198,1 99, 200,201,203,204,205,206,208,2 09,210,211,213,214,215,216,217,2 18, 219,220,221,223,224,225,226,2 27,228,229,230,230,231,232,233,2 34, 235,236,237,237,238,239,240,2 40,241,242,243,243,244,245,245,2 46, 246,247,247,248,248,249,249,2 50,250,251,251,251,252,252,253,2 53, 253,253,254,254,254,254,254,2 55,255,255,255,255,255,255,255,2 55 }; //调节部分--频率 void freq_ud(void) { unsigned int temp; if(freq_d==0) { FREQ--; } else if(freq_u==0) { FREQ++; } if(cho==1|cho==3) //锯齿波256次中断一周期,特殊处理下。否则他的频率是100(+\-)n*2Hz. { temp=0xffff-3906/FREQ; //方波,三角波默认为100hz,切换后频率也为50HZ 65336-10^6/(256*FREQ) TIME0_H=temp/256; TIME0_L=temp%256; } else if(cho==0|cho==3)//正弦波三角波默认周期50hz 65536-10^6//(512*FREQ) { temp=0xffff-1953/FREQ; TIME0_H=temp/256; TIME0_L=temp%256; } } //调节部分--方波的占空比 void duty_ud(void) //方波也采用512次中断构成一个周期。 { if(duty_d==0&sqar_num>0) sqar_num--; else if(duty_u==0&sqar_num<255) sqar_num++; } //波形发生函数 void sint(void) { if(!flag) { cs=0;P2=sin_num[num++];cs=1; if(num==0){num=255;flag=1;} } else if(flag) { cs=0;P2=sin_num[num--];cs=1; if(num==255){num=0;flag=0;} } } void square(void) { if(i++ {cs=0;P2=0XFF;cs=1;} else{cs=0;P2=0X00;cs=1;} } void triangle(void) { cs=0;P2=num++;cs=1; } void stw(void) { if(~flag) { cs=0;P2=num++;cs=1; if(num==0){num=255;flag=1;} } else if(flag) { cs=0;P2=num--;cs=1; if(num==255){num=1;flag=0;} } } //1602显示开始 void write_command(unsigned char command) { rs=0; P0=command; lcde=1; lcde=0; } void write_data(unsigned char data0) { rs=1; P0=data0; lcde=1; lcde=0; } void init_1602() { delay(5); write_command(0x38); delay(5); write_command(0x01); delay(5); write_command(0x06); delay(5); write_command(0x0c); delay(5); } //1602显示结束 /*******************频率值转换为 字符串**********************/ void temp_to_str() { // TempBuffer[0]=FREQ/1000+'0'; //千位 TempBuffer[1]=FREQ%1000/100+'0'; //百位 TempBuffer[2]=FREQ%1000%100/10+' 0';//十位 TempBuffer[3]=FREQ%1000%100%10+' 0';//个位 TempBuffer[4]='H'; TempBuffer[5]='z'; //TempBuffer[6]='\0'; } void LCD_disp_str(uchar x,uchar y,uchar *str) { uchar address; if(y==1) address=0x80+x; else address=0xc0+x; write_command(address); while(*str!='\0') { write_data(*str); str++; } } /*******************频率显示*****************/ void show_frequency() { unsigned char i,j; //init_1602();//初始化lcd write_command(0xc0); for(i=0;i { write_data(value1[i]); delay(2); } write_command(0xca);//液晶显示位置 temp_to_str(); for(j=0;j { write_data(TempBuffer[j]); delay(5); } } void show_wave() { if(j==0) {LCD_disp_str(0,1," Wave: Sine ");} else if(j==1) {LCD_disp_str(0,1," Wave: Square ");} else if(j==2) {LCD_disp_str(0,1," Wave: Sawtooth");} else if(j==3) {LCD_disp_str(0,1," Wave: Triangle");} } void main() { TMOD=0X01; TH0=0xff; TL0=0xd9; IT0=1; //设置中断触发方式,下降沿 EA=1; EX0=1; ET0=1; IP=0X01; //键盘中断级别高 init_1602();//初始化lcd TR0=1; write_command(0xc0);//液晶显示位置 /* delay(2); for (i=0;i { write_data(value1[i]); delay(2); } */ TR0=1; while(1) { show_frequency(); show_wave(); } } //按键中断处理程序。 void it0() interrupt 0 { if(chg==0) {delay(5); if(chg==0) {FREQ=150; j++; if(++cho==4) {j=0;cho=0;num=0;} } } //num=0;所有数据从新开始,保证波形的完整性 else if(freq_u==0|freq_d==0) {freq_ud();} else if (cho==1&(duty_d==0|duty_u==0)) {duty_ud();} else ; } //定时器中断处理程序。 void intt0() interrupt 1 { //TH0=0x00;TL0=0x00;sinx(); switch(cho) { case 0:{TH0=TIME0_H;TL0=TIME0_L;sint( ) ;break;} //正弦波//每半周期256取样。 case 1:{TH0=TIME0_H;TL0=TIME0_L;squar e();break;} //方波 //为了提高方波的最高频率,只有牺牲占空比的最小可调值。分100份每次1%。 case 2:{TH0=TIME0_H;TL0=TIME0_L;trian gle();break;} //三角波 case 3:{TH0=TIME0_H;TL0=TIME0_L;stw() ;break;} //锯齿波 default: ; } } 5 、调试及结果 图12 总体电路图 5.1仿真测试结果 现在使用软件Proteus进行仿真测试,下面就相应的仿真效果图: 图13 三角波仿真效果图 图14 方波仿真效果图