五月丁香激情综合免费视频_国产精品自产拍在线播放桃_午夜福利视频不卡a_免费在线观看亚洲视频_亚洲欧美精品丝袜一区区_国产情趣酒店鸳鸯浴在线观看_亚洲大片久久精品_巜朋友的人妻之诱感人妻

歡迎光臨東莞市飛江電子科技有限公司官網!
全國咨詢熱線

13926563901

18925580829

飛江淘寶店鋪

首頁>技術資料>其它單片機

STC的51單片機紅外遙控器讀碼、發射程序,已試成功

發布時間:2018-04-09   瀏覽量:

 

STC的51單片機紅外遙控器讀碼、發射程序,已試成功
wxleasyland@sina.com
2014.11.21

有一臺DVD機沒有遙控器,正好別的遙控器有的鍵可以用,但功能不一樣。

于是開展本工程,程序原為網上摘的,經過修改均已全部成功。

采用STC的51單片機,STC12C5A60S2,可以直接串口編程,而且是1T的,非常方便。

一、紅外遙控器讀碼
讀碼程序沒怎么修改就成功了。
注意:這里的延時程序是STC12C5A60S2的,如果用別的單片機,需要修改。

#include <STC\STC12C5A60S2.H>
#include <INTRINS.h>

//采用1T周期的STC12C5A60S2單片機,11.0592MHZ
//WXL:一體化接收頭默認是輸出高電平,有信號時輸出低電平;接P3.2腳。
//WXL:這里按“低位在先”

/******************************************************************/
/* 本程序的藍本從網上搜集,經修改并注釋,萬能遙控器解碼成功 */
/* 晶振:11.0592MHz */
/* 整理與測試:單片機教程網  胡琴 2012.5.15 */
/************************* 說 明 *********************************/
/* 以一個9ms的低電平和4.5ms的高電平為引導碼,后跟32位二進制代碼 */
/* 前16位為8位用戶碼及其反碼,后16位為8位的操作碼及其反碼 */
/* 以脈寬為低電平0.565ms、間隔高電平0.56ms、周期為1.125ms的組合表示"0"; */
/* 以脈寬為低電平0.565ms、間隔高電平1.685ms、周期為2.25ms的組合表示"1"。 */
/* 注意:接收碼的脈寬與間隔是對發射碼取反的,即間隔是0.565ms */
/* 解碼后共有四個十六進制碼,本程序取第三個作為識別碼 */
/*******************************************************************/

#define uchar unsigned char
uchar data IRcode[4]; //定義一個4字節的數組用來存儲代碼
uchar CodeTemp; //編碼字節緩存變量
uchar i,j,k; //延時用的循環變量
sbit IRsignal=P3^2; //HS0038接收頭OUT端直接連P3.2(INT0)
sbit P0_0=P0^0; //P0連接到 LED 上
sbit P0_1=P0^1;
sbit P0_2=P0^2;



/**************************延時0.6ms子程序**********************/
void Delay0_6ms(void) //@11.0592MHz
{
unsigned char i, j;

_nop_();
_nop_();
i = 7;
j = 112;
do
{
while (--j);
} while (--i);

}


/**************************延時0.9ms子程序**********************/
void Delay0_9ms(void) //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 10;
j = 170;
do
{
while (--j);
} while (--i);
}




/***************************延時1ms子程序**********************/
void Delay1ms(void)
{
unsigned char i, j;

_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}

/***************************延時4ms子程序**********************/
void Delay4ms(void)
{
unsigned char i, j;

_nop_();
_nop_();
_nop_();
i = 44;
j = 3;
do
{
while (--j);
} while (--i);
}

/**************************** 延時子程序 ************************/
void Delay(void)
{
uchar i,j,k;
for(i=200;i>0;i--)
for(j=200;j>0;j--)
for(k=3;k>0;k--)  ;
}

/******************** 中斷0解碼服務子程序 ********************/
void int0(void) interrupt 0 using 2
{
EA = 0; //??? 可以這樣,跳入中斷,但仍可對P3.2(INT0)進行電平變化的讀取
for(k=0;k<10;k++)
{
Delay0_9ms();
if (IRsignal==1)        //如果0.9ms后IRsignal=1,說明不是引導碼,退出中斷
{
k=10;
break;
}

else if(k==9)         //如果 持續了10×0.9ms=9ms的低電平,說明是引導碼。WXL:一定是從引導碼開始
{
while(IRsignal==0);      // WXL:因為紅外頭默認輸出是高電平,故用while(IRsignal==0)很安全,而用while(IRsignal==1)則可能會進入死循環
Delay4ms();       //跳過持續4.5ms的高電平     WXL:要超過4.5ms更好
Delay0_6ms();

for(i=0;i<4;i++)     //分別讀取4個字節
{
for(j=1;j<=8;j++)    //每個字節8個bit的判斷
{
while(IRsignal==0); //等待上升沿,此處用得很好:因為0.56ms的低電平(接收時)是代碼0與1的相同部分
Delay0_9ms(); //從上升沿那一時刻開始延時0.9ms(因為0.9介于0.56(=1.125-0.56)與1.69(=2.25-0.56)之間),再判斷IRsignal
if(IRsignal==1) //  如果IRsignal是"1",高位置"1",并向右移一位
{
Delay1ms(); //為什么要延時1ms呢?因為要使IRsignal跳至低電平(即0.56ms的0與1相同部分上)
CodeTemp=CodeTemp | 0x80; //此處的算法很好
if(j<8)  CodeTemp=CodeTemp>>1;
}
else       //  如果IRsignal是"0",高位置"0",并向右移一位
if(j<8) CodeTemp=CodeTemp>>1;   //如果IRsignal是"0",則直接向右移一位,自動補"0"
}
IRcode=CodeTemp;
CodeTemp=0;
}   //end for

for(i=0;i<4;i++) //通過串口將代碼發出
{
SBUF=IRcode;
while(!TI); //等待一個字節發送完畢
TI=0;
}

Delay();
}    //end else
}   //END for
EA = 1;
}

/***********************串口初始化程序*********************/
void initUart(void)
{
TMOD |= 0x20; //
SCON = 0x50; //
PCON |= 0x80; //
TH1 = 250; // 9600 bps @ 11.0592MHz
TL1 = 250;
TR1 = 1;
}

/**************************主程序*************************/
void main()
{
//P0=0XFF;
initUart();
IT0 = 1; //INT0為負邊沿觸發, (1:負邊沿觸發,0:低電平觸發)
EX0 = 1; //外部中斷INT0開, (1:開, 0:關 )
EA = 1; //開所有中斷

CodeTemp = 0; //初始化紅外編碼字節緩存變量
Delay();
while(1)
{

}
}






二、紅外遙控發射

網上的程序是http://gudeng614.blog.163.com/blog/static/818017420101545648734/

做發射程序費了很大波折,因為網上的程序不好用。

后來不得不用計算機的并口采集了發射數據,發現數據有異常,終于找到了問題所在。

原因是count變量是int的,對其賦值或比較時,匯編語句一句完不成,會被中斷服務程序中斷,造成count變量賦值或比較出現問題。
解決方法是必須在操作時屏蔽中斷。而flag變量是bit的,一句匯編即可完成賦值,故不會有問題。

其間還發現別的遙控器會在起始碼前加一個前脈沖,以為是這個問題,其實不是。

注意:由于13us會中斷一次,這里是采用1T的單片機。如果采用普通的51單片機,由于是12T的,不知道能不能成功。

//程序從網上修改而來
//由于中斷需要13us中斷一次,即中斷要在幾us處理完,因此需要單片機速度比較快,用24MHZ晶振才能保證正常
//但24MHZ晶振,用串口不方便
//這里采用1T周期的STC12C5A60S2單片機,11.0592MHZ,可以兼顧。
//STC12C5A60S2  引腳可灌入20mA電流,直接從正電源→紅外LED→串1K電阻→P0.0腳。
//串口1默認選T1作為波特率發生器
//TO用于中斷
//發送時,低比特位優先

#include <STC\STC12C5A60S2.H>
#include <INTRINS.h>

sbit  P0_0 = P0^0;

static bit g_OP;            //紅外發射管的亮滅
static unsigned int g_count;       //延時計數器
static unsigned int g_endcount;    //終止延時計數
static bit g_flag;       //紅外發送標志
unsigned char g_iraddr1;     //十六位地址的第一個字節
unsigned char g_iraddr2;     //十六位地址的第二個字節


//定時器0中斷處理
void timeint(void) interrupt 1
{
  g_count++;
  if (g_flag)    g_OP=~g_OP;
  else    g_OP = 1;    //LED不點亮
  P0_0 = g_OP;
}


/////////////////////////////////////////////////////
void SendIRdata_38KHZ(unsigned int temp1, bit temp2)
{
  g_endcount=temp1;
  g_flag=temp2;
  EA=0; g_count=0; EA=1;  //避免中斷影響count置數
  while(1)
  {
   EA=0;
   if( g_count < g_endcount ) EA=1;  //避免中斷影響count比較
   else
   {
   EA=1;
   break;
   }  
  }  
}

/////////////////////////////////////////////////////
void SendIRdata_BYTE(unsigned char irdata)
{
  unsigned char i;
  for(i=0;i<8;i++)
  {
     //先發送0.56ms的38KHZ紅外波(即編碼中0.56ms的高電平)
SendIRdata_38KHZ(43, 1);   //13.02*43=0.56ms

    //停止發送紅外信號(即編碼中的低電平)
     if(irdata & 1)  //判斷最低位為1還是0。   低位先發送!!
       SendIRdata_38KHZ(130, 0);         //1為寬電平,13.02*130=1.693ms
    else      SendIRdata_38KHZ(43, 0);   //0為窄電平,13.02*43=0.560ms

    irdata=irdata>>1;
  }
}

/////////////////////////////////////////////////////
void SendIRdata(unsigned char p_irdata)
{
  //有的遙控器會發一個前脈沖,如果不靈,可試試加上前脈沖
  //發送起始碼前脈沖,高電平有38KHZ載波
  //SendIRdata_38KHZ(18, 1);
  //發送起始碼前脈沖,低電平無38KHZ載波
  //SendIRdata_38KHZ(18, 0);

  //發送9ms的起始碼,高電平有38KHZ載波
  SendIRdata_38KHZ(692, 1); //13.02*692=9.010ms

  //發送4.5ms的結果碼,低電平無38KHZ載波
  SendIRdata_38KHZ(346, 0);    //13.02*346=4.505ms

  //發送十六位地址的前八位
  SendIRdata_BYTE(g_iraddr1);

  //發送十六位地址的后八位
  SendIRdata_BYTE(g_iraddr2);

  //發送八位數據
  SendIRdata_BYTE(p_irdata);

  //發送八位數據的反碼
  SendIRdata_BYTE(~p_irdata);  

  //發送總的結束位1bit
  SendIRdata_38KHZ(43, 1);     //13.02*43=0.56ms


/*  //后面這些可以不用發
  g_endcount=1766;
  g_flag=0;
  EA=0; g_count=0; EA=1;
  while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

  //發送9ms的起始碼,高電平有38KHZ載波
  g_endcount=692;   //13.02*692=9.010ms
  g_flag=1;
  EA=0; g_count=0; EA=1;
  while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

  //發送4.5ms的結果碼,低電平無38KHZ載波
  g_endcount=346;    //13.02*346=4.505ms
  g_flag=0;
  EA=0; g_count=0; EA=1;
  while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

//發送總的結束位1bit
  g_endcount=43;    //13.02*43=0.56ms
  g_flag=1;
  EA=0; g_count=0; EA=1;
  while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; }  }   

*/

  g_flag=0;


}



///////////////////////////////////////////////////////////
void main(void)
{
unsigned char com_data;    //數據字節

  g_count = 0;
  g_flag = 0;
  g_OP = 1;
  P0_0 = g_OP;   //LED接電源正極,不點亮

SCON=0x50;  //串口方式1    01 0 1 0 0 00  模式1,非多機,允許接收,無數據位8,清中斷標識TI和RI

  TMOD = 0x22; //(定時器0和1:方式2,自動重裝,8位)
TH1=253;  //11.0592MHZ,9600bps。沒有設置SMOD,故波特率沒有加倍。即:11.0592/12/3/32=9600bps
TL1=253;
TR1=1;    //啟動定時器

  TH0 = 244;
  TL0 = 244; //(WXL:即計數12次中斷一次,即11.0592MHZ晶振,機器周期是1.085us,12次*1.085=13.02us,這樣達38KHZ。  13us一次中斷,時間太短了,所以單片機要快)
  ET0 = 1;   //定時器0中斷允許
  EA = 1;    //允許CPU中斷
  TR0 = 1;   //開始計數

  g_iraddr1=0;       //地址碼
  g_iraddr2=255;     //地址反碼

RI=0;
while(1)
{
if(RI==1)
{
com_data =SBUF;
RI=0;       //要人工清RI
SendIRdata(com_data);   //發送紅外數據
TI=0;
SBUF = com_data;   //輸出字符
while(!TI) ;     //空語句判斷字符是否發完,TI=1表示發完
TI = 0;         //要人工清TI
}
}

}