OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 17140|回复: 16

【Alientek STM32 实验21】--红外遥控器实验

[复制链接]

230

主题

1950

帖子

10

精华

论坛元老

Rank: 8Rank: 8

积分
4562
金钱
4562
注册时间
2010-12-14
在线时间
32 小时
发表于 2010-12-14 23:34:44 | 显示全部楼层 |阅读模式

3.21  红外遥控实验

ALIENTKE MiniSTM3开发板给使用者配备了标准的红外接收头和一个很小巧的红外遥控器。本节将向大家介绍,如何在ALIENTEK MiniSTM32开发板上实现红外遥控器的控制。本节分为如下几个部分:

3.21.1 红外遥控简介

3.21.2 硬件设计

3.21.3 软件设计

3.21.4 下载与测试
3.21.1红外遥控简介

红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。

由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方面。由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。

红外遥控的编码目前广泛使用的是:NEC Protocol PWM(脉冲宽度调制)Philips

RC-5 Protocol PPM(脉冲位置调制)。我们配套的遥控器使用的是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位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。

我们遥控器的按键2按下时,从红外接收头端收到的波形如下:


                    3.21.1.1 按键2所对应的红外波形

从图中可以看到,其地址码为0,控制码为168。可以看到在100ms之后,我们还受到了几个脉冲,这是NEC码规定的连发码(9ms低电平+2.5m高电平+0.56ms低电平+97.94ms高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数
3.21.2 硬件设计    

本实验采用中断解码(也可以采用输入捕获解码)本节实验功能简介:开机在LCD上显示一些信息之后,即进入等待红外触发,如过接收到正确的红外信号,则解码,并在LCD上显示键值和所代表的意义,以及按键次数等信息。同样我们也是用LED0来指示程序正在运行。

所要用到的硬件资源如下:

1STM32F103RBT6

2DS0(外部LED0)。

3TFTLCD液晶模块。

4)红外接收头。

5)红外遥控器。

            前面三部分,在之前的实例已经介绍过了,遥控器属于外部器件,遥控接收头在板子上,与MCU的连接原理图如下:


                3.21.2.1 红外遥控接收头与STM32的连接电路图

红外遥控接收头与STM32PA1通过跳线帽连接,这个在硬件上要连接上,否则PA1将得不到信号,我们采用中断解码,PA1对应的中断为中断1,所以在程序设计的时候,我们只要开启中断1,然后在中断里执行解码就可以了。


3.21.3 软件设计

 

打开上一节的工程,首先在HARDWARE文件夹下新建一个REMOTE的文件夹。然后新建一个remote.cremote.h的文件保存在REMOTE文件夹下,并将这个文件夹加入头文件包含路径。

打开remote.c文件,输入如下代码:

#include "remote.h"

#include "delay.h"

//Mini STM32开发板

//红外遥控接收 驱动函数

//正点原子@ALIENTEK

//2010/6/17                      

u32 Remote_Odr=0;       //命令暂存处

u8  Remote_Cnt=0;    //按键次数,此次按下键的次数

u8  Remote_Rdy=0;    //红外接收到数据   

//初始化红外接收引脚的设置

//开启中断,并映射

void Remote_Init(void)

{                                          

   RCC->APB2ENR|=1<<2;       //PA时钟使能           

   GPIOA->CRL&=0XFFFFFF0F;

   GPIOA->CRL|=0X00000080;    //PA1输入   

   GPIOA->ODR|=1<<1;          //PA.1上拉     

   Ex_NVIC_Config(GPIO_A,1,FTIR);//line1映射到PA.1,下降沿触发.

   MY_NVIC_Init(2,1,EXTI1_IRQChannel,2);

}  

//检测脉冲宽度

//最长脉宽为5ms

//返回值:x,代表脉宽为x*20us(x=1~250);

u8 Pulse_Width_Check(void)

{

    u8 t=0;     

    while(RDATA)

    {    

          t++;delay_us(20);                              

        if(t==250)return t; //超时溢出

    }

    return t;

}                  

//处理红外接收 

/*-------------------------协议--------------------------

开始拉低9ms,接着是一个4.5ms的高脉冲,通知器件开始传送数据了

接着是发送48位二进制码,第一二个是遥控识别码(REMOTE_ID),第一个为

正码(0),第二个为反码(255),接着两个数据是键值,第一个为正码

第二个为反码.发送完后40ms,遥控再发送一个9ms,2ms高的脉冲,

表示按键的次数,出现一次则证明只按下了一次,如果出现多次,则可

以认为是持续按下该键.

---------------------------------------------------------*/           

//外部中断服务程序       

void EXTI1_IRQHandler(void)

{      

   u8 res=0;

    u8 OK=0;

    u8 RODATA=0;               

   while(1)

    {       

        if(RDATA)//有高脉冲出现

        {

            res=Pulse_Width_Check();//获得此次高脉冲宽度      

            if(res==250)break;//非有用信号

           if(res>=200&&res<250)OK=1; //获得前导位(4.5ms)

            else if(res>=85&&res<200)  //按键次数加一(2ms)

            {                                                           

                Remote_Rdy=1;//接受到数据

                Remote_Cnt++;//按键次数增加

                break;

            }

            else if(res>=50&&res<85)RODATA=1;//1.5ms

            else if(res>=10&&res<50)RODATA=0;//500us

            if(OK)

            {

                Remote_Odr<<=1;

                Remote_Odr+=RODATA;

                Remote_Cnt=0; //按键次数清零

            }  

        }                                                       

    }                

   EXTI->R=1<<1;      //清除中断标志位       

} 

//处理红外键盘

//返回相应的键值

u8 Remote_Process(void)

{              

    u8 t1,t2;  

    t1=Remote_Odr>>24; //红外解码

    t2=(Remote_Odr>>16)&0xff;

    Remote_Rdy=0;//清除标记                

    if(t1==(u8)~t2&&t1==REMOTE_ID)//检验遥控识别码(ID)

    {

        t1=Remote_Odr>>8;

        t2=Remote_Odr;

        if(t1==(u8)~t2)return t1; //处理键值 

    }    

    return 0;

}

该部分代码比较简单,主要是通过中断解码,解码程序是按照前面介绍的NEC码来解的,在这里我们就不再多说了。保存remote.c,然后把该文件加入HARDWARE组下。接下来打开remote.h在该文件里面加入如下代码:

#ifndef __RED_H

#define __RED_H

#include "sys.h"  

//Mini STM32开发板

//红外遥控接收 驱动函数

//正点原子@ALIENTEK

//2010/6/17       

#define RDATA PAin(1)  //红外数据输入脚

//红外遥控识别码(ID),每款遥控器的该值基本都不一样,但也有一样的.

//我们选用的遥控器识别码为0

#define REMOTE_ID 0     

 

extern u8 Remote_Cnt;    //按键次数,此次按下键的次数

extern u8 Remote_Rdy;    //红外接收到数据

extern u32 Remote_Odr;   //命令暂存处

void Remote_Init(void);    //红外传感器接收头引脚初始化

u8 Remote_Process(void);   //红外接收到数据处理

u8 Pulse_Width_Check(void);//检查脉宽             

#endif

这里的REMOTE_ID就是我们开发板配套的遥控器的识别码,对于其他遥控器可能不一样,只要修改这个为你所使用的遥控器的一致就可以了。其他是一些函数的声明,我们保存此部分代码,然后在test.c里面修改主函数如下:

int main(void)

{ 

   u8 key;

   u8 t;                                 

  Stm32_Clock_Init(9);//系统时钟设置

   delay_init(72);            //延时初始化

   uart_init(72,9600); //串口1初始化         

   LCD_Init();                //初始化液晶 

   LED_Init();         //LED初始化           

   Remote_Init();           //初始化红外接收

                                

   POINT_COLOR=RED;//设置字体为红色    

   LCD_ShowString(60,50,"Mini STM32");     

   LCD_ShowString(60,70,"REMOTE TEST");    

   LCD_ShowString(60,90,"ATOM@ALIENTEK");

   LCD_ShowString(60,110,"2010/6/17");

                        

   LCD_ShowString(30,130, "KEYVAL:");

   LCD_ShowString(130,130,"KEYCNT:");  

   LCD_ShowString(30,150, "SYMBOL:");

   while(1)

   {       

          if(Remote_Rdy)

          {

                 key=Remote_Process();

                 LCD_ShowNum(86,130,key,3,16);//显示键值

                 LCD_ShowNum(186,130,Remote_Cnt,3,16);//显示按键次数

                 switch(key)

                 {

                       case 0:

                              LCD_ShowString(86,150, "ERROR");

                              break;

                       case 162:

                              LCD_ShowString(86,150, "POWER ");

                              break;

                       case 98:

                              LCD_ShowString(86,150, "VOL+  ");

                              break;

                       case 2:

                              LCD_ShowString(86,150, "VOL-  ");

                              break;

                       case 226:

                              LCD_ShowString(86,150, "CH+   ");

                              break;

                       case 194:

                              LCD_ShowString(86,150, "CH-   ");

                              break;

                       case 34:

                              LCD_ShowString(86,150, "RECALL");

                              break;

                       case 56:

                              LCD_ShowString(86,150, "0     ");

                              break;

                       case 224:

                              LCD_ShowString(86,150, "1     ");

                              break;

                       case 168:

                              LCD_ShowString(86,150, "2     ");

                              break;

                       case 144:

                              LCD_ShowString(86,150, "3     ");

                              break;

                       case 104:

                              LCD_ShowString(86,150, "4     ");

                              break;

                       case 152:

                              LCD_ShowString(86,150, "5     ");

                              break;

                       case 176:

                              LCD_ShowString(86,150, "6     ");

                              break;

                       case 48:

                              LCD_ShowString(86,150, "7     ");

                              break;

                       case 24:

                              LCD_ShowString(86,150, "8     ");

                              break;

                       case 122:

                              LCD_ShowString(86,150, "9     ");

                              break;

                       case 16:

                              LCD_ShowString(86,150, "ZOOM  ");

                              break;

                       case 90:

                              LCD_ShowString(86,150, "STOP  ");

                              break;

                       case 66:

                              LCD_ShowString(86,150, "REC   ");

                              break;

                       case 82:

                              LCD_ShowString(86,150, "TSHIFT");

                              break;

                 }

          }else delay_ms(2);

          t++;

          if(t==200)

          {

                 t=0;

                 LED0=!LED0;

          }

   }

}

至此,我们的软件设计部分就结束了。

 

3.21.4 下载与测试

 

在代码编译成功之后,我们通过下载代码到ALIENTEK MiniSTM32开发板上,可以看到LCD显示如下内容:


3.21.4.1 开机后LCD显示内容

此时我们通过遥控器按下不同的按键,则可以看到LCD上显示了不同按键的键值以及按键次数和对应的遥控器上的符号。如下图所示:


3.21.4.2 遥控接收

 

ALIENTEK MINISTM32 实验21 红外遥控实验.rar

2.18 MB, 下载次数: 3312

红外遥控.pdf

441.59 KB, 下载次数: 1908

我是开源电子网?网站管理员,对网站有任何问题,请与我联系!QQ:389063473Email:389063473@qq.com
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
113
金钱
113
注册时间
2014-4-16
在线时间
13 小时
发表于 2014-4-17 10:22:31 | 显示全部楼层
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

新手上路

积分
44
金钱
44
注册时间
2013-6-25
在线时间
0 小时
发表于 2014-4-19 22:22:01 | 显示全部楼层
原子哥,keyval是如何确定的,为什么和这个不一样?不解
NEC红外编码.jpg
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

新手上路

积分
44
金钱
44
注册时间
2013-6-25
在线时间
0 小时
发表于 2014-4-19 22:35:51 | 显示全部楼层
这是图片

回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165475
金钱
165475
注册时间
2010-12-1
在线时间
2115 小时
发表于 2014-4-19 22:55:04 | 显示全部楼层
回复【3楼】嗜血、世界:
---------------------------------
不同的遥控器,可能不一样的。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

新手上路

积分
44
金钱
44
注册时间
2013-6-25
在线时间
0 小时
发表于 2014-4-20 09:17:24 | 显示全部楼层
我把ministm32带的遥控器给拆了,上面写得也是那样,可是原子哥的程序里面却和遥控器上的不一样
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

新手上路

积分
44
金钱
44
注册时间
2013-6-25
在线时间
0 小时
发表于 2014-4-20 09:21:31 | 显示全部楼层
原子哥,你当时怎么样确定这些keyval的?

回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165475
金钱
165475
注册时间
2010-12-1
在线时间
2115 小时
发表于 2014-4-20 13:21:57 | 显示全部楼层
回复【7楼】嗜血、世界:
---------------------------------
测出来的。
解码出来,按键就是数字,根据数字去对应遥控器上的图标,就完事了。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

新手上路

积分
44
金钱
44
注册时间
2013-6-25
在线时间
0 小时
发表于 2014-4-21 01:06:41 | 显示全部楼层
谢谢原子哥
回复 支持 反对

使用道具 举报

3

主题

10

帖子

0

精华

新手上路

积分
42
金钱
42
注册时间
2014-5-6
在线时间
0 小时
发表于 2014-8-3 16:48:32 | 显示全部楼层
原子哥,你那个mini板子的红外遥控编程有点问题。按照NEC协议,应该从低位先发送,高位后发送。而你在接受编码的时候却是把地位放在了高位,读出来的码是错误的。就如楼上那位贴的图,第一个键为0x45,你把0x45高低位颠倒再化成十进制就是162.

void EXTI1_IRQHandler(void)
{       
u8 res=0;
    u8 OK=0; 
    u8 RODATA=0;    
while(1)
    {        
        if(RDATA)//有高脉冲出现
        {
            res=Pulse_Width_Check();//获得此次高脉冲宽度       
            if(res==250)break;//非有用信号
            if(res>=200&&res<250)OK=1; //获得前导位(4.5ms)
            else if(res>=85&&res<200)  //按键次数加一(2ms)
            {        
                Remote_Rdy=1;//接受到数据
                Remote_Cnt++;//按键次数增加
                break;
            }
            else if(res>=50&&res<85)RODATA=1;//1.5ms
            else if(res>=10&&res<50)RODATA=0;//500us
            if(OK)
            {
                Remote_Odr<<=1;         //你把低位移到了高位,读出来的数据高位完全颠倒。。。这里的程序需要改
                Remote_Odr+=RODATA; 
                Remote_Cnt=0; //按键次数清零
            }   
        }  
    }    
EXTI_ClearITPendingBit(EXTI_Line1);  //清除EXTI1线路挂起位         
}  
问题出在图中标红处,原子哥,这是我的理解,冒犯之处,还请海涵哦。。。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165475
金钱
165475
注册时间
2010-12-1
在线时间
2115 小时
发表于 2014-8-3 23:44:51 | 显示全部楼层
回复【10楼】jsk:
---------------------------------
谢谢,确实是反过来的。。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

9

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2014-11-20
在线时间
1 小时
发表于 2014-11-29 21:39:07 | 显示全部楼层
void EXTI1_IRQHandler(void)
{       
    u8 res=0;
    u8 OK=0; 
    u8 RODATA=0;   
    u8 countOdr = 0;                  //更改第一处
while(1)
    {
        if(RDATA)//有高脉冲出现
        {
            res=Pulse_Width_Check();//获得此次高脉冲宽度       
            if(res==250) break;//非有用信号
            if(res>=200&&res<250)OK=1; //获得前导位(4.5ms)
            else if(res>=85&&res<200)  //按键次数加一(2ms)
            {         
                Remote_Rdy=1;//接受到数据
                Remote_Cnt++;//按键次数增加
                break;
            }
            else if(res>=50&&res<85)RODATA=1;//1.5ms
            else if(res>=10&&res<50)RODATA=0;//500us
            if(OK)                                                              //更改第二处
            {
Remote_Odr |= (RODATA<<(countOdr++));
Remote_Cnt = 0;
            }   
        }    
    }
EXTI->R=1<<1;      //清除中断标志位        
}  
不知这样修改后是不是就可以了?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165475
金钱
165475
注册时间
2010-12-1
在线时间
2115 小时
发表于 2014-11-29 23:35:23 | 显示全部楼层
回复【12楼】Don Fantom Juan:
---------------------------------
可以试试么?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

9

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2014-11-20
在线时间
1 小时
发表于 2014-11-30 20:22:15 | 显示全部楼层
回复【13楼】正点原子:
---------------------------------
等我万恶的快递到了……就试试。
回复 支持 反对

使用道具 举报

2

主题

102

帖子

1

精华

中级会员

Rank: 3Rank: 3

积分
281
金钱
281
注册时间
2014-12-21
在线时间
0 小时
发表于 2015-1-18 16:54:30 | 显示全部楼层
回复【10楼】jsk:
---------------------------------
这个也不好说就是错误,有点类似于编码的Intel与Motorola格式之分。
之前看了原子老师的代码觉得普通的NEC遥控器也能使唤,结果证明是错误的。
重新理了下代码,不难发现这里高低位是反着来的。
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
3
金钱
3
注册时间
2020-3-19
在线时间
0 小时
发表于 2020-3-19 12:08:00 | 显示全部楼层
那正确的方式是怎么改呀,求解
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
3
金钱
3
注册时间
2020-3-19
在线时间
0 小时
发表于 2020-3-19 12:08:40 | 显示全部楼层
exv 发表于 2015-1-18 16:54
回复【10楼】jsk:
---------------------------------
这个也不好说就是错误,有点类似于编码的Intel与 ...

那正确的方式是怎么改呀,求解
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-6 12:39

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表