基于MCS51設(shè)計的概念版的信號發(fā)生器
系統(tǒng)簡介:這款信號發(fā)生器的原理是利用單片機(jī)控制DAC0832數(shù)模轉(zhuǎn)化芯片,使其輸出-5~+5的模擬量信號,由于單片機(jī)的指令執(zhí)行周期很短,其中Atmel89s52最大可支持24M的晶振,可以用數(shù)字信號產(chǎn)生很好的“模擬”信號。
這款系統(tǒng)只是概念上的,因?yàn)槭艿綌?shù)模芯片轉(zhuǎn)化時間和單片機(jī)程序控制時間的限制,只能在很小的頻率范圍內(nèi)出比較好的波形,這款系統(tǒng)主要還是其程序的控制,其中控制程序占有整個程序的90%左右。
一塊4×4的矩陣鍵盤向單片機(jī)提供輸入,使LCD1602液晶顯示出頻率和幅值,通過抄作鍵盤可以改變相應(yīng)的頻率、幅值以及輸出波形。
系統(tǒng)構(gòu)成部件:
單片機(jī)Atmel89s52、數(shù)模轉(zhuǎn)換芯片DAC0832、4×4鍵盤、LCD1602液晶顯示
模塊介紹:
4×4的矩陣鍵盤
單片機(jī)系統(tǒng):24M晶振,30pf的電容,P0口:液晶的數(shù)據(jù)口,P2口:DAC0832的數(shù)據(jù)口,P1口:接4×4的矩陣鍵盤,P3.7:DAC0832片選信號
數(shù)模轉(zhuǎn)換:DAC0832,基本接法
顯示:LCD1602
波形輸出:DAC0832的雙極型輸出
仿真波形:
LCD顯示:
程序設(shè)計:
#include //包含單片機(jī)寄存器的頭文件
#include //包含_nop_()函數(shù)定義的頭文件
#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; //忙碌標(biāo)志位,,將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"}; //定義字符數(shù)組顯示數(shù)字
unsigned char code string[ ]= {" F: 100HZ "};
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};//正弦波碼
/*****************************************************
函數(shù)功能:延時1ms
***************************************************/
void delay1ms()
{
unsigned char i,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++)
;
}
/*****************************************************
函數(shù)功能:延時若干毫秒
入口參數(shù):n
***************************************************/
void delay(unsigned int n)
{
unsigned int i;
for(i=0;i
delay1ms();
}
/*****************************************************
函數(shù)功能:判斷液晶模塊的忙碌狀態(tài)
返回值:result。result=1,忙碌;result=0,不忙
***************************************************/
unsigned char BusyTest(void)
{
bit result;
RS=0; //根據(jù)規(guī)定,RS為低電平,RW為高電平時,可以讀狀態(tài)
RW=1;
E=1; //E=1,才允許讀寫
_nop_(); //空操作
_nop_();
_nop_();
_nop_(); //空操作四個機(jī)器周期,給硬件反應(yīng)時間
result=BF; //將忙碌標(biāo)志電平賦給result
E=0;
return result;
}
/*****************************************************
函數(shù)功能:將模式設(shè)置指令或顯示地址寫入液晶模塊
入口參數(shù):dictate
***************************************************/
void WriteInstruction (unsigned char dictate)
{
while(BusyTest()==1); //如果忙就等待
RS=0; //根據(jù)規(guī)定,RS和R/W同時為低電平時,可以寫入指令
RW=0;
E=0; //E置低電平(根據(jù)表8-6,寫指令時,E為高脈沖,
// 就是讓E從0到1發(fā)生正跳變,所以應(yīng)先置"0"
_nop_();
_nop_(); //空操作兩個機(jī)器周期,給硬件反應(yīng)時間
P0=dictate; //將數(shù)據(jù)送入P0口,即寫入指令或地址
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四個機(jī)器周期,給硬件反應(yīng)時間
E=1; //E置高電平
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四個機(jī)器周期,給硬件反應(yīng)時間
E=0; //當(dāng)E由高電平跳變成低電平時,液晶模塊開始執(zhí)行命令
}
/*****************************************************
函數(shù)功能:指定字符顯示的實(shí)際地址
入口參數(shù):x
***************************************************/
void WriteAddress(unsigned char x)
{
WriteInstruction(x|0x80); //顯示位置的確定方法規(guī)定為"80H+地址碼x"
}
/*****************************************************
函數(shù)功能:將數(shù)據(jù)(字符的標(biāo)準(zhǔn)ASCII碼)寫入液晶模塊
入口參數(shù):y(為字符常量)
***************************************************/
void WriteData(unsigned char y)
{
while(BusyTest()==1);
RS=1; //RS為高電平,RW為低電平時,可以寫入數(shù)據(jù)
RW=0;
E=0; //E置低電平(根據(jù)表8-6,寫指令時,E為高脈沖,
// 就是讓E從0到1發(fā)生正跳變,所以應(yīng)先置"0"
P0=y; //將數(shù)據(jù)送入P0口,即將數(shù)據(jù)寫入液晶模塊
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四個機(jī)器周期,給硬件反應(yīng)時間
E=1; //E置高電平
_nop_();
_nop_();
_nop_();
_nop_(); //空操作四個機(jī)器周期,給硬件反應(yīng)時間
E=0; //當(dāng)E由高電平跳變成低電平時,液晶模塊開始執(zhí)行命令
}
/*****************************************************
函數(shù)功能:對LCD的顯示模式進(jìn)行初始化設(shè)置
***************************************************/
void LcdInitiate(void)
{
delay(15); //延時15ms,首次寫指令時應(yīng)給LCD一段較長的反應(yīng)時間
WriteInstruction(0x38); //顯示模式設(shè)置:16×2顯示,5×7點(diǎn)陣,8位數(shù)據(jù)接口
delay(5); //延時5ms
WriteInstruction(0x38);
delay(5);
WriteInstruction(0x38);
delay(5);
WriteInstruction(0x0F); //顯示模式設(shè)置:顯示開,有光標(biāo),光標(biāo)閃爍
delay(5);
WriteInstruction(0x06); //顯示模式設(shè)置:光標(biāo)右移,字符不移
delay(5);
WriteInstruction(0x01); //清屏幕指令,將以前的顯示內(nèi)容清除
delay(5);
}
/**********************************************************
鍵盤掃描子函數(shù)
************************************************************/
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) //主函數(shù)
{
unsigned char i;
LcdInitiate(); //調(diào)用LCD初始化函數(shù)
delay(10);
WriteInstruction(0x01);//清顯示:清屏幕指令
WriteAddress(0x00); // 設(shè)置顯示位置為第一行的第1個字
i = 0;
while(string[i] != '\0') //'\0'是數(shù)組結(jié)束標(biāo)志
{ // 顯示字符
WriteData(string[i]);
i++;
delay(40);
}
WriteAddress(0x40); // 設(shè)置顯示位置為第二行的第1個字
i = 0;
while(string[i] != '\0') //'\0'是數(shù)組結(jié)束標(biāo)志
{ // 顯示字符
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'是數(shù)組結(jié)束標(biāo)志
{ // 顯示字符 SIN
WriteData(string2[i]);
i++;
delay(40);
}
}
if(Flag==1)
{ i = 0;
while(string3[i] != '\0') //'\0'是數(shù)組結(jié)束標(biāo)志
{ // 顯示字符 TRA
WriteData(string3[i]);
i++;
delay(40);
}
}
if(Flag==2)
{ i = 0;
while(string4[i] != '\0') //'\0'是數(shù)組結(jié)束標(biāo)志
{ // 顯示字符 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; //計算個位數(shù)字
D2=(Val%100)/10; //計算十位數(shù)字
D3=(Val%1000)/100; //計算百位數(shù)字
D4=Val/1000; //計算千位數(shù)字
WriteData(digit[D4]); //將千位數(shù)字的字符常量寫入LCD
WriteData(digit[D3]); //將百位數(shù)字的字符常量寫入LCD
WriteData(digit[D2]); //將十位數(shù)字的字符常量寫入LCD
WriteData(digit[D1]); //將個位數(shù)字的字符常量寫入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; //計算個位數(shù)字
D2=(Val%100)/10; //計算十位數(shù)字
D3=(Val%1000)/100; //計算百位數(shù)字
D4=Val/1000; //計算千位數(shù)字
WriteData(digit[D4]); //將千位數(shù)字的字符常量寫入LCD
WriteData(digit[D3]); //將百位數(shù)字的字符常量寫入LCD
WriteData(digit[D2]); //將十位數(shù)字的字符常量寫入LCD
WriteData(digit[D1]); //將個位數(shù)字的字符常量寫入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;
}
}
總結(jié):優(yōu)秀的程序設(shè)計源自于優(yōu)秀的硬件系統(tǒng),好的硬件電路可以大大提高開發(fā)效率,單片機(jī)開發(fā)應(yīng)該走接口的模塊話和標(biāo)準(zhǔn)化,這樣可以大大減少開發(fā)人員的工作量,單片機(jī)接口模塊的開發(fā)也較具有市場前景!