红外遥控器的代码实现方法有两种:
一种是外部中断实现,一种是定时器外部捕获方法实现,外部中断和定时器捕获两种实现方法各有千秋。根据实际的硬件需求进行选择,其中外部中断的实现的方法对应的.c文件如下:
static REMOTE_CTRL remote_ctrl = {0};
#define pRemote_ctrl (&remote_ctrl)
void delay_us(uint32_t microseconds)
{
// SystemCoreClock holds the system clock frequency.
uint32_t loops = (SystemCoreClock / 1000000L) * microseconds / 6; // 每次循环大约4个时钟周期
while (loops > 0)
{
loops--;
__asm("NOP"); // 调用一个NOP汇编指令,确保循环体不会被优化掉
}
}
//读取引脚的电平
#define IrDa_DATA_IN() HAL_GPIO_ReadPin(IR_RECEIVER_GPIO_Port,IR_RECEIVER_Pin)
void Driver_remote_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*Configure GPIO pins :IR_RECEIVER_PORT IR_RECEIVER_PIN */
GPIO_InitStruct.Pin = IR_RECEIVER_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(IR_RECEIVER_GPIO_Port, &GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI4_15_IRQn,3,3);
HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
memset(pRemote_ctrl, 0, sizeof(REMOTE_CTRL));
}
static uint8_t Infrared_Remote_Delay(void)
{
uint8_t tim = 0;
while(IrDa_DATA_IN())
{
tim++;
delay_us(20);//20us
if(tim == 250)
{
return tim;
}
}
return tim;
}
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
uint8_t pulse_time = 0; //脉冲时间
uint8_t leader_code_flag = 0; //引导码标志位
uint8_t irda_data = 0; //红外数据
while(1)
{
if(IrDa_DATA_IN())
{
pulse_time = Infrared_Remote_Delay();
// SEGGER_RTT_printf(0,"pulse_time:%d\r\n",pulse_time);
/* >=5ms 不是有用信号 当出现干扰或者连发码时,也会break跳出while(1)循环 */
if( pulse_time >= 250 )
{
break; /* 跳出while(1)循环 */
}
if(pulse_time >= 145 && pulse_time < 200) /* 获得前导位 4ms~4.5ms */
{
leader_code_flag = 1;
}
else if(pulse_time >= 15 && pulse_time < 25) /* 0.56ms: 0.2ms~1ms */
{
irda_data = 0;
}
else if(pulse_time >= 50 && pulse_time < 65) /* 1.68ms:1ms~2ms */
{
irda_data =1 ;
}
else if( pulse_time >= 75 && pulse_time <= 85 ) /* 2.1ms:2ms~4ms */
{/* 连发码,在第二次中断出现 */
pRemote_ctrl->frame_flag = 1; /* 一帧数据接收完成 */
pRemote_ctrl->frame_cnt++; /* 按键次数加1 */
break; /* 跳出while(1)循环 */
}
if( leader_code_flag == 1 )
{/* 在第一次中断中完成 */
pRemote_ctrl->frame_data <<= 1;
pRemote_ctrl->frame_data += irda_data;
pRemote_ctrl->frame_cnt = 0;
}
}
}
}
/*
* 帧数据有4个字节,第一个字节是遥控的ID,第二个字节是第一个字节的反码
* 第三个数据是遥控的真正的键值,第四个字节是第三个字节的反码
*/
uint8_t IrDa_Process(void)
{
uint8_t first_byte, sec_byte, tir_byte, fou_byte;
uint8_t res = 0;
first_byte = pRemote_ctrl->frame_data >> 24;
sec_byte = (pRemote_ctrl->frame_data>>16) & 0xff;
tir_byte = pRemote_ctrl->frame_data >> 8;
fou_byte = pRemote_ctrl->frame_data;
if(pRemote_ctrl->frame_flag == 1)/* 记得清标志位 */
{
if((first_byte == (uint8_t) ~sec_byte) && ((first_byte == 0) || (first_byte == 1)))//first_byte 遥控器 ID
{
if(tir_byte == (uint8_t) ~fou_byte)
{
// HAL_Delay(100);
res = tir_byte;
}
else
{
res = 255;
}
}
pRemote_ctrl->frame_flag = 0;
}
return res; /* 错误返回 */
}
上述.c文件对应的.h文件如下:
#ifndef __iruser_H__
#define __iruser_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
//#include "main.h"
typedef struct
{
uint32_t frame_data; /* 一帧数据缓存 */
uint8_t frame_cnt ;
uint8_t frame_flag; /* 一帧数据接收完成标志 */
}REMOTE_CTRL;
uint8_t IrDa_Process(void);
#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */
上述方式方法仅针对红外接收模块硬件连接在外部中断口类型,而接下来的代码主要进行了外部捕获就是说外设在定时器外部捕获的端口。.c文件如下:
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim7;
/* TIM1 init function */
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 63;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 10000;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2); //开始捕获TIM3的通道4
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE); //使能更新中断
/* USER CODE END TIM1_Init 2 */
}
/* TIM3 init function */
void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 6399;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 9999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 50;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
HAL_TIM_MspPostInit(&htim3);
}
/* TIM7 init function */
void MX_TIM7_Init(void)
{
/* USER CODE BEGIN TIM7_Init 0 */
/* USER CODE END TIM7_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM7_Init 1 */
/* USER CODE END TIM7_Init 1 */
htim7.Instance = TIM7;
htim7.Init.Prescaler = 1600-1;
htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
htim7.Init.Period = 15;
htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM7_Init 2 */
/* USER CODE END TIM7_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM1 GPIO Configuration
PA9 ------> TIM1_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* TIM1 interrupt Init */
HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM7)
{
/* USER CODE BEGIN TIM7_MspInit 0 */
/* USER CODE END TIM7_MspInit 0 */
/* TIM7 clock enable */
__HAL_RCC_TIM7_CLK_ENABLE();
/* TIM7 interrupt Init */
HAL_NVIC_SetPriority(TIM7_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(TIM7_IRQn);
/* USER CODE BEGIN TIM7_MspInit 1 */
/* USER CODE END TIM7_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(timHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspPostInit 0 */
/* USER CODE END TIM3_MspPostInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM3 GPIO Configuration
PB5 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM3_MspPostInit 1 */
/* USER CODE END TIM3_MspPostInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspDeInit 0 */
/* USER CODE END TIM1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/**TIM1 GPIO Configuration
PA9 ------> TIM1_CH2
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9);
/* TIM1 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM1_CC_IRQn);
/* USER CODE BEGIN TIM1_MspDeInit 1 */
/* USER CODE END TIM1_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspDeInit 0 */
/* USER CODE END TIM3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM3_CLK_DISABLE();
/* USER CODE BEGIN TIM3_MspDeInit 1 */
/* USER CODE END TIM3_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM7)
{
/* USER CODE BEGIN TIM7_MspDeInit 0 */
/* USER CODE END TIM7_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM7_CLK_DISABLE();
/* TIM7 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM7_IRQn);
/* USER CODE BEGIN TIM7_MspDeInit 1 */
/* USER CODE END TIM7_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
void TIM_SetTIM2Compare1(uint32_t compare)
{
TIM3->CCR2 = compare;
}
//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留
//[4]:标记上升沿是否已经被捕获
//[3:0]:溢出计时器
uint8_t RmtSta = 0;
uint16_t Dval; //下降沿时计数器的值
uint32_t RmtRec = 0; //红外接收到的数据
uint8_t RmtCnt = 0; //按键按下的次数
/**
* @brief TIM3中断服务函数
*
* @param void
*
* @return void
*/
void TIM1_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim1);
}
/**
* @brief TIM`更新(溢出)中断回调函数
*
* @param htim 定时器句柄
*
* @return void
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM1)
{
if(RmtSta & 0x80) //上次有数据被接收到了
{
RmtSta &= ~0X10; //取消上升沿已经被捕获标记
if((RmtSta & 0X0F) == 0X00)RmtSta |= 1 << 6; //标记已经完成一次按键的键值信息采集
if((RmtSta & 0X0F) < 14)RmtSta++;
else
{
RmtSta &= ~(1 << 7); //清空引导标识
RmtSta &= 0XF0; //清空计数器
}
}
}
}
/**
* @brief 定时器输入捕获中断回调函数
*
* @param htim 定时器句柄
*
* @return void
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
{
if(htim->Instance == TIM1)
{
if(RDATA)//上升沿捕获
{
TIM_RESET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_2, TIM_ICPOLARITY_FALLING); //CC1P=1 设置为下降沿捕获
__HAL_TIM_SET_COUNTER(&htim1, 0); //清空定时器值
RmtSta |= 0X10; //标记上升沿已经被捕获
}
else //下降沿捕获
{
Dval = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_2); //读取CCR1也可以清CC1IF标志位
TIM_RESET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_2); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_2, TIM_ICPOLARITY_RISING); //配置TIM5通道1上升沿捕获
if(RmtSta & 0X10) //完成一次高电平捕获
{
if(RmtSta & 0X80) //接收到了引导码
{
if(Dval > 200 && Dval < 900) //560为标准值,560us
{
RmtRec <<= 1; //左移一位.
RmtRec |= 0; //接收到0
}
else if(Dval > 1200 && Dval < 2000) //1680为标准值,1680us
{
RmtRec <<= 1; //左移一位.
RmtRec |= 1; //接收到1
}
else if(Dval > 2000 && Dval < 3000) //得到按键键值增加的信息 2500为标准值2.5ms
{
RmtCnt++; //按键次数增加1次
RmtSta &= 0XF0; //清空计时器
}
}
else if(Dval > 4000 && Dval < 5000) //4500为标准值4.5ms
{
RmtSta |= 1 << 7; //标记成功接收到了引导码
RmtCnt = 0; //清除按键次数计数器
}
}
RmtSta &= ~(1 << 4);
}
}
}
/**
* @brief 处理红外键盘
*
* @param void
*
* @return u8 0,没有任何按键按下
* 其他,按下的按键键值
*/
uint8_t Remote_Scan(void)
{
uint8_t sta = 0;
uint8_t t1, t2;
if(RmtSta & (1 << 6)) //得到一个按键的所有信息了
{
t1 = RmtRec >> 24; //得到地址码
t2 = (RmtRec >> 16) & 0xff; //得到地址反码
if(t1 == (uint8_t)~t2) //检验遥控识别码(ID)及地址
{
t1 = RmtRec >> 8;
t2 = RmtRec;
if(t1 == (uint8_t)~t2)
sta = t1
; //键值正确
}
if((sta == 0) || ((RmtSta & 0X80) == 0)) //按键数据错误/遥控已经没有按下了
{
RmtSta &= ~(1 << 6); //清除接收到有效按键标识
RmtCnt = 0; //清除按键次数计数器
}
}
return sta;
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
上文为定时器外部捕获方法,.h文件不做赘述就只有初始化和调用扫描按键的函数。属于比较简单的。上述为一个板子用两种方法实现的代码。参考即可。