红外遥控实验
红外遥控的编码目前广泛使用的是:NEC Protocol 的PWM(脉冲宽度调制)和Philips RC-5 Protocol 的PPM(脉冲位置调制)。PWM(脉冲宽度调制):以红外载波的占空比表示‘0’和‘1’♦发射红外载波的时间固定,通过改变不发射载波的时间来改变占空比我们一般长用的是NEC码位,他所对应的协议就是,连续发送560us个的载波周期加上,560us不发送载波周期代表一个低电平。
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;
}

更多推荐



所有评论(0)