STM8 调试红外遥控器HS003B

背景

项目中使用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位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大牛攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值