【51单片机】第十七讲 红外通信
2024.2.2日更新
一、使用模块
红外遥控
- 介绍:红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出。
- 红外LED波长:940nm,红外信号属于电磁波
- 通信协议标准:NEC标准
红外接收管
二、原理部分
一、红外通信的原理
- 空闲状态:红外LED不亮,接收头输出高电平
- 发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平
- 发送高电平:红外LED不亮,接收头输出高电平
总而言之,只有红外LED发送38KHZ的闪烁信号时,红外接收头OUT引脚才会对外输出低电平信号
红外发送–红外接收的本质是调制和解调,在这种条件下,可以过滤掉自然界中的“噪声信号”,只有38KHZ附近的红外信号才能被红外接收头识别转换为对应的低电平。
转换示意图:
1.红外LED发出的信号
2.红外接收头发出的信号
其中,红外接收头发出的低电平信号的宽度等价于红外LED发送的38KHz信号的时间。
二、NEC通信的原理
一、红外NEC协议
在51单片机中,使用NEC通信协议,虽然NEC标准上要求(比如说Start信号)低电平持续时间9ms,高电平持续时间4.5ms,但是stc89c52用红外通信要使用到外部中断,外部中断检测方式有两种:低电平/下降沿,我们这里使用下降沿检测方式,一个完整的Start信号,包含两个下降沿,即触发两次中断,所以只要从第一次中断开始起始,第二次中断判断时间长度,即可分辨是Start还是Repeat信号。
二、NEC编码
NEC编码包括地址码和命令码,其中地址吗和命令码都会进行一次反码校验。因此,一段完整的NEC数据码一共有4个字节,32位bit
遥控器键码
这里,我们定义地址码为Address,命令码为Command, 完整的数据码为DATA,如果我们按下0,那么发送的Address=0x00; Command =0x16; 那么DATA一共包含0X00,0XFF,0X16,~0X16四个字节的先后数据。
三、外部中断的原理
- STC89C52有四个外部中断
- STC89C52外部中断有两种方式:下降沿/低电平触发
- 中断号
外部中断配置的寄存器:
三、代码部分
#重要调试过程
11.0592MHZ和12MHZ的定时器计数速率不一致,硬件电路或者程序运行速度相关问题,可能导致实际的(比如Start命令)宽度并不是很接近于标准的通信协议,需要我们自己去调试,这里我在调试的过程中遇到了一些问题,以下为相关代码和解决方法
外部中断
#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"
#include "LCD1602.h"
#include "INT0.h"
unsigned char Num;
void main()
{
LCD_Init();
Int0_Init();
while(1)
{
LCD_ShowNum(1,1,Num,2);
}
}
void Int0_Rountine(void) interrupt 0
{
Num++;
}
#include <REGX52.H>
/**
* @brief
* @param
* @retval
*/
void Int0_Init(void) //@11.0592MHz
{
IT0=1;
IE0=0;
EX0=1;
EA=1;
PX0=1;
}
/*
void Int0_Rountine(void) interrupt 0;
{
}
*/
Timer0计时模块编写,用作跟红外通信相关的编码时序计时
#include <REGX52.H>
void Timer0_Init(void) //@11.0592MHz
{
TMOD &= 0xF0;
TMOD |= 0x01;
TL0 = 0x00;
TH0 = 0x00;
TF0 = 0;
TR0 = 0;
ET0=1;
EA=1;
PT0=0;
}
void Time0_SetCounter(unsigned int Number)
{
TL0=0x0F&Number;
TH0=0xF0&Number;
}
unsigned int Time0_GetCounter()
{
unsigned int number;
number=TL0+TH0;
return number;
}
void Time0_Run(unsigned char Flag)
{
if(Flag)
{
TR0=1;
}
else
{
TR0=0;
}
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1 //中断子程序
{
static unsigned int T0Count;//静态局部变量,保证退出函数之后不销毁
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
T0Count++;//每次进入中断子程序,秒控制器自加一
if(T0Count>=1000)//每1s执行对P2_0的操作
{
T0Count=0;
P2_0 =~P2_0;
}
}
*/
红外通信模块
测试结果:红外可以接收,但是无法发送起始命令(按下按键后,LED不亮)
问题分析 :计时器计数的时长不在我们 > if(IR_Time>=13500-500 && IR_Time<=13500+500) < 语句的判断范围内
解决方案:修改if语句的判断范围,使之能检测到Start命令,并用LCD1602显示定时器发送Start命令实际的定时器计数数值IR_Time。
#include <REGX52.H>
#include "Timer0.h"
#include "INT0.h"
unsigned char IR_ADDRESS;
unsigned char IR_State;
unsigned char IR_Time;
void IR_Init()//红外中断初始化
{
Timer0_Init();
Int0_Init();
}
void Int0_Rountine(void) interrupt 0 //外部中断函数,