寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難,但我還是想去做!
本文寫于:2025.03.30
51單片機學習——第38節: [17-2] 紅外遙控&紅外遙控電機
- 前言
- 開發板說明
- 引用
- 解答和科普
- 一、
- 二、紅外遙控接受
- 三、紅外遙控直流電機
- 總結
前言
? ?本次筆記是用來記錄我的學習過程,同時把我需要的困難和思考記下來,有助于我的學習,同時也作為一種習慣,可以督促我學習,是一個激勵自己的過程,讓我們開始51單片機的學習之路。
? ?歡迎大家給我提意見,能給我的嵌入式之旅提供方向和路線,現在作為小白,我就先學習51單片機了,就跟著B站上的江協科技開始學習了.
? ?在這里會記錄下江協科技51單片機開發板的配套視頻教程所作的實驗和學習筆記內容,因為我之前有一個開發板,我大概率會用我的板子模仿著來做.讓我們一起加油!
? ?另外為了增強我的學習效果:每次筆記把我不知道或者問題在后面提出來,再下一篇開頭作為解答!
開發板說明
? ?本人采用的是慧凈的開發板,因為這個板子是我N年前就買的板子,索性就拿來用了。不再另外購買視頻中的普中開發板了。
? ?原理圖如下
視頻中的都用這個開發板來實現,如果有資源就利用起來。
仔細看了看:開發板的晶振為:11.0592Mhz;12Mhz晶振是用來給CH340G芯片外置晶振;
下圖是實物圖
引用
51單片機入門教程-2020版 程序全程純手打 從零開始入門
還參考了下圖中的書籍:
手把手教你學51單片機(C語言版)
STC89C52手冊
解答和科普
一、
1.1測試中斷的執行
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "I2C.h"
#include "PCF8591.h"
#include "Init.h"unsigned char Num;
void main()
{LCD_Init();LCD_ShowString(1,1,"A");Nixie_OFF();DianZhengGuan();IT0=1; //下降沿觸發IE0=0; //中斷標志位EX0=1; //打開中斷EA=1; //打開所有中斷PX0=1; //優先級while(1){LCD_ShowNum(2,1,Num,3);}
}void Into_Routine(void) interrupt 0
{
Num++;
}
實驗現象:
外部中斷0低電平觸發
外部中斷0下降沿觸發
1.2紅外解碼
如何把東西解碼出來,包括讀取時間值進行計時,還有中斷。
狀態機:定義一個變量表示當前的狀態,0表示空閑狀態,然后當他收到下降沿之后,把定時器打開,開始計時,再把狀態轉為1狀態,尋找Start或者Repeat頭部的這一個信號,1狀態再來個下降沿的話,繼續判斷狀態,判斷中間的時間,如果是起始信號,就把狀態轉化為2狀態,2狀態定義為開始解碼1012總共32,如果是重發,就把重發標志位置1,還有變量緩存區用來存取,還會置為正確是否。
1.Timer0
#include <REGX52.H>void Timer0_Init(void) //1毫秒@11.0592MHz
{// TMOD不能復制,如果同時使用定時器1和0,它會覆蓋淹沒,TMOD &= 0xF0; //設置定時器模式TMOD |= 0x01; //設置定時器模式TL0 = 0; //設置定時初值TH0 = 0; //設置定時初值TF0 = 0; //清除TF0標志TR0 = 0; //定時器0不計時
}void Timer0_SetCounter(unsigned char Value)
{TH0=Value/256;TL0=Value%256;}unsigned int Timer0_Getcounter(void)
{return (TH0<<8)|TL0;}void Timer0_Run(unsigned char Flag)
{TR0=Flag;}```c
#ifndef __TIMER0_H
#define __TIMER0_Hvoid Timer0_Init(void) ;
void Timer0_SetCounter(unsigned char Value);
unsigned int Timer0_Getcounter(void);
void Timer0_Run(unsigned char Flag);
#endif
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "I2C.h"
#include "PCF8591.h"
#include "Init.h"
#include "INT0.h"
#include "Timer0.h"
#include "IR.h"unsigned int Time;
void main()
{LCD_Init();LCD_ShowString(1,1,"A");Nixie_OFF();DianZhengGuan();Timer0_Init();Timer0_SetCounter(0);Timer0_Run(1);Delay(1);Time=Timer0_Getcounter();while(1){LCD_ShowNum(2,1,Time,3);}
}
實驗現象:
測試遙控的進入
#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_RepearFlag;
unsigned char IR_Address; //Data又要接受數據又要輸出數據 變搜變輸出,可能會出錯
unsigned char IR_Command;void IR_Init(void)
{Timer0_Init();Int0_Init();
}void Into_Routine(void) interrupt 0
{if(IR_State==0){P1=0x00;Timer0_SetCounter(0);Timer0_Run(1);IR_State=1;}else if(IR_State==1){IR_Time=Timer0_Getcounter();Timer0_SetCounter(0); if(IR_Time>12442-500&& IR_Time<12442+500) //13500{IR_State=2;}else if(IR_Time>10368-500&& IR_Time<10368+500){IR_RepearFlag=1;Timer0_Run(0); IR_State=0; }else{IR_State=1;}}}
遙控能進入的話,會產生中斷,進入狀態0,然后會讓LED燈全亮。
實驗現象:
測試遙控燈
二、紅外遙控接受
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "I2C.h"
#include "PCF8591.h"
#include "Init.h"
#include "INT0.h"
#include "Timer0.h"
#include "IR.h"unsigned int Time;
unsigned char Address;
unsigned char Command;
void main()
{LCD_Init();LCD_ShowString(1,1,"LED");Nixie_OFF();DianZhengGuan();IR_Init();while(1){if(IR_GetDataFlag()){Address=IR_GetAddress();Command=IR_GetCommand();LCD_ShowHexNum(2,1,Address,2);LCD_ShowHexNum(2,6,Command,2);}if(Command==0x16){P1=0x00;}}
}
#include <REGX52.H>void Timer0_Init(void) //1毫秒@11.0592MHz
{// TMOD不能復制,如果同時使用定時器1和0,它會覆蓋淹沒,TMOD &= 0xF0; //設置定時器模式TMOD |= 0x01; //設置定時器模式TL0 = 0; //設置定時初值TH0 = 0; //設置定時初值TF0 = 0; //清除TF0標志TR0 = 0; //定時器0不計時
}void Timer0_SetCounter(unsigned char Value)
{TH0=Value/256;TL0=Value%256;}unsigned int Timer0_GetCounter(void)
{return (TH0<<8)|TL0;}void Timer0_Run(unsigned char Flag)
{TR0=Flag;}
#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>12442-500 && IR_Time<12442+500){IR_State=2; //置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>10368-500 && IR_Time<10368+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>1032-500 && IR_Time<1032+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //數據對應位清0IR_pData++; //數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2074-500 && IR_Time<2074+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}}
}
實驗現象:
紅外遙控顯示鍵碼值并控制LED
2.3遙控鍵碼值
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "I2C.h"
#include "PCF8591.h"
#include "Init.h"
#include "INT0.h"
#include "Timer0.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");Nixie_OFF();DianZhengGuan();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_X){Num--;}if(Command==IR_VOL_Z){Num++;}LCD_ShowNum(2,12,Num,3);}}
}
實驗現象:
紅外遙控鍵碼值控制值增加和減少
三、紅外遙控直流電機
1.Motor.c
#include <REGX52.H>
#include "Timer1.h"sbit Moter= P1^7;
unsigned char Counter,Compare;void Motor_Init(void)
{Timer1_Init();}void Motor_SetSpeed(unsigned char Speed)
{Compare=Speed;
}void Timer1_Routine() interrupt 3 //跳轉到這里,觸發中斷
{TL1 = 0xA4; //設置定時初值TH1 = 0xFF; //設置定時初值Counter++; //定時器定時遞增Counter%=100; //周期清零if(Counter<Compare) //比較{Moter=1; //給1轉}else{Moter=0;}
}
#ifndef __MOTOR_H
#define __MOTOR_Hvoid Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);#endif
2.Timer1.c
#include <REGX52.H>void Timer1_Init(void) //100us @11.0592MHz
{// TMOD不能復制,如果同時使用定時器1和0,它會覆蓋淹沒,TMOD &= 0x0F; //設置定時器模式TMOD |= 0x10; //設置定時器模式TL0 = 0xA4; //設置定時初值TH1 = 0xFF; //設置定時初值TF1 = 0; //清除TF0標志TR1 = 1; //定時器0開始計時ET1=1;EA=1;PT1=0;
}/*
void Timer1_Routine() interrupt 3 //跳轉到這里,觸發中斷
{static unsigned int T0Count; //Tcount為計數值 static只有本函數使用TL1 = 0x66; //重新設置定時初值TH1 = 0xFC; //重新設置定時初值T0Count++;if(T0Count>=1000){T0Count=0;P1_0=~P1_0;}}*/
#ifndef __TIMER1_H
#define __TIMER1_Hvoid Timer1_Init(void) ;
#endif
3.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>12442-500 && IR_Time<12442+500){IR_State=2; //置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>10368-500 && IR_Time<10368+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>1032-500 && IR_Time<1032+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //數據對應位清0IR_pData++; //數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2074-500 && IR_Time<2074+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}}
}
#ifndef __IR_H__
#define __IR_H__#define IR_CH_X 0x45
#define IR_CH 0x46
#define IR_CH_Z 0x47
#define IR_PREV 0x44
#define IR_NEXT 0x40
#define IR_PAUSE 0x43
#define IR_VOL_X 0x07
#define IR_VOL_Z 0x15
#define IR_EQ 0xD9
#define IR_0 0x16
#define IR_100+ 0x19
#define IR_200+ 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
4.main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Timer1.h"
#include "Nixie.h"
#include "Init.h"
#include "IR.h"
#include "Motor.h"unsigned char KeyNum,Speed,Command;void main()
{DianZhengGuan();Motor_Init();IR_Init();while(1){
// KeyNum=Key();
// if(KeyNum==1)
// {
// Speed++;
// Speed%=4;
//
// 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); //數碼管靜態顯示會鎖存
// }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); //數碼管靜態顯示會鎖存} }
}
5.INT0
#include <REGX52.H>void Int0_Init(void)
{IT0=1; //下降沿觸發IE0=0; //中斷標志位EX0=1; //打開中斷EA=1; //打開所有中斷PX0=1; //優先級}
#ifndef __INT0_H__
#define __INT0_H__
void Int0_Init(void);
#endif
實驗現象:
紅外控制直流電機
總結
本節課主要學了紅外遙控了,對定時器寫入和寫出的,還有解碼的功能,最重要的是這個解碼過程,設置了3個狀態,每個狀態執行分別執行不同的功能,識別出開始的信號和設置很多個標志位,通過操作進行定時器的開始,進而解碼出開始和連發,進而分辨出是發送的是信號0還是信號1,最后讀取值進入數組中,拆分為4個unsigned char類型,分別存入最后變量地址和數據中,并根據數據Command進行有關的操作。
2、最后進行了實驗:紅外遙控直流電機,根據讀到的Command,相當于讀取按鍵值,然后執行相關的功能,完成本次實驗。