最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

使用PY003基于外部中断+定时器的方式实现NEC红外解码

IT圈 admin 1浏览 0评论

使用PY003基于外部中断+定时器的方式实现NEC红外解码

写在前边

最近项目用到一款遥控器是38K红外载波,NEC协议的,找了很多帖子有看到用外部中断下降沿判断(但可惜判定数据的方式是while在外部中断里面死等的),有看到用100us定时器定时刷来判断,感觉都不太适合用在我这个工程里,最后没办法自己写了一个,使用没问题,不确定有没有bug。暂且记录着。

配置定时器: 10ms定时中断一次

void TIM3_Init(void)
{/* TIM3 配置*/Tim3Handle.Instance = TIM3;                                           /* 选择TIM3 */Tim3Handle.Init.Period            = 10000 - 1;                        /* 自动重装载值 */Tim3Handle.Init.Prescaler         = 24 - 1;                           /* 预分频为1000-1 */Tim3Handle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;           /* 时钟不分频 */Tim3Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;               /* 向上计数 */Tim3Handle.Init.RepetitionCounter = 1 - 1;                            /* 不重复计数 */Tim3Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;   /* 自动重装载寄存器没有缓冲 */if (HAL_TIM_Base_Init(&Tim3Handle) != HAL_OK)                         /* TIM3初始化 */{APP_ErrorHandler();}if (HAL_TIM_Base_Start_IT(&Tim3Handle) != HAL_OK)                     /* TIM3使能启动,并使能中断 */{APP_ErrorHandler();}
}

配置GPIO:GPIO_InitStruct.Mode这里将IO的外部中断配置成上升沿+下降沿中断

void GPIO_Init(void)
{__HAL_RCC_GPIOA_CLK_ENABLE();                           /* GPIOA时钟使能 *///PA5 红外解码输入GPIO_InitStruct.Pin   = GPIO_PIN_5 ;                    /* PIN脚 */GPIO_InitStruct.Mode  = GPIO_MODE_IT_RISING_FALLING ;	/* 工作模式 */GPIO_InitStruct.Pull  = GPIO_PULLUP ;	                /* 拉电阻 */GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH ;     /* IO时钟速度 */HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    }

主函数中开启定时器和外部中断:(因为是PA5 所以选外部中断5 外部中断4-15都是一个入口地址)

	TIM3_Init();HAL_TIM_Base_Stop_IT( &Tim3Handle );    //先关闭TIM3,等收到一个下降沿之后再打开TIM3->CNT = 0;                          //清零定时器,为下一次接受红外做准备/* 使能NVIC GPIO外部中断 */HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);

中断服务函数:
定时器的:

/*** @brief This function handles TIM3 Interrupt .*/
void TIM3_IRQHandler(void)
{__HAL_TIM_CLEAR_IT(&Tim3Handle, TIM_IT_UPDATE);         //最长10ms溢出中断,如果是超过10ms的溢出中断,说明此前为空闲,清零所有变量HAL_TIM_Base_Stop( &Tim3Handle );						//如果进入了定时器3的中断服务函数,说明是10ms溢出了,直接停止TIM3定时器TIM3->CNT = 0;IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;
}

外部中断5的:

/*** @brief This function handles EXIT Interrupt .*/
void EXTI4_15_IRQHandler(void)
{__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);IR.timer_count = TIM3->CNT;     //每次进外部中断,先保存当前的计数值 24M主频,24分频,CNT加1的时间为1usTIM3->CNT = 0;                  //清零寄存器if( GPIOA->IDR & 0x00000020 )   //检查PA5电平 上升沿    IR.timer_count的值为上一个下降沿到这次的上升沿,中间的间隔时间(即低电平时间){if(IR.IR_event == 0 && IR.f_head == 0)          //既没有开始接受01数据,也没有开始接受头码,那么进入接收头码判断{   if( IR.timer_count > 8900 && IR.timer_count < 9100)     //头码为9ms的低电平,允许误差100us{IR.f_head = 1;                                      //如果符合,开始判定头码}else if(IR.timer_count > 9100)                          //如果头码已经大于9.1ms 认为是错误{IR.f_head = 0;IR.IR_decode_bit = 0;                               //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}}else if(IR.IR_event == 1)                                   //如果头码判定正确,开始接受01数据{if(IR.timer_count > 500 && IR.timer_count < 650)        //所有的01数据,开头都是0.56ms的低电平{IR.get_bit = 1;                                     //如果符合,那么开始根据后面的高电平时间来判定此次为0还是1}else                                                    //否则清除所有数据{IR.f_head = 0;IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}}}else                                                            //上一个上升沿到这次的下降沿,之间的时间间隔(即高电平时间){if(IR.f_head==1)                                            //如果9ms的低电平成立了,开始判断是否有4.5ms的高电平时间{if( IR.timer_count > 4400 && IR.timer_count < 4600){IR.IR_event = 1;                                    //4.5ms成立,认为此次的头码成立,开始接收01数据}else{IR.IR_decode_bit = 0;                               //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}IR.f_head = 0;                                          //清除接受头码标志}if(IR.IR_event == 1)                                        //接收数据内容成立{if(IR.get_bit == 1)                                     //如果0.56ms的低电平已经成立,那么开始判断这次的高电平时间{if(IR.timer_count > 500 && IR.timer_count < 650)    //0.56ms低+0.56ms高,说明传输的数据为 0 {IR.IR_code <<= 1;IR.IR_decode_bit ++;}else if(IR.timer_count > 1500 && IR.timer_count < 1800)     //0.56ms低+1.68s高,说明传输的数据为 1{IR.IR_code <<= 1;   IR.IR_code ++;                                  //数据位置1IR.IR_decode_bit ++;}else                                                //如果0.56ms的低电平后接着的是不符合要求的高电平时间,那么清零所有数据{IR.f_head = 0;IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数IR.IR_event = 0;IR.IR_code = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}if( IR.IR_decode_bit == 32 )                        //接收了32位数据之后,开始判断内容{IR.IR_code_get = IR.IR_code;IR.low_8bit = IR.IR_code_get & 0x000000FF;      //对于本项目而言之后低16位数据有用,其中15-8为数据,7-0为数据反码IR.hig_8bit = (IR.IR_code_get & 0x0000FF00)>>8;IR.low_8bit ^= 0xFF;                            //拿反码异或1 得到取反数据if( IR.low_8bit == IR.hig_8bit )                //判定数据跟数据反码是否相符,如果相符则为有用数据,否则舍弃所有数据{IR.rece_ok = 1;} else{IR.rece_ok = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.IR_code_get = 0;}IR.IR_decode_bit = 0;IR.IR_code = 0;IR.f_head = 0;IR.IR_event = 0;}IR.get_bit = 0;                                     //每次处理完一位数据之后,清楚标志位,待下次0.56ms低电平成立之后再次判断}}}HAL_TIM_Base_Start_IT( &Tim3Handle );                           //每次进入外部中断都要启动计数器
}

IR结构体声明在main.h文件中:

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_hal.h"/* Private includes ----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/#define IR_CODE_GET_POWER (uint8_t)(0xE2)
#define IR_CODE_GET_SPEED (uint8_t)(0xA2)
#define IR_CODE_GET_TIMER (uint8_t)(0xA8)
#define IR_CODE_GET_SHAKE (uint8_t)(0x18)/* Exported variables prototypes ---------------------------------------------*/
extern UART_HandleTypeDef UartHandle;
extern __IO ITStatus UartReady;
extern TIM_HandleTypeDef TimHandle,Tim3Handle,Tim1Handle, Tim17Handle;
extern EXTI_HandleTypeDef exti_handle;
extern GPIO_InitTypeDef GPIO_InitStruct;typedef struct IR_decode
{uint32_t  timer_count; //定时器数据uint32_t  IR_code;     //读取到的红外码 uint32_t  IR_code_get;uint32_t  IR_decode_bit;//解码接收位数uint32_t  IR_event;    //红外输出事件uint8_t   data_recving_flag;//红外接收中的标志uint8_t   f_head;uint8_t   get_bit;uint8_t   low_8bit;uint8_t   hig_8bit;uint8_t   rece_ok;// int16_t   *send_data;//红外发送码数据的首地址// uint8_t   send_code_bits;//红外发送位// uint8_t   send_rep_times;//红外码重复发送次数// uint8_t   send_times;//切换档位所需要次数// uint8_t   send_delay;//多包间隔时间// uint8_t   data_send_flag;//红外发送标志 uint8_t   printf_datalen;}IR_struct;extern IR_struct IR;/* Exported functions prototypes ---------------------------------------------*/
void APP_ErrorHandler(void);#ifdef __cplusplus
}
#endif#endif /* __MAIN_H *//************************ (C) COPYRIGHT Puya *****END OF FILE******************/

结构体初始化在main.c中:

	IR_struct IR;           //红外部分需要用到的结构体变量声明IR.IR_code = 0;IR.IR_decode_bit = 0;IR.IR_event = 0;IR.timer_count = 0;IR.f_head = 0;IR.get_bit = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;

C文件内容是:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "string.h"
#include "setup.h"/* Private define ------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef UartHandle;
__IO ITStatus UartReady = RESET;
TIM_HandleTypeDef TimHandle,Tim3Handle,Tim1Handle, Tim17Handle;
TIM_OC_InitTypeDef Tim17OCHandle,Tim16OCHandle;
I2C_HandleTypeDef I2cHandle;
TIM_IC_InitTypeDef sConfig;
TIM_SlaveConfigTypeDef   sSlaveConfig;
EXTI_HandleTypeDef exti_handle;
GPIO_InitTypeDef GPIO_InitStruct;IR_struct IR;           //红外部分需要用到的结构体变量声明/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*//*** @brief  应用程序入口函数.* @retval int*/
int main(void)
{   /* 初始化所有外设,Flash接口,SysTick */HAL_Init();/* 系统时钟配置 */APP_SystemClockConfig();GPIO_Init();TIM3_Init();HAL_TIM_Base_Stop_IT( &Tim3Handle );    //先关闭TIM3,等收到一个下降沿之后再打开TIM3->CNT = 0;                          //清零定时器,为下一次接受红外做准备/* 使能NVIC GPIO外部中断 */HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);IR.IR_code = 0;IR.IR_decode_bit = 0;IR.IR_event = 0;IR.timer_count = 0;IR.f_head = 0;IR.get_bit = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;while (1){if( IR.rece_ok ){switch (IR.hig_8bit){case IR_CODE_GET_POWER :break;case IR_CODE_GET_SHAKE :break;case IR_CODE_GET_SPEED :break;case IR_CODE_GET_TIMER :break;  default:break;}IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;boot_cheak_link_over = 0;IR.rece_ok = 0;}}
}/*** @brief  错误执行函数* @param  无* @retval 无*/
void APP_ErrorHandler(void)
{/* 无限循环 */while (1){}
}#ifdef  USE_FULL_ASSERT
/*** @brief  输出产生断言错误的源文件名及行号* @param  file:源文件名指针* @param  line:发生断言错误的行号* @retval 无*/
void assert_failed(uint8_t *file, uint32_t line)
{/* 用户可以根据需要添加自己的打印信息,例如: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* 无限循环 */while (1){}
}
#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT Puya *****END OF FILE******************/

贴一下抓到的接收头波形:

数据内容发完之后如果一直按住不松手,那么发过来的其实就是重复码。

代码内容比较乱,其实核心就是开启一个上升沿+下降沿的中断,判断进入外部中断时是什么电平,如果进入外部中断时是高电平,说明TIM3的CNT存放的就是这次上升沿与上一次的下降沿之间低电平的持续时间;如果进入外部中断时是低电平,说明TIM3的CNT存放的就是这次下降沿与上一次的上升沿之间高电平的持续时间。通过不停进外部中断判断TIM3的CNT数值来判断高低电平的持续时间,完成解码。这种方式不会堵塞MCU,比进入外部中断之后判定电平死等的那种方式更加合理。

更加详细的内容可以看这篇博客,这个大佬写得比较详细,不过他用的是进中断后while的方式来判定时间:
链接: STM32入门开发: NEC红外线协议解码(超低成本无线传输方案)

使用PY003基于外部中断+定时器的方式实现NEC红外解码

写在前边

最近项目用到一款遥控器是38K红外载波,NEC协议的,找了很多帖子有看到用外部中断下降沿判断(但可惜判定数据的方式是while在外部中断里面死等的),有看到用100us定时器定时刷来判断,感觉都不太适合用在我这个工程里,最后没办法自己写了一个,使用没问题,不确定有没有bug。暂且记录着。

配置定时器: 10ms定时中断一次

void TIM3_Init(void)
{/* TIM3 配置*/Tim3Handle.Instance = TIM3;                                           /* 选择TIM3 */Tim3Handle.Init.Period            = 10000 - 1;                        /* 自动重装载值 */Tim3Handle.Init.Prescaler         = 24 - 1;                           /* 预分频为1000-1 */Tim3Handle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;           /* 时钟不分频 */Tim3Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;               /* 向上计数 */Tim3Handle.Init.RepetitionCounter = 1 - 1;                            /* 不重复计数 */Tim3Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;   /* 自动重装载寄存器没有缓冲 */if (HAL_TIM_Base_Init(&Tim3Handle) != HAL_OK)                         /* TIM3初始化 */{APP_ErrorHandler();}if (HAL_TIM_Base_Start_IT(&Tim3Handle) != HAL_OK)                     /* TIM3使能启动,并使能中断 */{APP_ErrorHandler();}
}

配置GPIO:GPIO_InitStruct.Mode这里将IO的外部中断配置成上升沿+下降沿中断

void GPIO_Init(void)
{__HAL_RCC_GPIOA_CLK_ENABLE();                           /* GPIOA时钟使能 *///PA5 红外解码输入GPIO_InitStruct.Pin   = GPIO_PIN_5 ;                    /* PIN脚 */GPIO_InitStruct.Mode  = GPIO_MODE_IT_RISING_FALLING ;	/* 工作模式 */GPIO_InitStruct.Pull  = GPIO_PULLUP ;	                /* 拉电阻 */GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH ;     /* IO时钟速度 */HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    }

主函数中开启定时器和外部中断:(因为是PA5 所以选外部中断5 外部中断4-15都是一个入口地址)

	TIM3_Init();HAL_TIM_Base_Stop_IT( &Tim3Handle );    //先关闭TIM3,等收到一个下降沿之后再打开TIM3->CNT = 0;                          //清零定时器,为下一次接受红外做准备/* 使能NVIC GPIO外部中断 */HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);

中断服务函数:
定时器的:

/*** @brief This function handles TIM3 Interrupt .*/
void TIM3_IRQHandler(void)
{__HAL_TIM_CLEAR_IT(&Tim3Handle, TIM_IT_UPDATE);         //最长10ms溢出中断,如果是超过10ms的溢出中断,说明此前为空闲,清零所有变量HAL_TIM_Base_Stop( &Tim3Handle );						//如果进入了定时器3的中断服务函数,说明是10ms溢出了,直接停止TIM3定时器TIM3->CNT = 0;IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;
}

外部中断5的:

/*** @brief This function handles EXIT Interrupt .*/
void EXTI4_15_IRQHandler(void)
{__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);IR.timer_count = TIM3->CNT;     //每次进外部中断,先保存当前的计数值 24M主频,24分频,CNT加1的时间为1usTIM3->CNT = 0;                  //清零寄存器if( GPIOA->IDR & 0x00000020 )   //检查PA5电平 上升沿    IR.timer_count的值为上一个下降沿到这次的上升沿,中间的间隔时间(即低电平时间){if(IR.IR_event == 0 && IR.f_head == 0)          //既没有开始接受01数据,也没有开始接受头码,那么进入接收头码判断{   if( IR.timer_count > 8900 && IR.timer_count < 9100)     //头码为9ms的低电平,允许误差100us{IR.f_head = 1;                                      //如果符合,开始判定头码}else if(IR.timer_count > 9100)                          //如果头码已经大于9.1ms 认为是错误{IR.f_head = 0;IR.IR_decode_bit = 0;                               //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}}else if(IR.IR_event == 1)                                   //如果头码判定正确,开始接受01数据{if(IR.timer_count > 500 && IR.timer_count < 650)        //所有的01数据,开头都是0.56ms的低电平{IR.get_bit = 1;                                     //如果符合,那么开始根据后面的高电平时间来判定此次为0还是1}else                                                    //否则清除所有数据{IR.f_head = 0;IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}}}else                                                            //上一个上升沿到这次的下降沿,之间的时间间隔(即高电平时间){if(IR.f_head==1)                                            //如果9ms的低电平成立了,开始判断是否有4.5ms的高电平时间{if( IR.timer_count > 4400 && IR.timer_count < 4600){IR.IR_event = 1;                                    //4.5ms成立,认为此次的头码成立,开始接收01数据}else{IR.IR_decode_bit = 0;                               //电平计数超过了码元长度 清除位数计数IR.IR_code = 0;IR.IR_event = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}IR.f_head = 0;                                          //清除接受头码标志}if(IR.IR_event == 1)                                        //接收数据内容成立{if(IR.get_bit == 1)                                     //如果0.56ms的低电平已经成立,那么开始判断这次的高电平时间{if(IR.timer_count > 500 && IR.timer_count < 650)    //0.56ms低+0.56ms高,说明传输的数据为 0 {IR.IR_code <<= 1;IR.IR_decode_bit ++;}else if(IR.timer_count > 1500 && IR.timer_count < 1800)     //0.56ms低+1.68s高,说明传输的数据为 1{IR.IR_code <<= 1;   IR.IR_code ++;                                  //数据位置1IR.IR_decode_bit ++;}else                                                //如果0.56ms的低电平后接着的是不符合要求的高电平时间,那么清零所有数据{IR.f_head = 0;IR.IR_decode_bit = 0; //电平计数超过了码元长度 清除位数计数IR.IR_event = 0;IR.IR_code = 0;IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;}if( IR.IR_decode_bit == 32 )                        //接收了32位数据之后,开始判断内容{IR.IR_code_get = IR.IR_code;IR.low_8bit = IR.IR_code_get & 0x000000FF;      //对于本项目而言之后低16位数据有用,其中15-8为数据,7-0为数据反码IR.hig_8bit = (IR.IR_code_get & 0x0000FF00)>>8;IR.low_8bit ^= 0xFF;                            //拿反码异或1 得到取反数据if( IR.low_8bit == IR.hig_8bit )                //判定数据跟数据反码是否相符,如果相符则为有用数据,否则舍弃所有数据{IR.rece_ok = 1;} else{IR.rece_ok = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.IR_code_get = 0;}IR.IR_decode_bit = 0;IR.IR_code = 0;IR.f_head = 0;IR.IR_event = 0;}IR.get_bit = 0;                                     //每次处理完一位数据之后,清楚标志位,待下次0.56ms低电平成立之后再次判断}}}HAL_TIM_Base_Start_IT( &Tim3Handle );                           //每次进入外部中断都要启动计数器
}

IR结构体声明在main.h文件中:

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "py32f0xx_hal.h"/* Private includes ----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/#define IR_CODE_GET_POWER (uint8_t)(0xE2)
#define IR_CODE_GET_SPEED (uint8_t)(0xA2)
#define IR_CODE_GET_TIMER (uint8_t)(0xA8)
#define IR_CODE_GET_SHAKE (uint8_t)(0x18)/* Exported variables prototypes ---------------------------------------------*/
extern UART_HandleTypeDef UartHandle;
extern __IO ITStatus UartReady;
extern TIM_HandleTypeDef TimHandle,Tim3Handle,Tim1Handle, Tim17Handle;
extern EXTI_HandleTypeDef exti_handle;
extern GPIO_InitTypeDef GPIO_InitStruct;typedef struct IR_decode
{uint32_t  timer_count; //定时器数据uint32_t  IR_code;     //读取到的红外码 uint32_t  IR_code_get;uint32_t  IR_decode_bit;//解码接收位数uint32_t  IR_event;    //红外输出事件uint8_t   data_recving_flag;//红外接收中的标志uint8_t   f_head;uint8_t   get_bit;uint8_t   low_8bit;uint8_t   hig_8bit;uint8_t   rece_ok;// int16_t   *send_data;//红外发送码数据的首地址// uint8_t   send_code_bits;//红外发送位// uint8_t   send_rep_times;//红外码重复发送次数// uint8_t   send_times;//切换档位所需要次数// uint8_t   send_delay;//多包间隔时间// uint8_t   data_send_flag;//红外发送标志 uint8_t   printf_datalen;}IR_struct;extern IR_struct IR;/* Exported functions prototypes ---------------------------------------------*/
void APP_ErrorHandler(void);#ifdef __cplusplus
}
#endif#endif /* __MAIN_H *//************************ (C) COPYRIGHT Puya *****END OF FILE******************/

结构体初始化在main.c中:

	IR_struct IR;           //红外部分需要用到的结构体变量声明IR.IR_code = 0;IR.IR_decode_bit = 0;IR.IR_event = 0;IR.timer_count = 0;IR.f_head = 0;IR.get_bit = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;

C文件内容是:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "string.h"
#include "setup.h"/* Private define ------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef UartHandle;
__IO ITStatus UartReady = RESET;
TIM_HandleTypeDef TimHandle,Tim3Handle,Tim1Handle, Tim17Handle;
TIM_OC_InitTypeDef Tim17OCHandle,Tim16OCHandle;
I2C_HandleTypeDef I2cHandle;
TIM_IC_InitTypeDef sConfig;
TIM_SlaveConfigTypeDef   sSlaveConfig;
EXTI_HandleTypeDef exti_handle;
GPIO_InitTypeDef GPIO_InitStruct;IR_struct IR;           //红外部分需要用到的结构体变量声明/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*//*** @brief  应用程序入口函数.* @retval int*/
int main(void)
{   /* 初始化所有外设,Flash接口,SysTick */HAL_Init();/* 系统时钟配置 */APP_SystemClockConfig();GPIO_Init();TIM3_Init();HAL_TIM_Base_Stop_IT( &Tim3Handle );    //先关闭TIM3,等收到一个下降沿之后再打开TIM3->CNT = 0;                          //清零定时器,为下一次接受红外做准备/* 使能NVIC GPIO外部中断 */HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);IR.IR_code = 0;IR.IR_decode_bit = 0;IR.IR_event = 0;IR.timer_count = 0;IR.f_head = 0;IR.get_bit = 0;IR.low_8bit = 0;IR.hig_8bit = 0;IR.rece_ok = 0;while (1){if( IR.rece_ok ){switch (IR.hig_8bit){case IR_CODE_GET_POWER :break;case IR_CODE_GET_SHAKE :break;case IR_CODE_GET_SPEED :break;case IR_CODE_GET_TIMER :break;  default:break;}IR.IR_code_get = 0;IR.low_8bit = 0;IR.hig_8bit = 0;boot_cheak_link_over = 0;IR.rece_ok = 0;}}
}/*** @brief  错误执行函数* @param  无* @retval 无*/
void APP_ErrorHandler(void)
{/* 无限循环 */while (1){}
}#ifdef  USE_FULL_ASSERT
/*** @brief  输出产生断言错误的源文件名及行号* @param  file:源文件名指针* @param  line:发生断言错误的行号* @retval 无*/
void assert_failed(uint8_t *file, uint32_t line)
{/* 用户可以根据需要添加自己的打印信息,例如: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* 无限循环 */while (1){}
}
#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT Puya *****END OF FILE******************/

贴一下抓到的接收头波形:

数据内容发完之后如果一直按住不松手,那么发过来的其实就是重复码。

代码内容比较乱,其实核心就是开启一个上升沿+下降沿的中断,判断进入外部中断时是什么电平,如果进入外部中断时是高电平,说明TIM3的CNT存放的就是这次上升沿与上一次的下降沿之间低电平的持续时间;如果进入外部中断时是低电平,说明TIM3的CNT存放的就是这次下降沿与上一次的上升沿之间高电平的持续时间。通过不停进外部中断判断TIM3的CNT数值来判断高低电平的持续时间,完成解码。这种方式不会堵塞MCU,比进入外部中断之后判定电平死等的那种方式更加合理。

更加详细的内容可以看这篇博客,这个大佬写得比较详细,不过他用的是进中断后while的方式来判定时间:
链接: STM32入门开发: NEC红外线协议解码(超低成本无线传输方案)

发布评论

评论列表 (0)

  1. 暂无评论