红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出
通信方式:单工,异步
红外LED波长:940nm
通信协议标准:NEC标准
空闲状态:红外LED不亮,接收头输出高电平
发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平
发送高电平:红外LED不亮,接收头输出高电平
NEC编码:
STC89C52有4个外部中断
STC89C52的外部中断有两种触发方式:
- 下降沿触发
- 低电平触发
低电平触发是中断允许后,只要中断引脚的信号是低电平,就触发中断,使用时注意,如果低电平一直保持,会导致多次触发中断。(一直按着按钮动作)
下降沿触发是中断允许后,只要中断引脚的信号出现下降沿,就触发中断,使用时注意,如果信号出现抖动,会导致多次触发中断。(每按一下动作一下)
中断号:
外部中断寄存器
// 配置外部中断
void Int0_Init(){
IT0 = 1; // 配置外部中断的触发模式:1下降沿触发,0低电平触发
IE0 = 0; // 中断位清零
EX0 = 1; // 外部中断打开
EA = 1; // 总中断开启
PX0 = 1; // 优先级(高)保证能及时响应
}
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"
unsigned int IR_Time; // 计时
unsigned char IR_State; // 红外解码状态
// 出错可能是0x01默认是int型,写成0x01LL就不会错了
unsigned char IR_Data[4]; // 数据区(4个字节)
unsigned char IR_pData; // 数据区下标
unsigned char IR_DataFlag; // 标识是否接受完数据
unsigned char IR_RepeatFlag; // 接收Repeat时的标识
unsigned char IR_Address; // 数据接收完后保存地址数据
unsigned char IR_Command; // 保存命令码数据
void IR_Init(){
Timer0_Init_Counter();
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{
// 外部中断函数
switch(IR_State){
case 0:{
// 空闲状态
// P2_0 = 0;
Timer0_SetCounter(0);
Timer0_CounterRun(1); // 开始程序计时
IR_State = 1;
break;
}
case 1:{
// 接收Start/Repeat状态
IR_Time = Timer0_GetCounter(); // 获取计时结果
Timer0_SetCounter(0); // 重置计时器
if(IR_Time>12442-500 && IR_Time<12442+500){
// 红外发送Start,范围误差
//P2_1 = 0;
IR_State = 2;
}else if(IR_Time>10368-500 && IR_Time<10368+500){
// 红外发送Repeat,范围误差
IR_RepeatFlag = 1; // 标识接收到Repeat
Timer0_CounterRun(0); // 停止计时
IR_State = 0; // 回到空闲状态
}
break;
}
case 2:{
// 接收0/1状态
IR_Time = Timer0_GetCounter(); // 获取计时结果
Timer0_SetCounter(0); // 重置计时器
if(IR_Time>1032-500 && IR_Time<1032+500){
// 红外发送数据0
IR_Data[IR_pData/8] &= ~(0x01 << (IR_pData % 8));
/*
这一句有点复杂
IR_Data一共能存四个字节,分别是地址码、地址反码、命令码、命令反码
每个字节8位,要存入0就用:&=1101 1111,但是按位移会把位置0,所以使用:&=~(0010 0000)
IR_pData是从0增加到32的,(IR_pData % 8)使得按位移时不超过字节
IR_pData/8使得IR_pData每增加8次就换到下一个字节,4(个字节)*8(位)=32
*/
IR_pData++;
}else if(IR_Time>2074-500 && IR_Time<2074+500){
// 红外发送数据1
IR_Data[IR_pData/8] |= (0x01 << (IR_pData % 8));
IR_pData++;
}else{
IR_pData = 0;
IR_State = 1;
}
if(IR_pData >= 32){
// 接收了32位数据
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_CounterRun(0);
IR_State = 0;
}
break;
}
default:
break;
}
}