基本介绍:
红外接收器是一种用于接收红外线信号的装置,常见于各种电子设备中,如电视遥控器、空调遥控器等。它能够接收来自发射器发送的红外信号,并将其转换成电信号,以便设备进行相应的操作。红外接收器通常包含红外光电二极管和相应的电路,用于检测和解码接收到的红外信号。
在电子设备中,红外接收器扮演着接收用户输入指令的角色。当用户按下遥控器上的按钮时,遥控器会发送相应的红外信号,被设备中的红外接收器接收后,设备会根据接收到的信号执行相应的操作,比如调整音量、切换频道等。
红外接收器的工作原理是利用红外光电二极管的特性,当红外线照射到光电二极管上时,会产生电流,该电流随着红外线强度的变化而变化,接收电路会将这些变化转换成数字信号或模拟信号,以供设备使用。
红外遥控介绍:
红外线简介:
红外遥控的原理:
红外遥控的原理基于红外线的特性以及红外遥控器的工作原理。
1. 红外线特性:红外线是一种电磁辐射,波长长于可见光,人眼无法看到。它在电磁谱中介于可见光和微波之间。红外线能够穿透大气中的很多物质,并且可以在空间中传播,因此被广泛应用于通信和控制领域。
2. 红外遥控器的工作原理:红外遥控器内置了一个发射器,通常是红外发光二极管。当用户按下遥控器上的按钮时,遥控器会向外发射一系列的红外脉冲信号,每个按钮对应一个特定的红外码。这些红外码是事先编程好的,用来代表不同的功能或指令。
3. 红外接收器的工作原理:被控制的设备内部有一个红外接收器,通常也是红外光电二极管。当接收器接收到红外遥控器发送的信号时,它会将红外光转换成电信号,并传递给设备的控制电路。控制电路根据接收到的红外码来识别用户的操作,并执行相应的功能,比如调整音量、切换频道等。
4. 编码解码:红外遥控器和设备之间的通信需要一种编码解码的机制,以确保正确的指令被传递和执行。遥控器发送的红外信号是经过编码的,接收器需要将其解码成可识别的指令,从而执行相应的操作。
总的来说,红外遥控的原理就是利用红外线作为通信媒介,通过遥控器发送编码的红外信号,设备内的红外接收器接收并解码这些信号,以实现对设备功能的控制。
红外发射装置:
二进制脉冲码的形式有多种,其中最为常用的是 NEC Protocol 的 PWM 码(脉冲宽度调制)和 Philips RC-5 Protocol 的 PPM 码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。如果要开发红外接收设备,一定要知道红外遥控器的编码方式和载波频率,我们才可以选取一体化红外接收头和制定解码方案。我们的红外遥控器使用的是 NEC 协议,其特征如下:
红外接收设备:
软件设置:
51单片机:
/********************************************************************
***********
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断 0 配置
* 输 入 : 无
* 输 出 : 无
*********************************************************************
**********/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断 0 允许
EA=1; //打开总中断
IRED=1; //初始化端口
}
红外解码函数:
void ired() interrupt 0 //外部中断 0 服务函数
{
u8 ired_high_time=0;
u16 time_cnt=0;
u8 i=0,j=0;
if(IRED==0)
{
time_cnt=1000;
while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
{
delay_10us(1);//延时约 10us
time_cnt--;
if(time_cnt==0)return;
}
if(IRED)//引导信号 9ms 低电平已过,进入 4.5ms 高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号 4.5ms 高电平结束,若超过 5ms 强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
for(i=0;i<4;i++)//循环 4 次,读取 4 个字节数据
{
for(j=0;j<8;j++)//循环 8 次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据 1 或 0 前面的 0.56ms结束,若超过0.6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt=20;
while(IRED)//等待数据 1 或 0 后面的高电平结束,若超过 2ms 强制退出
{
delay_10us(10);//约 0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于 0.8ms,数据则为1,否则为 0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
}
进入中断函数,表示以来下降沿,然后判断管脚是否为低电平,如果为低电平则首先判断引导信号,根据前面 NEC 协议可知,引导信号有 9ms 的低电平和4.5ms 的高电平,因此通过 time_cnt 赋值 1000,然后在 while 循环内判断,time_cnt 每递减一次约 10us,1000 次则为 10ms,在解码时,这个时间要适当放 宽一点范围,因为不同传感器性能会有差异,所以此处以 10ms 的低电平为界限, 如果超过 10ms 则强制退出,防止系统死机。判断完引导信号的低电平,接着判断高电平,实现方法一样。当引导信号判断完成后进入地址码、地址反码、控制码及控制反码共4个字节的数据判断,也就是数据 0 和 1 的判断,实现方法也是 和前面判断引导信号一样,这里使用到了嵌套循环,外层循环次数是 4,表示读取 4 个字节,内层循环次数是 8,表示读取每个字节的 8 位。注意,红外遥控解码数据是从低位开始,最后是高位。最后将读取的 4 个字节数据存储在全局变量数组 gired_data 中,外部可直接使用这四个字节。
代码主要逻辑:
1、IRED其实就是红外接收端的引脚:
#define IRED P3^2
2、这里进去一次中断,如果说一切正常,因为一次按键的最长数据 = 引导码 + 32个数据"1" = 9+4.5+2.25*32 = 85.5ms,所以中断处理了 85.5ms才出来。
3、其实就是通过阻塞轮询模式,不断调用cpu,来判断红外接收端电平,来实现解码。
4、关键点就是超时判断和通过高电平时间来确认接收的是逻辑1还是逻辑0。
主函数:
void main()
{
u8 ired_buf[3];
ired_init();//红外初始化
while(1)
{
ired_buf[0]=gsmg_code[gired_data[2]/16];//将控制码高 4 位转换为数码
管段码
ired_buf[1]=gsmg_code[gired_data[2]%16];//将控制码低 4 位转换为数码
管段码
ired_buf[2]=0X76;//显示 H 的段码
smg_display(ired_buf,6);
}
}
实验现象:
STM32:
总体思路其实都是一样,都是利用外部中断,然后判断高低电平的保持时间,来对红外进行解码。
这里红外接收端接的是PA10。
CubeMX配置该引脚为双边沿触发,完成红外接收初始化。可见比51简单不少。
解析中断回调函数里记录的时间序列,得到的device和key放入全局数组buf
/**********************************************************************
* 函数名称: IRReceiver_IRQTimes_Parse
* 功能描述: 解析中断回调函数里记录的时间序列,得到的device和key放入全局数组buf
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 0 - 成功, (-1) - 失败
* 修改日期: 版本号 修改人 修改内容
* -----------------------------------------------
* 2024/04/21 刻刻帝 创建
***********************************************************************/
static int IRReceiver_IRQTimes_Parse(void)
{
uint64_t time;
int i;
int m, n;
unsigned char datas[4];
unsigned char data = 0;
int bits = 0;
int byte = 0;
/* 1. 判断前导码 : 9ms的低脉冲, 4.5ms高脉冲 */
time = g_IRReceiverIRQ_Timers[1] - g_IRReceiverIRQ_Timers[0];
if (time < 8000000 || time > 10000000)
{
return -1;
}
time = g_IRReceiverIRQ_Timers[2] - g_IRReceiverIRQ_Timers[1];
if (time < 3500000 || time > 55000000)
{
return -1;
}
/* 2. 解析数据 */
for (i = 0; i < 32; i++)
{
m = 3 + i*2;
n = m+1;
time = g_IRReceiverIRQ_Timers[n] - g_IRReceiverIRQ_Timers[m];
data <<= 1;
bits++;
if (time > 1000000)
{
/* 得到了数据1 */
data |= 1;
}
if (bits == 8)
{
datas[byte] = data;
byte++;
data = 0;
bits = 0;
}
}
/* 判断数据正误 */
datas[1] = ~datas[1];
datas[3] = ~datas[3];
if ((datas[0] != datas[1]) || (datas[2] != datas[3]))
{
g_IRReceiverIRQ_Cnt = 0;
return -1;
}
buf[0]=datas[0];
buf[1]=datas[2];
return 0;
}
解析中断回调函数里记录的时间序列,判断是否重复码:
/**********************************************************************
* 函数名称: isRepeatedKey
* 功能描述: 解析中断回调函数里记录的时间序列,判断是否重复码
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 1 - 是, (0) - 不是
* 修改日期: 版本号 修改人 修改内容
* -----------------------------------------------
* 2024/04/21 刻刻帝 创建
***********************************************************************/
static int isRepeatedKey(void)
{
uint64_t time;
/* 1. 判断重复码 : 9ms的低脉冲, 2.25ms高脉冲 */
time = g_IRReceiverIRQ_Timers[1] - g_IRReceiverIRQ_Timers[0];
if (time < 8000000 || time > 10000000)
{
return 0;
}
time = g_IRReceiverIRQ_Timers[2] - g_IRReceiverIRQ_Timers[1];
if (time < 2000000 || time > 2500000)
{
return 0;
}
return 1;
}
红外接收器的中断回调函数,记录中断时刻:
/**********************************************************************
* 函数名称: IRReceiver_IRQ_Callback
* 功能描述: 红外接收器的中断回调函数,记录中断时刻
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 无
* 修改日期: 版本号 修改人 修改内容
* -----------------------------------------------
* 2024/04/21 刻刻帝 创建
***********************************************************************/
void IRReceiver_IRQ_Callback(void)
{
uint64_t time;
static uint64_t pre_time = 0;
/* 1. 记录中断发生的时刻 */
time = system_get_ns();
/* 一次按键的最长数据 = 引导码 + 32个数据"1" = 9+4.5+2.25*32 = 85.5ms
* 如果当前中断的时刻, 举例上次中断的时刻超过这个时间, 以前的数据就抛弃
*/
if (time - pre_time > 100000000)
{
g_IRReceiverIRQ_Cnt = 0;
}
pre_time = time;
g_IRReceiverIRQ_Timers[g_IRReceiverIRQ_Cnt] = time;
/* 2. 累计中断次数 */
g_IRReceiverIRQ_Cnt++;
/* 3. 次数达标后, 解析数据, 放入buffer */
if (g_IRReceiverIRQ_Cnt == 4)
{
/* 是否重复码 */
if (isRepeatedKey())
{
/* device: 0, val: 0, 表示重复码 */
buf[0]=0;
buf[1]=0;
g_IRReceiverIRQ_Cnt = 0;
}
}
if (g_IRReceiverIRQ_Cnt == 68)
{
IRReceiver_IRQTimes_Parse();
g_IRReceiverIRQ_Cnt = 0;
}
}
代码解析;
1、这里采用双边沿触发中断,然后用time = system_get_ns();记录当前时刻。
2、如果是重复码的话,会经历四个边沿改变,我们通过直接判断四个边沿里面的时间,就可以知道是不是重复码。
3、如果不是重复码,我们一共会经历68个边沿改变,我们会把每次边沿改变的时间记录下来,之后会去调用 IRReceiver_IRQTimes_Parse(),来对按键进行解析。
4、前面提到的,不论是逻辑0还是逻辑1,接收端接收到的一定是低电平,所以代码不必去思考下一个bit开始是高电平还是低电平。
与51的区别:
1、这个代码没有采用阻塞cpu的方式,更能节省cpu资源。
2、不是一个中断就直接解决接收一个键值,是不断的中断,最后才判断键值。
最后,我们讲键值放在了buf[0]和buf[1]里面,之后该如何处理,取决于你该实现什么功能。
总结:
无论是51还是32,处理红外接收的思路大体是一样的,通过外部中断,然后判断高低电平的持续时间,之后对他进行解析,得到发射器发送的键值。
如果觉得我写的还不错,可以给我点赞加关注,谢谢啦,这是对我最大的鼓励。