热卖开发板:
  • 51单片机开发板
  • USB开发板
  • AVR单片机开发板
  • 论坛FM发射板源程序(C语言)

      群里有人要,整理一下发在这里。不包含红外遥控功能,有兴趣的自己加,读取红外编码后转换为标准键值就可以,不需要改动很多地方。

       

      /****************wang1jin设计的FM发射板程序************************
      电路结构: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;   //仿真器用三字节空间

      sbit CE_1415= P1^4;   //;1415的使能端
      sbit CK_1415= P1^5;   //;1415的时钟
      sbit DT_1415= P1^6;   //;1415数据口
      sbit RS_1602=P3^3;    //各控制脚
      sbit RW_1602=P3^4;
      sbit EN_1602=P3^5;
      sfr  DataPort_1602=0x90;//P1数据口
      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;//菜单切换状态变量
      uchar Account;uchar MenuDelayTime;
      bit FlashState; //闪动状态变量

      /**************************延时变量*****************************/
      //#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


      /**********************延时子函数*******************************/
      void uDelay(uchar i)   //us延时子函数,入口参数每加1约加100us
      {
       for(;i>0;i--)
        {uchar j=Delay1;while(--j);} 
      }

      void mDelay(uint Dat)  //ms延时子函数
      {
       uint j;   
       for (;Dat>0;Dat--)
        for (j=Delay2;j>0;j--); 
      }


      /****************以下为EPPROM读写相关函数*******************************/
      /*以下为EPPROM保护,每次操作后更改指令值,使EPPROM免于被误操作*/
      void EPPROM_Protect()
      {
        ISP_CONTR=0x00;   //更改指令值,防止出现误操作
       ISP_CMD=0x00;
       ISP_TRIG=0x00;
       ISP_ADDRH=0x00;
       ISP_ADDRL=0x00;
      }

      /*以下为读入指令,入口参数为要读的地址(16位),返回内容(8位)*/
      uchar EEPROM_Read(uint Addr)
      {
       ISP_ADDRH=Addr/0x100;  //送地址
       ISP_ADDRL=Addr%0x100;
       ISP_CONTR=0x83;  //控制字节
       ISP_CMD=0x01;  //读命令
       ISP_TRIG=0x46;  //触发
       ISP_TRIG=0xB9;
       EPPROM_Protect(); //更改相关数值,防止误操作 
       return(ISP_DATA);  //返回读出的值
      }
       
      /*以下为扇区擦除指令,入口参数为扇区首地址 */
      void EEPROM_Erase(uint Addr) 
      {
       ISP_ADDRH=Addr/0x100;  //待写入扇区首地址
       ISP_ADDRL=0x00;
       ISP_CONTR=0x83;
       ISP_CMD=0x03;  //扇区擦除指令
       ISP_TRIG=0x46;  //触发,将擦除整个扇区
       ISP_TRIG=0xB9;
       EPPROM_Protect(); //更改相关数值,防止误操作 
      }

      /*以下为写入指令,入口参数为写入的地址(16位)和内容(8位),每次要写入的位置*/
      void EEPROM_Write(uint Addr,uchar Data)
      {
       ISP_DATA=Data; //待写入值
       ISP_ADDRH=Addr/0x100;  //待写入扇区首地址
       ISP_ADDRL=Addr%0x100;
       ISP_CONTR=0x83;
       ISP_CMD=0x02;  //写入指令
       ISP_TRIG=0x46;  //触发,数据写入
       ISP_TRIG=0xB9;
       EPPROM_Protect(); //更改相关数值,防止误操作 
      }

      /**********************数据存储与读取******************************/
      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;   //如果错误,设置为默认90.8M
        DatSave(); 
        }
      }

      /**************************M62429 发送用子函数**********************/
      void VolSet(uchar Volume) 
      {
       uint Dat;uchar i;
       Volume=87-Volume;  //得到要送到芯片的实际数
       Dat=0x600|(Volume&0xfc)|((Volume&0x03)<<7);  //音量合并成控制数据,详看DATASHEET
        
       for (i=0;i<10;i++)        //发送10位数据,共11位,最后1位另外发
        {
         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 发送子函数*********************/

      void PLLSend(uint Dat)    //数据发送到BH1415
      {
       uchar i;
       Dat+=0x4800;
       CE_1415=1;    //打开片选准备发送
       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)   //使能子函数
      {
        KeyGnd=1;    //释放键盘线
       uDelay(15);
       DataPort_1602&=0xf0; //数据送出,ASCII码
       DataPort_1602|=Dat>>4;
       RS_1602=0;    //控制发出
       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) //写字节子函数
      {
        KeyGnd=1;
       uDelay(15);
       DataPort_1602&=0xf0;  //数据送出,ASCII码
       DataPort_1602|=Dat>>4;
       RS_1602=1;     //控制发出
       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)  //写字符串子函数。入品:x行(0或1),数组
      {
       En1602(0x80+(x<<6));   
       while(*p)
       {
        WrCharTo1602(*p);p++;
       }
      }

      void Init1602(void)   //初始化子函数
      {
        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)     //自定义字符子函数
      {       
       En1602(0x40);   
       while(*p)
       {WrCharTo1602(*p);p++;mDelay(1);}
      }

      /****************************显示子函数*************************/
      void DispFreq(bit state) //频率值拆分显示。入口参数: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)    //声音值拆分显示。入口参数: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()    //发射部分工作屏幕显示内容初始化
      {
       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() //发射部分不工作屏幕显示内容初始化
      {
       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);} 
      }

      /************************按键读取及处理子函数**************************/
      void KeyRead()   //读键值子函数。返回值: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;  //清延时计数器
       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()    //键值处理子函数
      {
       switch(KeyValue)
       {
       case 1:  //KeyPow处理
        if(FmPow)  //根据当前电源状态切换至另一状态
         {FmOn();}
        else
         {MenuState=0;FmOff();}
        KeyValue=0; 
        break;
       case 2:  //KeyMenu处理
        if(FmPow)break; //关机状态下不处理这个按键
        MenuState++;
        DispFreq(1);DispVol(1); 
        if(MenuState==3)MenuState=0;
        KeyValue=0;
        break;
       case 3:  //KeyUp处理
        if(FmPow)break; //关机状态下不处理这个按键
        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; //关机状态下不处理这个按键
        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;
       } 
      }   

      /*******************计数器中断处理子函数*************************/
      void Timer0() interrupt 1   //计时中断子函数,50ms中断一次
      {

       Account++;       //中断一次加1
       MenuDelayTime++;
       if(Account>Delay3)   
        {
        Account=0;
        FlashState=~FlashState; 
        }
      }

      /****************************主函数*******************************/
      void main()
      {
       mDelay(1000);       //开机延时,等待设备稳定
       Init1602();
       mDelay(10);
       WrCG1602(CG);
       CE_1415=0;CK_1415=0,DT_1415=0; 
       CK_M62429=0;DATA_M62429=0;
       DatLoad();

       TH0=0x3c;         //设定中断初始值
       TL0=0xb0;
       TMOD=0x01;
       EA=1;
       TR0=1;
       ET0=1;
       
       FmOff();      //显示关闭画面

       while(1)  {KeyRead();DispFlash();KeyProc();};
           
      }

    声明:CEPARK已尽力确保内容的准确性,但因业务发展和技术服务的需要,有关内容可能会根据实际情况随时更新或者修改,恕不另行通知,不便之处敬请谅解!
      商城推荐
      社区指南
    CEPARK电子园-电子工程师自己的网络家园!
      迷你博客,轻松完成日志发布,管理相册;展示个性自我
      兴趣小组,热点话题,加入您感兴趣的话题讨论小组,一起交流吧
      最新资讯,热门话题与好友一起分享信息,他评论感兴趣的话题
      实名认证,真实交友,还可以轻松快捷的了解好友动态
      邀请您身边的好友,朋友,同事一起加入欢乐圈