工具
1.Proteus 8 仿真器
2.keil 5 编辑器
原理图
讲解
简介
红外遥控:是利用红外线进行通信的设备,由红外LED调制后的信号发出,由专用的红外接头进行解调;
通信方式:单工、异步;
红外LED波长:940nm;
通信协议标准:NEC标准;
通信双方需要完成的内容:调制与解调(防止干扰);
对于无线通信来说,通信双方中间空气中可能夹杂着许多其他的红外波(例如太阳的辐射),这样就对有用的信号产生了一定的干扰;为了抗除这种干扰,我们对发送的信号在发送前进行调制,在接收后进行解调,这样就可以很大程度上防止其他的干扰。
调制的原理就是将发送的信号电平,搭载在频率为38Khz的方波(称为载波)上,经过调制的信号经过发送设备进行发送;接收设备接收到信号后,先将38Khz信号进行解调,再按照协议进行读取;
NEC编码
发送
正常结构(单次信号)
整段信号由起始码,地址码,地址反码,命令码,命令反码,结束码组成
起始码:高电平---9ms---低电平---4.5ms
数据0:高电平---560us---低电平---560us
数据1:高电平---560us---低电平---1680us
连发结构(长按某个键发送信号)
整段信号由起始码,地址码,地址反码,命令码,命令反码,重复码,结束码组成
除正常信号外,需每隔110ms发送一段重复码(Command只在刚开始发一次)
重复码:高电平---9ms---低电平---2.25ms
注意:发送结束后必须要有结束码(构造下降沿供接收检测)
接收
注意:接收端的波形正好是相反的(根据两个下降沿之间的时间差得到数据0或数据1)
代码
发送端
#include <reg52.h>
#include "Delay.h"
#include "KEY.h"
#include "LCD1602.h"
sbit IR_IO = P1^0; //红外
sbit Button_IO = P1^7; //单独按键
int Key_Num=0; //键值
unsigned long int SendTime=0; //下发引导码 命令码 命令反码 数据码 数据反码 所消耗的时间
int TimeInitNum=-1,FirstLoopNum=1; //TimeInitNum 定时器0中断关键字 First_Loop 首次重发码关键字
void IR_Guide_Code(void); //引导码
void IR_Com_Code(int num);//命令码
void IR_Data_Code(int num);//数据码
void IR_Reverse_code(int num);//反码
void IR_Repeat_Code();//重发码
void IR_End_cod(void);//结束码
void IR_Send_0(void); //发送数据0
void IR_Send_1(void); //发送数据1
void Action(); //按下按键
/**
* @brief 定时器初始化
* @param 控制时间差
* @retval
*/
void timer0Init()
{
TMOD=0x01;//定时器0工作方式1,
TH0=0;
TL0=0;
ET0=1;//禁止中断
TR0=0;
}
/**
* @brief 开启定时器
* @retval
*/
void start_time(unsigned int us)
{
//清空定时器初始值
TH0=us?(65536-us)/256:0;
TL0=us?(65536-us)%256:0;
//开启定时器
TR0=1;
}
/**
* @brief 定时器0中断服务
* @retval
*/
void time0() interrupt 1
{
//按下的状态
if(Button_IO==0)
{
//停止定时器
TR0=0;
//第一次发重发码
if(FirstLoopNum)
{
IR_Repeat_Code();
}
if(TimeInitNum<=0)
{
TimeInitNum++;
FirstLoopNum=0;
start_time(55000); //定时器最大计时小于110ms 所以设置产生一次中断55毫秒 两次就是110ms
return;
}else{
IR_Repeat_Code();
start_time(55000);
TimeInitNum=0;
FirstLoopNum=0;
return;
}
}else{
TH0=0;
TL0=0;
TR0=0;
TimeInitNum=-1;
FirstLoopNum=1;
//结束码
IR_End_cod();
return;
}
}
/**
* @brief 中断初始化
* @param 触发方式(下降沿)
* @retval
*/
void Int0Init()
{
//设置 INT0
IT0=1;//触发方式(下降沿)
EX0=0;//INT0 的中断允许。 0关闭 //P3.2被矩阵键盘占用 键盘按下抬起时EX0=1; P3.2矩阵扫描时会存在下降沿 此时中断产生
EA=1;//打开总中断
}
/**
* @brief 外部中断 0 的中断函数
* @param
* @retval
*/
void Int0() interrupt 0
{
//执行所需的功能
//引导码
IR_Guide_Code();
//命令码
IR_Com_Code(7);
//数据码
IR_Data_Code(Key_Num);
IR_IO=1; //构造结束后的上升沿 使接收端有下降沿
delay_10us(56);
IR_IO=0; //使其空闲
//设置定时器时间
start_time(110000 - SendTime); //开始定时
EX0=0; //中断完成后 恢复允许位关闭状态
}
/**
* @brief 引导码
* @retval
*/
void IR_Guide_Code(void)
{
IR_IO=1; //拉高 通过与门 方波出入红外
delay_ms(9);
IR_IO=0;
delay_ms(4);
SendTime=13000;
}
/**
* @brief 命令码
* @retval
*/
void IR_Com_Code(int num)
{
unsigned int i=0;
for(i;i<8;i++)
{
if(num>>i&0x01)
{
IR_Send_1();
}else{
IR_Send_0();
}
}
IR_Reverse_code(num);
}
/**
* @brief 数据码
* @retval
*/
void IR_Data_Code(int num)
{
unsigned int i=0;
for(i;i<8;i++)
{
if(num>>i&0x01)
{
IR_Send_1();
}else{
IR_Send_0();
}
}
IR_Reverse_code(num);
}
/**
* @brief 反码
* @retval
*/
void IR_Reverse_code(int num)
{
unsigned int i=0;
for(i;i<8;i++)
{
if(~num>>i&0x01)
{
IR_Send_1();
}else{
IR_Send_0();
}
}
}
/**
* @brief 重发码
* @retval
*/
void IR_Repeat_Code()
{
IR_IO=1;
delay_ms(9);
IR_IO=0;
delay_ms(2);
IR_IO=1; //构造接收端的下降沿
delay_ms(3);
IR_IO=0; //释放
}
/**
* @brief 结束码
* @param 使电平跳跃一次 否则接收时最后一帧数据会丢失
* @retval
*/
void IR_End_cod(void)
{
IR_IO=1; //构造接收端的下降沿
delay_ms(2);
IR_IO=0; //释放
delay_ms(2);
IR_IO=1; //构造接收端的下降沿
delay_ms(2);
IR_IO=0; //释放
}
//数据0
void IR_Send_0(void)
{
IR_IO=1;
delay_10us(56);
IR_IO=0;
delay_10us(56);
SendTime+=1120;
}
//数据1
void IR_Send_1(void)
{
IR_IO=1;
delay_10us(56);
IR_IO=0;
delay_10us(168);
SendTime+=2240;
}
/**
* @brief 单按与长按 发送红外信号
* @retval
*/
void Action()
{
//引导码
IR_Guide_Code();
//命令码
IR_Com_Code(1);
//数据码
IR_Data_Code(Key_Num);
IR_IO=1; //构造结束后的上升沿 使接收端有下降沿
delay_10us(56);
IR_IO=0; //使其空闲
//设置定时器时间
start_time(110000 - SendTime); //开始定时
}
main(void)
{
Int0Init();
LCD_Init();
timer0Init();
IR_IO=0; //初始化时给低电平 保证与门不通
LCD_ShowNum(1,1,Key_Num,4);
while(1)
{
//矩阵单按
Key_Num=KEY();
if(Key_Num>=0)
{
LCD_ShowNum(1,1,Key_Num,4);
//进入中断
EX0=1;
}
//单键单按与长按
if(Button_IO==0)
{
//按键消抖
delay_ms(10);
if(Button_IO==0)
{
Key_Num=1; //key值设置为1;
LCD_ShowNum(1,1,Key_Num,4);
Action();
while(Button_IO==0);
}
}
}
}
接收端
#include <reg52.h>
#include "Delay.h"
#include "intrins.h"
#include "LCD1602.h"
#include "DS18B20.h"
sbit INT0_IO=P3^2;
unsigned char INT_flag=0; //中断
unsigned char getdata_flag=0; //数据接受完成标志
unsigned int time=0,pstate=0; //time用来表示 两个下降沿之间的时间差 us pstate 数据指针
unsigned char IRdata[4]; //数据缓存
unsigned int get_Time(void);
unsigned int get_address(void);
unsigned int get_data(void);
/**
* @brief 定时器初始化
* @param 用来计算时间差
* @retval
*/
void timer0Init()
{
TMOD=0x01;//定时器0工作方式1,
TH0=0;
TL0=0;
ET0=0;//禁止中断
TR0=0;
}
/**
* @brief 开启定时器
* @retval
*/
void start_time(void)
{
//清空定时器初始值
TH0=0;
TL0=0;
//开启定时器
TR0=1;
}
/**
* @brief 获取定时器计数值
* @retval
*/
unsigned int get_Time(void)
{
//关闭定时器
TR0=0;
return TH0<<8|TL0;
}
/**
* @brief 外部中断0初始化
* @param 捕捉下降沿
* @retval
*/
void Int0Init()
{
//设置 INT0
IT0=1;//触发方式(下降沿)
IE0=0;//清空标志位
EX0=1;//INT0 的中断允许。 0关闭
EA=1;//打开总中断
}
/**
* @brief 中断响应函数
* @retval
*/
void Int0() interrupt 0
{
//中断进入
if(INT_flag==0) //第一次下降沿触发
{
//开始定时
start_time();
getdata_flag=0;
INT_flag=1;
return;
}
if(INT_flag==1) //第二次下降沿触发
{
//结束定时 获取记录的时间值
time=get_Time();
//符合 引导码的两次下降沿时间差
if(13000-500<time&&time<13000+500)
{
start_time();
INT_flag=2;
return;
}
//符合 重发码
if(11000-500<time&&time<11000+500)
{
IRdata[2]++;
IRdata[3]=~IRdata[2];
getdata_flag=1;
INT_flag=0;
return;
}
//符合 结束码
if(4000-500<time&&time<4000+500)
{
//数据接受完成
getdata_flag=1;
INT_flag=0;
return;
}
}
//接受数据
if(INT_flag==2)
{
time=get_Time();
//符合数据 0 两次下降沿时间差
if(1120-500<time&&time<1120+500)
{
IRdata[pstate/8]&=~(0x01<<(pstate%8)); //数据对应位清0
pstate++;
if(pstate==32)
{
pstate=0;
INT_flag=0;
return;
}
start_time();
return;
}
//符合数据 1 两次下降沿时间差
if(2240-500<time&&time<2240+500)
{
IRdata[pstate/8]|=0x01<<pstate%8;
pstate++;
if(pstate==32)
{
pstate=0;
INT_flag=0;
return;
}
start_time();
return;
}
}
}
/**
* @brief 获取命令地址码
* @param 反馈地址
* @param
* @param
* @retval
*/
unsigned int get_address(void)
{
if(IRdata[0]==~IRdata[1])
{
return IRdata[0];
}
}
/**
* @brief 获取数据
* @param 反馈数据
* @param
* @param
* @retval
*/
unsigned int get_data(void)
{
if(IRdata[2]==~IRdata[3])
{
return IRdata[2];
}
}
void main(void)
{
//初始化定时器
timer0Init();
//中断初始化
Int0Init();
//初始化液晶
LCD_Init();
LCD_ShowString(1,1,"Old_man");
while(1)
{
if(getdata_flag)
{
LCD_ShowNum(2,1,get_address(),2);
LCD_ShowNum(2,4,get_data(),2);
getdata_flag=0;
}
}
}
实现
🚀本欧也处于学习阶段,所学所识将以笔记发布。
笔记会根据相关知识的接触而随时更新!
如果文章对你有帮助,请留下你宝贵的点赞吧👍
V:Werluo 本欧也很喜欢交朋友的哦!