基于MCS51设计的概念版的信号发生器
2016-09-24 来源:eefocus
这款系统只是概念上的,因为受到数模芯片转化时间和单片机程序控制时间的限制,只能在很小的频率范围内出比较好的波形,这款系统主要还是其程序的控制,其中控制程序占有整个程序的90%左右。
一块4×4的矩阵键盘向单片机提供输入,使LCD1602液晶显示出频率和幅值,通过抄作键盘可以改变相应的频率、幅值以及输出波形。
系统构成部件:
单片机Atmel89s52、数模转换芯片DAC0832、4×4键盘、LCD1602液晶显示
模块介绍:
4×4的矩阵键盘
单片机系统:24M晶振,30pf的电容,P0口:液晶的数据口,P2口:DAC0832的数据口,P1口:接4×4的矩阵键盘,P3.7:DAC0832片选信号
数模转换:DAC0832,基本接法
显示:LCD1602
波形输出:DAC0832的双极型输出
仿真波形:
LCD显示:
程序设计:
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit RS=P3^0; //寄存器选择位,将RS位定义为P2.0引脚
sbit RW=P3^1; //读写选择位,将RW位定义为P2.1引脚
sbit E=P3^2; //使能信号位,将E位定义为P2.2引脚
sbit BF=P0^7; //忙碌标志位,,将BF位定义为P0.7引脚
sbit CS=P3^7; //DA0832片选信号
bit F_Flag=0;
bit SET_Flag=0;
bit Wave_Flag=1;
unsigned char Flag=0;
unsigned char j=0;
unsigned char a=0;
unsigned int F_Val=100;
unsigned int A_Val=5000;
unsigned int u1,u2;
unsigned char code digit[ ]={'0123456789'}; //定义字符数组显示数字
unsigned char code string[ ]= {'
unsigned char code string1[ ]={' A:5000mV '};
unsigned char code string2[ ]={'SIN'};
unsigned char code string3[ ]={'TRA'};
unsigned char code string4[ ]={'REC'};
unsigned char code key_code[]={
0xee,0xde,0xbe,0x7e,0xed,0xdd,0xbd,0x7d,
0xeb,0xdb,0xbb,0x7b,0xe7,0xd7,0xb7,0x77 };
uchar code tosin[256]={
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,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfd,
0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf2,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,0x80,0x7c,0x79,0x76,
0x72,0x6f,0x6c,0x69,0x66,0x63,0x60,0x5d,0x5a,0x57,0x55,0x51,
0x4e,0x4c,0x48,0x45,0x43,0x40,0x3d,0x3a,0x38,0x35,0x33,0x30,
0x2e,0x2b,0x29,0x27,0x25,0x22,0x20,0x1e,0x1c,0x1a,0x18,0x16,
0x15,0x13,0x11,0x10,0x0e,0x0d,0x0b,0x0a,0x09,0x08,0x07,0x06,
0x05,0x04,0x03,0x02,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x02,0x03,0x04,0x05,
0x06,0x07,0x08,0x09,0x0a,0x0b,0x0d,0x0e,0x10,0x11,0x13,0x15,
0x16,0x18,0x1a,0x1c,0x1e,0x20,0x22,0x25,0x27,0x29,0x2b,0x2e,
0x30,0x33,0x35,0x38,0x3a,0x3d,0x40,0x43,0x45,0x48,0x4c,0x4e,
0x51,0x55,0x57,0x5a,0x5d,0x60,0x63,0x66,0x69,0x6c,0x6f,0x72,
0x76,0x79,0x7c,0x80};//正弦波码
/*****************************************************
函数功能:延时1ms
***************************************************/
void delay1ms()
{
unsigned char i,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++)
;
}
/*****************************************************
函数功能:延时若干毫秒
入口参数:n
***************************************************/
void delay(unsigned int n)
{
unsigned int i;
for(i=0;i delay1ms(); } /***************************************************** 函数功能:判断液晶模块的忙碌状态 返回值:result。result=1,忙碌;result=0,不忙 ***************************************************/ unsigned char BusyTest(void) { bit result; RS=0; //根据规定,RS为低电平,RW为高电平时,可以读状态 RW=1; E=1; //E=1,才允许读写 _nop_(); //空操作 _nop_(); _nop_(); _nop_(); //空操作四个机器周期,给硬件反应时间 result=BF; //将忙碌标志电平赋给result E=0; return result; } /***************************************************** 函数功能:将模式设置指令或显示地址写入液晶模块 入口参数:dictate ***************************************************/ void WriteInstruction (unsigned char dictate) { while(BusyTest()==1); //如果忙就等待 RS=0; //根据规定,RS和R/W同时为低电平时,可以写入指令 RW=0; E=0; //E置低电平(根据表8-6,写指令时,E为高脉冲, // 就是让E从0到1发生正跳变,所以应先置'0' _nop_(); _nop_(); //空操作两个机器周期,给硬件反应时间 P0=dictate; //将数据送入P0口,即写入指令或地址 _nop_(); _nop_(); _nop_(); _nop_(); //空操作四个机器周期,给硬件反应时间 E=1; //E置高电平 _nop_(); _nop_(); _nop_(); _nop_(); //空操作四个机器周期,给硬件反应时间 E=0; //当E由高电平跳变成低电平时,液晶模块开始执行命令 } /***************************************************** 函数功能:指定字符显示的实际地址 入口参数:x ***************************************************/ void WriteAddress(unsigned char x) { WriteInstruction(x|0x80); //显示位置的确定方法规定为'80H+地址码x' } /***************************************************** 函数功能:将数据(字符的标准ASCII码)写入液晶模块 入口参数:y(为字符常量) ***************************************************/ void WriteData(unsigned char y) { while(BusyTest()==1); RS=1; //RS为高电平,RW为低电平时,可以写入数据 RW=0; E=0; //E置低电平(根据表8-6,写指令时,E为高脉冲, // 就是让E从0到1发生正跳变,所以应先置'0' P0=y; //将数据送入P0口,即将数据写入液晶模块 _nop_(); _nop_(); _nop_(); _nop_(); //空操作四个机器周期,给硬件反应时间 E=1; //E置高电平 _nop_(); _nop_(); _nop_(); _nop_(); //空操作四个机器周期,给硬件反应时间 E=0; //当E由高电平跳变成低电平时,液晶模块开始执行命令 } /***************************************************** 函数功能:对LCD的显示模式进行初始化设置 ***************************************************/ void LcdInitiate(void) { delay(15); //延时15ms,首次写指令时应给LCD一段较长的反应时间 WriteInstruction(0x38); //显示模式设置:16×2显示,5×7点阵,8位数据接口 delay(5); //延时5ms WriteInstruction(0x38); delay(5); WriteInstruction(0x38); delay(5); WriteInstruction(0x0F); //显示模式设置:显示开,有光标,光标闪烁 delay(5); WriteInstruction(0x06); //显示模式设置:光标右移,字符不移 delay(5); WriteInstruction(0x01); //清屏幕指令,将以前的显示内容清除 delay(5); } /********************************************************** 键盘扫描子函数 ************************************************************/ uchar keyscan() { uchar scan1,scan2,keycode,j,key; P2=0xf0; scan1=P2; if((scan1&0xf0)!=0xf0) //判键是否按下 { delay(10); //延时10ms scan1=P2; if((scan1&0xf0)!=0xf0) //二次判键是否按下 { P2=0x0f; scan2=P2; keycode=scan1|scan2; //组合成键编码 for(j=0;j<=15;j++) { if(keycode== key_code[j]) //查表得键值 { key=j; return(key); } } } } else P1=0xff; return (16); } /****************延时中断子程序1*****************/ void timer0() interrupt 1 { if(Flag==0) //正弦波 P1=tosin[j]; if(Flag==1)//三角波 if(j<128) P1=A_Val*j/5000; else P1=A_Val*(255-j)/5000; if(Flag==2) //方波 {if(j<128) P1=A_Val/20; else P1=255-A_Val/20; a=0;} j++; } void main(void) //主函数 { unsigned char i; LcdInitiate(); //调用LCD初始化函数 delay(10); WriteInstruction(0x01);//清显示:清屏幕指令 WriteAddress(0x00); // 设置显示位置为第一行的第1个字 i = 0; while(string[i] != '\0') //'\0'是数组结束标志 { // 显示字符 WriteData(string[i]); i++; delay(40); } WriteAddress(0x40); // 设置显示位置为第二行的第1个字 i = 0; while(string[i] != '\0') //'\0'是数组结束标志 { // 显示字符 WriteData(string1[i]); i++; delay(40); } WriteAddress(0x67); while(1) { if(keyscan()==10&&SET_Flag==0) { delay(20); if(keyscan()==10&&SET_Flag==0) { Flag++; if(Flag==3) Flag=0; WriteAddress(0x01); _nop_(); _nop_(); _nop_(); _nop_(); if(Flag==0) { i = 0; while(string2[i] != '\0') //'\0'是数组结束标志 { // 显示字符 SIN WriteData(string2[i]); i++; delay(40); } } if(Flag==1) { i = 0; while(string3[i] != '\0') //'\0'是数组结束标志 { // 显示字符 TRA WriteData(string3[i]); i++; delay(40); } } if(Flag==2) { i = 0; while(string4[i] != '\0') //'\0'是数组结束标志 { // 显示字符 REC WriteData(string4[i]); i++; delay(40); } } delay(400); } WriteAddress(0x67); } if(keyscan()==11) { delay(20); if(keyscan()==11) { if(F_Flag==0) WriteAddress(0x46); else WriteAddress(0x06); delay(500) ; F_Flag=!F_Flag; SET_Flag=1; Wave_Flag=1; } } if(keyscan()==12&&SET_Flag==1) { delay(20); if(keyscan()==12&&SET_Flag==1) {unsigned char D1,D2,D3,D4; unsigned int Val; if(F_Flag==0) {Val=F_Val; WriteAddress(0x08);} else {Val=A_Val; WriteAddress(0x48);} Val=Val+10; D1=Val%10; //计算个位数字 D2=(Val%100)/10; //计算十位数字 D3=(Val%1000)/100; //计算百位数字 D4=Val/1000; //计算千位数字 WriteData(digit[D4]); //将千位数字的字符常量写入LCD WriteData(digit[D3]); //将百位数字的字符常量写入LCD WriteData(digit[D2]); //将十位数字的字符常量写入LCD WriteData(digit[D1]); //将个位数字的字符常量写入LCD if(F_Flag==0) F_Val=Val; else A_Val=Val; } if(F_Flag==0) WriteAddress(0x06); else WriteAddress(0x46); delay(400); } if(keyscan()==13&&SET_Flag==1) { delay(20); if(keyscan()==13&&SET_Flag==1) {unsigned char D1,D2,D3,D4; unsigned int Val; if(F_Flag==0) {Val=F_Val; WriteAddress(0x08); } else {Val=A_Val; WriteAddress(0x48);} Val=Val-10; D1=Val%10; //计算个位数字 D2=(Val%100)/10; //计算十位数字 D3=(Val%1000)/100; //计算百位数字 D4=Val/1000; //计算千位数字 WriteData(digit[D4]); //将千位数字的字符常量写入LCD WriteData(digit[D3]); //将百位数字的字符常量写入LCD WriteData(digit[D2]); //将十位数字的字符常量写入LCD WriteData(digit[D1]); //将个位数字的字符常量写入LCD if(F_Flag==0) F_Val=Val; else A_Val=Val; } if(F_Flag==0) WriteAddress(0x06); else WriteAddress(0x46); delay(400); } if(keyscan()==14) {delay(20); if(keyscan()==14) {SET_Flag=0; Wave_Flag=1; WriteAddress(0x76); } } if(Wave_Flag==1) { CS=0; u1=3906/F_Val; u2=3906%F_Val; if(u2>F_Val/2) u1++; TMOD=0x02; TH0=TL0=255-u1; EA=1; ET0=1; TR0=1; } if(keyscan()!=16) TR0=TR1=0; } } 总结:优秀的程序设计源自于优秀的硬件系统,好的硬件电路可以大大提高开发效率,单片机开发应该走接口的模块话和标准化,这样可以大大减少开发人员的工作量,单片机接口模块的开发也较具有市场前景! 电路的设计不能完全依赖于教科书上所说的,教科书只是一些理论性的东西,而一个优秀稳定的电路系统要求的更多。 51单片机的速度不够、处理浮点数运算的能力不强,代码效率不高,在ARM系统如火如荼的时候,我们竟然还在学51!