STM32驱动红外遥控实验

一.红外遥控的介绍

红外遥控是一种无线,非解除控制技术,具有抗干扰能力强,信息传输可靠,功耗低。成本低,易实现等显著优点,被诸多电子设备,特别是家用电气广泛采用,并越来越多的应用到计算机系统中
同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。
在这里插入图片描述

二.红外编解码协议介绍

红外遥控的编码目前广泛使用的是:NEC Protocol 的PWM(脉冲宽度调制)和Philips RC-5 Protocol 的PPM(脉冲位置调制)。
PWM(脉冲宽度调制):以红外载波的占空比表示‘0’和‘1’
♦发射红外载波的时间固定,通过改变不发射载波的时间来改变占空比

在这里插入图片描述
在这里插入图片描述

三.红外编解码协议介绍

我们一般长用的是NEC码位,他所对应的协议就是,连续发送560us个的载波周期加上,560us不发送载波周期代表一个低电平。

连续发送560us个的载波周期,加上1680us不发送载波周期代表一个高电平。
在这里插入图片描述
当我们每次按下红外遥控上的按键,他就会发送一个NEC命令,然后我们通过红外探头接收,并进行解码就可以知道他所对应的操作。
一个NEC命令主要由,同步码,地址码(8位),地址反码(8位),控制码(8位),控制反码(8位),连发码组成。
当红外探头接收到一个同步码之后,就代可以开始接收正常的数据了,当接收完最后一个控制反码之后,我们等待100ms,如果超过100ms还是高电平,说明没用连发,按键松开。
在这里插入图片描述

四.代码编写

我们需要使用输入捕获的功能来捕获红外探头接收到的信号,并且进行解码。我们设置72分频,也就是1us的周期,计数10000个,也就是一次溢出为10ms。

#include "stm32f1xx_hal.h"
#include "remote.h"

TIM_HandleTypeDef tim4;
TIM_IC_InitTypeDef tim4_ic;
void remote_init()
{
	tim4.Instance = TIM4;
//	tim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
	tim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
	tim4.Init.CounterMode = TIM_COUNTERMODE_UP;
	tim4.Init.Period = 10000;       //10ms
	tim4.Init.Prescaler = 72 - 1;   //1us
	tim4.Init.RepetitionCounter = 0;
    HAL_TIM_IC_Init(&tim4);
	
	tim4_ic.ICFilter = 0x03;     //输入捕获滤波
	tim4_ic.ICPolarity = TIM_ICPOLARITY_RISING;       //捕获有效边缘
	tim4_ic.ICPrescaler = TIM_ICPSC_DIV1;         //捕获分屏值
	tim4_ic.ICSelection = TIM_ICSELECTION_DIRECTTI;   //直连
	
	HAL_TIM_IC_ConfigChannel(&tim4, &tim4_ic, TIM_CHANNEL_4);
	HAL_TIM_IC_Start_IT(&tim4,TIM_CHANNEL_4);
	__HAL_TIM_ENABLE_IT(&tim4, TIM_IT_UPDATE);
	
}



void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM4){
		__HAL_RCC_TIM4_CLK_ENABLE();
		__HAL_RCC_GPIOB_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_Initstruct;
		GPIO_Initstruct.Mode = GPIO_MODE_INPUT;
//		GPIO_Initstruct.Pull = GPIO_PULLUP;
		GPIO_Initstruct.Pin = GPIO_PIN_9;
		GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOB,&GPIO_Initstruct);
		
		HAL_NVIC_SetPriority(TIM4_IRQn,0,0);
		HAL_NVIC_EnableIRQ(TIM4_IRQn);
	}
}

接下来,我们进行捕获解码,我们的思路是,首先捕获一个上升沿,并将计数器清零,再捕获一个下降沿,此时将捕获到计数值读出,如果是在300-800之间,说明是数据0,如果是1400-1800之间,则代表数据1。如果是2000-2600则代表是连发码,如果是4200-4700则是同步码。注意,每次触发连发码之后都会将[3:0]定时器溢出位给清除。

void TIM4_IRQHandler(TIM_HandleTypeDef *htim)
{
	HAL_TIM_IRQHandler(&tim4);
}



/* 遥控器接收状态
 * [7]  : 收到了引导码标志
 * [6]  : 得到了一个按键的所有信息
 * [5]  : 保留
 * [4]  : 标记上升沿是否已经被捕获
 * [3:0]: 溢出计时器
 */
uint8_t g_remote_sta = 0;
uint32_t g_remote_data = 0; /* 红外接收到的数据 */
uint16_t g_remote_cnt = 0;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM4)
	{
		uint16_t dval;  /* 下降沿时计数器的值 */
		
		if(RDATA){   //捕获到上升沿
			__HAL_TIM_SET_CAPTUREPOLARITY(&tim4,TIM_CHANNEL_4,TIM_INPUTCHANNELPOLARITY_FALLING);//CC4P=1 设置为下降沿捕获
			__HAL_TIM_SET_COUNTER(&tim4, 0);  /* 清空定时器值 */
            g_remote_sta |= 0X10;                      /* 标记上升沿已经被捕获 */ // 0001 0000
		}
		else{
			dval=HAL_TIM_ReadCapturedValue(&tim4, TIM_CHANNEL_4);   //获取捕获寄存器里的值
			__HAL_TIM_SET_CAPTUREPOLARITY(&tim4,TIM_CHANNEL_4,TIM_INPUTCHANNELPOLARITY_RISING);//CC4P=0 设置为上降沿捕获
			if(g_remote_sta & 0X10){  //判断是否接受到了上升沿
				if(g_remote_sta & 0X80){
					if(dval >300 && dval <800){  //接收到低电平
						g_remote_data >>= 1;   
						g_remote_data &= ~(0x80000000);   //将接收到的低电平放在最高位上。
					}
					else if(dval >1400 && dval <1800){
						g_remote_data >>= 1;   
						g_remote_data |= (0x80000000);   //将接收到的低电平放在最高位上。
					}
					else if(dval > 2000 && dval < 3000){  //得到按键键值增加的信息 2500为标准值2.5ms */
						g_remote_cnt++;
						 g_remote_sta &= 0XF0;   /* 清空计时器 */
					}
				}
				else if(dval >4200 && dval < 4700){  //判断是不是接受到了引导码
						g_remote_sta |= 1 << 7; /* 标记成功接收到了引导码 */
						g_remote_cnt = 0;       /* 清除按键次数计数器 */
					}
			}
			
			g_remote_sta &=~(0x10);   //清除上升沿标志位
		}
	}
}

当定时器溢出时,就代表没有遥控器按下,我就清除同位码标志位,并且g_remote_sta [3:0]: 溢出计时器开始自增,当溢出超过14次时,也就是130ms,则代表没用按键按下。接收数据结束。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM4)
	{
		if(g_remote_sta & 0x80){  //上次有数据被接收到了
			g_remote_sta &= ~0x10;  //取消上升沿标记
			
			 if ((g_remote_sta & 0X0F) == 0X00)
            {
                g_remote_sta |= 1 << 6; /* 标记已经完成一次按键的键值信息采集 */
            }
			
			 if ((g_remote_sta & 0X0F) < 14)   //判断是否溢出,如果溢出超过14次,也就是130ms,就代表没用按键按下,接收数据结束
            {
                g_remote_sta++;
            }
            else
            {
                g_remote_sta &= ~(1 << 7);    /* 清空引导标识 */
                g_remote_sta &= 0XF0;         /* 清空计数器 */
            }
		}
	}
}

接下来是红外扫描部分,每次进入先判断按键 * [6] : 得到了一个按键的所有信息,这个标志位是否被置为1,接下来将地址码和地址反码提取出来进行判断,判断完成后提取出控制码。

**
 * @brief       处理红外按键(类似按键扫描)
 * @param       无
 * @retval      0   , 没有任何按键按下
 *              其他, 按下的按键键值
 */

uint8_t remote_scan(void)
{
	uint8_t sta = 0;
	uint8_t t1,t2;
	if(g_remote_sta & (1 << 6)){   //判断是否接收到同位码
		 t1 = g_remote_data;                 /* 得到地址码 */
        t2 = (g_remote_data >> 8) & 0xff;   /* 得到地址反码 */
		if(t1 == (uint8_t)~t2 && t1 == 0){  //REMOTE_ID       0
			t1 = (g_remote_data >> 16) & 0xff;   //获取键值
            t2 = (g_remote_data >> 24) & 0xff;

            if (t1 == (uint8_t)~t2)
            {
                sta = t1;           /* 键值正确 */
            }
		}
		
		
		if ((sta == 0) || ((g_remote_sta & 0X80) == 0)) /* 按键数据错误/遥控已经没有按下了 */
        {
            g_remote_sta &= ~(1 << 6);  /* 清除接收到有效按键标识 */
            g_remote_cnt = 0;           /* 清除按键次数计数器 */
        }
		
	}
	return sta;
}

在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐