/****************wang1jin設(shè)計的FM發(fā)射板程序************************
電路結(jié)構(gòu):STC12C2052+M62429+BH1415+9018+3355+2053
LCD1602顯示,電子音量
完成時間:2007年11月初
程序編寫:sunhm
***************************************************************/
#include "STC89C51RC_RD_PLUS.H"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define nop _nop_();_nop_();_nop_();
uchar code C51BOX2[3] _at_ 0x43; //仿真器用三字節(jié)空間
sbit CE_1415= P1^4; //;1415的使能端
sbit CK_1415= P1^5; //;1415的時鐘
sbit DT_1415= P1^6; //;1415數(shù)據(jù)口
sbit RS_1602=P3^3; //各控制腳
sbit RW_1602=P3^4;
sbit EN_1602=P3^5;
sfr DataPort_1602=0x90;//P1數(shù)據(jù)口
sbit CK_M62429=P3^1;
sbit DATA_M62429=P3^0;
sbit KeyPow =P1^0; //按鍵
sbit KeyMenu =P1^1;
sbit KeyUp =P1^2;
sbit KeyDown =P1^3;
sbit KeyGnd =P3^7;
sbit FmPow=P1^7;
uchar code Megs0[]={"Sunhm QQ8932867"};
uchar code Megs1[]={" FM Power Off "};
uchar code Megs2[]={" FM Transmitter "};
uchar code Megs3[]={" . MHz - dB"};
uchar code CG[]={
0x02, 0x04, 0x19, 0x15, 0x15, 0x19, 0x04, 0x02, // 喇叭符號
0x20, 0x1f, 0x11, 0x0a, 0x04, 0x04, 0x04, 0x04, // 天線符號
};
uchar Vol; //存放音量值
uint Freq; //存放頻率值
uchar KeyValue; //鍵值
uchar MenuState;//菜單切換狀態(tài)變量
uchar Account;uchar MenuDelayTime;
bit FlashState; //閃動狀態(tài)變量
/**************************延時變量*****************************/
//#define Delay1 90 //STC12C2052+12M晶振使用
//#define Delay2 380
//#define Delay3 5
//#define Delay4 100
//#define Delay1 35 //STC12C2052+RC使用
//#define Delay2 200
//#define Delay3 2
//#define Delay4 50
#define Delay1 45 //AT89C2051+12M晶振使用
#define Delay2 150
#define Delay3 2
#define Delay4 100
/**********************延時子函數(shù)*******************************/
void uDelay(uchar i) //us延時子函數(shù),入口參數(shù)每加1約加100us
{
for(;i>0;i--)
{uchar j=Delay1;while(--j);}
}
void mDelay(uint Dat) //ms延時子函數(shù)
{
uint j;
for (;Dat>0;Dat--)
for (j=Delay2;j>0;j--);
}
/****************以下為EPPROM讀寫相關(guān)函數(shù)*******************************/
/*以下為EPPROM保護,每次操作后更改指令值,使EPPROM免于被誤操作*/
void EPPROM_Protect()
{
ISP_CONTR=0x00; //更改指令值,防止出現(xiàn)誤操作
ISP_CMD=0x00;
ISP_TRIG=0x00;
ISP_ADDRH=0x00;
ISP_ADDRL=0x00;
}
/*以下為讀入指令,入口參數(shù)為要讀的地址(16位),返回內(nèi)容(8位)*/
uchar EEPROM_Read(uint Addr)
{
ISP_ADDRH=Addr/0x100; //送地址
ISP_ADDRL=Addr%0x100;
ISP_CONTR=0x83; //控制字節(jié)
ISP_CMD=0x01; //讀命令
ISP_TRIG=0x46; //觸發(fā)
ISP_TRIG=0xB9;
EPPROM_Protect(); //更改相關(guān)數(shù)值,防止誤操作
return(ISP_DATA); //返回讀出的值
}
/*以下為扇區(qū)擦除指令,入口參數(shù)為扇區(qū)首地址 */
void EEPROM_Erase(uint Addr)
{
ISP_ADDRH=Addr/0x100; //待寫入扇區(qū)首地址
ISP_ADDRL=0x00;
ISP_CONTR=0x83;
ISP_CMD=0x03; //扇區(qū)擦除指令
ISP_TRIG=0x46; //觸發(fā),將擦除整個扇區(qū)
ISP_TRIG=0xB9;
EPPROM_Protect(); //更改相關(guān)數(shù)值,防止誤操作
}
/*以下為寫入指令,入口參數(shù)為寫入的地址(16位)和內(nèi)容(8位),每次要寫入的位置*/
void EEPROM_Write(uint Addr,uchar Data)
{
ISP_DATA=Data; //待寫入值
ISP_ADDRH=Addr/0x100; //待寫入扇區(qū)首地址
ISP_ADDRL=Addr%0x100;
ISP_CONTR=0x83;
ISP_CMD=0x02; //寫入指令
ISP_TRIG=0x46; //觸發(fā),數(shù)據(jù)寫入
ISP_TRIG=0xB9;
EPPROM_Protect(); //更改相關(guān)數(shù)值,防止誤操作
}
/**********************數(shù)據(jù)存儲與讀取******************************/
void DatSave()
{
EEPROM_Erase(0x1000);
EEPROM_Write(0x1000,Vol);
EEPROM_Write(0x1001,Freq/0x100);
EEPROM_Write(0x1002,Freq%0x100);
}
void DatLoad()
{
Vol=EEPROM_Read(0x1000);
Freq=EEPROM_Read(0x1001)*0x100+EEPROM_Read(0x1002);
if(Vol>83){Vol=10;DatSave();}
if ((Freq>1080)||(Freq<870)) //判斷頻率值是否有錯
{
Freq=908; //如果錯誤,設(shè)置為默認90.8M
DatSave();
}
}
/**************************M62429 發(fā)送用子函數(shù)**********************/
void VolSet(uchar Volume)
{
uint Dat;uchar i;
Volume=87-Volume; //得到要送到芯片的實際數(shù)
Dat=0x600|(Volume&0xfc)|((Volume&0x03)<<7); //音量合并成控制數(shù)據(jù),詳看DATASHEET
for (i=0;i<10;i++) //發(fā)送10位數(shù)據(jù),共11位,最后1位另外發(fā)
{
DATA_M62429=Dat&0x01;
CK_M62429=1;
nop;
DATA_M62429=0;
CK_M62429=0;
Dat>>=1;
}
DATA_M62429=1;
CK_M62429=1;nop;CK_M62429=0;
}
/*************************1415 發(fā)送子函數(shù)*********************/
void PLLSend(uint Dat) //數(shù)據(jù)發(fā)送到BH1415
{
uchar i;
Dat+=0x4800;
CE_1415=1; //打開片選準備發(fā)送
for(i=16;i>0;i--)
{
DT_1415=Dat&1;
CK_1415=1;
Dat>>=1;
CK_1415=0;
}
CE_1415=0;DT_1415=0;CK_1415=0;
}
/**********************1602 4線使用程序***********************/
void En1602(uchar Dat) //使能子函數(shù)
{
KeyGnd=1; //釋放鍵盤線
uDelay(15);
DataPort_1602&=0xf0; //數(shù)據(jù)送出,ASCII碼
DataPort_1602|=Dat>>4;
RS_1602=0; //控制發(fā)出
RW_1602=0;
EN_1602=1;EN_1602=0;
DataPort_1602&=0xf0;
DataPort_1602|=Dat&0x0f;
RS_1602=0;
RW_1602=0;
EN_1602=1;EN_1602=0;
KeyGnd=0;
}
void WrCharTo1602(uchar Dat) //寫字節(jié)子函數(shù)
{
KeyGnd=1;
uDelay(15);
DataPort_1602&=0xf0; //數(shù)據(jù)送出,ASCII碼
DataPort_1602|=Dat>>4;
RS_1602=1; //控制發(fā)出
RW_1602=0;
EN_1602=1;EN_1602=0;
DataPort_1602&=0xf0;
DataPort_1602|=Dat&0x0f;
RS_1602=1;
RW_1602=0;
EN_1602=1;EN_1602=0;
KeyGnd=0;
}
void WrStringTo1602(uchar x,uchar *p) //寫字符串子函數(shù)。入品:x行(0或1),數(shù)組
{
En1602(0x80+(x<<6));
while(*p)
{
WrCharTo1602(*p);p++;
}
}
void Init1602(void) //初始化子函數(shù)
{
En1602(0x28); //4位點陣方式
mDelay(100);
En1602(0x28);
mDelay(100);
En1602(0x28);
mDelay(100);
En1602(0x28);
mDelay(100);
En1602(0x0c); //開顯示
mDelay(100);
En1602(0x01); //清屏
}
void WrCG1602(uchar *p) //自定義字符子函數(shù)
{
En1602(0x40);
while(*p)
{WrCharTo1602(*p);p++;mDelay(1);}
}
/****************************顯示子函數(shù)*************************/
void DispFreq(bit state) //頻率值拆分顯示。入口參數(shù):1為有顯示,0為沒
{
uint Dat=Freq;
if(state) //正常顯示
{
En1602(0xc5);
WrCharTo1602(Dat%10+0x30);
Dat/=10;
En1602(0xc3);
WrCharTo1602(Dat%10+0x30);
Dat/=10;
En1602(0xc2);
WrCharTo1602(Dat%10+0x30);
En1602(0xc1);
if(Dat/10) WrCharTo1602(0x31);
else WrCharTo1602(0x20);
}
else //無顯示
{
En1602(0xc1);
WrCharTo1602(0x20);WrCharTo1602(0x20);WrCharTo1602(0x20);
En1602(0xc5);WrCharTo1602(0x20);
}
}
void DispVol(bit State) //聲音值拆分顯示。入口參數(shù):1為有顯示,0為沒
{
if(State)
{
En1602(0xcc);
if(Vol/10) WrCharTo1602(Vol/10+0x30);
else WrCharTo1602(0x20);
WrCharTo1602(Vol%10+0x30);
}
else
{
En1602(0xcc);WrCharTo1602(0x20);WrCharTo1602(0x20);
}
}
void FmOn() //發(fā)射部分工作屏幕顯示內(nèi)容初始化
{
FmPow=0;
WrStringTo1602(0,Megs2);
WrStringTo1602(1,Megs3);
En1602(0xc0);WrCharTo1602(1);
En1602(0xca);WrCharTo1602(0);
VolSet(Vol);
mDelay(50);
DispFreq(1);
DispVol(1);
PLLSend(Freq);
}
void FmOff() //發(fā)射部分不工作屏幕顯示內(nèi)容初始化
{
FmPow=1;
WrStringTo1602(0,Megs1);
WrStringTo1602(1,Megs0);
}
void DispFlash() //閃動顯示處理
{
if(!MenuState)return;
if(MenuState==1) DispFreq(FlashState);
if(MenuState==2) DispVol(FlashState);
if(MenuDelayTime>Delay4)
{MenuState=0;DispFreq(1);DispVol(1);}
}
/************************按鍵讀取及處理子函數(shù)**************************/
void KeyRead() //讀鍵值子函數(shù)。返回值:1、2、3、31、4、41。(31、41為長按)
{
uchar i=250;
KeyGnd=0;
KeyPow=1;KeyMenu=1;KeyUp=1;KeyDown=1;
if(KeyPow&KeyMenu&KeyUp&KeyDown) {KeyValue=0;return;}
MenuDelayTime=0; //清延時計數(shù)器
mDelay(10);
if(!KeyPow)
{
while(!KeyPow); //等待鍵釋放
KeyValue=1;
return;
}
if(!KeyMenu)
{
while(!KeyMenu);//等待鍵釋放
KeyValue=2;
return;
}
if(!KeyUp)
{
if(KeyValue) return; //短按
while(i)
{
if(KeyUp)
{KeyValue=3;return;}
mDelay(3);i--;
} //短按
KeyValue=31; //長按
return;
}
if(!KeyDown)
{
if(KeyValue) return;//短按
while(i)
{
if(KeyDown)
{KeyValue=4;return;}
mDelay(3);i--;
} //短按
KeyValue=41; //長按
return;
}
}
void KeyProc() //鍵值處理子函數(shù)
{
switch(KeyValue)
{
case 1: //KeyPow處理
if(FmPow) //根據(jù)當前電源狀態(tài)切換至另一狀態(tài)
{FmOn();}
else
{MenuState=0;FmOff();}
KeyValue=0;
break;
case 2: //KeyMenu處理
if(FmPow)break; //關(guān)機狀態(tài)下不處理這個按鍵
MenuState++;
DispFreq(1);DispVol(1);
if(MenuState==3)MenuState=0;
KeyValue=0;
break;
case 3: //KeyUp處理
if(FmPow)break; //關(guān)機狀態(tài)下不處理這個按鍵
if(MenuState==1)
{
Freq++;
if(Freq>1080)Freq=875; //頻率加1
DispFreq(1);PLLSend(Freq);
}
if(MenuState==2)
{
if(Vol)Vol--;
DispVol(1);VolSet(Vol);
}
KeyValue=0;DatSave();
break;
case 4: //KeyDown處理
if(FmPow)break; //關(guān)機狀態(tài)下不處理這個按鍵
if(MenuState==1)
{
Freq--;
if(Freq<875)Freq=1080;
DispFreq(1);PLLSend(Freq);
}
if(MenuState==2)
{
if(Vol<83)Vol++; //音量減
DispVol(1); VolSet(Vol);
}
KeyValue=0;DatSave();
break;
case 31: //KeyUp長按處理
if(FmPow)break;
if(MenuState==1)
{
Freq+=10;
if(Freq>1080)Freq=875;
DispFreq(1);PLLSend(Freq);
}
if(MenuState==2)
{
if(Vol>4)Vol-=5;
else Vol=0;
DispVol(1);VolSet(Vol);
}
mDelay(600);
DatSave();
break;
case 41: //KeyDown長按處理
if(FmPow)break;
if(MenuState==1)
{
Freq-=10;
if(Freq<875)Freq=1080;
DispFreq(1);PLLSend(Freq);
}
if(MenuState==2)
{
Vol+=5;
if(Vol>83)Vol=83;
DispVol(1);VolSet(Vol);
}
mDelay(600);DatSave();
break;
}
}
/*******************計數(shù)器中斷處理子函數(shù)*************************/
void Timer0() interrupt 1 //計時中斷子函數(shù),50ms中斷一次
{
Account++; //中斷一次加1
MenuDelayTime++;
if(Account>Delay3)
{
Account=0;
FlashState=~FlashState;
}
}
/****************************主函數(shù)*******************************/
void main()
{
mDelay(1000); //開機延時,等待設(shè)備穩(wěn)定
Init1602();
mDelay(10);
WrCG1602(CG);
CE_1415=0;CK_1415=0,DT_1415=0;
CK_M62429=0;DATA_M62429=0;
DatLoad();
TH0=0x3c; //設(shè)定中斷初始值
TL0=0xb0;
TMOD=0x01;
EA=1;
TR0=1;
ET0=1;
FmOff(); //顯示關(guān)閉畫面
while(1) {KeyRead();DispFlash();KeyProc();};
}