前言:
这个实验,有点恶心,其实也不算恶心,一开始我理解错了,错怪了实验的源代码。不过最差劲的是,实验指导书是错的,我看了半天,一开始以为实验源代码错了,后来代码能正确运行,才发现是实验指导书错了,从根上就错了。嘿,tui ~ .
致谢:
在此感谢一下 人美心又善的双双小姐姐 叭,我和她说我的代码闹鬼了,然后就开始给她分析代码为啥闹鬼了,给她讲这个实验的目的,实验的步骤以及原理,分析源代码逻辑,分析到最后,我就这么悟了,突然理解了源代码的逻辑。如果没有她,全靠我自己分析,分析懂了估计得驴年马月了。简直了,赐予双双 小福星 称号,特此表扬。哈哈哈哈哈哈
一、创建工程
创建工程部分,还是选择原来的板子,不同的是,这里多了几个这个东西。
其实我不太清楚放这个SYS的外设有啥意义,就简要的说一下其他外设的意义叭:
- RCC时钟控制器,控制时钟的
- TIM3定时器,用来实现周期定时,每过一个周期,产生一次定时器中断,进入中断服务函数
- 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编码一席之地了。
我找了几篇博客,有兴趣的可以看一下这些博客:
看完这些博客,还是听我详细的说一下叭:
这个实验的背景是遥控使用红外线发射信息,开发板子上的红外接收头接收红外线,并进行电压分析。
就是这个色儿的,具体的详细过程是这个样子的:
遥控器按下按键,产生了一堆数据信息,这些信息转换成一堆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就可以了,用之前,只需要验证一下是否和反码一样就行了。源码我就不再贴上来了,之前已经放过两次了。
刚才说的就是这个,直接强转,实在高明。
五、结语
好了,又是一篇博客,虽然出现了一点点小瑕疵,但是无伤大雅,终于完成了,撒花撒花,幸苦了一下午,不对,是一整天,因为我中午才起床,起床后一直写博客到现在,一天结束了。。。明天继续,奥里给,哈哈哈哈