【STM32F407 开发板】实验七 :IR 红外线接收实验

前言:

这个实验,有点恶心,其实也不算恶心,一开始我理解错了,错怪了实验的源代码。不过最差劲的是,实验指导书是错的,我看了半天,一开始以为实验源代码错了,后来代码能正确运行,才发现是实验指导书错了,从根上就错了。嘿,tui ~ .

致谢:

在此感谢一下 人美心又善的双双小姐姐 叭,我和她说我的代码闹鬼了,然后就开始给她分析代码为啥闹鬼了,给她讲这个实验的目的,实验的步骤以及原理,分析源代码逻辑,分析到最后,我就这么悟了,突然理解了源代码的逻辑。如果没有她,全靠我自己分析,分析懂了估计得驴年马月了。简直了,赐予双双 小福星 称号,特此表扬。哈哈哈哈哈哈

一、创建工程

创建工程部分,还是选择原来的板子,不同的是,这里多了几个这个东西。
在这里插入图片描述
在这里插入图片描述

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

其实我不太清楚放这个SYS的外设有啥意义,就简要的说一下其他外设的意义叭:

  1. RCC时钟控制器,控制时钟的
  2. TIM3定时器,用来实现周期定时,每过一个周期,产生一次定时器中断,进入中断服务函数
  3. USART1,串口信息打印

最后需要在管脚上配置一个PG8为外部中断,至于为啥配置外部中断,看一下红外接收器的电路图吧。
在这里插入图片描述

在这里插入图片描述
从图中可以看到红外接收器的输出管脚为PG8,当它接收到数据的时候,会产生中断,并且数据也是存在于PG8管脚上的,细心的可能会看到,PG8在没有数据来的时候,它与VCC是直连的,也就是没有数据的时候PG8一直是高压状态。

下面就是时钟配置:
在这里插入图片描述
这里注意到,我用红线画出了两个timer的时钟配置,也就是这俩timer不是一个,各自互不干扰。这个对我之后的协议分析大有帮助。

外设配置:

对于USART1的配置:
在这里插入图片描述
对于TIM3的配置:
在这里插入图片描述
可以看到,这里的TIM3预分频为83,并且是向上计数,计数周期为99
在这里插入图片描述
并且也开启了TIM3的全局中断,在它一个周期的计时结束之后会产生一个全局中断。

对于GPIO的配置:
在这里插入图片描述
可以看到这里的GPIO配置成了上升沿下降沿均会触发一次中断。也就是在电压跳变的时候会产生一次中断。

生成工程:
在这里插入图片描述
在这里插入图片描述
好了,工程这样就生成完毕了

MMP,这里我还是得吐槽两句,太恶心了,要不是看了源代码,我还意识不到实验指导书错到了什么程度,它对于工程的配置完全是错误的,尤其是这个GPIO这里,错的离谱,它指导书上说的是只有在上升沿触发中断,并且关于这个GPIO的管脚配置的是PG5,离谱,明明电路图是PG8。误人子弟,害。

二、编辑工程、编译工程

为什么我听这个李圣杰唱的《痴心绝对》会这么伤心呢。。。看来我有些感性呀,哈哈哈哈

好了,工程配置完毕,用Keil5打开,如下:
在这里插入图片描述
这里面的这个RemoteInfrared.c文件是自己写的一个c文件,这也是分析源代码的一个重点。稍后再说,

main.c文件中添加代码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
RemoteInfrared.h中的代码:

#include "stm32f4xx_hal.h"

#define	Remote_Infrared_DAT_INPUT HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8)

typedef struct _Remote_Infrared_data_struct 
{
	uint8_t bKeyCodeNot; 
	uint8_t bKeyCode; 
	uint8_t bIDNot; 
	uint8_t bID; 
}Remote_Infrared_data_struct;

typedef union _Remote_Infrared_data_union 
{
	Remote_Infrared_data_struct RemoteInfraredDataStruct; 
	uint32_t uiRemoteInfraredData; 
}Remote_Infrared_data_union;

void Remote_Infrared_KEY_ISR(void);
uint8_t Remote_Infrared_KeyDeCode(void);

RemoteInfrared.c中的代码:

#include "RemoteInfrared.h"

#define REPEAT_KEY  0xEE

extern __IO uint32_t GlobalTimingDelay100us;
extern __IO uint32_t GlobalTimingDelay100usTx;


__IO uint32_t FlagGotKey = 0;

__IO Remote_Infrared_data_union RemoteInfrareddata;

const uint32_t TIME_DELAY_6MS = 60;
const uint32_t TIME_DELAY_10MS = 100;
void Remote_Infrared_KEY_ISR(void)
{
	static __IO uint8_t  bBitCounter = 0; 
    static __IO uint32_t bKeyCode = 0;
	bBitCounter++;

	if(bBitCounter == 1)        
	{
        if(Remote_Infrared_DAT_INPUT) 
        {
            bBitCounter = 0;
        }
        else
        {
            GlobalTimingDelay100us = TIME_DELAY_10MS;
        }
	}
	else if(bBitCounter == 2)  
	{
        if(Remote_Infrared_DAT_INPUT)
        {
            if((GlobalTimingDelay100us > 2) && (GlobalTimingDelay100us < 18))
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
            }
            else
            { 
                bBitCounter = 0; 
                //printf(".");
            }
        }

        else
        {
            bBitCounter = 0;            
        }
	}
	else if(bBitCounter == 3)  
	{
        if(Remote_Infrared_DAT_INPUT)
        {
            bBitCounter = 0; 
        }
        else
        {
            if((GlobalTimingDelay100us > 5) && (GlobalTimingDelay100us < 20))
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
                
            }
            else if((GlobalTimingDelay100us > 32) && (GlobalTimingDelay100us < 46))
            {
                bBitCounter = 0;
                RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
                
                bBitCounter = 0;
                FlagGotKey = 1;
            }            
            else
            {
                bBitCounter = 0; 
                
            }          
        }
	}    
	else if(bBitCounter > 3 && bBitCounter < 68) 
	{  

        if(Remote_Infrared_DAT_INPUT)
        {
            if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58))
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
            }
            else
            {
                bBitCounter = 0; 
                //printf("#");
            }           
        }
        else
        {
            if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58)) // '0'
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
		        bKeyCode <<= 1;  // MSB First 
                bKeyCode += 0x00;
            }
            else if((GlobalTimingDelay100us > 40) && (GlobalTimingDelay100us < 48)) //'1'
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;            
		        bKeyCode <<= 1;  // MSB First 
                bKeyCode += 0x01;
            }              
            else
            {
			    //test = GlobalTimingDelay100us;
                bBitCounter = 0; 
                //printf("*%d", test);
            }  
        }

       if(bBitCounter == 67)
        {
            RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
            bBitCounter = 0;
            FlagGotKey = 1;
            //printf("KeyCode = 0x%X", bKeyCode);
        }
	}
	else
	{
		bBitCounter = 0;
        //printf("KeyCode = 0x%X", bKeyCode);
	}
}

uint8_t Remote_Infrared_KeyDeCode(void)
{
	uint8_t Key = 0xFF;

	if (FlagGotKey == 1)
	{
        FlagGotKey = 0;
        if((RemoteInfrareddata.RemoteInfraredDataStruct.bID == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bIDNot)
            && (RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCodeNot))
        {
            printf("\n\r IR Receive KeyCode = 0x%02X, ", RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode);
			switch(RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode)
			{
				 case 0:
						printf("ERROR ");
						break;
					case 0xEA:
						printf("开关");
						break;				
					case 0xDA:
						printf("静音");
						break;
					case  0x68:
						printf("白键");
						break;
					case 0x5A:
						printf("i    ");
						break;
					case 0x4A:
						printf("播放");
						break;
					case 0x0A:
						printf("鼠标");
						break;
					case 0xF0:
						printf("直播");
						break;
					case  0x32:
						printf("点播");
						break;
					case 0x1A:
						printf("VOL-   ");
						break;				
					case 0xD8:
						printf("VOL+   ");
						break;
					case 0xF2:
						printf("设置");
						break;
					case 0x58:
						printf("菜单");
						break;
					case 0xC2:
						printf("上箭头");
						break;
					case  0x60:
						printf("左箭头");
						break;				
					case 0x70:
						printf("右箭头");
						break;				
					case 0x40:
						printf("OK   ");
						break;				
					case 0x50:
						printf("下箭头");
						break;				
					case 0x72:
						printf("主页");
						break;				
					case 0xB2:
						printf("返回");
						break;				
					case 0x08:
						printf("1      ");
						break;
					case 0x88:
						printf("2      ");
						break;
					case 0x48:
						printf("3      ");
						break;
					case 0xC8:
						printf("4      ");
						break;
					case 0x28:
						printf("5      ");
						break;
					case 0xA8:
						printf("6      ");
						break;
					case 0xE8:
						printf("7      ");
						break;
					case 0x18:
						printf("8      ");
						break;
					case 0x98:
						printf("9      ");
						break;
					case 0xB8:
						printf("0      ");
						break;
					case 0x38:
						printf("HTML5/FLASH");
						break;
					case 0x78:
						printf("删除");
						break;				
					default:
							printf("Unknown key!");
			}
			printf("\n");
        }
        else
        {
          printf("\n\r ERR 0x%08X", RemoteInfrareddata.uiRemoteInfraredData);
        }
    }
    return Key = RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode;
}


这就是这个工程的核心代码了,也别纠结我代码贴的全不全了,反正即使全,看我博客的也复现不了这个实验,设备环境啥的都不一样,就听我讲讲原理就好了。

编译工程,下载到板子上。打开串口,按下复位键。
在这里插入图片描述
拿遥控器,随便按下一个键,串口打印信息如下:
在这里插入图片描述

可以看到实验成功运行了,接下来就是比较核心的源代码分析阶段。

三、背景介绍,知识点综述

为什么我听李圣杰的《手放开》又那么伤心呢。。。李圣杰是伤感情歌大佬吗?咋唱的歌都这么触动人心。

说源代码之前,还是先说一下这个实验的背景知识叭,这个就是要给PWM编码一席之地了。

我找了几篇博客,有兴趣的可以看一下这些博客:

  1. 红外编解码彻底解析
  2. STM32红外遥控NEC协议
  3. 基于stm32实现红外遥控信号接收解码的详细步骤

看完这些博客,还是听我详细的说一下叭:

这个实验的背景是遥控使用红外线发射信息,开发板子上的红外接收头接收红外线,并进行电压分析。
在这里插入图片描述
就是这个色儿的,具体的详细过程是这个样子的:

遥控器按下按键,产生了一堆数据信息,这些信息转换成一堆0 1串,然后经过调制:

将0调制成这样:
在这里插入图片描述
将1调制成这样:
在这里插入图片描述
注意,这是发送端,发送端,发送端是根据低压的长度不同来区分0 和1 的,调制好了之后,原先的一堆0 1 串就可以变成一堆高低电压,进而通过电压控制红外的小灯发射红外信号。

为什么我越来越喜欢听比莉唱的《DEAR JOHN》了,这烟嗓唱的太好听了。。。带劲,气氛带起来,摇起来

在发送的时候,是有协议要求的,具体的发送协议帧格式如下:
在这里插入图片描述
注意,注意,这是发送端,发送端,发送端才显示这样子。

好了,现在数据通过小灯发出去了,要来到板子的接收端了,板子的接收端是完全和发送端反着的,这也是为什么我一直强调发送端与接收端,电压是完全反着的

接收端的0显示如下:
在这里插入图片描述

接收端的1显示如下:
在这里插入图片描述
看到了嘛,在接收端,电压完全反着,发送端靠低压区分0 1,接收端靠高压区分0 1 。由于是完全反着的,所以接收端实际收到的协议帧格式如下:
在这里插入图片描述

在这里插入图片描述
但是时间是不变的啊,发送端发送9ms的高压,接收端就收到9ms的低压,发送端发送4.5ms的低压,接收端就接受4.5ms的高压。这俩组成了引导码,引导码之后才是真正的数据,这个地址反码与控制反码是校验的作用,为了防止传输错误,如果可以保证传输正确的话,其实是可以把其定义成其他意思,毕竟只是协议嘛,人为规定而已。

OK了,实验背景就介绍到这里,下面就是真正的源码分析了。

四、源代码分析

好啦,接下来就是源代码分析了。

1. 修改系统时钟配置

这里我选择修改了系统时钟频率,这样的话,就可以清晰的看到高低电压都持续了多长时间了,就可以对照着分析代码的逻辑了
在这里插入图片描述
就是这里,配置系统时钟这里,原来这里配置的是/1000,意思是每1ms进入一次中断,也就是它能计数的最小单位就是1ms,可是我的传输协议有4.5ms,有小数部分,所以我就把进入中断的频率改了,这里改成了/100000,变成了每10us进入一次中断,可以准确的分析高低电平持续的时间。

在main函数中,我维持了两个变量
在这里插入图片描述
flag代表当前的电压,因为当初分析电路图的时候就知道,PG8一直是高压状态,在这里高压用1表示,所以flag初始化为1,然后这里我又维护了一个start变量,记录当前flag开始的时间。这里的HAL_GetTick函数就是获取当前的系统Tick,之前不是说过每10us进入一次中断服务程序嘛,中断服务程序里面会自增这个系统Tick,相当于计时的作用吧。

在while循环中,有如下语句:
在这里插入图片描述
这里我一直循环获取PG8的电压,当电压发生变化的时候,就记录下当前时刻,与之前的start时刻相减,获得flag电压持续时间,然后更新flag、更新start 。如此可以实现记录每个电压的持续时间。

注意,这里我有一个对系统Tick差值/10的操作,因为当初的TIM3的周期是100us,TIM3是一个延时定时器,用来分析协议。这里由于之前的系统Tick是以10us为单位,如果想转换成以100us为单位,就需要将差值缩小十倍,从而产生小数,精确协议分析。

结果如下:
在这里插入图片描述

2. TIM3配置

这里的TIM3是一个周期定时器,周期为100us,每100us产生一次中断,调用一次中断服务函数。

至于为啥100us进入一次中断,分析如下:

TIM3在APHB1上,如图:
在这里插入图片描述
在这里插入图片描述

可以看到是84MHZ的频率,而在时钟配置的时候,预分频系数为83,所以真正的进入定时器的计数器的频率为84/(83+1)=1MHZ,也就是1s有1M的频率,而时钟的周期为100,也就是每100个脉冲过后才进入一次溢出中断处理函数。100/1M=0.0001s,换算成us,就是100us,也就是100个脉冲需要花费100us的时间,每100us进入一次中断服务程序。

TIM3的中断服务程序如下:
在这里插入图片描述
可以看到,这里的计时单位是以100us为一个单位的,每次中断来临后,都将GlobalTimingDelay100us减1 。实现100us级的计时操作。

3. 中断例程分析

查看stm32f4xx_it.c文件,这个文件是STM32的中断管理文件,可以明显的看到有三个中断例程。

系统滴答中断,就是每10us一次的中断。
在这里插入图片描述
红外接收器的中断,体现在PG8的外部中断。
在这里插入图片描述
TIM3的定时器中断,就是每100us一次的中断。
在这里插入图片描述
可以看到,这个TIM3的中断回调函数中就有对回调函数的调用:
在这里插入图片描述

4. 协议接收分析

在上面的外部中断的服务程序中,可以看到调用了一个回调函数。回调函数内容如下:
在这里插入图片描述
在这里插入图片描述
而最后的这个Remote_Infrared_KEY_ISR()函数,就是最终的协议分析部分。

好了下面正式进入RemoteInfrared.c文件进行函数分析。
在这里插入图片描述
RemoteInfrared.c文件中,有两个函数,一个是接收数据的中断回调函数Remote_Infrared_KEY_ISR,一个是识别最终数据,判断按下那个键的函数Remote_Infrared_KeyDeCode。

在这个文件中有一个标志位,
在这里插入图片描述
这个意为协议帧是否接收完整的标志位。

用于TIM3的延时时间的两个变量
在这里插入图片描述
这两个常数,就是在TIM3的中断回调函数中减1的那个变量。实现的是计时的功能。

算了,我还是再贴一下源代码叭,头文件我就不贴了,上面有。

#include "RemoteInfrared.h"

#define REPEAT_KEY  0xEE

extern __IO uint32_t GlobalTimingDelay100us;
extern __IO uint32_t GlobalTimingDelay100usTx;


__IO uint32_t FlagGotKey = 0;

__IO Remote_Infrared_data_union RemoteInfrareddata;

const uint32_t TIME_DELAY_6MS = 60;
const uint32_t TIME_DELAY_10MS = 100;
void Remote_Infrared_KEY_ISR(void)
{
	static __IO uint8_t  bBitCounter = 0; 
    static __IO uint32_t bKeyCode = 0;
	bBitCounter++;

	if(bBitCounter == 1)        
	{
        if(Remote_Infrared_DAT_INPUT) 
        {
            bBitCounter = 0;
        }
        else
        {
            GlobalTimingDelay100us = TIME_DELAY_10MS;
        }
	}
	else if(bBitCounter == 2)  
	{
        if(Remote_Infrared_DAT_INPUT)
        {
            if((GlobalTimingDelay100us > 2) && (GlobalTimingDelay100us < 18))
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
            }
            else
            { 
                bBitCounter = 0; 
                //printf(".");
            }
        }

        else
        {
            bBitCounter = 0;            
        }
	}
	else if(bBitCounter == 3)  
	{
        if(Remote_Infrared_DAT_INPUT)
        {
            bBitCounter = 0; 
        }
        else
        {
            if((GlobalTimingDelay100us > 5) && (GlobalTimingDelay100us < 20))
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
                
            }
            else if((GlobalTimingDelay100us > 32) && (GlobalTimingDelay100us < 46))
            {
                bBitCounter = 0;
                RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
                
                bBitCounter = 0;
                FlagGotKey = 1;
            }            
            else
            {
                bBitCounter = 0; 
                
            }          
        }
	}    
	else if(bBitCounter > 3 && bBitCounter < 68) 
	{  

        if(Remote_Infrared_DAT_INPUT)
        {
            if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58))
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
            }
            else
            {
                bBitCounter = 0; 
                //printf("#");
            }           
        }
        else
        {
            if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58)) // '0'
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;
		        bKeyCode <<= 1;  // MSB First 
                bKeyCode += 0x00;
            }
            else if((GlobalTimingDelay100us > 40) && (GlobalTimingDelay100us < 48)) //'1'
            {
                GlobalTimingDelay100us = TIME_DELAY_6MS;            
		        bKeyCode <<= 1;  // MSB First 
                bKeyCode += 0x01;
            }              
            else
            {
			    //test = GlobalTimingDelay100us;
                bBitCounter = 0; 
                //printf("*%d", test);
            }  
        }

       if(bBitCounter == 67)
        {
            RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
            bBitCounter = 0;
            FlagGotKey = 1;
            //printf("KeyCode = 0x%X", bKeyCode);
        }
	}
	else
	{
		bBitCounter = 0;
        //printf("KeyCode = 0x%X", bKeyCode);
	}
}

uint8_t Remote_Infrared_KeyDeCode(void)
{
	uint8_t Key = 0xFF;

	if (FlagGotKey == 1)
	{
        FlagGotKey = 0;
        if((RemoteInfrareddata.RemoteInfraredDataStruct.bID == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bIDNot)
            && (RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCodeNot))
        {
            printf("\n\r IR Receive KeyCode = 0x%02X, ", RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode);
			switch(RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode)
			{
				 case 0:
						printf("ERROR ");
						break;
					case 0xEA:
						printf("开关");
						break;				
					case 0xDA:
						printf("静音");
						break;
					case  0x68:
						printf("白键");
						break;
					case 0x5A:
						printf("i    ");
						break;
					case 0x4A:
						printf("播放");
						break;
					case 0x0A:
						printf("鼠标");
						break;
					case 0xF0:
						printf("直播");
						break;
					case  0x32:
						printf("点播");
						break;
					case 0x1A:
						printf("VOL-   ");
						break;				
					case 0xD8:
						printf("VOL+   ");
						break;
					case 0xF2:
						printf("设置");
						break;
					case 0x58:
						printf("菜单");
						break;
					case 0xC2:
						printf("上箭头");
						break;
					case  0x60:
						printf("左箭头");
						break;				
					case 0x70:
						printf("右箭头");
						break;				
					case 0x40:
						printf("OK   ");
						break;				
					case 0x50:
						printf("下箭头");
						break;				
					case 0x72:
						printf("主页");
						break;				
					case 0xB2:
						printf("返回");
						break;				
					case 0x08:
						printf("1      ");
						break;
					case 0x88:
						printf("2      ");
						break;
					case 0x48:
						printf("3      ");
						break;
					case 0xC8:
						printf("4      ");
						break;
					case 0x28:
						printf("5      ");
						break;
					case 0xA8:
						printf("6      ");
						break;
					case 0xE8:
						printf("7      ");
						break;
					case 0x18:
						printf("8      ");
						break;
					case 0x98:
						printf("9      ");
						break;
					case 0xB8:
						printf("0      ");
						break;
					case 0x38:
						printf("HTML5/FLASH");
						break;
					case 0x78:
						printf("删除");
						break;				
					default:
							printf("Unknown key!");
			}
			printf("\n");
        }
        else
        {
          printf("\n\r ERR 0x%08X", RemoteInfrareddata.uiRemoteInfraredData);
        }
    }
    return Key = RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode;
}


在函数Remote_Infrared_KEY_ISR中维护了两个变量,都是静态局部变量,
在这里插入图片描述
这个bBitCounter就是记录当前协议帧分析到哪一部分了。bKeyCode就是协议帧接收的0 1 数据。

首先声明,这是一个中断的回调函数,而这个中断是在上升沿下降沿都会触发的。结合当初我用系统Tick记录下来的每个高低电压持续的时间来分析。
在这里插入图片描述

好,那就说一下流程吧,一开始中断来临,bBitCounter加一,表示如果开始分析协议的话,应该首先第一个分析9ms的低电平,如下:
在这里插入图片描述
这里的如果这个中断来的时候,读取的电压是高电平的,说明是无效的。重新将bBitCounter归0。

如果是低电平,那么就开始定时器计时,因为最长不过9ms左右,所以延时10ms完全够用,TIM3开始定时10ms。

好,现在下一个中断又来了,bBitCounter现在又加一到二了。也就是该分析4.5ms的高电平了,如下:
在这里插入图片描述
可以看到,协议的正常规则就应该是4.5ms的高电平,这里只是说明高电平来了,说明不了这个高电平是否会持续4.5ms。这里如果是来到的高电平,那么就进入IF,注意,重点来了,这里的if里面的定时器定时余量的判断,是对之前的低电平的持续时长的判断,看之前的低电平是否持续了9ms左右(不会真正的准确持续9ms,是有误差的)之前的定时是定时10ms(100个100us),参照我用系统Tick记录的电压持续时间,低电平持续了90.70个100us,也就是GlobalTimingDelay100us应该还剩下不到10个,所以如代码所示,可以接受的低电平范围是82~98个100us。满足了这个范围条件,好了,之前的9ms低电平可以接收,下面就该看这个高电平会不会持续4.5ms了,所以定时器TIM3又开始定时6ms。

再次来了一个中断,bBitCounter再次自增,变成了3
在这里插入图片描述
再次来了一个中断,说明刚才的高电平结束了,这次应该接受的是低电平。所以一开始就判断,如果这次接受的是高电平,说明接收错误,重新归零,如果是低电平,那么好了最起码说明这次的来到的电压是符合标准的,接下来就应该判断,之前的高电压是否真的持续了4.5ms。因为之前TIM3定时了6ms。而刚刚的高电压持续了44.70个100us。对于60个100us,还剩下15.4个100us,符合40~55的范围,可以接受。然后就再次延时6ms,来测量此次的低电平会持续多长时间,接下来就该接收数据了。

又来了一个中断,这次的中断应该是高电平了,
在这里插入图片描述
注意,在接收端,电压是反向的,也就是应该根据高电压来区分0 1,低电压是没有啥意义的,可能它唯一的意义就是一个契机,说明高电压结束了,应该看一下定时器余量,看一下高电平持续了多长时间。

如上图,这次来了一个高电平,确实应该来了一个高电平,进入高电平的if语句内,在语句内体现的是刚刚的低电压的持续时间,看它的持续时间是否在允许范围内,这里低电平持续了30.60个100us,还剩下29.6个100us,允许接受的GlobalTimingDelay100us应该在50~58,

咦????????????????这是啥玩意?咋回事?是我计数计错了吗为什么会持续这么长时间,完了,我迷惑了。。。

我感觉我写的没问题呀,之前的测量也都是对的,为啥会差这么多呢。。。

好了,不管它咋测量的了,现在可以保证的是,我的理解是对的,只不过测量测错了。

按照正常来说,需要有0.56ms左右的低电平,也就是走了5.6个100us,还应该剩下54.4个100us,符合50~58范围内,可以接受。然后再次开启定时,再次定时6ms,来测量本次来到的高电平的持续时间。

好,现在又一次中断来了,这次来了低电平,进入下面的else里面,这时的GlobalTimingDelay100us中的余量就代表了刚刚的高电平持续的时间,可以看到这里又分两种,体现了根据高电平来区分0 1 的思路,如果高电平持续了0.56ms,也就是GlobalTimingDelay100us还剩下54.4个100us在50 ~ 58范围内,说明刚刚接收到了数据0,如果高电平持续了1.685ms,也就是GlobalTimingDelay100us还剩下43.15个100us,在40 ~ 48范围之内,说明刚刚接收到了数据1,将这些数据放到bKeyCode中暂存,然后开启下次定时,计时这次来的低电平的时间。

如此循环直到循环到bBitCounter为67为止(包括67),也就是从1到67,需要分析67次高低电平,好,根据之前的协议帧格式,有8个地址位、8个地址位反码、8个操作码、8个操作码反码,也就是32个0 或1 ,换成高低电平就是64个高低电平,再加上两个引导码,也就是66个,那还差一个,差一个差在,因为我们需要测量最后一个高电平的持续时间,就需要再来一次中断,再次进入函数,来测量最后的那个电平的持续时间,至于具体的协议帧格式,就是红外接收头的事了。

5. RemoteInfrared.h 中数据结构的分析

这里面有一个极其骚气的一点,就是共用体真的是太骚了。我写是肯定写不出来的。
在这里插入图片描述
我的天哪,惊为天人…

要知道在RemoteInfrared.c中的变量定义的时候。它定义的是共用体。
在这里插入图片描述
之后的操作就是在bBitCounter为67的时候,将记录下来的bKeyCode赋值给uiRemoteInfraredData
在这里插入图片描述
注意看,这里是整型数之间的赋值,都是uint32_t类型,因为是共用体,注意到那个RemoteInfraredDataStruct类型,包括了4个uint8_t,刚好就是32位,所以之后在解码操作的时候,直接用共用体的RemoteInfraredDataStruct成员,相当于直接把uiRemoteInfraredData给强转类型了,太厉害了,没想到,没想到。

6. 键盘解码程序

键盘解码程序就很简单了,因为实验里面已经提供了每个键位的Code,直接用一下switch/case就可以了,用之前,只需要验证一下是否和反码一样就行了。源码我就不再贴上来了,之前已经放过两次了。
在这里插入图片描述
刚才说的就是这个,直接强转,实在高明。

五、结语

好了,又是一篇博客,虽然出现了一点点小瑕疵,但是无伤大雅,终于完成了,撒花撒花,幸苦了一下午,不对,是一整天,因为我中午才起床,起床后一直写博客到现在,一天结束了。。。明天继续,奥里给,哈哈哈哈

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值