红外遥控(借助外部中断实现红外接收)

文章目录

  • 一、介绍部分
    • 红外遥控简介
    • 硬件电路
    • 基本发送与接收
    • NEC编码
    • 遥控器对应键码
    • 外部中断
      • 外部中断介绍
      • 外部中断寄存器
      • 外部中断原理图
  • 二、实例
    • 1.显示遥控键码
      • IR.c代码实现如下
      • main.c主函数内容如下
      • Int0.c外部中断配置如下:
    • 2.使用红外遥控改写电机调速
  • 总结
      • 对于IR.c中下段函数解释



一、介绍部分

红外遥控简介

红外遥控(借助外部中断实现红外接收)_第1张图片

硬件电路

红外遥控(借助外部中断实现红外接收)_第2张图片

基本发送与接收

红外遥控(借助外部中断实现红外接收)_第3张图片

NEC编码

NEC编码:对红外高低电平等的规则

红外遥控(借助外部中断实现红外接收)_第4张图片
部分按键的编码示例

最后会多一个上升沿来结束

红外遥控(借助外部中断实现红外接收)_第5张图片

遥控器对应键码

红外遥控(借助外部中断实现红外接收)_第6张图片

外部中断

外部中断介绍

红外遥控(借助外部中断实现红外接收)_第7张图片

外部中断寄存器

红外遥控(借助外部中断实现红外接收)_第8张图片

外部中断原理图

红外遥控(借助外部中断实现红外接收)_第9张图片

二、实例

1.显示遥控键码

设计思路:定义三个状态(0、1、2),状态0为最初状态,状态1为start开始状态,状态2开始接收数据,如果是状态0则开始计时并使状态变为1,为状态1时获取计时器时间,通过时间判断得到的命令,若为start信号,则使状态变为2,获取定时器时间并让定时器重新从零开始,通过定时器的时间判断获得的数据,最后获取数据成功后进行验证。

IR.c代码实现如下

#include 
#include "Time0Init.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;			// 接收命令数据
// 初始化计时器与外部中断
void IR_Init(){
	Time0_Init();
	Int0_Init();
}
// 获取数据标志
unsigned char IR_GetDataFlag(){
	if(IR_DataFlag){
		IR_DataFlag = 0;	// 归零用于下次判断
		return 1;
	}
	return 0;
}
// 获取重复标志
unsigned char IR_GetRepeatFlag(){
	if(IR_RepeatFlag){
		IR_RepeatFlag = 0;	// 归零用于下次判断
		return 1;
	}
	return 0;
}
// 获取地址
unsigned char IR_GetAddress(){
	return IR_Address;
}
// 获取命令(键码)
unsigned char IR_GetCommand(){
	return IR_Command;
}
 

// 外部中断函数
void Int0_Routine() 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);
		//如果计时为12.442ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
		if(IR_Time > 12442 - 500 && IR_Time < 12442 + 500){
			IR_State = 2;
		}
		//如果计时为10.368ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
		else if(IR_Time > 10368 - 500 && IR_Time < 10368 + 500){
			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);
		//如果计时为1032us,则接收到了数据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++;			//数据位置指针自增
		}
		//如果计时为2074us,则接收到了数据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;
			IR_State = 1;
		}
		// 获取全部数据后进行验证
		if(IR_PData>=32){
			// P2 = 0;  // 用于测试中断内容是否可行
			IR_PData = 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;
			}
			Timer0_Run(0);
			IR_State = 0;
		}
	}
	
}

main.c主函数内容如下

#include 
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"
#include "Time0Init.h"

unsigned char Num;
unsigned char Address,Command;
void main(){
	LCD_Init();
	IR_Init();
	LCD_ShowString(1,1,"ADDR  CMD  Num");
	LCD_ShowString(2,1,"00    00   00");
	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);
			// 用音量键控制Num值加减
			if(Command == 0x15){
				Num--;
			}
			if(Command == 0x09){
				Num++;
			}
			LCD_ShowNum(2,12,Num,2);
		}
	}
}


Int0.c外部中断配置如下:

#include 

void Int0_Init(){
	IT0 = 1;		// 下降沿触发
	IE0 = 0;		// 中断标志位
	EX0 = 1;		// 打开中断
	EA = 1;			// 打开所有中断
	PX0 = 1;		// 高优先级
}

2.使用红外遥控改写电机调速

模块化电机调试主体内容

#include 
#include "Timer1.h"


sbit Motor = P1^0;

unsigned char Counter,Compare;	// 周期、比较值
// 初始化定时器
void Motor_Init(){
	Timer1_Init();
}
// 修改比较值调节速度
void Motor_ChangeSpeed(unsigned char Speed){
	Compare = Speed;
}

// 中断函数
void Timer1_Routine() interrupt 3
{	
	// 100um
	TL1 = 0xA4;				//设置定时初始值
	TH1 = 0xFF;				//设置定时初始值
	Counter++;
	// 设置周期
	if(Counter>=100){
	  Counter = 0;
	}	
	//	与比较值进行比较
	if(Counter<Compare){
		Motor = 1;			// 给电
	}else{
		Motor = 0;			// 不给电
	}
}

为与红外模块所使用的定时器区分,为电机模块重新配置一个定时器Timer1

#include 

// 定时器1初始化
void Timer1_Init(void)		//100微秒@11.0592MHz
{
	// AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0xA4;				//设置定时初始值
	TH1 = 0xFF;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
	ET1 = 1;				//打开中断
	EA = 1;					//打开中断总开关
	PT1 = 0;				//低优先级
}

主函数内容

#include 
#include "Delay.h"
#include "Motor.h"
#include "IR.h"

unsigned char Command;
void main(){
	IR_Init();
	Motor_Init();
	while(1){
		if(IR_GetDataFlag()){
			Command = IR_GetCommand();
			// 改变挡位(比较值)
			// 按键0或电源按键地址
			if(Command == 0x16 || Command == 0x45){
				Motor_ChangeSpeed(0);
			}
			// 按键1地址
			if(Command == 0x0c){
				Motor_ChangeSpeed(40);		//  太小会导致电压太小不足以驱动电机
			}
			// 按键2地址
			if(Command == 0x18){
				Motor_ChangeSpeed(60);
			}
			// 按键3地址
			if(Command == 0x5e){
				Motor_ChangeSpeed(100);
			}
		}
	}
}



总结

  • 最主要的还是时序图部分,知道NEC编码对高低电平的标准使什么。
  • 这个红外遥控发送的红外线的频率就是38KHz,所以在对红外接收判断时,只需要对时序部分分析。
    由于红外的接收很快,单片机执行速度不快,需要借助外部中断。

对于IR.c中下段函数解释

if(IR_Time > 1032 - 500 && IR_Time < 1032 + 500){
				IR_Data[IR_PData/8] &= ~(0X01 << (IR_PData%8));  // 数据对应位清0
				IR_PData++;			//数据位置指针自增
		}
		else if(IR_Time > 2074 - 500 && IR_Time < 2074 + 500){
				IR_Data[IR_PData/8] |= (0X01 << (IR_PData%8));  // 赋1
				IR_PData++;			//数据位置指针自增
		}

第一个if是接收到的是0时,IR_Data[IR_PData/8] &= ~(0X01 << (IR_PData%8)); ,与运算,这里有取反,所以就是对应位置赋值为0。IR_PData相当于数据位的指针,从0依次增加。这里把32位拆分为8位+8位+8位+8位计算,整除运算IR_PData/8,即每个下标放8位(0 ~ 7),取余运算IR_PData%8,即每8位一轮,一直是0、1、2、3、4、5、6、7,所以这种判断方法可以正确的赋值这个32位数组

你可能感兴趣的:(51单片机学习记录,51单片机,嵌入式硬件,单片机,iot,物联网)