学习使用的开发板:STC89C52RC/LE52RC
编程软件:Keil5
烧录软件:stc-isp
开发板实图:
文章目录
- 红外遥控
- 硬件电路
- NEC协议编码
- 编程实例
- LCD1602显示Data
- 红外遥控控制扇叶转速
红外遥控
红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出
- 通信方式:单工、异步
- 红外LED波长:940nm
- 通信协议标准:NEC标准
NEC(National Electrical Code),中文翻译为国家电气规范。由美国国家安全委员会(NEC)发布的国家电气安全标准,涉及到电气系统的设计、安装、检查、维护和保养等方面的安全规则,这些规则被广泛应用于各种电气系统的设计、安装、检查、维护和操作。
也包括红外信号与数据的转换
红外发送通常应用在电视、空调遥控器
红外接收即在对应的设备上。单片机使用接收头进行接收
硬件电路
发送电路
- Q1、Q2:三极管,低电平导通
- VCC:电源正极;GND:电源负极
- 38KHz:固定输出一个38KHz的方波
- IN:输出的数据
因为自然界有很多红外光,为了避免被其影响,将输出的数据和38KHz方波结合
当 IN 输出低电平时,LED1IR 以 38KHz 频率闪着亮;反之则没有输出
下图也是发送电路,但省略了 38KHz 方波部分,但这就要求 IN 输入也要能发送一定的方波来抗干扰
接收电路
接收部分会接收到自然光和发送的红外信号,所以首先就需要一个滤波电路
而对于接收的信号,需要对滤波后的红外信号进行增强,还因为 38KHz 只是用于防干扰的,IN 输入才是真正发送的红外信号,也需要对此进行滤波
而红外接收头集成了滤波电路,OUT 端口的数据就是真正需要的红外信号
因为红外信号的时序较短,而且有很多的高低电平,不适合使用按键检测的方法。
因为异步,需要实时检测,可以使用外部中断的方式,将 OUT 端口接在外部中断,这样一旦接收到红外信号,程序就会跳转到红外中断处理,处理红外信号
中断可参看【51单片机】中断 & 定时器
中断0 和 中断1 分别接在 P3^2 和 P3^3 端口
本单片机将红外接收头的 OUT 端接在了中断0——P3^2
在红外信号的发送和接收中,规定有三种状态:
- 空闲状态:红外LED不亮,接收头输出高电平
- 发送低电平:红外LED以 38KHz 频率闪烁发光,接收头输出低电平
- 发送高电平:红外LED不亮,接收头输出高电平
NEC协议编码
NEC协议规定有三种信号:
- Start信号:用于标识即将发送 Data 数据
- Data信号:实际要传输的数据,分为 Address(用户码) 和 Command 还有对应的反码,用于校验。从低位发起
- Repeat信号:标识重复发送上一次的 Data 数据
不同信号有不同的时序结构
以下时序结构都是基于12MHz系统频率
-
Start信号:由
9ms低电平+4.5ms高电平
组成 -
Data信号:数据0 由
560us低电平+560us高电平
组成;数据1 由560us低电平+1690us高电平
组成
-
Repeat信号:由
9ms低电平+2.25ms高电平
组成
示例:
该时序由 Start信号 和 Data信号组成
其中 Address:0000 0000;Command:0100 0101
数据由低位发起
该时序由 Repeat信号组成,其时序的高电平持续时间比 Start信号短一些
红外发送没有数据发出时,红外接收为高电平
遥控器键码
首先先介绍 Data数据
Address (8位)
:该字段用于标识红外设备的地址。它的作用是确保信号只会被目标设备接收和解析,避免其它设备误接收命令。地址通常是由设备制造商分配的,在同一设备组内,每个设备的地址是唯一的。
举个例子:
- 假设有两个设备:一个电视机(设备A)和一个空调(设备B)。
- 电视机的红外遥控器发送的红外信号包含地址 0x01(即设备A的地址),并发送了一个命令。
- 空调的红外接收器只会响应它自己的地址(例如 0x02),而忽略来自地址 0x01 的信号。
- 如果设备A(电视机)接收到地址 0x01 的信号,它会执行相应的命令;如果设备B(空调)接收到信号地址是 0x01,它则会忽略该命令,因为它的地址不匹配。
Command (8位)
:命令数据,表示设备执行的操作指令。
遥控器键码如下:
编程实例
LCD1602显示Data
首先,使用外部中断0 检测红外信号的到来
中断通道如下:
- INT0:外部中断0
- TCON:配置外部中断。
- IT0配置中断由
下降沿触发/低电平
触发,置1代表下降沿触发 - IE0为中断标志位,当中断来临时,该位由硬件置1,检测到该位为1,若该中断开启,则跳转到相应处理函数
- IT0配置中断由
- IE:中断开关。
- EX0为外部中断0开关
- EA为中断总开关
- IP:中断优先级。
- PX0:置1为高优先级,置0位低优先级
下降沿触发和低电平触发的区别在于
下降沿触发
:输入信号由高电平转为低电平的瞬间触发
低电平触发
:当输入信号处于低电平时触发,不一定要求信号发生变化。只要信号持续处于低电平,触发事件就会发生。
因为红外信号的时序结构通过高低电平的时间决定
我们通过下降沿触发,获取两次下降沿间隔时间来判断属于哪个信号
如Start信号的两个下降沿之间间隔13.5ms
为此,我们还需要使用定时器,来获取间隔时间。定时器参看【51单片机】中断 & 定时器
外部中断0模块
Int0.c
#include <REGX52.H>
/**
* @brief 外部中断0初始化
* @parm 无
* @retval 无
*/
void Int0_Init()
{
IT0 = 1;//控制中断是下降沿/低电平触发,1为下降沿
IE0 = 0;//中断标志位置零
EX0 = 1;//外部中断0开关
EA = 1; //中断总开关
PX0 = 1;//优先级,1为高优先级
}
//外部中断0处理函数模版 —— 引脚为P3^2
/*
void Int0_Routine() interrupt 0
{
}
*/
定时器模块
提供定时器设初值,获取时间,停止方法
Timer.c
#include <REGX52.h>
/**
* @brief 计数器0初始化
* @parm 无
* @retval 无
*/
void Timer0_Init()
{
TMOD &= 0xF0; //高4位保持不变,低4位清零
TMOD |= 0x01; //高4位保持不变,低4位为0101
//初始化清零
TH0 = 0;
TL0 = 0;
TF0 = 0; //溢出标志位清零
TR0 = 0; //停止计时
}
/**
* @brief 计时器0设置初值
* @parm Value:计时器初值,范围:0 ~ 65535
* @retval 无
*/
void Timer0_SetValue(unsigned int Value)
{
TH0 = Value / 256;
TL0 = Value % 256;
}
/**
* @brief 计时器0设置初值
* @parm 无
* @retval 计时器此时的数值,范围:0 ~ 65535
*/
unsigned int Timer0_GetValue()
{
return (TH0 << 8) | TL0;
}
/**
* @brief 计时器0启动/停止
* @parm Flag:1代表启动,0代表停止
* @retval 无
*/
void Timer0_Run(unsigned char Flag)
{
TR0 = Flag;
}
红外接收头模块
定义三种状态
- 空闲状态:启动定时器,切换至检测状态
- 检测状态:检测Start信号 和 Repeat信号
- 若为Start信号,切换至接收状态
- 若为Repeat信号,置连发标志位,切回空闲状态
- 接收状态:接收 Address、Command 和 其反码,并进行校验
还需对外提供获取 Address 和 Command 的方法
IR.c
#include <REGX52.H>
#include "Int0.h"
#include "Timer.h"
//State=0,空闲状态
//State=1,接收状态,接收Start信号,Repeat信号
//State=2,接收数据
unsigned char IR_State, DataReady, RepeatFlag;
//红外信号的地址和命令
unsigned char Address, Command;
//存储数据的数组和读取的位数
unsigned char DataArr[4], DataBit;
/**
* @brief IR初始化
* @parm 无
* @retval 无
*/
void IR_Init()
{
Timer0_Init(); //计时器初始化
Int0_Init(); //外部中断0初始化
}
/**
* @brief 获取红外信号中的地址信息
* @parm 无
* @retval 地址信息
*/
unsigned char IR_GetAddress()
{
return Address;
}
/**
* @brief 获取红外信号中的命令信息
* @parm 无
* @retval 命令字:16进制
*/
unsigned char IR_GetCommand()
{
return Command;
}
/**
* @brief 获取红外信号中的命令信息
* @parm 无
* @retval 命令字:16进制
*/
unsigned char IR_DataReady()
{
if(DataReady){
DataReady = 0;//清零
return 1;
}
return 0;
}
/**
* @brief 获取红外信号中的命令信息
* @parm 无
* @retval 命令字:16进制
*/
unsigned char IR_GetRepeatFlag()
{
if(RepeatFlag){
RepeatFlag = 0;//清零
return 1;
}
return 0;
}
//外部中断0处理函数,持续检测信号
void Int0_Routine() interrupt 0
{
unsigned int Time;
if(IR_State == 0)//空闲状态
{
Timer0_SetValue(0); //计时器清零
Timer0_Run(1); //启动计数器
IR_State = 1; //切换置监听开始/重复信号
}
else if(IR_State == 1)//接收开始信号&重复信号
{
Time = Timer0_GetValue(); //获取两次下降沿之间的间隔
Timer0_SetValue(0); //计时器清零
//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
if(Time > 12442 - 500 && Time < 12442 + 500)
IR_State = 2;
//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
else if(Time > 10368 - 500 && Time < 10368 + 500)
{
RepeatFlag = 1; //连发标志位
Timer0_Run(0); //停止计时器
IR_State = 0; //空闲状态
}
else //接收错误信号
IR_State = 1; //重新检测
}
else if(IR_State == 2)
{
Time = Timer0_GetValue(); //获取两次下降沿之间的间隔
Timer0_SetValue(0); //计时器清零
//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
if(Time > 1032 - 500 && Time < 1032 + 500)
{
DataArr[DataBit/8] &= ~(0x01 << (DataBit % 8));//数据先发低位
DataBit++;
}
//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
else if(Time > 2074 - 500 & Time < 2074 + 500)
{
DataArr[DataBit/8] |= (0x01 << (DataBit % 8));//数据先发低位
DataBit++;
}
else
{
//数据错误,重新接受
IR_State = 1;
DataBit = 0;
}
if(DataBit >= 32){//接收数据完成
if((DataArr[0] == ~DataArr[1]) && (DataArr[2] == ~DataArr[3])){
Address = DataArr[0], Command = DataArr[2];
DataReady = 1;
}
DataBit = 0; //数据位清零
IR_State = 0; //回到空闲状态
Timer0_Run(0); //计时器停止
}
}
}
主程序
检测是否有 Data 数据或 Repeat 连发。IR模块会保存接收到的数据,Repeat 连发直接读取上一次的数据即可
按下遥控上的VOL- 和 VOL+ 可对 Num 进行加减
#include <REGX52.H>
#include "LCD1602.h"
#include "IR.h"
#include "Timer.h"
#include "Delay.h"
unsigned char Add, Com, Num;
void main()
{
LCD_Init();
IR_Init();
LCD_ShowString(1, 1, "Add Com Num");
LCD_ShowString(2, 1, "00 00 000");
while(1)
{
if(IR_DataReady() || IR_GetRepeatFlag())
{
Add = IR_GetAddress();
Com = IR_GetCommand();
LCD_ShowHexNum(2, 1, Add, 2);
LCD_ShowHexNum(2, 6, Com, 2);
if(Com == IR_VOL_MINUS)
Num--;
else if(Com == IR_VOL_ADD)
Num++;
LCD_ShowNum(2, 11, Num, 3);
}
}
}
完整项目链接:接收红外遥控
红外遥控控制扇叶转速
此处暂不讲解,项目链接:红外遥控调速步进电机
以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。