STC89C52单片机学习——第38节: [17-2] 红外遥控&红外遥控电机

发布于:2025-03-31 ⋅ 阅读:(23) ⋅ 点赞:(0)

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

本文写于: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_H

void 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);	//定时计数器清0
		Timer0_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;	//置收到连发帧标志位为1
			Timer0_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));	//数据对应位清0
			IR_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));	//数据对应位置1
			IR_pData++;			//数据位置指针自增
		}
		else					//接收出错
		{
			IR_pData=0;			//数据位置指针清0
			IR_State=1;			//置状态为1
		}
		if(IR_pData>=32)		//如果接收到了32位数据
		{
			IR_pData=0;			//数据位置指针清0
			if((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_H

void 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_H

void 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);	//定时计数器清0
		Timer0_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;	//置收到连发帧标志位为1
			Timer0_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));	//数据对应位清0
			IR_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));	//数据对应位置1
			IR_pData++;			//数据位置指针自增
		}
		else					//接收出错
		{
			IR_pData=0;			//数据位置指针清0
			IR_State=1;			//置状态为1
		}
		if(IR_pData>=32)		//如果接收到了32位数据
		{
			IR_pData=0;			//数据位置指针清0
			if((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			0x4A


void 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,相当于读取按键值,然后执行相关的功能,完成本次实验。