建網站用什么發(fā)票創(chuàng)建軟件平臺該怎么做
文章目錄
- 紅外遙控的介紹
- 硬件電路
- NEC編碼
- 外部中斷
- 紅外遙控實例代碼
紅外遙控的介紹
紅外遙控是一種無線、非接觸控制技術,通過使用紅外線來傳送控制信號。它具有抗干擾能力強、信息傳輸可靠、功耗低、成本低、易實現等顯著優(yōu)點,因此被廣泛應用于各種電子設備和家用電器,也越來越多地應用于計算機和手機系統中。
硬件電路
紅外遙控系統一般由發(fā)射和接收兩個部分組成。發(fā)射部分包括紅外發(fā)光二極管,它是一種特殊的發(fā)光二極管,能夠發(fā)射調制后的紅外光波。接收部分通常采用紅外接收模塊,用于接收來自遙控器發(fā)射的紅外信號。當遙控器處于學習狀態(tài)時,接收模塊會接收外來紅外信號,并將其轉換成電信號。經過檢波、整形和放大等處理后,這些電信號會被傳送到中央處理單元(CPU)進行解碼和執(zhí)行相應的控制動作。
發(fā)射端:
IN是輸入信號端口,輸入的信號會通過38kHz的頻率在三極管中放大,到發(fā)光二極管中,二極管發(fā)出閃爍著的特制紅外光線;
在現實生活中有很多種類型的紅外線,為了讓接收方只接收這一類型的紅外線,就在電路中加入了頻率,讓這個信號能有一定頻率的閃爍;
當我們沒有輸入時,默認為高電平,而當我們輸入信號時,就會在低電平發(fā)出有頻率波動的信號;
接收端:
這里將接收的發(fā)光二極管內置在了電路中,電路也很容易,電源、接地、以及信號輸出;
在這里我們會讓OUT端口接上外部中斷,當產生下降沿就進入中斷;
NEC編碼
NEC編碼是一種紅外遙控協議,常用于遙控器與設備之間的通信。它是一種常用的編碼格式;將遙控發(fā)送過來的信號進行一定形式的編碼,轉換為對應的信息;
NEC編碼的數據格式通常由以下幾部分組成:
-
引導碼(Leader Code):持續(xù)低電平(通常為9ms)和持續(xù)高電平(通常為4.5ms),用于標識開始傳輸數據。
-
系統碼(Customer Code):8位二進制代碼,用于識別具體的設備或品牌。
-
數據碼(Data Code):8位二進制代碼,表示具體的指令或按鍵信息。在傳輸數據時一般持續(xù)低電平(560us)和持續(xù)高電平(560us)表示傳輸‘0’,持續(xù)低電平(560us)和持續(xù)高電平(1690us)表示傳輸‘1’;
-
取反碼(Inverted Data Code):與數據碼和系統碼相反的二進制代碼,用于檢驗數據的正確性。
-
結束碼(Stop Bit):持續(xù)高電平(通常為0.56ms),表示數據傳輸結束。在這里,我們沒有引用到;
-
重復碼(Repeat Code):數據傳輸之后,若持續(xù)低電平(9ms)和持續(xù)高電平(2.25ms),那么將會重復這個命令信息;
NEC編碼使用38kHz的載波頻率進行傳輸,通過在紅外光線的調制和解調實現遙控功能。在發(fā)射時,數據按照引導碼、系統碼、數據碼和取反碼的順序發(fā)送;在接收端,通過解碼器將紅外信號還原為對應的系統碼和數據碼,從而實現遙控器與設備的交互。
外部中斷
51單片機的外部中斷是指通過外部信號觸發(fā)單片機進行中斷處理的一種機制。單片機通過外部引腳接收到特定的信號后,會立即暫停當前的任務,轉而執(zhí)行與該中斷相關的程序。
在51單片機中,有兩個外部中斷源,分別為外部中斷0(INT0)和外部中斷1(INT1)。這兩個中斷源分別對應單片機的引腳P3.2和P3.3。
外部中斷可以通過兩種觸發(fā)方式來進行中斷處理:電平觸發(fā)和邊沿觸發(fā)。電平觸發(fā)是指當外部信號維持在某個電平上時觸發(fā)中斷,可以是低電平觸發(fā)或高電平觸發(fā);邊沿觸發(fā)是指當外部信號的跳變沿(上升沿或下降沿)出現時觸發(fā)中斷。
中斷號:
此次操作相關寄存器以及原理:
使用外部中斷前,需要進行相應的初始化操作。
初始化:
void Interrupt0_Init()
{IT0=1;//外部中斷0選擇邊沿觸發(fā)IE0=1;//外部中斷0請求標志EX0=1;//外部中斷0請求允許位EA=1;//總中斷允許PX0=1;//中斷優(yōu)先級控制
}
紅外遙控實例代碼
接下來,我們要實現的是在屏幕上顯示地址,命令碼,數字增減,通過遙控的控制,讓命令碼隨之改變,數字達到增減;
我們將通過定時器和外部中斷0來進行操控,寫出NEC編碼;
利用不同的狀態(tài)表示到達不同的編碼階段;
Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0Init();
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter();
void Timer0_Run(unsigned char Flag);
#endif
Timer0.c
#include <REGX52.H>/*** @brief 定時器0初始化* @param 無* @reval 無*/
void Timer0Init(void) //1毫秒@11.0592MHz
{TMOD &= 0xF0; //設置定時器模式TMOD |=0x01;TL0 = 0; //設置定時初值TH0 = 0; //設置定時初值TF0 = 0; //清除TF0標志TR0 = 0; //定時器0不計時}//定時器0設置計數器值,范圍0-65535
void Timer0_SetCounter(unsigned int Value)
{TH0=Value/256;TL0=Value%256;
}//定時器0獲取計數器值
unsigned int Timer0_GetCounter()
{return (TH0<<8)|TL0;
}//定時器0啟動停止控制
void Timer0_Run(unsigned char Flag)
{TR0=Flag;
}
將定時器初始化為0,讓定時器實現計時器的功能;
Interrupt.h
#ifndef __INTERRUPT_H__
#define __INTERRUPT_H__void Interrupt0_Init();#endif
Interrupt.c
#include <REGX52.H>//外部中斷0初始化
void Interrupt0_Init()
{IT0=1;IE0=1;EX0=1;EA=1;PX0=1;
}
按鍵對應命令碼:
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();
unsigned char IR_GetDataFlag();
unsigned char IR_GetRepeatFlag();
unsigned char IR_GetAddress();
unsigned char IR_GetCommand();#endif
IR.c
#include <REGX52.H>
#include"Timer0.h"
#include"Interrupt.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;//紅外遙控初始化
void IR_Init()
{Timer0Init();Interrupt0_Init();
}//紅外遙控獲取收到數據幀標志位
unsigned char IR_GetDataFlag()
{if(IR_DataFlag){IR_DataFlag=0;return 1;}return 0;
}//紅外遙控獲取收到連發(fā)標志位
unsigned char IR_GetRepeatFlag()
{if(IR_DataFlag){IR_RepeatFlag=0;return 1;}return 0;
}//紅外遙控獲取收到的地址數據
unsigned char IR_GetAddress()
{return IR_Address;
}//紅外遙控獲取收到的命令數
unsigned char IR_GetCommand()
{return IR_Command;
}//外部中斷0函數
void Int0_Routine() interrupt 0
{if(IR_State==0) //狀態(tài)0,空閑狀態(tài){Timer0_SetCounter(0); //計數器清0Timer0_Run(1); //定時器啟動IR_State=1; //變?yōu)闋顟B(tài)1}else if(IR_State==1) //狀態(tài)1,等待Start信號或Repeat信號{IR_Time=Timer0_GetCounter(); //獲取開始的時間或重復的時間Timer0_SetCounter(0); //計時器清0if(IR_Time>12442-500&&IR_Time<12442+500)//表示是開始信號,進入下一階段{IR_State=2;}else if(IR_Time>10368-500&&IR_Time<10368+500)//是重復信號,回到最初的起點{IR_RepeatFlag=1; //連發(fā)幀標志位置1Timer0_Run(0); //定時器停止IR_State=0; //狀態(tài)置0}else{IR_State=1;}}else if(IR_State==2) //狀態(tài)2,接收數據{IR_Time=Timer0_GetCounter();//獲取接收數據的時間段Timer0_SetCounter(0);if(IR_Time>1032-500&&IR_Time<1032+500)//數據接到為0{IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));IR_pData++;}else if(IR_Time>2074-500&&IR_Time<2074+500)//數據接收到為1{IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));//除8是將32位下標分為4個元素,模8將32位下標從0~7有序循環(huán)IR_pData++;}else //接收錯誤{IR_pData=0;IR_State=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; //狀態(tài)置0}}
}
LCD1602.c
#include <REGX52.H>//引腳配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0//函數定義:
/*** @brief LCD1602延時函數,12MHz調用可延時1ms* @param 無* @retval 無*/
void LCD_Delay()
{unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);
}/*** @brief LCD1602寫命令* @param Command 要寫入的命令* @retval 無*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief LCD1602寫數據* @param Data 要寫入的數據* @retval 無*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief LCD1602設置光標位置* @param Line 行位置,范圍:1~2* @param Column 列位置,范圍:1~16* @retval 無*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}/*** @brief LCD1602初始化函數* @param 無* @retval 無*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位數據接口,兩行顯示,5*7點陣LCD_WriteCommand(0x0c);//顯示開,光標關,閃爍關LCD_WriteCommand(0x06);//數據讀寫操作后,光標自動加一,畫面不動LCD_WriteCommand(0x01);//光標復位,清屏
}/*** @brief 在LCD1602指定位置上顯示一個字符* @param Line 行位置,范圍:1~2* @param Column 列位置,范圍:1~16* @param Char 要顯示的字符* @retval 無*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}/*** @brief 在LCD1602指定位置開始顯示所給字符串* @param Line 起始行位置,范圍:1~2* @param Column 起始列位置,范圍:1~16* @param String 要顯示的字符串* @retval 無*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}/*** @brief 返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief 在LCD1602指定位置開始顯示所給數字* @param Line 起始行位置,范圍:1~2* @param Column 起始列位置,范圍:1~16* @param Number 要顯示的數字,范圍:0~65535* @param Length 要顯示數字的長度,范圍:1~5* @retval 無*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief 在LCD1602指定位置開始以有符號十進制顯示所給數字* @param Line 起始行位置,范圍:1~2* @param Column 起始列位置,范圍:1~16* @param Number 要顯示的數字,范圍:-32768~32767* @param Length 要顯示數字的長度,范圍:1~5* @retval 無*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief 在LCD1602指定位置開始以十六進制顯示所給數字* @param Line 起始行位置,范圍:1~2* @param Column 起始列位置,范圍:1~16* @param Number 要顯示的數字,范圍:0~0xFFFF* @param Length 要顯示數字的長度,范圍:1~4* @retval 無*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief 在LCD1602指定位置開始以二進制顯示所給數字* @param Line 起始行位置,范圍:1~2* @param Column 起始列位置,范圍:1~16* @param Number 要顯示的數字,范圍:0~1111 1111 1111 1111* @param Length 要顯示數字的長度,范圍:1~16* @retval 無*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__//用戶調用函數:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);#endif
main.c
#include <REGX52.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(); //NEC編碼初始化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){Num--;}if(Command==IR_VOL_ADD){Num++;}LCD_ShowNum(2,12,Num,3);}}
}
通過判斷標志位是否為1來表示是否有遙控信息傳輸