本帖最后由 xld0932 于 2022-3-5 10:31 编辑
#申请原创# @21小跑堂
在之前的分享中有介绍基于MM32的IrDA红外通讯功能,IrDA其本身不具体载波通讯的功能,在干扰较大或者远距离通讯时,会有明显的不足;所以本文分享了红外通讯的另外一种实现方式:红外载波通讯。常用的红外载波频率有36kHz、38kHz、40kHz等等,对于红外接收头来说,当接收到载波信号时会解析成低电平,当没有载波信号时会解析成高电平,通过高低电平的组合切换,实现数据位传输,从而实现数据通讯。本文包含如下几个小节的内容: - 基于MM32红外抄表通讯实现
- 基于MM32红外遥控器NEC解码实现
- 基于MM32红外遥控器NEC编码实现
- 基于MM32带自学功能的红外遥控器
1、基于MM32红外抄表通讯实现
在典型的红外抄表电路中,常用到38kHz频率的载波信号来实现串口通讯功能;载波通讯的调制电路有很多,本文使用的是基于或门的标准调制方式,如下图所示:
LED为红外发射管,或门的2引脚一直保持38kHz的波形输入,当或门的1引脚UART_TX输出高电平时,或门的3引脚输出将不会受到2引脚输入的是38kHz高低电平的影响,会一直保持输出高电平,此时红外发射管处于截止状态;而当或门的1引脚UART_TX输出低电平时,或门的3引脚的输出电平状态将跟随2引脚电平状态的变化而变化,此时红外发射管则会发送出38kHz频率的波形,用被红外接收管检测到。
上图的原理图中,我们串联了一个1k欧姆的限流电阻,这个在实际的应用时需要做相应的调整,用于调节红外发射管的工作电流,直接影响到发射管的功率和传输距离。
我们先来看一下UART传输一个8位字节长度数据的时序图,如下所示;在开始数据传输时,会先传输一个比特位的低电平,用于起始位功能,接下来是数据位第0位到第7位依次传输,最后会传输一个比特位的高电平,用于停止位功能;其中传输每一比特位(包括起始位、数据位和停止位)的传输时间,都是相等的,由串口通讯波特率决定的;如果9600bps的波特率,所表示的含意是在1秒的时间位,可传输9600个比特位,这样算下来每一个比特位所占用的时间为1/9600秒,约一个比特位的传输需要104.17微秒。
其次我们来看一下选用的红外接收头的电气特性,如下图所示;可以从下图中看出,当红外发射头发送38kHz频率的载波信号时,红外接收头解析得到的是低电平;当红外发射头不发送载波信号时,红外接收头解析得到的高电平。
所以结合UART发送时序、IR接收头的特性、以及发送头或门的设计电路,可以使用TIM定时器配置一个标准的38kHz频率的PWM波形输出到或门的2引脚;然后初始化UART,配置成收发模式,波特率设置为4800,此时在初始化完成后的TX电平为高电平,通过或门电路后,红外发射头处于截止状态,相对的红外接收头也没有接收到载波信号,处于高电平状态;在通过串口发送起始位时,TX电平变成低电平,正好导致或门输出了38kHz频率波形,对于红外接收头来说,它接收到了载波频率后,解析输出就为低电平,而低电平对于UART接收时序来说,正好代表起始位;这样的电平状态正好符合了UART收发送时序,通过或门电平与UART结合实现了UART的红外通讯功能。
细心的小伙伴可能会有疑问,为什么串口波特率设置为4800,而不是其它更快速率呢?这是因为在我下面的硬件电路设计中,选择的红外接收头有这样一个特性Min burst length的值为8 cycles;而我们发送的38kHz载波的时间是由UART的每一比特位传输时间来决定的,所以如果UART的波特率越大,那每一比特位的传输时间就会越短,这个位传输时间就直接影响了38kHz输出的周期数量。38kHz波形的一个周期时间约为26.3微秒,那最小8个周期就是210微秒左右,而UART波特率设置为9600时对应的位时间只有104微秒左右,38kHz载波周期满足不了红外接收头的器件要求,在接收数据时就会出现误码的情况;而当UART波特率设置为4800时对应的位时间大概在208.3微秒,刚刚达到红外接收后的要求,如下图所示;所以为了保证UART红外通讯的稳定,建议将波特率设置成2400或者1200,UART的传输速率并不是因为UART性能的问题,而是因为38kHz的载波频率和红外接收头的特性所决定的,为了提升UART红外通讯速率,可以选择性能更好的红外接收头。
原理图设计:
硬件上选用MM32F0133C6P作为主控芯片,搭载最小系统电路及CH340作为打印调试接口,使用TC7SZ32FU或门芯片结合IR383-A实现红外发送电路,使用IRM-3638MF56红外接收头实现红外接收电路,通过引出8个GPIO引脚,作为后面扩展4*4按键矩阵接口,来实现遥控器的功能。
PCB设计:
焊接成品:
UART红外通讯代码实现:配置TIM输出38kHz载波频率:
/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]
* @param
* @retval
* [url=home.php?mod=space&uid=93590]@Attention[/url]
*******************************************************************************/
void IRM_InitTIM1(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.PCLK2_Frequency / 380000 - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = (10 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 5;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_Cmd(TIM1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_3);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
UART红外通讯代码实现:配置UART工作在收发模式,参数为4800/N/8/1:
/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]
* @param
* @retval
* [url=home.php?mod=space&uid=93590]@Attention[/url]
*******************************************************************************/
void IRM_InitUART(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
UART_InitTypeDef UART_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = UART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
UART_StructInit(&UART_InitStructure);
UART_InitStructure.UART_BaudRate = 4800;
UART_InitStructure.UART_WordLength = UART_WordLength_8b;
UART_InitStructure.UART_StopBits = UART_StopBits_1;
UART_InitStructure.UART_Parity = UART_Parity_No;
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(UART2, &UART_InitStructure);
UART_ITConfig(UART2, UART_IT_RXIEN, ENABLE);
UART_Cmd(UART2, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void UART2_IRQHandler(void)
{
uint8_t Data = 0;
if(UART_GetITStatus(UART2, UART_IT_RXIEN) != RESET)
{
Data = UART_ReceiveData(UART2);
UART_ClearITPendingBit(UART2, UART_IT_RXIEN);
printf("\r\nRX : 0x%02x", Data);
}
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_UART_SendData(uint8_t Data)
{
UART_SendData(UART2, Data);
while(UART_GetFlagStatus(UART2, UART_IT_TXIEN) == RESET);
}
调试演示结果:
如下图是红外发送0x55数据时的UART调制波形:
如下图是红外发送一串数据后,红外接收到数据后,产生UART接收中断后将数据打印出来的运行截图:
2、基于MM32红外遥控器NEC解码实现
NEC协议是红外遥控器协议中的一种,它由引导码、地址码、地址反码、命令码和命令反码组成;逻辑1为2.25ms,其中脉冲时间为560us;逻辑0为1.12ms,其中脉冲时间为560us;如下图所示:
重复码为9ms的脉冲电平和2.25ms的低电平组成,如下图所示:
NEC协议格式:
正常发送:首先发送的是引导码,它是由9ms的脉冲电平和4.5ms的低电平组成,后面跟着的是8位数据的地址码、地址反码、命令码和命令反码,以LSB的发送方式进行位传输 。
重复发送:如果你想重复发送一个命令码,在完成正常发送后,就不需要重复的发送命令码了,只需要间隔发送重复码就可以了。
对于红外遥控器NEC解码,对于红外硬件接收头来说就不能使用UART功能,NEC协议本身与UART位发送时序是完全不一样的,我们就需要通过MCU的其它外设功能来实现;这里我们将UART_RX这个GPIO引脚当作EXTI外部中断线触发引脚,结合TIM定时器来实现对NEC的解码功能;通过TIM计数EXTI触发的时间间隔来解析NEC传输的数据,支持重复命令码的识别,具体的配置代码如下:
uint8_t IRM_RX_Buffer[32];
uint8_t IRM_RX_Index = 0;
uint8_t IRM_RX_Repeat = 0;
uint8_t IRM_RX_Complete = 0;
uint32_t IRM_RX_Overtime = 0;
uint8_t IRM_RX_Step = 0;
uint8_t IRM_RX_Flag = 0;
uint32_t IRM_RX_Tick = 0;
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_InitEXTI(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);
EXTI_StructInit(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 0x03;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void EXTI2_3_IRQHandler(void)
{
uint32_t t = 0;
t = IRM_RX_Tick;
IRM_RX_Tick = 0;
IRM_RX_Overtime = 0;
switch(IRM_RX_Step)
{
case 0:
if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 850) && (t < 950))
{
IRM_RX_Step = 1;
}
break;
case 1:
if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 400) && (t < 500))
{
IRM_RX_Step = 2; IRM_RX_Index = 0; IRM_RX_Flag = 0;
}
else if(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 175) && (t < 275))
{
IRM_RX_Step = 4;
}
break;
case 2:
if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 46) && (t < 66))
{
IRM_RX_Step = 3;
}
break;
case 3:
if( !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 46) && (t < 66))
{
IRM_RX_Buffer[IRM_RX_Index++] = 0;
}
else if(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 119) && (t < 219))
{
IRM_RX_Buffer[IRM_RX_Index++] = 1;
}
if(IRM_RX_Index == 32)
{
IRM_RX_Step = 0; IRM_RX_Repeat = 0; IRM_RX_Flag = 1;
}
else
{
IRM_RX_Step = 2;
}
break;
case 4:
if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) && (t > 46) && (t < 66))
{
IRM_RX_Step = 0; IRM_RX_Repeat++;
}
break;
default:
break;
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_InitTIM3(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.PCLK1_Frequency * 2 / 1000000 - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = (10 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void TIM3_IRQHandler(void)
{
IRM_RX_Tick++;
if(IRM_RX_Complete == 0)
{
if(IRM_RX_Overtime++ > 2000)
{
IRM_RX_Complete = 1; IRM_RX_Step = 0;
}
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
通过NEC编码制式的控制器,对着开发板按键,这时在红外接收头解析到数据后,进行输出打印,实际运行效果如下所示:
3、基于MM32红外遥控器NEC编码实现
对于红外遥控器NEC编码,我们硬件电路也是共用的,还是需要一个TIM定时器来固定输出38kHz频率的载波;然后不能使用UART_TX功能了,它同样不符合NEC协议,此时我们将UART_TX这个GPIO引脚当作普通GPIO引脚使用,通过控制这个GPIO引脚输出高低电平的时序来实现NEC编码;此时需要另外一个TIM定时器来实现精确控制,配合GPIO来实现NEC编码的发送,具体的配置代码如下:
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_InitGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_InitTIM1(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.PCLK2_Frequency / 380000 - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = (10 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 5;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_Cmd(TIM1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_3);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_InitTIM2(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM2, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.PCLK1_Frequency * 2 / 1000000 - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = (10 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
uint16_t IRM_TX_Buffer[70] =
{
900, /* 9.0ms */
450, /* 4.5ms */
56, 56, /* Bit01 */
56, 56, /* Bit02 */
56, 56, /* Bit03 */
56, 56, /* Bit04 */
56, 56, /* Bit05 */
56, 56, /* Bit06 */
56, 56, /* Bit07 */
56, 56, /* Bit08 */
56, 56, /* Bit09 */
56, 56, /* Bit10 */
56, 56, /* Bit11 */
56, 56, /* Bit12 */
56, 56, /* Bit13 */
56, 56, /* Bit14 */
56, 56, /* Bit15 */
56, 56, /* Bit16 */
56, 56, /* Bit17 */
56, 56, /* Bit18 */
56, 56, /* Bit19 */
56, 56, /* Bit20 */
56, 56, /* Bit21 */
56, 56, /* Bit22 */
56, 56, /* Bit23 */
56, 56, /* Bit24 */
56, 56, /* Bit25 */
56, 56, /* Bit26 */
56, 56, /* Bit27 */
56, 56, /* Bit28 */
56, 56, /* Bit29 */
56, 56, /* Bit30 */
56, 56, /* Bit31 */
56, 56, /* Bit32 */
56, 3000, /* STOP */
0, 0,
};
uint8_t IRM_TX_Index = 0;
uint8_t IRM_TX_Finish = 0;
uint8_t IRM_TX_Enable = 0;
uint32_t IRM_TX_Tick = 0;
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void TIM2_IRQHandler(void)
{
if(IRM_TX_Enable == 1)
{
if(IRM_TX_Tick++ >= IRM_TX_Buffer[IRM_TX_Index])
{
IRM_TX_Tick = 0;
if(++IRM_TX_Index >= 68)
{
IRM_TX_Enable = 0;
IRM_TX_Finish = 1;
}
else
{
if(!GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2))
{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
}
else
{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
}
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRM_SendData(uint8_t *Data)
{
for(uint8_t i = 0; i < 4; i++)
{
for(uint8_t j = 0; j < 8; j++)
{
if(Data[i] & (0x80 >> j))
{
IRM_TX_Buffer[i * 16 + j * 2 + 3] = 56 * 3;
}
else
{
IRM_TX_Buffer[i * 16 + j * 2 + 3] = 56;
}
}
}
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
IRM_TX_Index = 0;
IRM_TX_Finish = 0;
IRM_TX_Enable = 1;
while(IRM_TX_Finish == 0);
}
通过调用NEC编码发送函数,在NEC编码发出的同时,NEC也同样接收到了编码数据,对其进行解析和打印输出,实际运行效果如下所示:
4、基于MM32带自学功能的红外遥控器
通过外接4*4矩阵按键,如下图所示:
实现功能:系统启动后,从MCU内部FLASH存储空间读取已经存储的按键对应的红外编码数据,在按下相对应的按键后,通过红外发射头发送NEC数据;按下板子上的KEY按键后,可以在按键发码和学习其它遥控器编码这两个工作模式之间切换,在学习编码的过程中,先按下一对应的按键,确认是哪个按键要学习,然后再用其它的遥控器对着开发板按键发送NEC编码,如果连接上USB调试接口,还会有对应的操作提示和过程打印信息,在学习完成后,再通过板载的KEY按键切换到正常发码模式;在进入学习模式的时候,板载的LED灯处于闪烁的状态,用于提示当前的工作状态,在退出学习模式时,LED将会熄灭,同时会将刚刚学到的编码保存到MCU的内部FLASH存储空间,这样就可以实现掉电保存了;
具体的关键实现代码如下所示:
基于MCU内部FLASH加载和存储红外遥控器NEC编码数据: /* Private define ------------------------------------------------------------*/
#define IRMC_FLASH_ADDRESS (0x08000000 + 60 * 1024)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t IRMC_StudyState = 0;
uint8_t IRMC_StudyIndex = 0;
/* Private variables ---------------------------------------------------------*/
uint8_t IRMC_CodeTable[16][4] =
{
{0x12, 0x34, 0x45, 0x78},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00},
};
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRMC_Load(void)
{
printf("\r\n%s", __FUNCTION__);
for(uint8_t i = 0; i < 16; i++)
{
for(uint8_t j = 0; j < 4; j++)
{
IRMC_CodeTable[i][j] = *(volatile uint8_t *)(IRMC_FLASH_ADDRESS + i * 4 + j);
}
}
for(uint8_t i = 0; i < 16; i++)
{
printf("\r\nNEC[%02d] : 0x%02x, 0x%02x, 0x%02x, 0x%02x",
i,
IRMC_CodeTable[i][0], IRMC_CodeTable[i][1],
IRMC_CodeTable[i][2], IRMC_CodeTable[i][3]);
}
printf("\r\n");
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void IRMC_Save(void)
{
uint32_t Data;
FLASH_Status Status;
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
Status = FLASH_ErasePage(IRMC_FLASH_ADDRESS);
FLASH_ClearFlag(FLASH_FLAG_EOP);
if(Status == FLASH_COMPLETE)
{
for(uint8_t i = 0; i < 16; i++)
{
Data <<= 8; Data = IRMC_CodeTable[i][3];
Data <<= 8; Data |= IRMC_CodeTable[i][2];
Data <<= 8; Data |= IRMC_CodeTable[i][1];
Data <<= 8; Data |= IRMC_CodeTable[i][0];
Status = FLASH_ProgramWord(IRMC_FLASH_ADDRESS + i * 4, Data);
FLASH_ClearFlag(FLASH_FLAG_EOP);
if(Data != *(uint32_t *)(IRMC_FLASH_ADDRESS + i * 4))
{
printf("\r\nIRMC Save Error!!!");
}
}
}
FLASH_Lock();
}
按键和矩阵按键识别及应用处理:
红外接收处理函数实现部分:
实际操作演示过程:
上电读取NEC按键编码:
进入学习模式,KEY1学习按键编码,然后退出学习模式:
系统重新上电后,加载按键红外编码,刚刚学习到的已经被保存下来了:
正常模式下,按键发送红外编码数据:
附件:
|