【www.guakaob.com--报告】
以下是中国招生考试网www.chinazhaokao.com为大家整理的关于《“简易正弦波发生器”设计、调试报告》的文章,希望大家能够喜欢!更多资源请搜索文档频道与你分享!第五组成员:
葛跃凯 3005204337 05级电信四
宋 怡 3005204349 05级电信四
韩 瑜 3005204308 05级电信三
一.设计目标
1.频率可调,分辨率为1Hz
2.频率由LED数码管显示并可按位置数
3.输出幅度:1Vp-p(1k ohm)
4.提高部分:即可能拓展频率范围(低端和高端),采用数字超取样技术,附带跟踪滤波器
二.所用元器件
1.微控制器:AT89C51
2.数模转换器:DAC0832
3.显示元件:LED数码管
4.输入元件:按键开关
5.开关电容滤波器:MAX293
三. 完成情况
1.软件自己独立完成,并用PROTEUS仿真成功,频率范围1HZ--2000HZ (可以得到设定频率的正弦波,通过控制按键,可以改变小数点的位置以及数值,从而改变频率的显示值).
2.硬件部分全部搭接完成,包括数码管内部构造的测试,印制板连线的关系,三极管类型的选择以及上拉电阻的阻值计算,熟悉各芯片引脚的功能并准确连接,整个电路的焊接。
3.调试部分还没有进行彻底,数码管有显示,但是按键按下后数值没有改变.
四.系统总体描述及系统框图
单片机产生时钟信号和数据信号,控制LED的位选和段选,单片机向D/A 输入数据,转换成模拟信号后有跟踪滤波器输出所需频率的正弦波,频率选择关系由时钟信号控制.频率可有按键置数并由LED 显示. 设置四个按键的功能分别是左移、右移、增加、减少。
系统框图如下:
五.各模块说明
主体器件为单片机.D/A转换器.跟踪滤波器.根据它们的性能进行调试和设计的.具体模块的说明将结合软件的设计思路进行阐述,这样便于理解,具体如下:
单片机的输入信号是键盘信号,输出信号有两个:分别是max293的时钟信号和正弦表信号。整个程序由以下函数组成:延迟子函数,显示子函数,键盘扫描子函数,初始化子函数和两个中断函数。首先使用初始化函数对单片机内部寄存器和各输出管脚赋值,包括给计数器寄存器赋值,开cpu中断和定时器中断,初始化程序之后计时器开始计数。然后主程序反复执行显示子函数和键盘扫描子函数。显示子函数通过反复扫描各个led显示管显示输出正弦波的频率,键盘扫描函数监视按键,如果按下的话就触发相应的程序。在主函数循环的过程中会定时进入中断状态。此时整个程序由中断函数接管,中断函数的作用有两个:一个是输出max293时钟信号和正弦波的离散幅度信号;一个是为中断计时器赋值,通过开关计时器和cpu中断为下一次中断做准备。中断函数结束后程序回到主函数的断点继续执行,等待下一次中断的到来。如此反复。
DAC0832将输入的正弦波的离散幅度信号进行A/D变换,输出展宽的带有高频分量的正弦波,在经过跟踪滤波器max293得到理想的正弦波形。Max293的截止频率 与输入时钟信号成正比例关系,如果使时钟信号与输出频率保持一定关系,就可以保证输出频率与时钟信号频率相同,达到跟踪滤波的效果。
六. 调试流程
软件部分调试过程如下:
1.常数及变量的设定
输出管脚设定:
P0:8位正弦信号离散幅度值输出端,经上拉电阻后与dac0832连接。
P2:8为段选信号输出端,与4个led数码管相连。
P1:p1^0~p1^3为位选信号,分别控制四个led数码管,数码管从左向右看是个,十,百,千。P1^4是max293时钟信号输出端。
输入管脚设定:
P3:p3^0~p3^4为按键信号输入端
其他:在程序中设定了数组uchar data datal[4],用来存储显示在数码管上的个,十,百,千位上的数字,而且使用十进制表示,所以每个元素的取值范围是0~9.认为这个数组是这个程序的关键。显示函数,键盘扫描函数都使用了这个数组,而且输出频率值也是由这个数组决定的。应该说正是这个数组决定了整个程序的设计方法。
改进过程:
(1).define max_cl p1^4是错误的。p1^4不能使用define定义。正确的写法是使用sbit:
sbit max_cl=P1^4;
(2).开始时将数组datal[4]的类型定为code,但是程序总是出错。后来才发现code类型在程序中是不能被改变的,而datal在程序中是随着按键信号不断变化的,所以肯定是错误的。把它定义为data就可以了。
.
2延迟子函数的设计
延迟子函数是从参考资料1中找到的,延迟大约在1ms左右,不过并不准确。
3显示子函数设计
基本设计思想:利用单片机对四个led数码管同时控制其段选是不切实际的,没有4×8=32的管脚对其进行控制。最好的方法是将数码管的控制同一段的管脚相连,这样段选只需要8个管脚,再加入四个管脚输出位选信号,利用人的视觉暂留效应分时选通四个数码管,一共使用12个管脚。至于输出信号的过程如下:首先从datal[]取出某位应该显示的数字,然后用这个数字查段选表,从段选输出位输出的电平信号,位选是用循环逐次读取位选表的值。
最早的程序设计如下:
void display(void)
{
uchar k;
for(l=0;l<500;l++)
for(k=0;k<4;k++)
{disdata=seg[datal[k]]; /*disdata为数码管段选输出端*/
Bit=scan[k];/*bit为数码管位选输出端*/
if(k==flag)
dp=1;
delay(1);
Bit=0xFF; /*一个都不选,防止显示混乱,不过还可以修改*/
}
}
其中有两个错误
第一个错误是for(l=0;l<500;l++) 这个语句是从参考资料1中看到的。在运行程序时发现在输出频率较高时程序对按键的反应很慢,长时间按键才能看见显示屏上数字的变化,于是怀疑是显示函数占用的时间太长了。把这个语句去掉以后依然能够正常显示,而且对键盘的响应也快得多。
第二个错误是Bit=scan[k]; 和Bit=0xFF; Bit是位选输出端,但实际上只有P1^0~P1^3是位选信号。上面的程序错误在于在给P1^0~P1^3赋值时也给P1^4(也就是max293时钟信号输出端)赋值,破坏了严格的方波信号,会使max293的截止频率产生偏移,无法得到正确的波形。
对程序进行改进后如下:
void display(void)
{
uchar k;
for(k=0;k<4;k++)
{disdata=seg[datal[k]];
Bit=(Bit&0xF0)|scan[k];
if(k==flag)
dp=1;
delay(1);
Bit=(Bit&0xF0)|0x0F;
}
}
4.键盘扫描子函数设计
这部分程序是设计中最耗费时间的一部分。
基本设计思想:一共设置五个按键,各个按键的管脚和作用为:
P3^0:可控制位左移
P3^1:可控制位右移
P3^2:可控制位增大,步进为1,上限为9,下限为0。
P3^3:外部中断1,可以控制定时器的中断的开闭。
P3^4:可控制位减小,步进为1,上限为9,下限为0.
首先,通过延时函数去除抖动。然后使用case语句针对不同的按键执行相应的语句。最后计算计数器的初值,在中断函数中代入。
改进过程:
(1). 加入按键P3^3
在初期的程序中并没有加入按键P3^3,也没有关闭/打开计时器中断的功能,但在仿真过程中发现当输出频率相当高以后(大概在3000左右),led显示有明显的闪烁,程序对键盘的反应也很迟钝。这个时候如果想要改变输出频率会很困难,单片机已经不能及时对按键作出反应。这应该是因为中断的发生频率过快,对显示子函数和输入子函数产生了影响。所以设想设置一个按键来控制计时器中断的开闭。在需要改变出输出频率的时候停止波形的输出使程序接受按键信息,然后再按下此键来恢复输出。于是加入按键P3^3, 在仿真中起到了预期的效果。还有一种方法,就是对程序的结构进行优化,使输出和输入并行不悖。但是限于编程水平,时间和精力的限制,没有使用这种方法。
(2).计数器初值计算的位置
在初期的程序中,计数器初值的计算是放在计数器中断函数中的,但是考虑到初值的计算涉及高位的乘除法,相当耗费时间,而中断函数应求短求简,于是把初值计算移到键盘扫描函数中,计算完成后可以在中断函数中直接把初值赋给TH1和TH0,节省计算时间。而且在没有按键按下的情况下,计数器初值的计算是不启动的,提高了程序运行的效率。
(3).计数器初值计算的表达式
设输出的正弦波f=1000*datal[3]+100*datal[2]+10*datal[1]+datal[0],
使用24mhz晶振,计数器模式为1,设计数器初值为x。根据参考资料2,max293的时钟频率与截止频率之比为88:1,所以可以时钟频率应为88f。可以得到下列等式
得到x=65536-11363/(1000*datal[3]+100*datal[2]+10*datal[1]+datal[0])
而正弦表元素有176个,每发生一个中断,就输出一个数据,保证输出频率和max293时钟频率的比例关系为1:88.
5.中断函数设计。
本程序的中断函数有两个:计时器中断和外部中断。
计数器中断程序主要从参考资料1中获得,每发生一次计时器中断就将max_cl取反,而且输出一个正弦表数据。
外部中断用来停止和开启计时器的中断。
6.使用proteus7进行程序仿真
先按下p3^3,停止波形的输出,输入想要输出的频率
再按下p3^3,开始输出波形。
七.遇到的问题及解决方法
1.LED显示
由于之前对数码管的分析不充分,导致耽误时间过多,最后用软件模拟,试验成功并焊接.
2. 硬件电路测试部分
由于显示不正确,有检查一遍硬件设置和软件的程序,发现位选的的六排接口和原设的位选不是对应的所以那部分又重新分析并焊接的.
八.原理图及实物照片
实物图如下:
原理图
九.程序流程图和源代码(附注释)
程序流程图: 计时器中断
主程序 外部中断1
CPU关中断
停止计数
赋初值
开始计数
数据输出
Count加1
Count==176
Count=0
CPU开中断
延时
初始化
ET1取反
显示子函数
键盘扫描子函数
返回
Y
返回
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define daout P0 /*正弦信号输出端*/
#define disdata P2 /*段选信号输出端*/
#define Bit P1 /*p1为位选信号输出端*/
#define key P3 /*键盘信号输入端*/
sbit max_cl=P1^4; /*max293的时钟信号输出*/
sbit dp=P2^7; /*用小数点来标志可以被调整的位*/
ucharcode sin[176]={0x80,0x85,0x89,0x8E,0x92,0x97,0x9B,0x9F,0xA4,0xA8,0xAC,0xB1,0xB5,0xB9,0xBD,0xC1,0xC5,0xC8,0xCC,0xD0,0xD3,0xD7,0xDA,0xDD,0xE0,0xE3,0xE6,0xE8,0xEB,0xED,0xEF,0xF2,0xF4,0xF5,0xF7,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFD,0xFC,0xFB,0xFA,0xF9,0xF7,0xF5,0xF4,0xF2,0xEF,0xED,0xEB,0xE8,0xE6,0xE3,0xE0,0xDD,0xDA,0xD7,0xD3,0xD0,0xCC,0xC8,0xC5,0xC1,0xBD,0xB9,0xB5,0xB1,0xAC,0xA8,0xA4,0x9F,0x9B,0x97,0x92,0x8E,0x89,0x85,0x80,0x7B,0x77,0x72,0x6E,0x69,0x65,0x61,0x5C,0x58,0x54,0x4F,0x4B,0x47,0x43,0x3F,0x3B,0x38,0x34,0x30,0x2D,0x29,0x26,0x23,0x20,0x1D,0x1A,0x18,0x15,0x13,0x11,0x0E,0x0C,0x0B,0x09,0x07,0x06,0x05,0x04,0x03,0x02,0x02,0x01,0x01,0x01,0x01,0x01,0x02,0x02,0x03,0x04,0x05,0x06,0x07,0x09,0x0B,0x0C,0x0E,0x11,0x13,0x15,0x18,0x1A,0x1D,0x20,0x23,0x26,0x29,0x2D,0x30,0x34,0x38,0x3B,0x3F,0x43,0x47,0x4B,0x4F,0x54,0x58,0x5C,0x61,0x65,0x69,0x6E,0x72,0x77,0x7B};
uchar code seg[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; /*0~9八段led段码选择*/
uchar code scan[4]={0x0E,0x0D,0x0B,0x07}; /*位选*/
uchar data datal[4]={0x00,0x00,0x01,0x00}; /*每位显示数字*/
uchar data flag=0x01; /*标志,用来表示可以编辑的位*/
uchar data keyword;
uint data thhl; /*计数器初始值*/
uchar data th,tl; /*预存计数器初值*/
uchar data count; /*中断计数*/
/********************************延迟子函数**********************/
void delay(uchar k)
{
uchar i,j;
for(i=0;i<k;i++)
for(j=0;j<120;j++)
{;}
}
/********************************显示子函数***********************/
void display(void)
{
uchar k;
for(k=0;k<4;k++)
{disdata=seg[datal[k]]; /*disdata为数码管段选输出端*/
Bit=(Bit&0xF0)|scan[k]; /*bit为数码管位选输出端*/
if(k==flag)
dp=1;
delay(1);
Bit=(Bit&0xF0)|0x0F; /*一个都不选,防止显示混乱*/
}
}
/**********************键盘扫描子函数 *****************************/
keyscan()
{
keyword=key&0x1F;
if(keyword!=0x1F)
{delay(1000); /*消除键抖动*/
if((key&0x1F)!=0x1F)
{ switch(keyword)
{case 0x1E: {if(flag>=3)flag=3;else{flag++;}break;} /*左移使标志位加1,上限为3 */
case 0x1D: {if(flag==0)flag=0;else{flag--;}break;} /*右移使标志位减1,下限为0*/
case 0x1B: {if(datal[flag]>=9)datal[flag]=9;else{datal[flag]++;}break;} /*使可控制位加1,上限为9*/
case 0x0F: {if(datal[flag]==0)datal[flag]=0;else{datal[flag]--;}break;} /*使可控制位减1,下限是0*/
default:{break;}
}
thhl=65536-11363/(1000*datal[3]+100*datal[2]+10*datal[1]+datal[0]); /*假设max293的时钟频率:截止频率=88:1*/
th=thhl/256; /*预存计数器高八位,中断时直接赋值,加快速度*/
tl=thhl%256; /*预存计数器第八位,中断时直接赋值。加快速度*/
}
}
}
/************************* 初始化函数 ******************************/
void clear(void)
{
key=0xFF; /*键盘输入端置高位*/
daout=0x00;
max_cl=1;
thhl=65422;
TH1=thhl/256;
TL1=thhl%256;
TMOD=0x11;
IP=0x04; /*外部中断1置高优先级*/
EX1=1; /*开外部中断1*/
ET1=1; /*开计时器1中断*/
TR1=1; /*记时器开始计数*/
EA=1;
}
/****************************主函数 *******************************/
main()
{
clear();
while(1)
{keyscan();
display();
}
}
/***********************中断子函数***************************/
void t1int(void) interrupt 3
{
EA=0;
TR1=0;
TH1=th;
TL1=tl;
TR1=1;
max_cl=!max_cl;
daout=sin[count];
count++;
while(count==176)
{count=0;}
EA=1;
}
void exint(void) interrupt 2 using 2/*用来停止计时器中断*/
{
if(P3^3==0) /*开启和关断定时器中断,关闭时输入频率值,开启时直接输出程序就不用再扫描键盘了*/
ET1=!ET1;
}
参考文献:
1马忠梅,籍顺心,张凯,马岩.单片机的C语言应用程序设计.北京航空航天大学出版社,2003.
2 李敏,刘京诚等. 一种新型的跟踪低通滤波器.电子器件,2007 ,30(4).
下一篇:辞职报告模板范文呕心沥血之作