背景
项目中使用STM8S003F3P6,控制红外遥控器HS003B,调试过程也遇到了很多问题,这里做个记录。
问题1:长按按键问题,如果长按遥控的某个按键,应该是一直能响应该按键值(需要对接收状态、接收信息清零操作)
问题2:按键响应不是很灵敏,有时候,有些按键按下了,却没有识别到(需要对接收状态、接收信息清零操作
原理图
红外遥控器的原理图设计,比较简单
如下图所示
使用STM8S003F3P6的管脚PA3进行数据监控
软件设计
一些头文件的宏定义
//红外遥控识别码(ID),每款遥控器的该值基本都不一样,但也有一样的.
//我们选用的遥控器识别码为0
#define REMOTE_ID 0
#define REMOTE_IDH 0x0C
#define REMOTE_IDL 0xF3
extern u8 RmtCnt; //按键按下的次数
void Remote_Init(void); //红外传感器接收头引脚初始化
u8 Remote_Scan(void);
红外初始化函数
这里应该是最难调的地方,当时用了很多配置,都不能搞定,或者能用了,但是对红外遥控的按键指令响应不是太好
感觉遥控开关按键很难用
之后也调试了很久,可能还是针对红外的时序理解不是很深刻,这里就把实际调试的配置记录下来。
这里的核心思想就是使用定时器TIM2去捕获PA3的管脚的上升沿,这里TIM2的定时器配置,可以参考之前的开发板,结合自己的STM8S003F3P6使用时的主频进行配置,这个也很关键,如果读者想要移植到,自己的平台上,那么要着重注意这里的修改。
这里比较复杂,采用了定时器2,使用溢出中断和通道3的捕获功能,刚好对应管脚PA3
//红外遥控初始化
//设置IO以及TIM2_CH1的输入捕获
void Remote_Init(void)
{
/* Set PA.3 TIM2_CH3 pin */
///GPIO_Init(GPIOA, GPIO_PIN_3, GPIO_MODE_IN_PU_NO_IT);
GPIO_DeInit(GPIOA);//恢复指定端口
GPIO_Init(GPIOA, GPIO_PIN_3, GPIO_MODE_IN_FL_NO_IT);//设置PA3为浮空输入无中断
TIM2_DeInit();
/* Time base configuration */
//TIM2_TimeBaseInit(TIM2_PRESCALER_2048, 65535);
//TIM2_TimeBaseInit(TIM2_PRESCALER_1, 999);//2Mhz
///TIM2_TimeBaseInit(TIM2_PRESCALER_1, 1999);//1Mhz
TIM2_TimeBaseInit(TIM2_PRESCALER_16, 10000 - 1);//1Mhz
TIM2_ARRPreloadConfig(ENABLE);
//TIM2_ClearFlag(TIM2_FLAG_UPDATE);//清除更新标志
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);//允许更新/溢出中断
TIM2_Cmd(ENABLE);//开启定时器
enableInterrupts(); //开启中断
/* Prescaler configuration */
///TIM2_PrescalerConfig(TIM2_PRESCALER_2048, TIM2_PSCRELOADMODE_IMMEDIATE);
//TIM2_ICInit(TIM2_CHANNEL_3, TIM2_ICPOLARITY_RISING, TIM2_ICSELECTION_DIRECTTI, TIM2_ICPSC_DIV1, 3);
TIM2_ICInit(TIM2_CHANNEL_3, TIM2_ICPOLARITY_RISING, TIM2_ICSELECTION_DIRECTTI, TIM2_ICPSC_DIV1, 0);
TIM2_ITConfig(TIM2_IT_CC3 , ENABLE);//中断配置与使能函数
TIM2_ClearITPendingBit(TIM2_IT_CC3);
TIM2_ClearFlag(TIM2_FLAG_CC3);
//TIM2_CCxCmd(TIM2_CHANNEL_2, ENABLE);//输入捕获/比较输出使能
}
定时器2的
中断溢出响应函数
定时器2的捕获响应函数
/**
* @brief Timer2 Update/Overflow/Break Interrupt routine.
* @param None
* @retval None
*/
//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留
//[4]:标记上升沿是否已经被捕获
//[3:0]:溢出计时器
u8 RmtSta=0;
u16 Dval; //下降沿时计数器的值
u32 RmtRec=0; //红外接收到的数据
u8 RmtCnt=0; //按键按下的次数
//定时器1溢出中断
INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
/* In order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction.
*/
///if(TIM2_GetITStatus(TIM2_IT_UPDATE)==SET) //溢出中断
if(RmtSta&0x80)//上次有数据被接收到了
{
RmtSta&=~0X10; //取消上升沿已经被捕获标记
if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;//标记已经完成一次按键的键值信息采集
else
{
RmtSta&=~(1<<7);
RmtSta&=0XF0;
}
//if((RmtSta&0X0F)<14)RmtSta++;
//else
//{
// RmtSta&=~(1<<7);//清空引导标识
// RmtSta&=0XF0; //清空计数器
//}
}
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
TIM2_ClearITPendingBit(TIM2_IT_UPDATE); //清除中断标志位
}
/**
* @brief Timer2 Capture/Compare Interrupt routine.
* @param None
* @retval None
*/
//定时器1输入捕获中断服务程序
INTERRUPT_HANDLER(TIM2_CAP_COM_IRQHandler, 14)
{
/* In order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction.
*/
BitStatus bs;
bs = GPIO_ReadInputPin(GPIOA, GPIO_PIN_3);//读PA3的输入电平状态
//if(TIM2_GetITStatus(TIM2_IT_CC3)==SET) //处理捕获(CC1IE)中断
if(bs)//上升沿捕获
{
TIM2_OC3PolarityConfig(TIM2_OCPOLARITY_LOW); //CC1P=1 设置为下降沿捕获
TIM2_SetCounter(0x0000); //清空定时器值
RmtSta|=0X10; //标记上升沿已经被捕获
}else //下降沿捕获
{
Dval = TIM2_GetCounter();//读取CCR2也可以清CC2IF捕获标志位
TIM2_OC3PolarityConfig(TIM2_OCPOLARITY_HIGH); //CC1P=0 设置为上升沿捕获
if(RmtSta&0X10) //完成一次高电平捕获
{
if(RmtSta&0X80)//接收到了引导码
{
if(Dval>300&&Dval<800) //560为标准值,560us
{
RmtRec<<=1; //左移一位.
RmtRec|=0; //接收到0
}else if(Dval>1400&&Dval<1800) //1680为标准值,1680us
{
RmtRec<<=1; //左移一位.
RmtRec|=1; //接收到1
}else if(Dval>2200&&Dval<2600) //得到按键键值增加的信息 2500为标准值2.5ms
{
RmtCnt++; //按键次数增加1次
RmtSta&=0XF0; //清空计时器
}
}else if(Dval>4200&&Dval<4700) //4500为标准值4.5ms
{
RmtSta|=1<<7; //标记成功接收到了引导码
RmtCnt=0; //清除按键次数计数器
}
}
RmtSta&=~(1<<4);
}
TIM2_ClearITPendingBit(TIM2_IT_CC3); //清除中断标志位
}
main函数中扫描按键值函数
//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留
//[4]:标记上升沿是否已经被捕获
//[3:0]:溢出计时器
extern u8 RmtSta;
extern u16 Dval; //下降沿时计数器的值
extern u32 RmtRec; //红外接收到的数据
extern u8 RmtCnt; //按键按下的次数
//处理红外键盘
//返回值:
//0,没有任何按键按下
//其他,按下的按键键值.
u8 Remote_Scan(void)
{
u8 sta=0;
u8 t1,t2;
if(RmtSta&(1<<6))//得到一个按键的所有信息了
{
t1=RmtRec>>24; //得到地址码
t2=(RmtRec>>16)&0xff; //得到地址反码
//if((t1==(u8)~t2)&&t1==REMOTE_ID)//检验遥控识别码(ID)及地址
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);//清除接收到有效按键标识
RmtSta&=~(1<<7);//清楚引导码标识
RmtCnt=0; //清除按键次数计数器
RmtRec = 0;
}
}
return sta;
}
实际中main函数的使用方法
扫描按键值,获取到按键值以后,进行不同按键值的处理
key = Remote_Scan();
if(key)
{
switch(key)
{
case 0X11://开机按键
{
if(powerOn == 0)
{
powerOn = 1;
GPIO_WriteHigh(GPIOA, GPIO_PIN_2);
}
else
{
powerOn = 0;
GPIO_WriteLow(GPIOA, GPIO_PIN_2);
}
needSend2Ctl |= 1<<1;
break;
}
case 0X61:
{
speed++;
if(speed > 5)
speed = 5;
updata();
needSend2Ctl |= 1<<1;
break;
}
case 0XE1:
{
speed--;
if(speed < 1)
speed = 1;
updata();
needSend2Ctl |= 1 << 1;
break;
}
case 0XA9:
{
//s,模式按键
break;
}
default:
break;
}
}
其他
红外遥控波形
红外传感器原理,原始信号就是我们要发送的一个数据“0”位或者一位数据“1”位,而所谓 38K 载波就是频率为 38K 的方波信号,调制后信号就是最终我们发射出去的波形。我们使用原始信号来控制 38K 载波,当信号是数据“0”的时候,38K 载波毫无保留的全部发送出去,当信号是数据“1”的时候,不发送任何载波信号。
下图为红外调制信号
NEC码的位定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0的传输需要1.125ms(560us的脉冲+560us低电平)。
NEC遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码都是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性。