红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则, 就会隔墙控制或干扰邻居的家用电器), 所以同类产品的红外线遥控器, 可以有相同的遥控频率或编码, 而不会出现遥控信号“ 串门” 的情况。 这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方面。 由于红外线为不可见光, 因此对环境影响很小, 再由红外光波动波长远小于无线电波的波长, 所以红外线遥控不会影响其他家用电器, 也不会影响临近的无线电设备。
红外遥控的编码方式目前广泛使用的是: PWM(脉冲宽度调制)的 NEC 协议和 Philips PPM(脉冲位置调制)的 RC-5 协议的。 目前大部分遥控器使用的是 NEC 协议, 其特征如下:
8 位地址和 8 位指令长度;
地址和命令 2 次传输( 确保可靠性)
PWM 脉冲宽度调制, 以发射红外载波的占空比代表“ 0” 和“ 1” ;
载波频率为 38Khz;
位时间为 1.125ms 或 2.25ms;
一个脉冲对应 560us 的连续载波, 一个逻辑 1 传输需要 2.25ms( 560us 低+1680us 高) , 一个逻辑 0 的传输需要 1.125ms( 560us 低+560us 高) 。
NEC 遥控指令的数据格式为: 同步码头、 地址码、 地址反码、 控制码、 控制反码。
同步码由一个 9ms的低电平和一个 4.5ms 的高电平组成;
地址码、地址反码、控制码、控制反码均是 8 位数据格式。 按照低位在前, 高位在后的顺序发送。
连发码:由 97.94ms 的高电平组成、9ms 的低电平、 2.5ms 的高电平、 0.56ms 的低电平;
下面是一个红外信号的波形图
从上图中可以看到, 其地址码为 0, 控制码为 96。 可以看到在 100ms 之后, 我们还收到了几个脉冲, 这是NEC 码规定的连发码(由 9ms 的低电平、 2.5ms 的高电平、 0.56ms 的低电平、 97.94ms 的高电平组成), 如果在一帧数据发送完毕之后, 按键仍然没有放开, 则发射重复码, 即连发码, 可以通过统计连发码的次数来标记按键按下的长短/次数。
接收部分电路连接:
当红外接收二极管收到连续的38KHz的红外信号后,将输出连续的低电平,否则输出高电平。即:
发送方: 38KHzPWM波信号驱动红外发射二极管 输出低电平,个、发射二极管不工作
接收方: 收到低电平 收到高电平
综上,可以使用STM32的定时器的输入捕获功能,实现对红外信号的解码,使用STM32的定时器的PWM输出实现对红外信号的编码功能。
红外解码:
//遥控器接收状态 //bit7:收到了引导码标志 //bit6:得到了一个按键的所有信息 //bit5:保留 //bit4:标记上升沿是否已经被捕获 //bit[3:0]:溢出计时器 u8 remoteState=0; u16 captureva lue;//下降沿时计数器的值 u32 remoteCode=0;//红外接收到的数据 u8 remoteCnt=0;//按键按下的次数 void TIM2_IRQHandler(void) { u16 state; if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET) { if(remoteState&0x80)//上次有数据被接收到了 { remoteState&=~0X10;//取消上升沿已经被捕获标记 if((remoteState&0X0F)==0X00)remoteState|=1<<6;//标记已经完成一次按键的键值信息采集 if((remoteState&0X0F)<14)remoteState++; else { remoteState&=~(1<<7);//清空引导标识 remoteState&=0XF0;//清空计数器 } } } if(TIM_GetITStatus(TIM2,TIM_IT_CC2)!=RESET) { if(RDATA)//上升沿捕获 { TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);//CC1P=1 设置为下降沿捕获 TIM_SetCounter(TIM2,0); //清空定时器值 remoteState|=0X10;//标记上升沿已经被捕获 } else //下降沿捕获 { captureva lue=TIM_GetCapture2(TIM2);//读取CCR2也可以清CC1IF标志位 TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC4P=0设置为上升沿捕获 if(remoteState&0X10)//完成一次高电平捕获 { if(remoteState&0X80)//接收到了引导码 { if(captureva lue>300&&captureva lue<800)//560为标准值,560us { remoteCode<<=1;//左移一位. remoteCode|=0;//接收到0 } else if(captureva lue>1400&&captureva lue<1800)//1680为标准值,1680us { remoteCode<<=1;//左移一位. remoteCode|=1;//接收到1 } else if(captureva lue>2200&&captureva lue<2600)//得到按键键值增加的信息 2500为标准值2.5ms { remoteCnt++; //按键次数增加1次 remoteState&=0XF0;//清空计时器 } } else if(captureva lue>4200&&captureva lue<4700)//4500为标准值4.5ms { remoteState|=1<<7;//标记成功接收到了引导码 remoteCnt=0;//清除按键次数计数器 } } remoteState&=~(1<<4); } } TIM_ClearFlag(TIM2,TIM_IT_Update|TIM_IT_CC2); } |
u8 Remote_Scan(void) { u8 state=0; u8 t1,t2; if(remoteState&(1<<6))//得到一个按键的所有信息了 { printf("收到的数据是:0x%X\r\n",remoteCode); t1=remoteCode>>24;//得到地址码 t2=(remoteCode>>16)&0xff;//得到地址反码 if((t1==(u8)~t2)&&t1==REMOTE_ID)//检验遥控识别码(ID)及地址 { t1=remoteCode>>8; t2=remoteCode; if(t1==(u8)~t2)state=t1;//键值正确 } if((state==0)||((remoteState&0X80)==0))//按键数据错误/遥控已经没有按下了 { remoteState&=~(1<<6);//清除接收到有效按键标识 remoteCnt=0;//清除按键次数计数器 } } return state; } |
PWM红外编码:
void remote_repeat(u8 cnt) { for(;cnt;cnt--) { Remote_H; Delay_ms(97); Delay_us(940); //97.94ms高电平 Remote_L; Delay_ms(9); //9ms低电平 Remote_H; Delay_us(2500); //2.5ms高电平 Remote_L; Delay_us(560); //560us低电平 } } /* u8 addr:地址码 u8 cmd :控制码 u8 cnt :连发码个数 */ void remote_send(u8 addr,u8 cmd,u8 cnt) { remote_start(); remote_send_byte(addr); remote_send_byte(~addr); remote_send_byte(cmd); remote_send_byte(~cmd); remote_repeat(cnt); remote_end(); } void remote_init(void) { RCC->AHB1ENR |=1<<0; //GPIOA时钟使能 GPIOA->MODER &=~(0x3<<(1*2)); //GPIOA1模式配置 GPIOA->MODER |=1<<(1*2); //输出模式 GPIOA->OTYPER &=~(1<<1); // GPIOA->OSPEEDR &=~(0x3<<(1*2)); GPIOA->OSPEEDR |=1<<(1*2); //25MHz GPIOA->PUPDR &=~(0x3<<(1*2)); //无上下拉 GPIOA->ODR &=~(1<<1); } void remote_start(void) //红外同步码头 { Remote_L; Delay_ms(9); //9ms低电平 (发送端脉冲 ) Remote_H; Delay_ms(4); //4.5ms高电平 (发送端低电平) Delay_us(500); Remote_L; Delay_us(560); //560us脉冲 //没有这560us码值会错OK? } void remote_end(void) { Remote_H; } //高位在前你敢信? void remote_send_byte(u8 byte) { u8 i=0; for(i=0;i<8;i++) { Remote_H; if( byte & (0x80>>i) ) Delay_us(1680); //1680us低电平 else Delay_us(560); //1680us低电平 Remote_L; Delay_us(560); //560us脉冲 } } |