stm32正点原子教程(正点原子STM32Mini板资料连载)

1)实验平台:正点原子STM32mini开发板

2)摘自《正点原子STM32 不完全手册(HAL 库版)关注官方微信号公众号,获取更多资料:正点原子

stm32正点原子教程(正点原子STM32Mini板资料连载)(1)

第二十七章 红外遥控实验

本章,我们将向大家介绍如何通过 STM32 来解码红外遥控器的信号。ALIENTKMiniSTM32

开发板标配了红外接收头和一个小巧的红外遥控器。在本章中,我们将利用 STM32 的输入捕

获功能,解码开发板标配的这个红外遥控器的编码信号,并将解码后的键值 TFTLCD 模块上显

示出来。本章分为如下几个部分:

27.1 红外遥控简介

27.2 硬件设计

27.3 软件设计

27.4 下载验证

27.1 红外遥控简介

红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成

本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计

算机系统中。

由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设

计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率

或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有

相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用

电器上普及红外线遥控提供了极大的方面。由于红外线为不可见光,因此对环境影响很小,再

由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影

响临近的无线电设备。

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

PPM(脉冲位置调制) 的 RC-5 协议的。ALIENTEK MiniSTM32 开发板配套的遥控器使用的是

NEC 协议,其特征如下:

1、8 位地址和 8 位指令长度;

2、地址和命令 2 次传输(确保可靠性)

3、PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;

4、载波频率为 38Khz;

5、位时间为 1.125ms 或 2.25ms;

NEC 码的位定义:一个脉冲对应 560us 的连续载波,一个逻辑 1 传输需要 2.25ms(560us

脉冲 1680us 低电平),一个逻辑 0 的传输需要 1.125ms(560us 脉冲 560us 低电平)。而遥控

接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到

的信号为:逻辑 1 应该是 560us 低 1680us 高,逻辑 0 应该是 560us 低 560us 高。

NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码

由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是

8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可

用于校验)。

我们遥控器的按键▽按下时,从红外接收头端收到的波形如图 27.1.1 所示:

stm32正点原子教程(正点原子STM32Mini板资料连载)(2)

图 27.1.1 按键 2 所对应的红外波形

从图 27.1.1 中可以看到,其地址码为 0,控制码为 168。可以看到在 100ms 之后,我们还

收到了几个脉冲,这是 NEC 码规定的连发码(由 9ms 低电平 2.5m 高电平 0.56ms 低电平

97.94ms 高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,

即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数。

第十四章我们曾经介绍过利用输入捕获来测量高电平的脉宽,本章解码红外遥控信号,刚

好可以利用输入捕获的这个功能来实现遥控解码。关于输入捕获的介绍,请参考第十四章的内

容。

27.2 硬件设计

本实验采用定时器的输入捕获功能实现红外解码,本章实验功能简介:开机在 LCD 上显示

一些信息之后,即进入等待红外触发,如过接收到正确的红外信号,则解码,并在 LCD 上显示

键值和所代表的意义,以及按键次数等信息。同样我们也是用 LED0 来指示程序正在运行。

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

1) 指示灯 DS0

2) TFTLCD 模块(带触摸屏)

3) 红外接收头

4) 红外遥控器

前两个,在之前的实例已经介绍过了,遥控器属于外部器件,遥控接收头在板子上,与

MCU 的连接原理图如 27.2.1 所示:

stm32正点原子教程(正点原子STM32Mini板资料连载)(3)

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

红外遥控接收头通过 P2 与 P3,连接在 STM32 的 PA1(TIM5_CH2)上。硬件上,我们只

需要拿一个跳线帽把 RMT 和 PA1 短接即可(默认已经短接)。然后,程序将 TIM5_CH2 设计

为输入捕获,然后将收到的脉冲信号解码就可以了。

开发板配套的红外遥控器外观如图 27.2.2 所示:

stm32正点原子教程(正点原子STM32Mini板资料连载)(4)

图 27.2.2 红外遥控器

27.3 软件设计

打开我们光盘的红外遥控器实验工程,可以看到我们添加了remote.c和remote.h两个文件,

同时因为我们使用的是输入捕获,所以还用到定时器相关的库函数源文件 stm32f1xx_hal_tim.c

和头文件 stm32f1xx_hal_tim.h。

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

TIM_HandleTypeDef TIM5_Handler; //定时器 5 句柄

//红外遥控初始化

//设置 IO 以及 TIM4_CH4 的输入捕获

void Remote_Init(void)

{

TIM_IC_InitTypeDef TIM5_CH2Config;

TIM5_Handler.Instance=TIM5; //通用定时器 5

TIM5_Handler.Init.Prescaler=(72-1); //预分频器,1M 的计数频率,1us 加 1.

TIM5_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器

TIM5_Handler.Init.Period=10000; //自动装载值

TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;

HAL_TIM_IC_Init(&TIM5_Handler);

//初始化 TIM1 输入捕获参数

TIM5_CH2Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获

TIM5_CH2Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到 TI2 上

TIM5_CH2Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频

TIM5_CH2Config.ICFilter=0x03; //IC4F=0003 8 个定时器时钟周期滤波

HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH2Config,

TIM_CHANNEL_2);//配置 TIM5 通道 2

HAL_TIM_IC_Start_IT(&TIM5_Handler,TIM_CHANNEL_2); //开始捕获 TIM5 的通道 2

__HAL_TIM_ENABLE_IT(&TIM5_Handler,TIM_IT_UPDATE); //使能更新中断

}

//定时器 1 底层驱动,时钟使能,引脚配置

//此函数会被 HAL_TIM_IC_Init()调用

//htim:定时器 1 句柄

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)

{

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM5_CLK_ENABLE(); //使能 TIM5 时钟

__HAL_RCC_GPIOA_CLK_ENABLE();

//开启 GPIOA 时钟

GPIO_Initure.Pin=GPIO_PIN_1; //PA1

GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; //复用输入

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

HAL_NVIC_SetPriority(TIM5_IRQn,1,3);//设置中断优先级,抢占优先级 1,子优先级 3

HAL_NVIC_EnableIRQ(TIM5_IRQn); //开启 ITM4 中断

}

//遥控器接收状态

//[7]:收到了引导码标志

//[6]:得到了一个按键的所有信息

//[5]:保留

//[4]:标记上升沿是否已经被捕获

//[3:0]:溢出计时器

u8 RmtSta=0;

u16 Dval;

//下降沿时计数器的值

u32 RmtRec=0;

//红外接收到的数据

u8 RmtCnt=0;

//按键按下的次数

//定时器 5 中端服务函数

void TIM5_IRQHandler(void)

{

HAL_TIM_IRQHandler(&TIM5_Handler);//定时器共用处理函数

}

//定时器更新(溢出)中断回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

if(htim->Instance==TIM5)

{

if(RmtSta&0x80)//上次有数据被接收到了

{

RmtSta&=~0X10;

//取消上升沿已经被捕获标记

if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;

//标记已经完成一次按键的键值信息采集

if((RmtSta&0X0F)<14)RmtSta ;

else

{

RmtSta&=~(1<<7);//清空引导标识

RmtSta&=0XF0; //清空计数器

}

}

}

}

//定时器输入捕获中断回调函数

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行

{

if(htim->Instance==TIM5)

{

if(RDATA)//上升沿捕获

{

TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_2);

//一定要先清除原来的设置!!

TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_2,

TIM_ICPOLARITY_FALLING);//CC2P=1 设置为下降沿捕获

__HAL_TIM_SET_COUNTER(&TIM5_Handler,0); //清空定时器值

RmtSta|=0X10;

//标记上升沿已经被捕获

}else //下降沿捕获

{

Dval=HAL_TIM_ReadCapturedValue(&TIM5_Handler,TIM_CHANNEL_2);

//读取 CCR2 也可以清 CC2IF 标志位

TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_2);

//一定要先清除原来的设置!!

TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_2,

TIM_ICPOLARITY_RISING);//配置 TIM5 通道 2 上升沿捕获

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);

}

}

}

//处理红外键盘

//返回值:

//

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;

}

我们介绍一下该部分代码,首先是 Remote_Init 函数,该函数用于初始化 IO 口,并配置

TIM5_CH2 为输入捕获,并设置其相关参数。TIM5_IRQHandler 函数是 TIM5 的中断服务函数,

在该函数里面,实现对红外信号的高电平脉冲的捕获,同时根据我们之前简介的协议内容来解

码 ,该函数用到几个全局变量,用于辅助解码,并存储解码结果。

这里简单介绍一下高电平捕获思路:首先输入捕获设置的是捕获上升沿,在上升沿捕获到

以后,立即设置输入捕获模式为捕获下降沿(以便捕获本次高电平),然后,清零定时器的计数

器值,并标记捕获到上升沿。当下降沿到来时,再次进入捕获中断服务函数,立即更改输入捕

获模式为捕获上升沿(以便捕获下一次高电平),然后处理此次捕获到的高电平。

最后是 Remote_Scan 函数,该函用来扫描解码结果,相当于我们的按键扫描,输入捕获解

码的红外数据,通过该函数传送给其他程序。

保存 remote.c,然后把该文件加入 HARDWARE 组下。接下来打开 remote.h 在该文件里面加

入如下代码:

#ifndef __RED_H

#define __RED_H

#include "sys.h"

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

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

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

#define REMOTE_ID 0

extern u8 RmtCnt;

//按键按下的次数

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

u8 Remote_Scan(void);

#endif

这里的 REMOTE_ID 就是我们开发板配套的遥控器的识别码,对于其他遥控器可能不一样,

只要修改这个为你所使用的遥控器的一致就可以了。remote.h 其他是一些函数的声明,我们不

做过多讲解,最后我们看看主函数代码如下:

int main(void)

{

u8 key;

u8 t=0;

u8 *str=0;

HAL_Init();

//初始化 HAL 库

Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M

delay_init(72);

//初始化延时函数

uart_init(115200);

//初始化串口

usmart_dev.init(84);

//初始化 USMART

LED_Init();

//初始化 LED

KEY_Init();

//初始化按键

LCD_Init();

//初始化 LCD

Remote_Init();

//初始化 红外接收

POINT_COLOR=RED;

LCD_ShowString(30,50,200,16,16,"Mini STM32");

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,"2019/11/15");

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

LCD_ShowString(30,150,200,16,16,"KEYCNT:");

LCD_ShowString(30,170,200,16,16,"SYMBOL:");

POINT_COLOR=BLUE;//设置字体为蓝色

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 上面。

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

27.4 下载验证

在代码编译成功之后,我们通过下载代码到 ALIENTEK MiniSTM32 开发板上,可以看到

LCD 显示如图 27.4.1 所示的内容:

stm32正点原子教程(正点原子STM32Mini板资料连载)(5)

图 27.4.1 程序运行效果图

此时我们通过遥控器按下不同的按键,则可以看到 LCD 上显示了不同按键的键值以及按键

次数和对应的遥控器上的符号。如图 27.4.2 所示:

stm32正点原子教程(正点原子STM32Mini板资料连载)(6)

图 27.4.2 解码成功

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。