资源描述
目 录
1.系统总体方案选择与说明.............. ............1
2.系统结构框图与工作原理.............. .............1
3.各单元硬件设计说明及计法.............. ...........1
4.软件设计与说明(包括流程图).............. .......8
5.调试结果与必要的调试说明.............. ..........14
6.使用说明.............. .............. ...........18
7.程序清单.............. .............. ..........18
8.总结.............. .............. ..............34
9.参考文献.............. .............. ...........36
附录.............. .............. .................36
附录A 系统原理图
一、 系统总体方案选择与说明
为了实现低频信号的产生和频率以及移相角度的显示我们采用的用D/A数模转换电路进行波形的输出。并通过数码管进行显示。通过按键进行波形的选择以及频率和移相的调整。
二、系统结构框图与工作原理
系统选用AT89S52作为CPU;波性选择及频率和移相角调整:通过按键来选择来产生中断0,以实现波形的选择,通过按键来产生中断1,实现频率和移相角度的选择,通过另外两键的是实现加减;数模转换及波形输出:通过DAC0832来实现模数转换,并通过两级运放实现双极性输出。系统结构框图如图1所示
图1 系统结构框图
三、 各单元硬件设计说明及计算方法
1) 主电路
如图2所示
图2 主电路
2)功能键电路
通过建S1来产生外部中断1(INT1),由中断1 的中断程序来记录按键的次数(CNT_EX1(0-2))。不同键数代表的功能不一样。相应的处理程序根据CNT_EX1,实现调频和移相。
通过S2键来产生外部中断0(INT0),由中断0 的中断程序来记录按键的次数(CNT_EX0(0-5))。不同键数代表的波形不一样。相应的处理程序根据CNT_EX0,产生相应的波形 ;S3、S4 分别用于实现调频和移相的增加和减少;R6 ―R9是上拉电阻。功能键电路如图2所示
图3 功能键电路
3)单片机的复位电路
如图3所示
图3 复位电路
4)数码管显示电路
该电路选择MAX7219芯片,它是一种集成化的串行输入/输出共阴极显示驱动器,它连接微处理器与8位数字的7段数字LED显示。
1 DIN 串行数据输入端口。在时钟上升沿时数据被载入内部的16位寄存器。 2,3,5-8,10,11 DIG 0–DIG7 八个数据驱动线路置显示器共阴极为低电平。关闭时7219此管脚输
12 LOAD (MAX7219) 载入数据。连续数据的后16位在LOAD端的上升沿时被锁定。
CS (MAX7221) 片选端。该端为低电平时串行数据被载入移位寄存器。连续数据的后
13 CLK 时钟序列输入端。最大速率为 10MHz.在时钟的上升沿, 数据移入内 部移位寄存器。 下降沿时, 数据从DOUT端输出。对MAX7221来
16位在cs端的上升沿时被锁定。
14-17,20-23 SEG 7 段和小数点驱动,为显示器提供电流。当一个段SEGA–SEG G, 动关闭时,7219的此端呈低电平,7221呈现高阻抗。
18 SET 通过一个电阻连接到VDD 来提高段电流。
19 V+ 正极电压输入,+5V
24 DOUT 串行数据输出端口,从DIN输入的数据在16.5个时钟周期后在此端有效。当使用多个MAX7219/MAX7221时用此端方便扩展。
图4 MAX7219的时序图
根据时序图,连接电路图,P1.0接CLK,P1.1接LOAD,P1.2接DINSET 通过一个电阻连接到VDD 来提高段电流 V+ 正极电压输入,+5V,DOUT不用空着不接,A-DP分别接到共阴数码管的A-DP,DIG0-DIG3接到数码管的P1-P4以实现位选。数码管用以显示实时频率。数码管显示电路如图5所示。
图5 数码管显示电路
5)数模转换电路 波形产生电路
(1) DAC0832的功能特性
DAC0832是8分辨率的D/A转换集成芯片。与微处理器完全兼容。这个DA芯片以其价格低廉、接口简单、转换控制容易等优点,在单片机应用系统中得到广泛的应用。D/A转换器由8位输入锁存器、8位DAC寄存器、8位D/A转换电路及转换控制电路构成。DAC0832引脚分布如图6
图6 DAC0832引脚分布图
(2)DAC0832的引脚特性
* D0~D7:8位数据输入线,TTL电平,有效时间应大于90ns
* ILE:数据锁存允许控制信号输入线,高电平有效;
* CS:片选信号输入线(选通数据锁存器),低电平有效;
* WR1:数据锁存器写选通输入线,负脉冲(脉宽应大于500ns)有效。由ILE、CS、WR1的逻辑组合产生LE1,当LE1为高电平时,数据锁存器状态随输入数据线变换,LE1的负跳变时将输入数据锁存;
* XFER:数据传输控制信号输入线,低电平有效,负脉冲(脉宽应大于500ns)有效;
* WR2:DAC寄存器选通输入线,负脉冲(脉宽应大于500ns)有效。由WR2、XFER的逻辑组合产生LE2,当LE2为高电平时,DAC寄存器的输出随寄存器的输入而变化,LE2的负跳变时将数据锁存器的内容打入DAC寄存器并开始D/A转换。
* IOUT1:电流输出端1,其值随DAC寄存器的内容线性变化;
* IOUT2:电流输出端2,其值与IOUT1值之和为一常数;
* Rfb:反馈信号输入线,改变Rfb端外接电阻值可调整转换满量程精度;
* Vcc:电源输入端,Vcc的范围为+5V~+15V;
* VREF:基准电压输入线,VREF的范围为-10V~+10V;
* D/AGND:数/模拟信号地
(3)DAC0832的工作方式
DAC0832有如下3种工作方式:
⑴单缓冲方式。单缓冲方式是控制输入寄存器和DAC寄存器同时接收资料,或者只用输入寄存器而把DAC寄存器接成直通方式。此方式适用只有一路模拟量输出或几路模拟量异步输出的情形。
⑵双缓冲方式。双缓冲方式是先使输入寄存器接收资料,再控制输入寄存器的输出资料到DAC寄存器,即分两次锁存输入资料。此方式适用于多个D/A转换同步输出的情节。
⑶直通方式。直通方式是资料不经两级锁存器锁存,即 CS*,XFER* ,WR1* ,WR2* 均接地,ILE接高电平。此方式适用于连续反馈控制线路和不带微机的控制系统,不过在使用时,必须通过另加I/O接口与CPU连接,以匹配CPU与D/A转换。
我们的选择的是DAC0832工作在单缓冲方式下,将DAC0832的WR1和WR2引脚接到单片机的WR(P3.6),把CS引脚接到单片机的P2.0从而产生了端口地址为FEFFH,参考电压VREF接VCC,XFER直接接GND。接线方式如图7所示。
图7 DAC0832接线图
(4)单极性与双极性输出
再实际应用中,有时仅要求输出是单方向的,即单极性输出,其电压通常是0-5V或0-10V;有时则要求输出是双方向的,即双极性输出,如电压为正负5V、10V。单极性(图8所示)和双极性输出电路(如图9所示)
图8 单极性输出
图9 双极性输出
1) 中用反相比例放大器实现电流到电压的转换,因为输出的模拟量电压的极性与参考电压的极性相反,若要获得与VREF同相的输出电压可用同相放大器或采用两级反相放大。
2) 通过运算放大器A2将单极性输出转变为双极性输出。由VREF为A2提供一个偏移电流,该电流方向应与A1输出电流的方向相反,且选择R3=R2=2R1,使得由VREF引入的偏移电流恰为A1输出电流的1/2,因而A2的输出将在A1输出的基础上产生偏移。双极性输出电压与VREF及A1输出VOUT1的关系是 VOUT2=-(2VOUT1+VREF)
四、软件设计与说明
1)主程序流程图:
如图10所示
中断/显示7219初始化
开始
判断信号类型并产生及输出
开始输出?
调频程序
移相程序
赋频率值
Start_flag=1
Start_flag=0
Start_flag=1??
YES
NO
CNT_EX1=?
=1
=2
=3
置位Start_flag
图10 主程序流程图
2)外部0中断服务程序:
外部0中断服务程序是为了实现:开机后,连续按S2键两下,就产生正弦波,若继续按则相继产生方波,锯齿波,三角波。然后又回到正弦波。
由按键触发。这其中就是通过设置标志位来改变输出波形如图11所示
置位Start_flag
外部0中断服务程序
改变信号类型
返回
图11 外部0中断服务程序流程图
CNT_EX0>5?
YES
CNT_EX0=1
NO
3)外部1中断服务程序:
单片机的P3.3接的S1键,通过按键对当前输出波形进行移相和频率调整。也是通过设定标志位以实现频率和和移相的调整。因为在调整的过程中要响应中断1的服务程序,所以暂时没有波形输出。等到调整结束后再次按S1键才有波形输出。如图12所示
清除Start_flag
CNT_EX1>3?
CNT_EX1加1
外部1中断服务程序
CNT_EX0=1
YES
返回
NO
图12 外部中断1服务程序流程图
4)定时/计数器0中断服务程序:
定时器是用于改变当前输出波形的频率的。当给DAC0832一个输入值之后开启定时器,通过不同的定时时间长度以实现不同频率的输出。频率的大小是可以改变的因为当选定波形之后,可以同过中断1的服务程序改变频率的大小,当确定之后以新的频率继续输出。如图13所示
重新写入初值:
TL0=(65536-Freq)%256
TH0=(65536-Freq)/256
T0中断服务程序
关闭计时TR0=0
返回
图13 定时/计数0中断服务程序流程图
置位flag_freq
5)频率调节、相位调节程序:
频率和移相角度是通过中断 1来实现的。当确定当前调节状态时,就判断当前是出于调频还是调相。然后再扫描是要增大还是要减小频率或移相角度。然后再做出相应的处理。最后再扫描S1的状态,以确定是否退出扫描状态,若是久以新的频率和移相角度输出。如图14所示
用CNT_KEY寻址二维数组并显示对应频率值
频率调节程序开始
CNT_KEY--
freq_add=1?
YES
Freq_add=1?
延时消抖
NO
YES
频率减少程序段,于左边频率增加段相似
返回
NO
CNT_EX1=1?
YES
NO
图14频率调节、相位调节程序流程图
6)信号产生程序
该程序主要是用以产生相应的信号波形。但是输出波形还是要通过S2键进行选择。这就要扫描S1键所处的状态。根据S1的状态来判断输出波形。根据按键的状态,能产生的信号波形有:正弦波信号、方波信号、锯齿波信号、三角波信号。:如图15所示
寻址数据输出到0832,并地址加一
信号产生程序开始
一个周期完成
超出范围?
赋值相位控制值
YES
返回
NO
CNT_EX1=1 &&
Start_flag=1?
YES
NO
启动T0
等待T0中断
图15信号产生程序
五、调试结果与调试说明
1) 开机时的结果
开机时是没有输出的只有数码管显示为56Hz 如图16所示
图16 开机结果
2)正弦波
当连续按2下S2键便产生正弦波如图17所示
图17正弦波
3)方波
当按下S2键3-5次就一次产生方波、锯齿波、三角波如图18-20所示
图18 方波
4)锯齿波
图19锯齿波
5)三角波
图20三角波
6)幅度或频率调节增大的结果
如果需要调节输出频率和移相角度可调节S1键,第一次按下是调节频率,第二次是移相角度。S3、S4分别用来减小和增大频率和移相角度的键。在调节频率和移相时,是没有波形输出的。若果调整好后,再一次S1键就可产生所需的波形。如图21-22所示
图21 幅度或频率调节增大的结果
7)幅度或频率调节减小的结果
图22 幅度或频率调节减小的结果
六、使用说明
1. 开机后,连续按S2键两下,就产生正弦波,若继续按则相继产生方波,锯齿波,三角波。然后又回到正弦波。
2. 在默认情况产生的波形的频率是56Hz,移相角度为0,若要改变频率或移相角度,可按S1键进行功能选择:第一次按下S1选择的是频率调节,按第二次是移相选择。然后通过键盘S3和S4进行频率或移相角度得较少或增加。再次按S1键就可以看见调整之后的波形输出(在调整的过程当中没有波形输出)。
七、程序清单
1) 主函数
#include"reg52.h"
#include"absacc.h"
#include"intrins.h"
#include"Display.h"
#include"Init0832.h"
#include"regulate.h"
bit Start_flag; //信号发生开始标志,由外部中断0启动
void init_Int(); //中断初始化函数
void main()
{
init_7219(); //初始化7219显示控制器
init_Int(); //初始化中断
Write_display_data(freq_table[CNT_KEY][1]); //初始化数码管,显示默认频率
while(1)
{
while(Start_flag)
{
switch(CNT_EX0)
{
case 2: sin_F();
break;
case 3: pulse_F();
break;
case 4: hackle_F();
break;
case 5: triangle_F();
break;
}
}
freqmo_F();
transPha_F();
Freq = freq_table[CNT_KEY][0]; //将调节后的频率值赋给频率控制变量
Start_flag = 1; //调节结束,返回信号发生
}
}
void init_Int()
{
TMOD = 0x01; //定时器0为定时器方式,T0为方式1
TH0 = 0xff;
TL0 = 0xff;
EA = 1;
ET0 = 1;
EX0 = 1;
EX1 = 1;
IT0 = 1;
Freq = 0;
Phase = 0;
CNT_EX0 = 0;
CNT_EX1 = 0;
CNT_KEY = 34; //开机默认频率为56Hz ,此时处于频率调节分辨率转折处
}
void Ex0() interrupt 0
{
Start_flag = 1;
CNT_EX0++;
if(CNT_EX0>5)
CNT_EX0=2;
}
void Timer0() interrupt 1
{
TR0 = 0 ;
TH0 = (65536 - Freq)/256;
TL0 = (65536 - Freq)%256;
flag_freq = 1;
}
void Ex1() interrupt 2
{
Start_flag = 0;
CNT_EX1++;
if(CNT_EX1>2)
CNT_EX1=0;
}
2)频率及移相角度显示程序
//display.h
#include"reg52.h"
#include"intrins.h"
#define reg0 0x01 //数字1寄存器
#define reg1 0x02 //数字2寄存器
#define reg2 0x03 //数字3寄存器
#define reg3 0x04 //数字4寄存器
#define reg8 0x09 //译码方式寄存器 ,值为0x0F
#define reg9 0x0a //亮度调节寄存器 , 值为0x0F
#define reg10 0x0b //扫描界限寄存器 ,值为0x03
#define reg11 0x0c //停机寄存器 ,值为0x01正常,0x00为停机
#define reg12 0x0d //测试寄存器 ,值为0x00正常,0x01测试
sbit CLK = P1^0; //7129时钟控制
sbit DIN = P1^2; //7219数据口
sbit LOAD = P1^1; //7219数据装载控制口
void Write_7219_Byte(char); //写一个字节到7219
void init_7219(); //7219初始化
void Write_display_data(int); //写显示数据
void init_7219()
{
LOAD = 0;
Write_7219_Byte(reg8);
Write_7219_Byte(0x0f);
LOAD = 1;
LOAD = 0;
Write_7219_Byte(reg9);
Write_7219_Byte(0x0f);
LOAD = 1;
LOAD = 0;
Write_7219_Byte(reg10);
Write_7219_Byte(0x03);
LOAD = 1;
LOAD = 0;
Write_7219_Byte(reg11);
Write_7219_Byte(0x01);
LOAD = 1;
Write_7219_Byte(reg12);
Write_7219_Byte(0x00);
LOAD = 1;
}
void Write_7219_Byte(char i)
{
unsigned char j;
for(j=0;j<8;j++)
{
i = i << 1;
CLK = 0;
_nop_();
_nop_();
DIN = CY;
CLK=1;
_nop_();
_nop_();
}
}
void Write_display_data(int i)
{
LOAD = 0;
Write_7219_Byte(reg0);
Write_7219_Byte(i%10);
LOAD = 1;
LOAD = 0;
Write_7219_Byte(reg1);
Write_7219_Byte(i%100/10);
LOAD = 1;
LOAD = 0;
Write_7219_Byte(reg2);
Write_7219_Byte(i%1000/100);
LOAD = 1;
LOAD = 0;
Write_7219_Byte(reg3);
Write_7219_Byte(i/1000);
LOAD = 1;
}
3)DAC0832的初始化及波形发生函数
//init0832.h
#define DA_0832 XBYTE[0xFEFF] //0832端口地址
unsigned char Phase ; //移向控制变量
int Freq; //频率控制变量
bit Start_flag;
bit flag_freq; //频率控制标志
/*
#define flag_start 0x01; //开始
#define flag_sin 0x02; //正弦波控制
#define flag_pulse 0x03; //方波控制
#define flag_hackle 0x04; //锯齿波控制
#define flag_triangle 0x05; //三角波控制
#define flag_freqmo 0x01; //调频控制
#define flag_transpo 0x02; //移相控制 */
unsigned char CNT_EX0; //外部0中断计数器
unsigned char CNT_EX1; //外部1中断计数器
unsigned char CNT_T1; //T1中断计数器
unsigned char CNT_KEY; //按键连续按下次数计数器,即频率查表值
unsigned char code sin[180]={ //******************上半周****************************
128,131,134,137,140,143,146,149,152,155,158,161,164,167, //14
170,173,176,178,181,184,186,188,190,192,194,197,200,202,204, //15
206,208,210,212,214,216,218,219,220,221,222,222,223,223,224, //15
224,224,224,223,223,222,222,221,220,219,218,216,214,212, //14
210,208,206,204,202,200,197,194,192,190,188,186,184,181, //14
178,176,173,170,167,164,161,158,155,152,149,146,143,140,137, //15
134,131,128, //3
//******************下半周****************************
127,124,121,118,115,112,109,106,103,100,97,94,91,88, //14
85,82,79,77,74,71,69,67,65,63,61,58,55,53,51, //15
49,47,45,43,41,39,37,36,35,34,33,33,32,32,31,31, //16
31,31,32,32,33,33,34,35,36,37,39,41,43,45,47,49,51, //17
53,55,58,61,63,65,67,70,72,74,77,80,83,85,88,91, //16
94,97,100,103,106,109,112,115,118,121,124,127, //12
}; //正弦函数表
void sin_F()
{
unsigned char i;
while(CNT_EX0==0x02)
{
for(i=Phase;i<180;i++)
{
DA_0832=sin[i];
TR0= 1 ;
while(!flag_freq);
flag_freq = 0;
}
for(i=0;i<Phase;i++)
{
DA_0832=sin[i];
TR0= 1 ;
while(!flag_freq);
flag_freq = 0;
}
}
}
void pulse_F()
{
unsigned int i;
while(CNT_EX0==0x03)
{
for(i=Phase;i<180;i++)
{
if(i<90)
DA_0832 =0;
if(i>=90)
DA_0832 = 255;
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
for(i=0;i<Phase;i++)
{
if(i<90)
DA_0832 =0;
if(i>=90)
DA_0832 = 255;
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
}
}
void hackle_F()
{
unsigned char i;
while(CNT_EX0==0x04)
{
for(i=Phase;i<180;i++)
{
DA_0832=i+38;
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
for(i=0;i<Phase;i++)
{
DA_0832=i+38;
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
}
}
void triangle_F()
{
unsigned char i ;
while(CNT_EX0==0x05&&Start_flag)
{
for(i=Phase;i<180;i++)
{
DA_0832 = i+38;
_nop_();_nop_();
_nop_();_nop_();
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
for(i=255;i>Phase;i--)
{
DA_0832 = i+38;
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
for(i=0;i<Phase;i++)
{
DA_0832 = i+38;
_nop_();_nop_();
_nop_();_nop_();
TR0 = 1;
while(!flag_freq);
flag_freq = 0;
}
}
}
4)频率和移相角度调节函数
// regulate.h
int code freq_table[90][2]={{0,250},{1,227},{2,208},{3,192},{4,178},{5,166},{6,156},{7,147},{8,138},{9,131},
{10,125},{11,119},{12,113},{13,108},{14,104},{15,100},{16,96},{17,92},{18,89},{19,86},
{20,83},{21,80},{22,78},{23,75},{24,73},{25,71},{26,69},{27,67},{28,65},{29,64},
{30,62},{31,60},{32,59},{33,58},{34,56},{35,55},{36,54},{37,53},{38,52},{39,51},
{40,50},{41,49},{42,48},{43,47},{44,46},{45,45},{46,44},{47,43},{48,42},{49,41},
{50,40},{53,39},{54,38},{56,37},{58,36},{60,35},{62,34},{64,33},{67,32},{69,31},
{74,30},{75,29},{78,28},{81,27},{85,26},{88,25},{92,24},{96,23},{102,22},{107,21},
{113,20},{119,19},{125,18},{133,17},{141,16},{152,15},{163,14},{176,13},{191,12},{210,11},
{234,10},{260,9},{290,8},{347,7},{416,6},{500,5},{625,4},{832,3},{1250,2},{2500,1}};
//T0值和其对应的可得到的频率值表
sbit freq_add = P3^0; //频率增加键
sbit freq_dec = P3^1; //频率减小键
void delay(unsigned char i)
{
int x;
unsigned char y;
for(x=5000;x>0;x--)
for(y=i;y>0;y--);
}
void freqmo_F()
{
while(CNT_EX1==0x01)
{
while(!freq_add)
{
delay(40);
if(!freq_add&&CNT_KEY>0) //频率相应增加,且防止溢出
CNT_KEY--;
Write_display_data(freq_table[CNT_KEY][1]); //显示当前频率值
}
while(!freq_dec)
{
delay(40);
if(!freq_dec&&CNT_KEY<90) //频率相应减小,且防止溢出
CNT_KEY++;
Write_display_data(freq_table[CNT_KEY][1]);
}
}
}
void transPha_F()
{
while(CNT_EX1==0x02)
{
while(!freq_add)
{
delay(40);
if(!freq_add&&Phase<180)
Phase++;
}
while(!freq_dec)
{
delay(40);
if(!freq_dec&&Phase>0)
Phase--;
if(Phase == 0)
Phase = 180;
}
}
}
八、总结
经过两周的单片机设计,让我更加熟悉了单片机的编程过程。下面说说我的一些体会。刚开始那到这个课题的时候觉得比较简单,我们很快就设计出了一个大概的电路图,而且我们觉得安全可以满足设计要求,于是便开始写程序代码。首先是DAC0832因为我们要实现正弦波得输出,这就必然会用到反相,我们查找资料知道可以用两级运方实现双极性的输出,电路的设计倒不是很难,可是当我们编程的时候才发现,波形不能反相啊。尝试过各种方法但是还是不行,最后就否定这种做法。我们就选择了4053作为模拟开关。通过程序来实现反相,这样是可以实现反相了。我们还在庆幸自己成功了,接下来就是移相,这时我们才发现我们错了。通过模拟开关实现的反相在移相这个功能上实现起来非常困难,而且存在很大的误差。无奈之下,我们只能寻求它路,最后我们还是回到之前的电路我们认真看了下书才发现当数字输入为0时输出的-VREF,输入为128时输出是0,输入255时输出是+VREF(127/128),原来是我们以前的数据取错了,从而导致输出一直为正。绕了一大圈还是回到原点,不过还好总是把问题解决了。
为了实现调频和移相我们最初设计的是用ADC0809,后来因为Protues当中没有相应的模型,就改为ADC0808,但是到后来我们在编程中发现ADC0808的精度太低了,如果在程序里面再乘以一个数虽说是频率的范围增加了 但是不能实现连续可调。经过考虑我们决定不要用ADC 直接通过按键来实现频率和移相角度的调节。通过S1键产生中断并进行功能的选择。然后通过S3、S4键分别实现减少和增加的功能。
最后就是频率和移相角度的显示问题了,刚开始打算直接显示,但是若果直接显示就会使输出波形失真,因为数码管采用的是扫描方式必须在一定的时间之类重新扫描,于是我又打算用锁存器,可是程序还是不好编写,后来搜索到MAX7219 这款芯片可以实现锁存和自动扫描,这样当产生波形的时候就不用再去不断地扫描输出。
通过这次课程设计我对单片机系统设计有了一个更加全面的认识,也认识到自己在实际问题中锁存在的不足。使自己的能力有一定程度的提高。
九、参考文献
附录
如图23所示
图23系统总电路图
37
展开阅读全文