【正点原子探索者STM32F407开发板例程连载+教学】第34章 红外遥控器实验

2019-07-20 11:53发布

第三十四章  红外遥控实验

       [mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0 [/mw_shl_code]

本章,我们将向大家介绍如何通过STM32来解码红外遥控器的信号。ALIENTK探索者STM32F4开发板标配了红外接收头和一个很小巧的红外遥控器。在本章中,我们将利用STM32F4的输入捕获功能,解码开发板标配的这个红外遥控器的编码信号,并将解码后的键值TFTLCD模块上显示出来。本章分为如下几个部分: 34.1 红外遥控简介 34.2 硬件设计 34.3 软件设计 34.4 下载验证  

34.1红外遥控简介

红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。 由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方面。由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。 红外遥控的编码目前广泛使用的是:NEC Protocol PWM(脉冲宽度调制)Philips RC-5 Protocol PPM(脉冲位置调制)ALIENTEK探索者STM32F4开发板配套的遥控器使用的是NEC协议,其特征如下: 18位地址和8位指令长度; 2、地址和命令2次传输(确保可靠性) 3PWM脉冲位置调制,以发射红外载波的占空比代表“0”和“1”; 4、载波频率为38Khz 5、位时间为1.125ms2.25ms NEC码的位定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms560us脉冲+1680us低电平),一个逻辑0的传输需要1.125ms560us脉冲+560us低电平)。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑1应该是560us+1680us高,逻辑0应该是560us+560us高。 NEC遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。 我们遥控器的按键“▽”按下时,从红外接收头端收到的波形如图34.1.1所示:
34.1.1 按键“▽”所对应的红外波形 从图34.1.1中可以看到,其地址码为0,控制码为168。可以看到在100ms之后,我们还收到了几个脉冲,这是NEC码规定的连发码(9ms低电平+2.5m高电平+0.56ms低电平+97.94ms高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数。 第十五章我们曾经介绍过利用输入捕获来测量高电平的脉宽,本章解码红外遥控信号,刚好可以利用输入捕获的这个功能来实现遥控解码。关于输入捕获的介绍,请参考第十五章的内容。  

34.2 硬件设计

本实验采用定时器的输入捕获功能实现红外解码,本章实验功能简介:开机在LCD上显示一些信息之后,即进入等待红外触发,如过接收到正确的红外信号,则解码,并在LCD上显示键值和所代表的意义,以及按键次数等信息。同样我们也是用LED0来指示程序正在运行。 所要用到的硬件资源如下: 1)  指示灯DS0 2) TFTLCD模块 3)  红外接收头 4)  红外遥控器     前两个,在之前的实例已经介绍过了,遥控器属于外部器件,遥控接收头在板子上,与MCU的连接原理图如34.2.1所示:                    34.2.1 红外遥控接收头与STM32的连接电路图 红外遥控接收头连接在STM32PA8TIM1_CH1)上。硬件上不需要变动,只要程序将TIM1_CH1设计为输入捕获,然后将收到的脉冲信号解码就可以了。不过需要注意:REMOTE_INDCMI_XCLK共用了PA8,所以他们不可以同时使用。 开发板配套的红外遥控器外观如图34.2.2所示:  34.2.2 红外遥控器

34.3 软件设计

打开我们光盘的红外遥控器实验工程,可以看到我们添加了remote.cremote.h两个文件,同时因为我们使用的是输入捕获,所以还用到定时器相关的库函数源文件stm32f4xx_tim.c和头文件stm32f4xx_tim.h 打开remote.c文件,代码如下: //红外遥控初始化 //设置IO以及TIM2_CH1的输入捕获 void Remote_Init(void)                       {              GPIO_InitTypeDef  GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   TIM_ICInitTypeDef  TIM1_ICInitStructure;          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟   RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//TIM1时钟使能     //GPIOA8  复用功能,上拉   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化        GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); //GPIOA8复用为TIM1        TIM_TimeBaseStructure.TIM_Prescaler=167;  ////预分频器,1M的计数频率,1us1.      TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_Period=10000;   //设定计数器自动重装值 最大10ms溢出  TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);            //初始化TIM1输入捕获参数 TIM1_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入端 IC1映射到TI1   TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获   TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1   TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;      //配置输入分频,不分频   TIM1_ICInitStructure.TIM_ICFilter = 0x03;//IC1F=0003 8个定时器时钟周期滤波   TIM_ICInit(TIM1, &TIM1_ICInitStructure);//初始化定时器2输入捕获通道          TIM_ITConfig(TIM1,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断捕获中断   TIM_Cmd(TIM1,ENABLE );    //使能定时器1     NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;        //响应优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                   //IRQ通道使能 NVIC_Init(&NVIC_InitStructure);   //初始化NVIC寄存器     NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;        //响应优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                   //IRQ通道使能 NVIC_Init(&NVIC_InitStructure);   //初始化NVIC寄存器 }   //遥控器接收状态 //[7]:收到了引导码标志 [6]:得到了一个按键的所有信息 //[5]:保留       [4]:标记上升沿是否已经被捕获              [3:0]:溢出计时器 u8   RmtSta=0;               u16 Dval;              //下降沿时计数器的值 u32 RmtRec=0;      //红外接收到的数据                      u8  RmtCnt=0;     //按键按下的次数  //定时器1溢出中断 void TIM1_UP_TIM10_IRQHandler(void) {   if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET) //溢出中断        {               if(RmtSta&0x80)//上次有数据被接收到了               {                          RmtSta&=~0X10;                       //取消上升沿已经被捕获标记        if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;//标记已经完成一次按键的键值信息采集                      if((RmtSta&0X0F)<14)RmtSta++;                      else                      {                             RmtSta&=~(1<<7);//清空引导标识                             RmtSta&=0XF0;    //清空计数器                       }                                                                }                                                          }        TIM_ClearITPendingBit(TIM1,TIM_IT_Update);  //清除中断标志位 } //定时器1输入捕获中断服务程序     void TIM1_CC_IRQHandler(void) {                                if(TIM_GetITStatus(TIM1,TIM_IT_CC1)==SET) //处理捕获(CC1IE)中断        {                     if(RDATA)//上升沿捕获               {                      TIM_OC1PolarityConfig(TIM1,TIM_ICPolarity_Falling);//下降沿捕获                      TIM_SetCounter(TIM1,0);         //清空定时器值                      RmtSta|=0X10;                           //标记上升沿已经被捕获               }else //下降沿捕获               {                      Dval=TIM_GetCapture1(TIM1);//读取CCR1也可以清CC1IF标志位                      TIM_OC1PolarityConfig(TIM1,TIM_ICPolarity_Rising); //设置为上升沿捕获                      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);               }                                                                                                  }        TIM_ClearITPendingBit(TIM1,TIM_IT_CC1);  //清除中断标志位 } //处理红外键盘 //返回值: //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)及地址            {                t1=RmtRec>>8;                t2=RmtRec;                  if(t1==(u8)~t2)sta=t1;//键值正确               }                 if((sta==0)||((RmtSta&0X80)==0))//按键数据错误/遥控已经没有按下了               {                     RmtSta&=~(1<<6);//清除接收到有效按键标识                      RmtCnt=0;            //清除按键次数计数器               }        }      return sta; } 该部分代码包含4个函数,首先是Remote_Init函数,该函数用于初始化IO口,并配置TIM1_CH1为输入捕获,并设置其相关参数。由于TIM1是高级定时器,它有2个中断服务函数: TIM1_UP_TIM10_IRQHandler用于处理TIM1的溢出事件,在本例程里面,该函数用来处理TIM1溢出,并标用于标记键值获取完成。每一次红外按键解码,都必须通过定时器溢出事件来标记完成。 TIM1_CC_IRQHandler用于处理TIM1的输入捕获事件,在本例程里面,该函数实现对红外信号的高电平脉冲的捕获,同时根据我们之前简介的协议内容来解码。 这两个函数用到几个全局变量,用于辅助解码,并存储解码结果。 这里简单介绍一下高电平捕获思路:首先输入捕获设置的是捕获上升沿,在上升沿捕获到以后,立即设置输入捕获模式为捕获下降沿(以便捕获本次高电平),然后,清零定时器的计数器值,并标记捕获到上升沿。当下降沿到来时,再次进入捕获中断服务函数,立即更改输入捕获模式为捕获上升沿(以便捕获下一次高电平),然后处理此次捕获到的高电平。 最后是Remote_Scan函数,该函用来扫描解码结果,相当于我们的按键扫描。输入捕获解码的红外数据,通过该函数传送给其他程序。 接下来我们打开remote.h文件,可以看到下面一行代码: #define REMOTE_ID 0           这里的REMOTE_ID就是我们开发板配套的遥控器的识别码,对于其他遥控器可能不一样,只要修改这个为你所使用的遥控器的一致就可以了。remote.h其他是一些函数的声明,我们不做过多讲解,最后我们看看主函数代码如下: int main(void) {        u8 key, t=0, *str=0;        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2        delay_init(168);  //初始化延时函数        uart_init(115200);         //初始化串口波特率为115200        LED_Init();                                //初始化LED       LCD_Init();       Remote_Init();                            //红外接收初始化                    POINT_COLOR=RED;//设置字体为红 {MOD}        LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");              LCD_ShowString(30,70,200,16,16,"REMOTE TEST");            LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");        LCD_ShowString(30,110,200,16,16,"2014/5/7");      LCD_ShowString(30,130,200,16,16,"KEYVAL:");          LCD_ShowString(30,150,200,16,16,"KEYCNT:");          LCD_ShowString(30,170,200,16,16,"SYMBOL:");                                                                                                                                                while(1)        {               key=Remote_Scan();                   if(key)               {                          LCD_ShowNum(86,130,key,3,16);             //显示键值                      LCD_ShowNum(86,150,RmtCnt,3,16);       //显示按键次数                                   switch(key)                      {                             case 0:str="ERROR";break;                                                case 162:str="POWER";break;                                      case 98:str="UP";break;                                 case 2:str="PLAY";break;                                         case 226:str="ALIENTEK";break;                                      case 194:str="RIGHT";break;                                      case 34:str="LEFT";break;                                         case 224:str="VOL-";break;                                        case 168:str="DOWN";break;                                             case 144:str="VOL+";break;                                         case 104:str="1";break;                                       case 152:str="2";break;                                 case 176:str="3";break;                                  case 48:str="4";break;                                           case 24:str="5";break;                                           case 122:str="6";break;                                       case 16:str="7";break;                                                                               case 56:str="8";break;                                case 90:str="9";break;                             case 66:str="0";break;                             case 82:str="DELETE";break;                                  }                      LCD_Fill(86,170,116+8*8,170+16,WHITE);      //清楚之前的显示                      LCD_ShowString(86,170,200,16,16,str);     //显示SYMBOL               }else delay_ms(10);                       t++;               if(t==20)               {   t=0;                      LED0=!LED0;               }        } } main函数代码比较简单,主要是通过Remote_Scan函数获得红外遥控输入的数据(键值),然后显示在LCD上面。 至此,我们的软件设计部分就结束了。

34.4 下载验证

在代码编译成功之后,我们通过下载代码到ALIENTEK探索者STM32F4开发板上,可以看到LCD显示如图34.4.1所示的内容:  34.4.1 程序运行效果图 此时我们通过遥控器按下不同的按键,则可以看到LCD上显示了不同按键的键值以及按键次数和对应的遥控器上的符号。如图34.4.2所示:  34.4.2 解码成功
  实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm  正点原子探索者STM32F407开发板购买地址http://item.taobao.com/item.htm?id=41855882779
  

0条回答

一周热门 更多>