51单片机波形发生器.docx
《51单片机波形发生器.docx》由会员分享,可在线阅读,更多相关《51单片机波形发生器.docx(40页珍藏版)》请在冰豆网上搜索。
51单片机波形发生器
第大组第小组
课程设计报告
课程设计名称低频正弦波、锯齿波
信号发生器的设计与实现
姓名学号
专业班级
院别
指导老师
完成时间
低频正弦波、锯齿波信号发生器的设计与实现
摘要在以51单片机为核心的HNIST-2型单片机实验装置上设计一个低频正弦波、锯齿波信号发生器。
可以通过键盘选择输出哪种类型的信号,还可以通过键盘对输出信号的频率、幅度进行实时设置。
键盘设立了数字小键盘,用户使用非常方便。
各种工作状态的切换也同样通过数码管显示出来,方便用户进行操作。
装置断电后重新上电,可以按掉电前的设置继续进行工作,所有的设置数据都不会丢失。
输出波形的同时输出波形的类型、幅度和频率通过数码管进行显示。
其中,波形的每个周期之内输出32个采样点,基本接近波形的形态。
关键词单片机;正弦波;锯齿波;EE2PROM;
1.概述
此次单片机课程设计,要求使用以51单片机为核心的HNIST-2型单片机实验装置实现正弦波、锯齿波波形发生器。
1.1课程设计的背景
在学习过了《数字电路》、《单片机原理与接口技术》之后,拥有了利用51单片机设计出小项目的能力和经验。
此次单片机课程设计,正是为了巩固和检验学过的知识。
1.2课程设计的要求
以AT89C51为核心设计一个信号发生器,可产生锯齿波、正弦波信号输出。
其中用户可以方便的选择和设置输出波形,波形形态基本与理论波形一致。
1.3课程设计完成情况
(1)课程设计完成情况:
完全符合课程设计要求。
作品具备数码管显示、矩阵键盘、EEPROM掉电保护与存储、D/A输出功能,经过检测,发现作品完全符合课程实际要求。
(2)在实验平台上随机抽取输出波形的数据,用示波器检测的结果如下:
检测数据依次是正弦波5.0V23HZ4.0V46HZ
锯齿波5.0V23HZ4.0V46HZ
可以看到,实际输出波形也基本与输出数据一致,可见达到了课程设计要求。
2.总体设计思想
此次软件设计,分为以下模块:
1.主函数模块:
进行初始化,并且调用其他函数。
2.波形产生模块:
产生波形数据。
3.D/A转换模块:
讲波形数据传送到D/A芯片,进行D/A转换。
4.人机交互模块:
包括键盘扫描和数码管显示,方便用户使用。
5.EEPROM模块:
进行数据保护,即使突然断电,也可以保存设置的数据。
基本原理:
将波形数据放在数组里面,间隔一段时间Ts产生一个中断,将数据送到D/A进行转换,就可以得到输出波形。
假设波形频率为f,那么Ts=1/f/32。
图1-1功能模块框图
3.单元模块设计
主函数模块流程图如图1-2所示,波形产生模块流程图如图1-3所示,D/A转换模块流程图如图1-4所示,人机交互模块流程图如图1-5所示,EEPROM流程图如图1-6所示。
图1-21.c流程图图1-3T1_INTERRUPT.H流程图
图1-4DA.H的流程图图1-5HCI.H的流程图
图1-6E2PROM.H中读函数和写函数各自的流程图
DA.H模块,EEPROM模块,1.c模块,HCI相对较易,是严格按照器件手册上的参数进行时序控制,不赘述。
其中T1_INTERRUPT.H模块涉及到中断频率的设定,决定了本次课程设计的成败。
现在详细分析如何计算参数。
T1定时器的初始值由如下分析确定:
根据公式
T=1/f;(2.1)
T1的中断间隔理论上等于Ts=1/f/32;然后添加了软件修正time_repire。
由于晶振为12MHz,所以一个脉冲是1us,一秒钟有1000000us。
所以定时器初值为:
TH1=((65536-(1000000/fre/32)-time_repire)/256);
TL1=((65536-(1000000/fre/32)-time_repire)%256);
4、系统调试与使用操作说明
一、调试中出现的问题以及排查
问题1:
输出波形的频率不对。
输入数据50Hz,但是示波器测出来却只有30Hz左右。
原因:
第一,T0中断优先级高,对T1中断有影响,而T1才是控制波形输出的。
第二,使用的思路是定时器初始值固定,而在内部使用中间变量time,对time进行自增运算,判断time的值来判断延时的时长。
然而T1中断服务子程序需要消耗大量时间。
这对中断时放入到定时器的初始值有限制。
不可能用很频繁的中断来保证输出波形。
解决:
改变思路,使用改变定时器初始值的办法来控制波形输出频率。
关键代码模板如下
LOAD_H=…;
LOAD_L=…;
voidT1_int(void)interrupt3//定时器T1服务函数
{
TH1=(LOAD_H);
THL=(LOAD_L);
…
}
问题2:
在问题1解决之后,输入数据50Hz,输出实际频率47Hz,还有3HZ的误差。
原因:
51单片机中断发生时,并不能立即执行中断服务程序;因为它还要先执行保存现场的工作,所以有一个中断响应时间为3-8个机器周期。
此外,给定时器装初值同样需要消耗时间。
解决:
添加软件修正变量time_repair,让定时器装的初始值有所增加,抵消这些干扰性的延时影响。
关键代码模板如下
…
inttime_repire=-40;//40us偏差修正
#defineLOAD_H_two_fre_num_2((65536-(1000000/two_fre_num_2/32)-time_repire)/256)
#defineLOAD_L_two_fre_num_2((65536-(1000000/two_fre_num_2/32)-time_repire)%256)
…
问题3:
EEPROM有时写不进。
原因:
I2C把数据传送到它的数据缓冲区之后,EEPROM还需要时间去把数据缓冲区的数据烧录到自身中。
烧录时间手册上写有5ms左右。
解决:
在通过I2C写入数据之后,进行适当的延时。
关键代码模板如下
…
#define_Nop(){_nop_();_nop_();_nop_();_nop_();_nop_();}//定义空操作,5μs
Inti=0;
init_E2PROM();//EEPROM初始化,释放总线
save_date(1,0xad);//通过I2C将数据发送到EEPROM
for(i=1000;i>0;i--)//进行延时
{
_Nop()
}
…
问题4:
锯齿波输出幅度比实际的值要小一点。
原因:
1.步进过小。
2.单片机不支持浮点运算,造成运算结果的偏差。
解决:
步进=256/31;且根据这个步进,就适合使用波形数组来保存波形数据,免得在运算之中出现偏差。
51单片机不支持浮点运算。
二、操作使用说明
a.键盘布局
b.设置
(1)、进入与退出设置状态
按设置键,进入设置状态;处于设置状态再按设置键,退出设置状态。
(2)、选择波形
进入设置状态后,按选择波形键,选择输出信号波形,按一下切换一次。
例如选择输出信号波形为正弦波,按一下选择波形键,选择输出信号波形为锯齿波。
(3)、参数选择
进入设置后,按选择参数键,选择参数,按一下切换一次参数,例如正弦波,若处于选择频率状态,按选择参数键,切换到处于选择幅度状态。
在选择参数状态,按←键、→键改变调整数字位,按数字键改变参数值,要求各输出信号的参数值按如下范围设置。
正弦波参数:
频率,10Hz—50Hz;幅度,1.0V—5.0V。
锯齿波参数:
频率,10Hz—50Hz;幅度,1.0V—5.0V。
(4)、设置时的显示
例如处于设置状态正弦波频率显示为:
S1F---xx
显示的各位从左到右意义是:
最高位:
S表示是在设置状态,
次高位:
1、2表示是正弦波、锯齿波,处于选择波形时,该位闪烁。
第3位:
F表示频率,A表示幅度,d表示占空比,处于选择参数时,该位闪烁。
第4-8位:
x表示设置数据,当前要设置的数据位闪烁,按←、→键改变设置数据的位数,按确认键,当前参数设置完毕,进入下一个参数或波形设置。
设置状态下各种显示如下
正弦波频率显示S1F---xx
正弦波幅度显示S1A---xx
锯齿波频率显示S2F---xx
锯齿波幅度显示S2A---xx
c、选择输出波形
在非设置状态,按选择波形键,选择输出波形,按一下切换一次,这时显示所选择输出波形代码和频率,但并不输出所选择的输出波形,要按确认键后,才输出所选择的输出波形。
在非设置状态,按选择参数键,选择显示的参数,按一下切换一次。
在非设置状态的显示与设置时的显示仅最高位不同,最高位显示“O”,并且没有闪烁。
例如
输出正弦波频率显示O1F---xx
输出正弦波幅度显示O1A---xx
5、总结
(1)此次作品的优缺点分析
此次课程设计的作品,其优点在于成本低,用户方便控制,不足之处在于输出波形的采样点数单个周期之内只有32个点,导致波形看上去有明显的阶梯感,且输出频率不能达到很高,并不能满足大多数的使用要求。
我建议的方式是使用并行的、高速率的D/A芯片和高性能的单片机。
高性能的单片机控制能力强大,管脚的功能相对复杂,不但能够输出采样点数更多的波形,还能提高输出频率,并且还可以设计出更加好的用户界面。
并且方便建立波形数据库,不单单只能输出正弦波、锯齿波这么几种简单波形,还能按照用户需要设计出多种多样譬如被调制的高频正弦波等等。
但是成本较高。
(2)本次课程设计的心得体会
只有去认真的探索,才能发现和解决问题。
只有真正在查找问题,解决问题,才能使得人的能力得到提高,经验也慢慢丰富起来。
随随便便搞一搞,对老师不敬重,更是对自己的不负责任。
这样的课程设计,不论难易,认真的做,肯定是有收获。
就算是查找了很多问题,也没有把问题解决,你也明白了自己解决问题的方式有可能存在欠缺,这本身也是一笔很大的收获。
参考文献
[1]郭天祥.51单片机C语言教程.电子工业出版社,2007
[2]张毅刚,王少军,付宁.单片机原理及接口技术.人民邮电出版社,2015
[3]恰汗·合孜尔.C语言程序设计.中国铁道出版社,2010
[4]刘卫国.MATLAB程序设计与应用.高等教育出版社,2006.7
[5]谭浩强.C程序设计.北京:
清华大学出版社,1991
附件1:
程序代码
/*---------------------------------------1.c--------------------------------------------------*/
/*
注意:
time_repire可以考虑软件修正时间(我们学院的单片机真的是12M,而不是11.0592M)
*/
#include
sbitTEST=P1^1;
inttime_repire=-40;//修复中断响应时间以及TL1=(LOAD_L);TH1=(LOAD_H)的耗时
intLOAD_H=(65536-276)/256;
intLOAD_L=(65536-276)%256;
#defineLOAD_H_two_fre_num_2((65536-(1000000/two_fre_num_2/32)-time_repire)/256)
#defineLOAD_L_two_fre_num_2((65536-(1000000/two_fre_num_2/32)-time_repire)%256)
#defineLOAD_H_one_fre_num_2((65536-(1000000/one_fre_num_2/32)-time_repire)/256)
#defineLOAD_L_one_fre_num_2((65536-(1000000/one_fre_num_2/32)-time_repire)%256)
#include
#include
#include
#include
bittest=0;
intsave=0;//防止I2C冲突
voidmain()
{
PT1=1;//t1中断为高优先级中断
PT0=0;//t0中断为低优先级中断
TEST=0;
//----------------------上电初始化,从EEPROM读入数据----------------------//
disp[3]=17;
disp[4]=17;
disp[5]=17;
disp[0]=5;
save=1;
ET1=0;
init_E2PROM();
wave=read_add
(1);
fre_amp=read_add
(2);
one_fre_num=read_add(3);
one_amp_num=read_add(4);
two_fre_num=read_add(5);
two_amp_num=read_add(6);
//上电初始化
wave_2=wave;//副本更新,主本从i2c获取数据
fre_amp_2=fre_amp;
one_fre_num_2=one_fre_num;
one_amp_num_2=one_amp_num;
two_fre_num_2=two_fre_num;
two_amp_num_2=two_amp_num;//(变量副本:
仅用于输出)
if(wave_2==0)//波形数据更新
{sawtooth(two_amp_num_2);LOAD_H=LOAD_H_two_fre_num_2;LOAD_L=LOAD_L_two_fre_num_2;}
if(wave_2==1)
{sine(one_amp_num_2);LOAD_H=LOAD_H_one_fre_num_2;LOAD_L=LOAD_L_one_fre_num_2;}
save=0;
ET1=1;
//-----------------------------------------------------------------------
DA_init();
T1_init();
key_display();
}
/*----------------------HCI.H-----------------------------------------*/
/*人机交互模块
输入:
按下矩阵键盘输出:
keynum=0-15对应矩阵键盘
输入:
disp[X]=Y输出:
在第X位上显示table[Y]的数据
关键变量:
waveset_allow=0,wave=0,fre_amp=0;one_fre_num=12;one_amp_num=34;two_fre_num=56;two_amp_num=78;
//设置?
波形?
频率/幅度?
频率和幅度具体的数值
intone_fre_num_low=0,one_fre_num_hign=0;
intone_amp_num_low=0,one_amp_num_hign=0;//位数分离
inttwo_fre_num_low=0,two_fre_num_hign=0;//
inttwo_amp_num_low=0,two_amp_num_hign=0;//位数分离
*/
#ifndef_HCI_
#define_HCI_
#defineucharunsignedchar
#defineuintunsignedint
sbitoe=P1^3;//数码管段选、位选锁存器输出控制信号
sbitdula=P1^4;//数码管段选锁存器控制信号
sbitwela=P1^5;//数码管位选锁存器控制信号
ucharj=0;
uchara,b,c,i,keynum=-1;
externintsave;
externintSAWFRE;
externintSINFRE;
externvoidsawtooth(intampSAW);
externvoidsine(intampSIN);
externvoidsave_date(ucharaddress,uchardate);
externucharread_date(ucharaddress);
externvoidinit_E2PROM();
externvoidDA_init();
externvoidT1_init();
ucharcodesled_bit[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//定义点亮数码管位选码
datauchardisp[8]={16,16,16,16,16,16,16,16};//要显示在8个数码管上的数据在table[]上的位置
ucharcodetable[18]={0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,
0x00,0x40};//共阴极数码管显示段码(0-F)
//关键变量
ucharwaveset_allow=0,wave=0,fre_amp=0;one_fre_num=12;one_amp_num=5;two_fre_num=30;two_amp_num=5;
ucharwaveset_allow_2=0,wave_2=0,fre_amp_2=0;one_fre_num_2=12;one_amp_num_2=5;two_fre_num_2=30;two_amp_num_2=5;//(变量副本)
//设置?
波形?
频率/幅度?
频率和幅度具体的数值
inttime_flash=0,time_flash1=0,time_flash2=0,waveflash=0,fre_ampflash=0;
//闪烁时间计数
//---其他变量------------
intamp_point_dis=0;
intset;//是否在设置状态
ucharkeydef='N';//键位定义
intone_fre_num_low=0,one_fre_num_hign=0;
intone_amp_num_low=0,one_amp_num_hign=0;//位数分离
inttwo_fre_num_low=0,two_fre_num_hign=0;//
inttwo_amp_num_low=0,two_amp_num_hign=0;//位数分离
inthigh_dis=0;low_dis=0;
inthign_flash=0,low_flash=0;
//------------------------
voiddelay(n)//延时函数
dataucharn;
{dataucharm;
while(n--)
for(m=0;m<1;m++);
}
voidkey_display(void)
{
TMOD=0x01|TMOD;//设置定时器T0为方式1定时
TH0=(65536-1000)/256;//给T0装入初值
TL0=(65536-1000)%256;//给T0装入初值
ET0=1;//允许T0中断
EA=1;//CPU开中断
TR0=1;//启动T0
oe=0;
P2=0xff;
while
(1)
{P2=0xf0;
delay(5);
P2=0xf0;
a=P2;
P2=0x0f;
delay(5);
P2=0x0f;
b=P2;
a=a|b;
if(a!
=0xff)
{while(P2!
=0x0f);
switch(a)//keynum=0-15对应矩阵键盘16个按键
{//keydef为实际模型
case0xee:
{keynum=0;keydef=0;}break;
case0xde:
{keynum=1;keydef=1;}break;
case0xbe:
{keynum=2;keydef=2;}break;
case0x7e:
{keynum=3;keydef=3;}break;
case0xed:
{keynum=4;keydef=4;}break;
case0xdd:
{keynum=5;keydef=5;}break;
case0xbd:
{keynum=6;keydef=6;}break;
case0x7d:
{keynum=7;keydef=7;}break;
case0xeb:
{keynum=8;keydef='<';}break;
case0xdb:
{keynum=9;keydef=8;}break;
case0xbb:
{keynum=10;keydef=9;}break;
case0x7b:
{keynum=11;keydef='>';}break;
case0xe7:
{keynum=12;keydef='S';}break;
case0xd7:
{keynum=13;keydef='W';}break;
case0xb7:
{keynum=14;keydef='X';}break;
case0x77:
{keynum=15;k