本帖最后由 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学习按键编码,然后退出学习模式:
系统重新上电后,加载按键红外编码,刚刚学习到的已经被保存下来了:
正常模式下,按键发送红外编码数据:
附件:
|