写在前边

最近项目用到一款遥控器是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红外解码相关推荐

  1. 瑞萨e2studio(15)----外部中断定时器配置输入捕获测量频率

    瑞萨e2studio.15--外部中断&定时器配置输入捕获测量频率 概述 视频教学 csdn课程 完整代码下载 样品申请 硬件准备 开发板 新建工程 工程模板 保存工程路径 芯片配置 工程模板 ...

  2. 51单片机:中断系统(外部中断,定时器中断,串口通信)

    目录 中断系统简介: 中断的优先级和嵌套: 8个中断请求源及其优先级: 中断的分别介绍: 1.外部中断0:INT0 2.外部中断1 3.T0和 T1:定时计数器的功能 4.串口中断(串口为什么使用定时 ...

  3. STM32CubeMX EC11旋转编码器开发心路历程(encode模式 外部中断模式 普通IO口模式 定时器模式探索)

    文章目录 ENCODE模式探索 普通IO口探索 定时器使用 外部中断探索 编码器开关的理解 最后的实现方案 这篇文章主要还是记录整个过程以及想法的不断改进,对于一些实际的操作还有代码,我准备都分开写到 ...

  4. STM32单片机外部中断配置讲解

    2019独角兽企业重金招聘Python工程师标准>>> 单片机外部中断简介 所谓外部中断,就是通过外部信号所引起的中断,如单片机引脚上的电平变化(高电平.低电平).边沿变化(上升沿. ...

  5. 瑞萨单片机之外部中断实现频率采集(三)

    很多低端MCU没有像STM32 那样拥有强大脉冲捕获测频率,那么MCU只能依靠自身有的资源来实现这个功能,比较通用的方法是使用了外部中断+定时器来实现,该方法经过我在实际项目中的使用来看,频率50/6 ...

  6. 外部中断(红外通信)

    以前用来红外通信控制小车的程序 typedef unsigned char UCHAR8; /* 初始化 指定下降沿触发 1 产生外部中断2 IRTime 对下降沿计数 将脉冲时序信号转为长度接收完成 ...

  7. 基于STM32F103ZET6库函数定时器中断实验

    基于STM32F103ZET6库函数定时器中断实验 STM32F1 通用定时器简介 定时器相关的库函数主要集中在固件库文件 stm32f10x_tim.h 和 stm32f10x_tim.c 文件中 ...

  8. 51单片机学习笔记(郭天祥版)(3)——引脚讲解、数码管静态显示、中断系统(外部中断,定时器中断)...

    学习引脚的功能 9引脚 复位管脚,当给2个机器周期(24个时钟振荡周期)的高电平时会复位,单片机正常工作时会给0.5v的低电平 VPD备用电源的输入端,当主电源VCC发生故障降低到某一规定的低电平时, ...

  9. 51单片机复习:外部中断,定时器/计数器中断

    中断: 什么是中断,就是打断,你一开始在做一件事,突然意外发生(中断源),你不得不停止手中的事,(中断响应),去处理那 件意外(中断服务),处理完意外后你继续做刚才那件事(中断返回),这就是中断,而单 ...

最新文章

  1. rust做嵌入式开发_Rust 嵌入式开发 STM32 amp; RISC-V
  2. OpenGL SDK glew(OpenGL Extension Wrangler Library )
  3. iptables(下)规则
  4. LeetCode 218. 天际线问题(multiset优先队列)*
  5. iOS 覆盖率检测原理与增量代码测试覆盖率工具实现
  6. Netty技术细节源码分析-内存池之PoolChunk设计与实现
  7. 13.FutureTask异步计算
  8. JAVA中堆和栈的区别
  9. MFC入门示例之组合框(CComboBox)、列表框(CListBox)
  10. delphi java jni 互通_编程应用实例:Java通过JNI与Delphi交互
  11. 魏俊妮《如何成为支持业务的HR》课程大纲
  12. 计算机对未来和生活,计算机的发展对人类生活的影响
  13. Excel技能培训之十 选择性粘贴,单元格公式转换为数值,对每个单元格进行运算,行列转换,只粘贴非空值
  14. 使用第三方广告服务器的好处
  15. openbsd_仔细看一下OpenBSD
  16. wordpress教程
  17. 避免2.4GHz ISM频段各种类型无线设备干扰的技术【转】
  18. pptpd 安装步骤
  19. MySQL中 反引号、单引号 和 双引号 的区别
  20. CAD如何快速查找图纸的存放位置?怎么实现快速看图?

热门文章

  1. MIUI 10——启用与关闭开发者模式/开发者选项
  2. 看板的作用是什么?任务看板如何跟进
  3. html隐藏域保存数组,关于给JS组合数组赋值给隐藏域问题
  4. 教你如何创建一个免费的网站
  5. 如何将pdf转换成jpg图片的格式
  6. OmniPlayer Pro for Mac v1.4.12 支持无线投屏的视频播放器
  7. Mac工具:Karabiner,解决mac上无法使用的情况
  8. Chapter 4 Part 5 WPAS扫描源码走读
  9. 一加Ace竞速版,手游党的开心搭档
  10. CCTV-5在线直播 视频网 关注体育赛