目錄
1.紅外遙控簡介
通信方式:
紅外LED波長:
通信協議標準:
2.硬件電路
發送部分1:
內部元件介紹:
工作原理:
為什么要以38KHZ亮滅?
電路圖:
發送部分2:
電路圖:
接收部分:
電路圖:
接收部分原理圖:
3.基本發送與接收
4.NEC編碼
Data(數據格式):
5.NEC編碼:示波器采樣得到的圖
6.遙控器按鍵和鍵碼對比圖
7.51單片機的外部中斷
芯片圖:
中斷號:
8.外部中斷寄存器
9.紅外遙控代碼
第一步:
第二步:
第三步:
第四步:
模塊思路:
紅外解碼方法:
對于上述的闡述的理解圖:
第五步:
頭文件聲明:
第六步:
第七步:
第八步:
最終代碼:
模塊:
IR.c
IR.h
Int0.c
Int0.h
main.c
10.紅外遙控直流機調速代碼
第一步:
第二步:
第三步:
第四步:
第五步:
最終代碼:
模塊:
Timer1.c
Timer1.h
Motor.c
Motor.h
main.c
1.紅外遙控簡介
紅外遙控是利用紅外光進行通信的設備,由紅外LED將調制后的信號發出,由專用的紅外接收頭進行解調輸出
通信方式:
單工,異步
紅外LED波長:
940nm
通信協議標準:
NEC標準
2.硬件電路
發送部分1:
內部元件介紹:
LED1IR是紅外的發光二極管(我們的這款開發版沒有)
38KHZ的調制頻率,會一直輸入一個38KHZ的方波,固定的
IN是波形,IN給高電平LED都是不亮的,IN給低電平LED亮不亮取決于38KHZ的調制頻率
工作原理:
IN和38KHZ的調制頻率,加起來效果,給高電平LED不亮,給低電平LED會以38KHZ亮滅
為什么要以38KHZ亮滅?
這樣做的原因是抗干擾,自然界的紅外光會淹沒我們的紅外發射器的紅光,我們以38KHZ亮滅可以讓接受部分更容易接受我們的紅外光,外界的紅外光是不可能以38KHZ亮滅的
電路圖:
發送部分2:
IN輸入高電平就LED不亮,給低電平LED亮;所以就需要我們自行輸入一個38KHZ的方波,將IN接入I/O口中輸入
電路圖:
接收部分:
這個紅外接收器,很方便,里面的電路會自行濾波;接收時因為38KHZ頻率高,所以不能用掃描按鍵的方法了,我們要將OUT接入外部中斷,一旦有下降沿的信號,會立馬中斷,進行計時處理,這樣響應實時性高
電路圖:
接收部分原理圖:
OUT直接接入引腳P32
3.基本發送與接收
空閑狀態:紅外LED不亮,接收頭輸出高電平
發送低電平:紅外LED以38KHz頻率閃爍發光,接收頭輸出低電平
發送高電平:紅外LED不亮,接收頭輸出高電平
4.NEC編碼
Data(數據格式):
總共4個字節,一共32個bit位
第一個字節(地址碼):標識遙控器的地址,防止不同遙控器互相干擾
第二個字節(地址碼反碼):是跟隨第一個字節的,我們可以把第二個字節取反,看是否與第一個字節相同,用于一定的數據驗證
第三個字節(命令):寫入命令
四個字節(命令反碼):跟隨第三個字節,取命令反碼的反碼,可以進行一定的數據驗證
5.NEC編碼:示波器采樣得到的圖
6.遙控器按鍵和鍵碼對比圖
7.51單片機的外部中斷
STC89C52有4個外部中斷(傳統的89C52只有個中斷,但這里只引兩個中斷,所以我們就當只有兩個中斷)
STC89C52的外部中斷有兩種觸發方式: ?? ?下降沿觸發和低電平觸發
芯片圖:
中斷號:
8.外部中斷寄存器
9.紅外遙控代碼
第一步:
復制粘貼LCD液晶屏模塊、Delay模塊到本工程
第二步:
配置外部中斷;設置中斷優先級,這個實驗要求高,其他中斷來了要打斷他,達到外部中斷的絕對精度
第三步:
在主函數中,寫一個外部中斷的子函數,參考下表,這個是中斷輔助函數
下載進入單片機,我們按下K3獨立按鍵,會給與中斷一個下降沿,LCD液晶屏會加1;如果我們把ITO 賦值為0,初識給與下降沿,我們按下K3獨立按鍵數字會一直加
第四步:
建立新的模塊,外部中斷0模塊化Int0,將外部中斷函數注釋
模塊思路:
建立一個紅外解碼模塊,調用定時器模塊和外部中斷0模塊,main.c調用紅外模塊解碼模塊,進行邏輯的操作。
紅外解碼方法:
首先定義一個變量,這個變量表示當前狀態為空閑狀態,記為0狀態
然后空閑狀態收到一個下降沿,定時器打開開始計時,這個狀態會尋找start和repeat,這些頭部信號,尋找狀態,記為1狀態
尋找狀態給給一個下降沿,進入判斷狀態,會把定時器的值讀取出來,判斷0到1的時間,如果是start起始信號,就開始解碼1010 32bit這個數據,記為2狀態,如果是repeat重發信號,就直接回到0狀態。
狀態2會連續進行32次,每進行一次2狀態,就會計算出1 0這個時間差,會把這個數據填寫到一個變量緩存區,變量緩存區其中會再定義一個變量,指示現在存到第幾位了,存完32次以后就認為數據已經收到了,根據Data(數據格式)中的二、四字節取反驗證數據,驗證成功后置一個驗證成功的標志位,然后回到0狀態。
對于上述的闡述的理解圖:
第五步:
復制粘貼定時器模塊進行改造
頭文件聲明:
第六步:
紅外解碼模塊
第七步:
對按鍵的解碼,進行宏定義,在紅外解碼模塊的頭文件中
第八步:
主函數調用
最終代碼:
模塊:
IR.c
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"unsigned int IR_Time;
unsigned char IR_State;unsigned char IR_Data[4];
unsigned char IR_pData;unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;/*** @brief 紅外遙控初始化* @param 無* @retval 無*/
void IR_Init(void)
{Timer0_Init();Int0_Init();
}/*** @brief 紅外遙控獲取收到數據幀標志位* @param 無* @retval 是否收到數據幀,1為收到,0為未收到*/
unsigned char IR_GetDataFlag(void)
{if(IR_DataFlag){IR_DataFlag=0;return 1;}return 0;
}/*** @brief 紅外遙控獲取收到連發幀標志位* @param 無* @retval 是否收到連發幀,1為收到,0為未收到*/
unsigned char IR_GetRepeatFlag(void)
{if(IR_RepeatFlag){IR_RepeatFlag=0;return 1;}return 0;
}/*** @brief 紅外遙控獲取收到的地址數據* @param 無* @retval 收到的地址數據*/
unsigned char IR_GetAddress(void)
{return IR_Address;
}/*** @brief 紅外遙控獲取收到的命令數據* @param 無* @retval 收到的命令數據*/
unsigned char IR_GetCommand(void)
{return IR_Command;
}//外部中斷0中斷函數,下降沿觸發執行
void Int0_Routine(void) interrupt 0
{if(IR_State==0) //狀態0,空閑狀態{Timer0_SetCounter(0); //定時計數器清0Timer0_Run(1); //定時器啟動IR_State=1; //置狀態為1}else if(IR_State==1) //狀態1,等待Start信號或Repeat信號{IR_Time=Timer0_GetCounter(); //獲取上一次中斷到此次中斷的時間Timer0_SetCounter(0); //定時計數器清0//如果計時為13.5ms,則接收到了Start信號(判定值在12MHz晶振下為13500,在11.0592MHz晶振下為12442)if(IR_Time>13500-500 && IR_Time<13500+500){IR_State=2; //置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>11250-500 && IR_Time<11250+500){IR_RepeatFlag=1; //置收到連發幀標志位為1Timer0_Run(0); //定時器停止IR_State=0; //置狀態為0}else //接收出錯{IR_State=1; //置狀態為1}}else if(IR_State==2) //狀態2,接收數據{IR_Time=Timer0_GetCounter(); //獲取上一次中斷到此次中斷的時間Timer0_SetCounter(0); //定時計數器清0//如果計時為1120us,則接收到了數據0(判定值在12MHz晶振下為1120,在11.0592MHz晶振下為1032)if(IR_Time>1120-500 && IR_Time<1120+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //數據對應位清0IR_pData++; //數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2250-500 && IR_Time<2250+500){IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //數據對應位置1IR_pData++; //數據位置指針自增}else //接收出錯{IR_pData=0; //數據位置指針清0IR_State=1; //置狀態為1}if(IR_pData>=32) //如果接收到了32位數據{IR_pData=0; //數據位置指針清0if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //數據驗證{IR_Address=IR_Data[0]; //轉存數據IR_Command=IR_Data[2];IR_DataFlag=1; //置收到連發幀標志位為1}Timer0_Run(0); //定時器停止IR_State=0; //置狀態為0}}
}
IR.h
#ifndef __IR_H__
#define __IR_H__#define IR_POWER 0x45
#define IR_MODE 0x46
#define IR_MUTE 0x47
#define IR_START_STOP 0x44
#define IR_PREVIOUS 0x40
#define IR_NEXT 0x43
#define IR_EQ 0x07
#define IR_VOL_MINUS 0x15
#define IR_VOL_ADD 0x09
#define IR_0 0x16
#define IR_RPT 0x19
#define IR_USD 0x0D
#define IR_1 0x0C
#define IR_2 0x18
#define IR_3 0x5E
#define IR_4 0x08
#define IR_5 0x1C
#define IR_6 0x5A
#define IR_7 0x42
#define IR_8 0x52
#define IR_9 0x4Avoid IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);#endif
Int0.c
#include <REGX52.H>/*** @brief 外部中斷0初始化* @param 無* @retval 無*/
void Int0_Init(void)
{IT0=1;IE0=0;EX0=1;EA=1;PX0=1;
}/*外部中斷0中斷函數模板
void Int0_Routine(void) interrupt 0
{}
*/
Int0.h
#ifndef __INT0_H__
#define __INT0_H__void Int0_Init(void);#endif
main.c
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"unsigned char Num;
unsigned char Address;
unsigned char Command;void main()
{LCD_Init();LCD_ShowString(1,1,"ADDR CMD NUM");LCD_ShowString(2,1,"00 00 000");IR_Init();while(1){if(IR_GetDataFlag() || IR_GetRepeatFlag()) //如果收到數據幀或者收到連發幀{Address=IR_GetAddress(); //獲取遙控器地址碼Command=IR_GetCommand(); //獲取遙控器命令碼LCD_ShowHexNum(2,1,Address,2); //顯示遙控器地址碼LCD_ShowHexNum(2,7,Command,2); //顯示遙控器命令碼if(Command==IR_VOL_MINUS) //如果遙控器VOL-按鍵按下{Num--; //Num自減}if(Command==IR_VOL_ADD) //如果遙控器VOL+按鍵按下{Num++; //Num自增}LCD_ShowNum(2,12,Num,3); //顯示Num}}
}
10.紅外遙控直流機調速代碼
第一步:
將寫好的直流機調速代碼,復制一份,這個代碼看小編的直流機博客
第二步:
將定時器模塊中的定時器0改為定時器1,避免沖突
第三步:
復制粘貼紅外遙控代碼中的紅外解碼模塊、改造后的定時器模塊、外部中斷0模塊到本工程
第四步:
將電機模塊化
第五步:
主函數調用
最終代碼:
模塊:
Timer1.c
#include <REGX52.H>/*** @brief 定時器1初始化,100us@12.000MHz* @param 無* @retval 無*/
void Timer1_Init(void)
{TMOD &= 0x0F; //設置定時器模式TMOD |= 0x10; //設置定時器模式TL1 = 0x9C; //設置定時初值TH1 = 0xFF; //設置定時初值TF1 = 0; //清除TF1標志TR1 = 1; //定時器1開始計時ET1=1;EA=1;PT1=0;
}/*定時器中斷函數模板
void Timer1_Routine() interrupt 3
{static unsigned int T1Count;TL1 = 0x9C; //設置定時初值TH1 = 0xFF; //設置定時初值T1Count++;if(T1Count>=1000){T1Count=0;}
}
*/
Timer1.h
#ifndef __TIMER1_H__
#define __TIMER1_H__void Timer1_Init(void);#endif
Motor.c
#include <REGX52.H>
#include "Timer1.h"//引腳定義
sbit Motor=P1^0;unsigned char Counter,Compare;/*** @brief 電機初始化* @param 無* @retval 無*/
void Motor_Init(void)
{Timer1_Init();
}/*** @brief 電機設置速度* @param Speed 要設置的速度,范圍0~100* @retval 無*/
void Motor_SetSpeed(unsigned char Speed)
{Compare=Speed;
}//定時器1中斷函數
void Timer1_Routine() interrupt 3
{TL1 = 0x9C; //設置定時初值TH1 = 0xFF; //設置定時初值Counter++;Counter%=100; //計數值變化范圍限制在0~99if(Counter<Compare) //計數值小于比較值{Motor=1; //輸出1}else //計數值大于比較值{Motor=0; //輸出0}
}
Motor.h
#ifndef __MOTOR_H__
#define __MOTOR_H__void Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);#endif
main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Motor.h"
#include "IR.h"unsigned char Command,Speed;void main()
{Motor_Init();IR_Init();while(1){if(IR_GetDataFlag()) //如果收到數據幀{Command=IR_GetCommand(); //獲取遙控器命令碼if(Command==IR_0){Speed=0;} //根據遙控器命令碼設置速度if(Command==IR_1){Speed=1;}if(Command==IR_2){Speed=2;}if(Command==IR_3){Speed=3;}if(Speed==0){Motor_SetSpeed(0);} //速度輸出if(Speed==1){Motor_SetSpeed(50);}if(Speed==2){Motor_SetSpeed(75);}if(Speed==3){Motor_SetSpeed(100);}}Nixie(1,Speed); //數碼管顯示速度}
}
基于上述的兩個代碼在11.0592MHz晶振基礎上改造:51單片機紅外遙控(外部中斷)代碼(11.0592MHz晶振)