51单片机STC89C52RC——17.1 红外线遥控器

发布于:2024-07-14 ⋅ 阅读:(209) ⋅ 点赞:(0)

目的/效果

LCD1602显示红外遥控按键值

一,STC单片机模块

二,红外线遥控器

2.1 简介

人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。

光的波长和频率如下图

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

通信方式:单工,异步

红外LED波长940nm

通信协议NEC标准

通常我们说的红外遥控器是由键盘电路、红外编码电 路、电源电路和红外发射电路组成。

2.2 电路 

发送模块电路图

接收模块电路图

2.3 NEC 协议

NEC 码的位定义

一个脉冲对应 560us 的连续载波,

一个逻辑 1 传输需要 2.25ms(560us 脉冲+1680us 低电平)

一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平),为逻辑“1”的一半。 

NEC协议采用PPM(Pulse Position Modulation,脉冲位置调制)的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲 (carrier burst) 进行调制,推荐的载波占空比为 1/3至 1/4。有载波脉冲的地方,其宽度都为 560us,而载波脉冲的间隔时间是不同的。

每次信息都是按照引导码 (9ms载波脉冲+4.5ms 空闲信号)地址码、地址反码、控制码和控制反码的格式进行传输,因此,单次信息传输的时间是固定不变的

当按键被一直按下时,红外遥控器只会发送一次完整的信息,其后会每隔 110ms 发送一次重复码(连发码)。重复码的数据格式比较简单,同样是由 9ms的载波脉冲开始,紧接着是2.25ms的空闲信号,随后是560us的载波脉冲。 

红外接收头通常被厂家集成在一个元件中,成为一体化红外接收头。红外接收头内部的三极管电路具有信号反向的功能,也就是将1变为0,0变为1,即数据0是0.56ms的低电平和0.56ms的高电平,数据1是0.5ms的低电平和1.69ms的高电平,9ms是高电平变为低电平。

总结:

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

发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平

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

2.4 代码构建逻辑

2.4.1 外部中断配置

由于红外线信号可能转瞬即逝,我们利用外部中断下降沿触发 ,计时用。有关中断和定时器的详细介绍详请参考51单片机STC89C52RC——6.1 中断系统》《51单片机STC89C52RC——6.2 定时器,这里我们不再做细述。

	IT0=1;	//打开定时器中断  下降沿触发
	IE0=0;	//中断请求标识位
	EX0=1;	//允许中断进入
	EA=1;	//打开总中断
	PX0=0;	//中断优先级 设置到最高

 

/**
 * 函    数:定时器0初始化
 * 参    数:无
 * 返 回 值:无
 */
void Timer0_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0;		//设置定时初值
	TH0 = 0;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 0;		//定时器0不计数
}

2.4.2 数据接收

接收头内部已经调制解调过滤掉38KHz,所以可NEC直接解析数据。

注意:低位在前,高位在后

只需要按照时长解析外部中断引脚的电平。两个下降沿之间的时长。

当中断进入时开始计时,到下一次中断进入时停止计时。数据流程如下

红外信号中断解码


/**
 * 函    数:外部中断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
		}
	}
}

三,创建Keil项目

详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客

四,代码 

完整代码参考《https://gitee.com/oopxiajun/STC89C52

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"
 
unsigned char Address;
unsigned char Command;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"Addr  Cmd  ");
	LCD_ShowString(2,1,"00    00   ");
	
	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);	//显示遥控器命令码
		}
	}
}

五,代码编译、下载到51单片机

代码编译请参考

51单片机STC89C52RC——代码编译-CSDN博客

代码下载请参考

《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客


网站公告

今日签到

点亮在社区的每一天
去签到