NEC协议
红外遥控的编码目前广泛使用的是:NEC Protocol
的 PWM
(脉冲宽度调制)和 PhilipsRC-5 Protocol
的 PPM
(脉冲位置调制),下面是 NEC
协议的特征:
- 8 位地址和 8 位指令长度(控制码);
- 地址和命令 2 次传输(确保可靠性)
- PWM 脉冲位置调制,以发射红外载波的占空比代表 “0” 和 “1” ;
- 载波频率为
38Khz
; - 位时间为
1.125ms
或 2.25ms
;
NEC码的位定义:
- 一个脉冲对应
560us
的连续载波 - 一个逻辑
1
传输需要 2.25ms
(560us
脉冲+1680us
低电平=2240us) - 一个逻辑
0
的传输需要 1.125ms
(560us
脉冲+560us
低电平=1120us)
红外接收头端的信号为:
- 收到脉冲的时候为低电平,在没有脉冲的时候为高电平(即与发出的NEC码反向)
- 逻辑
1
( 560us 低+1680us 高) - 逻辑
0
( 560us 低+560us 高)
NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。从红外接收头端的角度来看:
- 同步码:由一个
9ms
的低电平和一个 4.5ms
的高电平组成 - 地址码、地址反码:8位数据,遥控器的地址码要与红外接收头端的地址码要对应?
- 控制码、控制反码:8位数据,键值相关
- 按照低位在前,高位在后的顺序发送
- 采用反码是为了增加传输的可靠性(可用于校验)
以正点原子的STM32开发板为例,配带的遥控器的地址码为0x00:
- 地址码为0
- 控制码为0x15(从接收端来看)
- 连发码(由
9ms
低电平+2.5m
高电平+0.56ms
低电平+97.94ms
高电平组成=110ms
),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数。
项目实践中的NEC码
一般给出的码值表是从发送端的角度来看的,而客户码实际上就是地址码+地址反码这一项(不一定构成反码),也就是说如果从接收端接收到的波形来看,83F4对应的是C12F(先发低位,再发高位);OK键15对应A8
对应原理图
对应的原理图如下:
软件设计思路一
仿正点原子的做法,该方式不适合要做低功耗模式的需求:
- 开启定时器对应通道输入捕获功能,默认上升沿捕获。定时器的计数频率为
1MHz
,自动装载值为10000
,也就是溢出时间为10ms
。 - 开启定时器 输入捕获中断 和 溢出/更新中断,当捕获到上升沿产生捕获中断,当定时器计数溢出,产生更新中断 。
- 当捕获到上升沿的时候,设置捕获极性为下降沿捕获(为下次捕获下降沿做准备),然后设置定时器计数值为0(清空定时器),同时设置变量RmtSta的为4值为1,标记已经捕获到上升沿
- 当捕获到下降沿的时候,读取定时器的值赋给变量Dval,然后设置捕获极性为上升沿捕获(为下次捕获上升沿做准备),同时对变量RmtSta的位4进行判断:如果RmtSta位4为1,说明之前已经捕获到过上升沿,那么对Dval进行判断,
300-800(560)
之间,说明接收到的是数据0;1400-1800(1680)
之间说明接收到的数据为1;2200-2600(2500)
,说明是连发码;4200-4700(4500)
说明为同步码;分析后甚至相应的标志位 - 如果是定时器发生溢出中断,那么分析,如果之前接收到了同步码,并且是第一次溢出,标记完成一次按键信息采集(有两种情况,情况1:接收完码后一直处于高电平后产生溢出;情况2:处于连发码中
97.94ms
高电平这段时间产生溢出,正点原子例程用是否>130ms
来判断是处于两种情况中的哪一种) - 接收码值的时候采用的是左移的方式,跟接收端看到的波形数值是一致的,所以是遥控器码值的反码
用 定时器捕获 + 外部中断(低功耗) 的方式时,会产生问题,猜测是在软件优先级一样的情况下,硬件优先级(根据中断向量表判断)高的外部中断常常打断了定时器的捕获,所以造成了有时解不了码的问题。
remote.h代码
#ifndef _REMOTE_H_
#define _REMOTE_H_
#include "stm8s.h"
#define REMOTE_IDH 0x0C
#define REMOTE_IDL 0xF3
extern u8 RmtCnt;
void Clk_Init(void);
void Remote_Init(void);
u8 Remote_Scan(void);
#endif
remote.c代码
#include "remote.h"
void Clk_Init(void){
CLK_DeInit();
CLK_HSICmd(ENABLE);
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
}
void IR_GPIO_Init(void){
GPIO_DeInit(GPIOD);
GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_IN_FL_NO_IT);
}
void TIM2_Init(void){
TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_16, 10000);
TIM2_ARRPreloadConfig(ENABLE);
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);
TIM2_Cmd(ENABLE);
enableInterrupts();
TIM2_ICInit(TIM2_CHANNEL_2, TIM2_ICPOLARITY_RISING, TIM2_ICSELECTION_DIRECTTI, TIM2_ICPSC_DIV1, 0x00);
TIM2_ITConfig(TIM2_IT_CC2, ENABLE);
TIM2_ClearITPendingBit(TIM2_IT_CC1);
TIM2_ClearFlag(TIM2_FLAG_CC1);
}
void Remote_Init(){
IR_GPIO_Init();
TIM2_Init();
}
u8 RmtSta=0;
u16 Dval;
u32 RmtRec=0;
u8 RmtCnt=0;
INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13){
if(RmtSta&0x80){
RmtSta&=~0X10;
if((RmtSta&0X0F)==0X00)
RmtSta|=1<<6;
if((RmtSta&0X0F)<14){
RmtSta++;
}else{
RmtSta&=~(1<<7);
RmtSta&=0XF0;
}
}
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
TIM2_ClearITPendingBit(TIM2_IT_UPDATE);
}
INTERRUPT_HANDLER(TIM2_CAP_COM_IRQHandler, 14){
BitStatus bs;
bs = GPIO_ReadInputPin(GPIOD, GPIO_PIN_3);
if (bs){
TIM2_OC2PolarityConfig(TIM2_OCPOLARITY_LOW);
TIM2_SetCounter(0x0000);
RmtSta|=0X10;
}else{
Dval = TIM2_GetCounter();
TIM2_OC2PolarityConfig(TIM2_OCPOLARITY_HIGH);
if(RmtSta&0x10){
if(RmtSta&0X80){
if(Dval>300&&Dval<800){
RmtRec<<=1;
RmtRec|=0;
}else if(Dval>1400&&Dval<1800){
RmtRec<<=1;
RmtRec|=1;
}else if(Dval>2200&&Dval<2600){
RmtCnt++;
RmtSta&=0XF0;
}
}else if(Dval>4200&&Dval<4700){
RmtSta|=1<<7;
RmtCnt=0;
}
}
RmtSta&=~(1<<4);
}
TIM2_ClearITPendingBit(TIM2_IT_CC2);
}
u8 Remote_Scan(void){
u8 sta=0;
u8 t1,t2;
if(RmtSta&(1<<6))
{
t1=RmtRec>>24;
t2=(RmtRec>>16)&0xff;
if((t1==REMOTE_IDH)&&t2==REMOTE_IDL)
{
t1=RmtRec>>8;
t2=RmtRec;
if(t1==(u8)~t2)sta=t1;
}
if((sta==0)||((RmtSta&0X80)==0))
{
RmtSta&=~(1<<6);
RmtCnt=0;
}
}
return sta;
}
main.c代码
#include "stm8s.h"
#include "stdio.h"
#include "remote.h"
#include "uart.h"
#include "led.h"
#include "delay.h"
#include "gpio_init.h"
void main(void)
{
u8 key;
Clk_Init();
init_TIM1();
Remote_Init();
Default_GPIO_Init();
while(1){
if(!GPIO_ReadInputPin(GPIOC, GPIO_PIN_6)){
key=Remote_Scan();
if(key&&RmtCnt==1){
switch(key){
case 0:
break;
case 0x8A:
GPIO_WriteReverse(GPIOD, GPIO_PIN_5);
Delay100ms(20);
GPIO_WriteReverse(GPIOD, GPIO_PIN_5);
break;
}
}
}
}
}
软件设计思路二
采用外部中断的方式来做,适合做低功耗模式,但没做对连发码处理。
- 具体思路:将IR接收脚设置为下降沿触发,解码的整个过程都在中断服务函数里搞,高低电平的持续时间是在中断函数中通过等待IR接收脚的电平状态翻转得到的。
- 接收码值的时候采用的是右移的方式,所以得到和遥控器的码值一致
- stm8在进入中断服务函数时,不用对标志位进行清零,会自动进行清零
remote.h代码
#ifndef _REMOTE_H_
#define _REMOTE_H_
#include "stm8s.h"
#define REMOTE_IDH 0x30
#define REMOTE_IDL 0xCF
extern u8 IR_Data[4];
extern bool IR_State;
void Clk_Init(void);
void Remote_Init(void);
u8 getKey(void);
#endif
remote.c代码
#include "remote.h"
u8 IR_Data[4];
bool IR_State;
BitStatus NEC_IR;
u16 x;
void Clk_Init(void){
CLK_DeInit();
CLK_HSICmd(ENABLE);
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV16);
}
void IR_GPIO_Init(void){
EXTI_DeInit();
GPIO_DeInit(GPIOD);
GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_IN_FL_IT);
EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_FALL_ONLY);
}
void TIM2_Init(void){
TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_16, 65535);
TIM2_ARRPreloadConfig(ENABLE);
}
void Remote_Init(){
IR_GPIO_Init();
TIM2_Init();
}
u16 IR_GetTime_H(void){
TIM2_SetCounter(0);
TIM2_Cmd(ENABLE);
while(GPIO_ReadInputPin(GPIOD, GPIO_PIN_3)){}
x=TIM2_GetCounter();
TIM2_Cmd(DISABLE);
return x;
}
u16 IR_GetTime_L(void){
TIM2_SetCounter(0);
TIM2_Cmd(ENABLE);
while(!GPIO_ReadInputPin(GPIOD, GPIO_PIN_3)){}
x=TIM2_GetCounter();
TIM2_Cmd(DISABLE);
return x;
}
u8 getKey(void){
u8 key=0;
if((IR_Data[0]==REMOTE_IDH)&&IR_Data[1]==REMOTE_IDL){
if(IR_Data[2]==(u8)~IR_Data[3]) key=IR_Data[2];
}
return key;
}
INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6){
u16 time;
u8 i,j,data=0;
time=IR_GetTime_L();
if(time<7000||time>10000)return;
time=IR_GetTime_H();
if(time<3000||time>5500)return;
for(i=0;i<4;i++)
{
for(j=0;j<8;j++)
{
time=IR_GetTime_L();
if(time<400||time>700)return;
time=IR_GetTime_H();
if(time>1400&&time<1800)
{
data>>=1;
data|=0x80;
}
else if(time>400&&time<700)
{
data>>=1;
}
else return;
}
IR_Data[i]=data;
}
IR_State=TRUE;
}
main.c代码
#include "stm8s.h"
#include "stdio.h"
#include "remote.h"
#include "uart.h"
#include "led.h"
#include "delay.h"
#include "gpio_init.h"
void main(void)
{
u8 key;
Clk_Init();
init_TIM1();
Remote_Init();
UART_Init();
enableInterrupts();
Default_GPIO_Init();
printf("all init \n");
while(1){
if(!GPIO_ReadInputPin(GPIOC, GPIO_PIN_6)){
if(IR_State){
IR_State=FALSE;
key=getKey();
switch(key){
case 0:
printf("other remote \n");
break;
case 0x51:
printf("Press Power \n");
GPIO_WriteReverse(GPIOD, GPIO_PIN_5);
Delay100ms(20);
GPIO_WriteReverse(GPIOD, GPIO_PIN_5);
break;
}
}
}
halt();
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)