基于STC89C52实现红外遥控功能

文章详细介绍了如何实现红外遥控系统,包括红外发射器按照38KHz频率发送NEC编码的红外信号,红外接收器HS0038接收并解调信号,单片机处理解调后的数字信号。通过分析信号时间来识别不同内容,并通过中断和定时器处理信号。最后,文章展示了如何将红外遥控应用于电机速度控制,通过不同的遥控指令调整PWM信号来改变电机转速。
摘要由CSDN通过智能技术生成

实现一个红外遥控系统的基本要素包括红外发射器、红外接收器、解码器和单片机。
其中,红外发射器负责发送红外信号,红外接收器用来接收红外信号,解码器将红外信号解码成数字信号,而单片机则是用来处理数字信号并控制外设的。

本文章主要是实现红外遥控功能与红外遥控控制电机调速。

红外发射信号

首先,红外发射器发送的红外信号,是以38KHz的频率发送,用于区分外界自然持续发出的红外光。根据发送的数据内容不同,进行区分按下的按钮。
发送的红外信号遵循NEC红外协议编码

NEC红外编码协议具体来说,一个NEC遥控信号包含了以下内容:

  1.  帧头: 9ms的空闲时间,后面跟着4.5ms的载波信号,表示信号开始。
  2. 地址码: 由8位组成,表示接收设备的地址。可以用来区分不同的设备。
  3. 地址码的反码: 地址码的每一位都取反,用于校验
  4. 命令码: 由8位组成,表示具体的操作命令。比如开关机、音量调节等。
  5. 命令码的反码: 命令码的每一位都取反,用于校验。
  6. 结束位: 一个1.5ms的高电平,表示信号传输结束。

红外信号接收与解调

发送的红外信号被HS0038接收与解调。HS0038是一种常用的红外接收器芯片,它具有以下特点:

  • 1. 工作频率为38kHz,在NEC红外协议中得到广泛应用。
  • 2. 强抗干扰能力,能够滤除大部分的杂波干扰。
  • 3. 高灵敏度,能够接收到较远距离的红外信号。
  • 4. 具有内置解调器,能够自动将解调后的数字信号输出到引脚上,方便后续处理。
  • 5. 与常用的微控制器(如8051、AVR、PIC等)兼容。

 被HS0038解调后的数据被放在P32线上,发送给单片机,所以我们要通过编写程序分析在P32线上的数据,进行对遥控信号的处理,进而转为其他程序的驱动指令。

因为解调后的数据遵循NEC红外协议,所以我们只需要对信号的时间长短进行分析,则可以区分不同的信号内容。对信号时间长短进行分析,就需要使用到定时器与外部中断,定时器是记录中断前的信号时间长短,外部中断主要是在发完了一段信号后进行中断
(外部中断触发方式分为下降沿触发与低电平触发,在此处设为下降沿触发,因为在发完一段信号时,都伴随着一段下降沿,则可以在该下降沿进行中断,进而对中断前的一段信号发送的总时间进行记时,达到分析不同信号内容的目的)。
通过定时器与外部中断,就可以实现对不同段信号内容的读取。

代码实现(STC89C52)

所以我们先要分别对定时器与外部中断进行设置,以下是代码实现

定时器对一段时间的记录

#include <REGX52.H>

//定时器初始化
void Timer0Init(void)		
{
	TMOD &= 0xF0;		
	TMOD |= 0x01;		
	TL0 = 0;		
	TH0 = 0;		
	TF0 = 0;
	TR0 = 0;
}

//设置定时器初值函数
void Timer0_SetCounter(unsigned int 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>

//外部中断0初始化
void Int0_Init(void)
{
	IT0 = 1;  //1为下降沿驱动,0为低电平驱动
	IE0 = 0;
	EX0 = 1;
	EA = 1;
	PX0 = 1;
}

//中断函数
/*
void Int0_Routine(void)  interrupt 0
{
	
}
*/


接下来就是在中断后对前段的信号内容进行处理,将定时器与外部中断集成在一个模块中

#include <REGX52.H>
#include "Int0.h"
#include "Timer0.h"

unsigned int IR_Time;					//计时
unsigned char IR_State;					//状态位

unsigned char IR_Data[4];				//零时存放32位Data数据

unsigned char IR_pData;					//指向32位数据的哪个位置

unsigned char IR_DataFlag;			//数据标志位
unsigned char IR_RepeatFlag; 		//结束标志位
unsigned char IR_Address;  			//存放数据
unsigned char IR_Command;  			//存放指令

//初始化
void IR_Init(void)
{
	Timer0Init();
	Int0_Init();
}

//用于判断是否取得数据
unsigned char IR_GetDataFlag(void)
{
	if(IR_DataFlag)
	{
		IR_DataFlag = 0;
		return 1;
	}
	return 0;
}

//判断是否结束
unsigned char IR_GetRepeat(void)
{
	if(IR_RepeatFlag)
	{
		IR_RepeatFlag = 0;
		return 1;
	}
	return 0;
}

//返回取得的数据
unsigned char IR_GetAddress(void)
{
	return IR_Address;
}

//返回取得的指令
unsigned char IR_GetCommand(void)
{
	return IR_Command;
}

void Int0_Routine(void) interrupt 0
{
	if(IR_State == 0)  //空闲时开始计数
	{
		Timer0_SetCounter(0);
		Timer0_Run(1);
		IR_State = 1;
	}
	else if(IR_State == 1)  //计数完成后开始分析
	{
		
		IR_Time = Timer0_GetCounter();
		Timer0_SetCounter(0);
		if(IR_Time > 13500 - 600 && IR_Time < 13500 + 600) //检测到Start
		{
			//转为对Data记录处理
			IR_State = 2;
		}
		else if(IR_Time > 11250 - 600 && IR_Time < 11250 + 600)  //检测到Repeat
		{
			IR_RepeatFlag = 1;
			Timer0_Run(0);
			IR_State = 0;
		}
		else
		{
			IR_State = 1;
		}
	}
	else if(IR_State == 2)
	{
		IR_Time = Timer0_GetCounter();
		Timer0_SetCounter(0);
		
		if(IR_Time > 1120 - 600 && IR_Time < 1120 + 600)  //检测到0
		{
			//IR_Data[4]将原本的32位数据用四个八位存放,
                        所以要精确的找到对应数组元素的对应位数进行改变
			//因为IR_pData最大只能到达32,所以IR_pData/8的范围只有0~4,,IR_pData%8的范围为0~7
			IR_Data[IR_pData/8] &= ~(0x01 << (IR_pData%8));  //对应位清零
			IR_pData++;
		}
		else if(IR_Time > 2250 - 600 && IR_Time < 2250 + 600)  //检测到1
		{
			IR_Data[IR_pData/8] |= (0x01 << (IR_pData%8));  //对应位清零
			IR_pData++;
		}
		else
		{
			IR_pData = 0;
			IR_State = 1;
		}
		if(IR_pData >= 32)
		{
			IR_pData = 0;
			if((IR_Data[0] == ~IR_Data[1]) && (IR_Data[2] == ~IR_Data[3]))  
                //如果满足NEC红外协议中的Data格式
			{
				IR_Address = IR_Data[0];
				IR_Command = IR_Data[2];
				IR_DataFlag = 1;
			}
			Timer0_Run(0);
			IR_State = 0;
		}
	}
}

 综上就实现了最简单的红外遥控功能


下面是通过红外遥控控制电机速度(前置知识PWM)

//定时器1初始化
#include <REGX52.H>


void Timer1Init(void)		
{
	TMOD &= 0x0F;		
	TMOD |= 0x10;		
	TL1 = 0xF4;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	TF1 = 0;		
	TR1 = 1;		
	
	ET1 = 1;
	EA = 1;
	PT1 = 0;
}

//

//main.c

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


unsigned char Counter,Compare;
unsigned char Command;



void main()
{
	P1 = 0;
		
	IR_Init();
	Timer1Init();
	
	while(1)
	{
		if(IR_GetDataFlag())
		{
            //通过接收不同的指令去执行不同的速度
			Command = IR_GetCommand();
			
			if(Command == 0x16)
			{
				Compare = 0;
			}
			if(Command == 0x0C)
			{
				Compare = 50;
			}
			if(Command == 0x18)
			{
				Compare = 75;
			}
			if(Command == 0x5E)
			{
				Compare = 100;
			}
		}
		
	}
}


void Timer1_Rountine(void) interrupt 3
{
	TL1 = 0xF4;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值

	Counter++;
	Counter %= 100;
	if(Counter >= Compare)
	{
		P1_0 = 0;
	}
	if(Counter < Compare)
	{
		P1_0 = 1;
	}

}

遥控器键码

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
#include "remote.h" #include "delay.h" #include "usart.h" ////////////////////////////////////////////////////////////////////////////////// //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK战舰STM32开发板 //红外遥控解码驱动 代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //修改日期:2012/9/12 //版本:V1.0 //版权所有,盗版必究。 //Copyright(C) 广州市星翼电子科技有限公司 2009-2019 //All rights reserved ////////////////////////////////////////////////////////////////////////////////// u8 g_IR_RecFlag = 0; //红外接收到标志 //红外遥控初始化 //设置IO以及定时器4的输入捕获 void Remote_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //TIM4 时钟使能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PB9 输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //上拉输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO;_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_9); //初始化GPIOB.9 TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出 TIM_TimeBaseStructure.TIM_Prescaler =(35-1); //预分频器,1M的计数频率,1us加1. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM;_TimeBaseStructure); //根据指定的参数初始化TIMx TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; // 选择输入端 IC4映射到TI4上 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //上升沿捕获..改为下降沿捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频 TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波 TIM_ICInit(TIM4, &TIM;_ICInitStructure);//初始化定时器输入捕获通道 TIM_Cmd(TIM4,ENABLE ); //使能定时器4 NVIC_InitStructure.NV
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值