红外控制原理
红外遥控器是通过发送一定的控制信号来实现对电器的控制,这个控制信号就是一串红外脉冲编码信号。通过发送的不同编码脉冲来表示不同的功能按键信号,电器通过红外接收系统接收到编码脉冲,并进行相应的解码执行相应的功能,这样就实现了红外遥控家用电器的目的。由此可见编码在红外遥控系统中的重要性,不过编码方式还没有一个统一的国际标准,每个生产厂家所使用的编码格式各不相同。使用的编码标准主要有RC5、NEC、SONY、REC80、SAMSWNG等,主要是欧洲和日本生产厂家所使用的编码格式。国内家用电器的生产厂家,其编码方式多数是按照上述的各种编码方式进行编码的,应用较多的是NEC型编码方式。下面介绍最常用的NEC编码标准。
NEC编码格式的特征为:
(1)使用38kHz载波频率;
(2)引导码间隔是9ms+4.5ms;
(3)使用16位客户代码;
(4)使用8位数据代码和8位取反的数据代码;
数据0和数据1图:
红外发射器:
发送协议数据‘0’ = 发射载波信号 560us + 不发射载波信号 560us
发送协议数据‘1’ = 发射载波信号 560us + 不发射载波信号 1680us
一般NEC协议是,同步码+8位地址码+8位地址反码+8位控制码+8位控制反码
以红外接受波形为例子:
同步码:9ms 低电平+ 4.5ms高电平
数据中接受数据“0”:560us 低电平 + 560us 高电平
数据中接受数据“1”:560us 低电平 + 1680us 高电平
红外发射器:
红外载波信号38KHZ,当发出的时候,红外接收器接收到后,会显示地低电平,红外接收器空闲状态为高电平,也就是说不发射PWM载波的时候为高电平。(载波实际上就是PWM波)
红外接收器:
当接收到红外载波信号时, OUT 引脚输出低电平;假如没有接收到红外载波信号时, OUT 引脚输出高电平。
美的空调的解码
首先第一建议的是去找示波器去测量红外接收器接受到美的遥控的红外码,
这是你确定你遥控的码是否真的网上博客所说的波形一致的很好的途径。
附上图片:
这个是DS100示波器,使用单次测量红外接收器的信号输出端捕获的波形。
遥控器发射信号,基本可以得到波形。得到波形后,再用单片机进行模拟PWM发射38KHZ的波形基本可以得到实现。
注意的是还是上面的,当发送波形的时候,红外接收器得到的是低电平,如果空闲即不发射波形的时候是高电平。
美的空调的解码:
红外接收器接受的波形为例子:
数据格式:引导码+48位数据+分隔码+48位数据(LAA’BB’CC’ S LAA’BB’CC’)
引导码:4.5ms低电平+4.5ms高电平
逻辑0:550us低电平+550us高电平
逻辑1:550us低电平+1.68ms高电平
分隔码:550us低电平+5.22ms高电平
(其中差几十us的电平实际上不太影响)
可以参考这个博客的一个具体解码的各个功能:
https://blog.csdn.net/weixin_44453694/article/details/118225368
美的解析源码:
解析:这里用的是STM32F103RCT6的主控MCU,定时器的频率是72Mhz(如果你的定时器频率不是的话,可以根据公式自行计算),红外发射器IO接的是PB6,用的是定时器通道1 ,对定时器AutoReloadPreload自动重装载值使能与否关系都不大,以及PWM模式以及极性不是很重要,比较值一半或者三分之一都可。
//TIM PWM部分初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
void TIM4_PWM_Init(void)
{
uint16_t psc = 19 - 1;
uint16_t arr = 100 - 1; //PWM频率=72000/(1899+1)=38Khz
TIM4_Handler.Instance=TIM4; //定时器1
TIM4_Handler.Init.Prescaler=psc; //定时器分频 1us
TIM4_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
TIM4_Handler.Init.Period=arr; //自动重装载值
TIM4_Handler.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE;
TIM4_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM4_Handler); //初始化PWM
TIM4_CH1Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM4_CH1Handler.OCPolarity=TIM_OCPOLARITY_HIGH; //输出比较极性为高
TIM4_CH1Handler.Pulse=arr/3; //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
HAL_TIM_PWM_ConfigChannel(&TIM4_Handler,&TIM4_CH1Handler,TIM_CHANNEL_1);//配置TIM4通道1
}
//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
if(htim->Instance==TIM4)
{
__HAL_RCC_TIM4_CLK_ENABLE(); //使能定时器4
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
GPIO_Initure.Pin=GPIO_PIN_6; //PB6
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure); //GPIOB初始化。
}
}
/********************************************美的**********************************************/
/*引导码*/
void Send_logic_symbol_boot()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4;
delay_us(4500);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(4500);
}
/*间隔码*/
void Send_logic_symbol_intaval()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(540);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(5220);
}
void Send_logic_symbol_end()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(540);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(20000);
}
/*逻辑0电平*/
void Send_logic_symbol_0()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(550);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(550);
}
/*逻辑1电平*/
void Send_logic_symbol_1()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(550);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(1680);
}
void Send_logic_Infrared_TX(uint8_t *F_Tra,uint8_t *S_Tra)
{
uint8_t tmp,i;
Send_logic_symbol_boot();
Send_logic_symbol_intaval();
Send_logic_symbol_boot();
F_Tra++;
S_Tra+=2;
for(i = 0;i < 8; i++)
{
tmp = (*F_Tra)|0x80;
if(0 != tmp)
{
Send_logic_symbol_0();
}else if(1 != tmp)
{
Send_logic_symbol_1();
}
}
for(i = 0;i < 8; i++)
{
tmp = (*S_Tra)|0x80;
if(0 != tmp)
{
Send_logic_symbol_1();
}else if(1 != tmp)
{
Send_logic_symbol_0();
}
}
}
void Ir_Out_Frame_OP(unsigned char y)
{
char num;
for (num=0; num<8; num++) //循环8次移位
{
if(y&0x80) //小端存储,取位高字节。
{
Send_logic_symbol_1();
}
else //否则
{
Send_logic_symbol_0();
}
y <<= 1; //高字节左移一位
}
}
//在主函数调用这个即可(发送)
void send_frame_buffer_OP(uint8_t B,uint8_t C) //美的协议
{
uint8_t a = 0xB2;
Send_logic_symbol_boot();
Ir_Out_Frame_OP(a);
Ir_Out_Frame_OP(~a);
Ir_Out_Frame_OP(B);
Ir_Out_Frame_OP(~B);
Ir_Out_Frame_OP(C);
Ir_Out_Frame_OP(~C);
Send_logic_symbol_intaval();
Send_logic_symbol_boot();
Ir_Out_Frame_OP(a);
Ir_Out_Frame_OP(~a);
Ir_Out_Frame_OP(B);
Ir_Out_Frame_OP(~B);
Ir_Out_Frame_OP(C);
Ir_Out_Frame_OP(~C);
Send_logic_symbol_end();
}
格力空调解码:
引导码(L)+35位数据码+连接码(C)+32位数据码 +结束码E
引导码“L”:9ms低电平+4.5ms高电平
逻辑“0”:640us低电平+550us高电平,
逻辑“1”:640us低电平+1680us高电平
连接码C:640us低电平+20000us高电平
结束码E: 540us低电平+20000us高电平
(格力空调的解码不同于美的空调的数据,但是协议大致是不差的。)
格力继续解码可以参考下这个博主:
https://blog.csdn.net/yannanxiu/article/details/48174649
附上源码:
说明:其中定时器部分照抄上面的美的空调部分定时器源码即可,都是一个工程。
其中格力空调的解码比较麻烦,直接读取的示波器上的参数,就没细一步解析优化了,
有兴趣的朋友可以去进一步研究一下。我的实现都只有开机和关机。
/************************************************************格力*****************************************/
/*引导码*/
void Send_logic_symbol_boot_G()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4;
delay_us(9000);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(4500);
}
/*连接码*/
void Send_logic_symbol_intaval_G()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(640);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(20000);
}
/*结束码*/
void Send_logic_symbol_end_G()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(540);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(20000);
}
/*逻辑0电平*/
void Send_logic_symbol_0_G()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(640);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(550);
}
/*逻辑1电平*/
void Send_logic_symbol_1_G()
{
HAL_TIM_PWM_Start(&TIM4_Handler,TIM_CHANNEL_1);//开启PWM通道4
delay_us(640);
HAL_TIM_PWM_Stop(&TIM4_Handler,TIM_CHANNEL_1);//停止PWM通道4
delay_us(1680);
}
void Ir_Out_Frame_OP_G(unsigned char y)
{
char num;
for (num=0; num<8; num++) //循环8次移位
{
if(y&0x80) //小端存储,取位高字节。
{
Send_logic_symbol_1_G();
}
else //否则
{
Send_logic_symbol_0_G();
}
y <<= 1; //高字节左移一位
}
}
void Ir_Fixed_Value()
{
Send_logic_symbol_0_G();
Send_logic_symbol_1_G();
Send_logic_symbol_0_G();
}
void send_frame_buffer_OP_G() //格力协议关机 ,主函数直接调用即可
{
Send_logic_symbol_boot_G();
Ir_Out_Frame_OP_G(0x86);
Ir_Out_Frame_OP_G(0X50);
Ir_Out_Frame_OP_G(0X04);
Ir_Out_Frame_OP_G(0X0A);
Ir_Fixed_Value();
Send_logic_symbol_intaval_G();
Ir_Out_Frame_OP_G(0x88);
Ir_Out_Frame_OP_G(0x04);
Ir_Out_Frame_OP_G(0x00);
Ir_Out_Frame_OP_G(0x01);
Send_logic_symbol_end_G();
}
void send_frame_buffer_OP_Start() //格力协议开机。主函数直接调用即可
{
Send_logic_symbol_boot_G();
Ir_Out_Frame_OP_G(0x96);
Ir_Out_Frame_OP_G(0X50);
Ir_Out_Frame_OP_G(0X04);
Ir_Out_Frame_OP_G(0X0A);
Ir_Fixed_Value();
Send_logic_symbol_intaval_G();
Ir_Out_Frame_OP_G(0x88);
Ir_Out_Frame_OP_G(0x04);
Ir_Out_Frame_OP_G(0x00);
Ir_Out_Frame_OP_G(0x00);
Send_logic_symbol_end_G();
}
想法碎碎念
这途中一开始去直接实现解码,自行实现接受,实际还是有点困难的,还是要根据协议去进行具体的一个实现解码。所以通过示波器接受波形得到实际数据,算是一个比较快的一个方法了。还有一个就是自行实现电路的,三极管要注意一下快关速率。还有网上买红外发射模块要注意一些坑,很多红外发射模块是没有自带三极管的都是上面两个电阻接了二极管,还要自行接个三极管,说实话比较坑。这些都是要避一下坑的,注意要上面带三极管就好。至于这个红外接收器是用的正点原子板子的红外接受器。直接测量的板子上的IO。