51单片机从零开始入门教程(红外遥控篇)

参考教程:[17-1] 红外遥控(外部中断)_哔哩哔哩_bilibili

1、红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出。

(1)通信方式:单工,异步。

(2)红外LED波长:940nm。

(3)通信协议标准:NEC标准。

2、硬件电路:

(1)注意,在开发板原理图中独立按键K3也与P32连接,而P32同时也用于接收外部的信号。

(2)在下面会讲到外部中断,由于K3与P32连接,这就导致按下按键K3后就会产生一个外部中断(K3断开时P32处于高电平,按下按键K3后P32处于低电平,相当于给了一个下降沿,如果按住K3不放,就是一直处于低电平)。

3、基本发送与接收:

(1)空闲状态:红外LED不亮,接收头输出高电平。

(2)发送低电平:红外LED以38KHz频率闪烁发光(规定特定频率的目的是抗干扰,否则容易被自然界的红外光影响,这个部分是在底层就封装好了的,下面提到的发送高电平其实落实到底层都是指不断闪烁的光,只是没在时序图上表现出来而已),接收头输出低电平

(3)发送高电平:红外LED不亮,接收头输出高电平。

4、NEC编码:

(1)发送Data时低位在前,高位在后(Data共32位)。

(2)地址码是为了区分不同(品牌)的遥控器

(3)设置反码是为了验证Data在传输过程中有没有出现错误。

(4)发送Data时,前560us低电平,后560us高电平,表示发送数据0;前560us低电平,后1690us高电平,表示发送数据1

(5)如果按住按键不放,会不断发送Repeat部分,直至松开按键。

(6)Start是起始标志

5、遥控器键码:

6、外部中断:STC89C52有4个外部中断,外部中断有下降沿触发和低电平触发两种触发方式。(红外遥控必须通过外部中断处理,且由于信号可能转瞬即逝,外部中断的优先级必须很高)

7、外部中断寄存器:

将IT0配置为1,那么外部中断0采取的就是下降沿触发(否则是低电平触发,低电平触发的意思是只要一直收到低电平信号,那么中断就会反复执行,而选择下降沿触发的话中断只会执行一次),IT1同理。

8、红外遥控的基本实现:

本例需要液晶屏模块的代码,剩余缺失代码会在下面给出,将它们添加到项目中,然后进行编译,根据主函数中的注释调试(温馨提示:IR.c文件中有大量注释用来帮助理解NEC协议)。

①Int0.h文件:

#ifndef __Int0_H__
#define __Int0_H__

void Int0_Init();

#endif

②Int0.c文件:

#include <REGX52.H>


void Int0_Init()
{
	IT0 = 1;  //下降沿触发
	IE0 = 0;
	EX0 = 1;  //打开中断
	EA = 1;   //打开中断
	PX0 = 1;  //外部中断给予高优先级(见原理图)
}

/*外部中断函数模板
void Int0_Routine() interrupt 0
{
	
}
*/

③Timer0.h文件:

#ifndef __Timer0_H__
#define __Timer0_H__

void Timer0_Init();
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter();
void Timer0_Run(unsigned char Flag);

#endif

④Timer0.c文件:

#include <REGX52.H>

/**
  * @brief  定时器0初始化,100us@11.0592MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init()
{
	
	TMOD = 0x01;
	
	TF0 = 0;  //中断标志位初始化为0,计数产生溢出时该位被置为1,向CPU发出中断请求,CPU响应中断后该位被置为0
	TR0 = 0;  //定时器T0不计时
	
	TL0 = 0x00;		//设置定时初值
	TH0 = 0x00;		//设置定时初值
	
}

void Timer0_SetCounter(unsigned int Value)
{
	TH0 = Value/256;		//设置定时初值
	TL0 = Value%256;		//设置定时初值
}

unsigned int Timer0_GetCounter()
{
	return (TH0 << 8)| TL0;  //返回计时值
}

void Timer0_Run(unsigned char Flag)
{
	TR0 = Flag;  //启动计时
}

/* 定时器0函数中断模板
void Timer0_Routine()  interrupt 1  //CPU响应中断后执行的函数
{
	static unsigned int T0Count = 0;  //定义计数器
	
	T0Count++;
	
	if(T0Count >= 10000)  //每10000个中断信号(1秒)执行一次下面的代码段
	{
		T0Count = 0;
	}
	
	//每次中断结束都要重置计数单元
	TL0 = 0xA4;		//设置定时初值
	TH0 = 0xFF;		//设置定时初值
}
*/

⑤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			0x4A

void 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 "Int0.h"

unsigned char IR_State;
unsigned int IR_Time;

unsigned char IR_Data[4];  //Data有32位,使用4个sizeof为1的数组存储
unsigned char IR_pData;    //用于辅助访问数组的变量

unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;

void IR_Init()  //外部中断和定时器0初始化
{
	Timer0_Init();
	Int0_Init();
}

unsigned char IR_GetDataFlag()  //通过调用该函数可以获知有没有接收到外部传来的命令
{
	if(IR_DataFlag)
	{
		IR_DataFlag = 0;  //有命令,将标志位置为0,因为同一个命令只接收一次,只响应一次
		return 1;
	}
	return 0;
}

unsigned char IR_GetRepeatFlag()  //通过调用该函数可以实现按住按键不松手一直执行某种行为
{
	if(IR_RepeatFlag)
	{
		IR_RepeatFlag = 0;  
		//如果有Repeat信号,标志位先置为0,如果接下来还有Repeat信号,标志位会被重新置为1
		return 1;
	}
	return 0;
}

unsigned char IR_GetAddress()
{
	return IR_Address;  //返回接收的地址码
}

unsigned char IR_GetCommand()
{
	return IR_Command;  //返回接收的命令
}

void Int0_Routine() interrupt 0
{
	//注意,晶振不同,信号总时长的相关参数也会不同,下面的参数是按照11.0592MHz给出的(12MHz的参数在注释中给出)
	//外部信号出现一次下降沿,执行一次外部中断函数
	if(IR_State == 0)  //从Start部分(或Repeat信号)第一个下降沿开始,IR_State初值为0,执行下面三句代码
	{
		Timer0_SetCounter(0);  //定时器0初始计时数据为0
		Timer0_Run(1);         //启动定时器0计时
		IR_State = 1;          //下一个应该接收起始信号Start或者Repeat信号
	}
	else if(IR_State == 1)  //Start部分(或Repeat信号)第二个下降沿又一次触发,第二次执行中断函数
	{
		IR_Time = Timer0_GetCounter();  //获取定时器0计时时间(Start部分前后两个下降沿的相隔时长)
		Timer0_SetCounter(0);   //定时器0计时清空,直接开始下一个计时
		
		//分析刚刚的Start部分(或者Repeat部分)
		if(IR_Time > 12442-500 && IR_Time < 12442+500)  //9ms+4.5ms=13500us(起始信号总时长,±500是误差)
		{
			IR_State = 2;  //起始部分结束,进入Data部分
		}
		else if(IR_Time > 10368-500 && IR_Time < 10368+500)  //11250us是Repeat信号总时长
		{
			IR_RepeatFlag = 1;  //重复标志置为1
			Timer0_Run(0);      //停止计时
			IR_State = 0;       //回到状态0,状态0必定走向状态1,状态1判断下一个信号是Start还是Repeat
		}
		else
		{
			IR_State = 1;  //出错,重新回到初始状态接收新的起始信号
		}
	}
	else if(IR_State == 2)  //接收Data部分
	{
		IR_Time = Timer0_GetCounter();
		//如果是第一个位,那么这个位信号的第一个下降沿就是Start部分结束的下降沿
		//位信号的第二个下降沿触发,IR_Time记录两个下降沿的时间间隔
		Timer0_SetCounter(0);    //计时清零,马上开始记录下两个下降沿的时间间隔
		
		//分析位信号的两个下降沿时间间隔,判断是0还是1(如果无误则继续接收下一个位信号)
		if(IR_Time > 1032-500 && IR_Time < 1032+500)  //560us+560us是数据0信号的时长
		{
			IR_Data[IR_pData/8] &= ~(0x01 << (IR_pData%8));  //将Data等分成4个1字节存进数组
			IR_pData++;
		}
		else if(IR_Time > 2074-500 && IR_Time < 2074+500)  //560us+1690us是数据1信号的时长
		{
			IR_Data[IR_pData/8] |= (0x01 << (IR_pData%8));  //将Data等分成4个1字节存进数组
			IR_pData++;
		}
		else
		{
			IR_State = 1;  //出错,重新回到初始状态接收新信号
			IR_pData = 0;  //防止影响下一个Data的接收
		}
		if(IR_pData>=32)  //成功接收到Data的32位数据,进行验证
		{
			IR_pData = 0;
			if((IR_Data[0] == ~IR_Data[1])&&(IR_Data[2] == ~IR_Data[3]))  //地址码=地址反码&&命令码=命令反码
			{
				//如果验证成功,将地址码和命令码分别存储在IR_Address和IR_Command中
				IR_Address = IR_Data[0];
                IR_Command = IR_Data[2];
				IR_DataFlag = 1;  //成功接收到数据的标志
			}
			Timer0_Run(0);  //停止计时
			IR_State = 0;  
			//验证成功,接着判断下一个信号是Repeat还是Start;验证失败,判断下一个信号是否是Start
			//无论如何都要回到状态0
		}
	}
}

⑦main.c文件:

#include <REGX52.H>
#include "LCD1602.h"
#include "IR.h"

unsigned char Address,Command,Num;

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
		}
	}
}

9、红外遥控电机调速:

(1)由于定时器0用于计时,其计数单元经常被重置,因此定时器0已经不适合用于提供中断了,故引入定时器1。

(2)本项目需要的文件如下图所示,需要重写的会在下面给出。

(3)补充缺失的代码文件,然后进行编译,按照主函数的注释进行调试即可。

①Timer1.h文件:

#ifndef __TIMER1_H__
#define __TIMER1_H__

void Timer1_Init(void);

#endif

②Timer1.c文件:

#include <REGX52.H>

/**
  * @brief  定时器1初始化,100us@11.0592MHz
  * @param  无
  * @retval 无
  */
void Timer1_Init(void)
{
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0xA4;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1=1;
	EA=1;
	PT1=0;
}

/*定时器中断函数模板
void Timer1_Routine() interrupt 3
{
	static unsigned int T1Count;
	TL1 = 0xA4;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	T1Count++;
	if(T1Count>=1000)
	{
		T1Count=0;
		
	}
}
*/

③Motor.h文件:

#ifndef __MOTOR_H__
#define __MOTOR_H__

void Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);

#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 = 0xA4;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		Motor=1;		//输出1
	}
	else				//计数值大于比较值
	{
		Motor=0;		//输出0
	}
}

⑤main.c文件:

#include <REGX52.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);						//数码管显示当前速度
	}
}
  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: C51单片机红外遥控小车是一种使用C51单片机控制的具备红外遥控功能的小型车辆。该小车通过接收红外信号来实现远程控制功能,并利用C51单片机进行信号的解码和控制电机的转动。 C51单片机是一种经典的8位单片机,具有丰富的外设资源和强大的运算能力。通过编程,我们可以将红外接收头连接至单片机的外部中断口,使其能够实时接收外界通过红外遥控器发送的信号。 当红外接收头接收到红外信号时,C51单片机会利用软件对信号进行解码,提取出对应的指令信息。通过编写相应的控制程序,我们可以实现小车的各项功能,如前进、后退、左转、右转等。经过编码和解码处理,我们可以将指令信息转换为控制信号,通过控制电机及相关驱动电路实现小车的动作。 此外,为了使红外遥控小车能够实现更加复杂的功能,我们可以通过编写更加复杂的控制程序,赋予其避障、跟踪等功能。通过利用C51单片机的强大运算能力,我们可以根据小车周围的传感器数据,实现对小车运动的智能控制。 总之,C51单片机红外遥控小车是一种灵活、易于控制的小型车辆,通过C51单片机和红外接收头的配合,实现了对小车的远程控制功能。 ### 回答2: C51单片机红外遥控小车是一种由C51单片机控制的红外遥控车辆。该小车通过接收红外信号进行控制,可以实现远程控制和操控。 C51单片机是一种经典的单片机,具有高性能,强大的处理能力和丰富的外设接口,非常适合用于控制小车等智能设备的开发。 红外遥控技术是一种基于红外光信号的无线通信技术,通过发送和接收红外光信号来实现设备之间的数据交互。在红外遥控小车中,我们可以通过使用红外发射器和红外接收器,发送和接收红外信号来实现对小车的控制。 当我们按下红外遥控器上的按键时,红外发射器会向小车发送特定的红外信号。然后,红外接收器会接收到信号,并将其发送给C51单片机进行解码。C51单片机会根据接收到的信号进行相应的处理,然后控制小车的电机启动、停止、转向等动作。 在小车的开发过程中,我们需要对C51单片机进行编程,并设计电路板连接红外接收器、红外发射器、电机等元件。通过合理的代码设计和电路连接,我们可以实现红外遥控小车的功能。 总结来说,C51单片机红外遥控小车是一种通过C51单片机控制的小车,通过发送和接收红外信号来实现远程控制。它利用C51单片机的高性能和丰富的接口,实现了对小车的精确控制,是一种非常有趣和有用的智能设备。 ### 回答3: C51单片机红外遥控小车是一种使用C51单片机作为控制核心的小车,它能够通过红外遥控方式实现控制操作。 红外遥控技术是一种将电子信号转化为红外线信号进行无线传输的技术。通过红外遥控器向C51单片机发送指令,C51单片机接收到指令后进行解析,并执行相应的操作。因此,C51单片机红外遥控小车能够实现根据不同的遥控指令进行前进、后退、左转、右转等运动。 该小车的设计原理是利用C51单片机的输入输出口与一些电动元件进行连接。红外遥控器通过发送特定的红外信号,C51单片机接收到信号后进行解码,得到相应的指令。然后,控制电机、车轮等电动元件进行相应的运动。 在设计过程中,需要首先明确红外遥控器的编码方式,即确定不同按键对应的红外信号。然后,利用C51单片机提供的相应的编码/解码功能,对接收到的红外信号进行解码处理。根据解码结果,控制电机实现小车的运动。 此外,为了实现更复杂的功能,可以在C51单片机中添加其他传感器,例如距离传感器、红外避障传感器等。通过这些传感器的反馈信号,可以实现自动避障、自动停止等功能。 总的来说,C51单片机红外遥控小车是一种将C51单片机红外遥控技术相结合的智能小车。通过遥控器发送指令,实现小车运动的控制。此外,也可以通过添加其他传感器,为小车增加更多的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值