阅读量:0
一、红外遥控的原理
•红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出。
•通信方式:单工,异步
•通信协议标准:NEC标准
具体的通信原理不在赘述,而且具体的原理对STM32的学习来说也不是重点
二、NEC协议
NEC协议的数据帧说明:
起始码:由9ms低电平和4.5ms高电平表示
重复码:由9ms低电平和2.25ms低电平表示
数据帧:由8位地址码+8位地址反码+8位命令码+8位命令反码组成,总计32位
低电平:由560us低电平和560us高电平表示
高电平:由560us低电平和1690us高电平表示
三、代码实现
本代码是通过状态机来实现的,示意图如下,弄懂状态机有助于理解后面的代码
Timer.c
/*实现思路:通过外设的外部触中断来捕获NEC码,同时通过定时器来进行定时,判断定时器的值来进行解码*/ #include "stm32f10x.h" void Timer_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2的时钟 TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //配置时基单元 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //此参数用于配置滤波器时钟,不影响时基单元功能 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数 TIM_TimeBaseInitStructure.TIM_Period = 65536-1; //计数的周期,也就是计数到这里,然后下一个数清零 TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1; //分频器,对TIM2时钟周期进行分频 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); } void Timer_Run() //开启定时器部分 { TIM_SetCounter(TIM2, 0); //先将CNT的值清零 TIM_Cmd(TIM2, ENABLE); //使能TIM2定时器 } void Timer_Stop() //停止定时器 { TIM_Cmd(TIM2, DISABLE); //失能TIM2定时器 } uint16_t Timer_GetCounter() { return TIM_GetCounter(TIM2); }
Timer.h
#ifndef __TIMER_H #define __TIMER_H void Timer_Init(void); uint16_t Timer_GetCounter(void); void Timer_Run(void); void Timer_Stop(void); #endif
IR.c
#include "stm32f10x.h" // Device header #include "Timer.h" #include "IR.h" /* IRState:状态机变量,根据状态机来编写相应的代码,0表示空闲状态,1表示搜寻起始信号或者重复信号,2表示进行数据解码*/ uint8_t IRState; uint8_t IRRepeatFlag; //重复标记位 uint16_t IR_Time; //定时器的计数值,因为是72分频,每记一个数就是1us uint32_t Data; //用来保存数据 uint8_t pData; //数据帧的位数 uint8_t DataFlag; // 数据标记位 void IR_Init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); /* 配置AFIO */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); EXTI_ClearITPendingBit(EXTI_Line14); /* 配置EXIT */ EXTI_InitTypeDef EXIT_InitStructure; EXIT_InitStructure.EXTI_Line = EXTI_Line14; //选择哪条中断线 EXIT_InitStructure.EXTI_LineCmd = ENABLE; EXIT_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式or事件模式,这里选择中断模式 EXIT_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿 EXTI_Init(&EXIT_InitStructure); /* 选择NVIC通道组 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 配置NVIC */ NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); } uint8_t IR_GetDataFlag() //获取标记位的函数 { if(DataFlag==1) { DataFlag = 0; //每次获取到数据后都要将数据标记位清0 return 1; } return 0; } uint8_t IR_GetRepeatFlag() //获取重复标记位函数 { if(IRRepeatFlag==1) { IRRepeatFlag = 0; //每次获取到重复标记后都要将数据标记位清0 return 1; } return 0; } uint16_t IR_GetCounter() //获取计数值函数 { uint16_t temp; Timer_Stop(); //先停止 temp = Timer_GetCounter(); //获取值 Timer_Run(); //再开启 return temp; } void EXTI15_10_IRQHandler(void) //在中断函数里进行数据解码 { if( EXTI_GetITStatus(EXTI_Line14)==SET) { if(IRState==0) //如果状态是0,表示是空闲状态 { Timer_Run(); IRState = 1; } else if(IRState==1) { IR_Time = IR_GetCounter(); if(IR_Time > 13500-1500 && IR_Time < 13500+1500) //起始信号 { IRState = 2; } else if(IR_Time<11250+500 && IR_Time>11250-500) //重复信号 { IRRepeatFlag=1; Timer_Stop(); IRState = 0; } else { IRState = 1; } } else if(IRState==2) { IR_Time = IR_GetCounter(); if(IR_Time<1120+500 && IR_Time>1120-500) //数据0 { Data <<=1; Data += 0; pData++; } else if(IR_Time<2250+500 && IR_Time>2250-500) //数据1 { Data <<=1; Data += 1; pData++; } else //错误数据处理 { Data = 0; IRState = 1; } if(pData >= 32) { pData = 0; DataFlag = 1; Timer_Stop(); IRState=0; } } EXTI_ClearITPendingBit(EXTI_Line14); } }
IR.h
#ifndef __IR_H #define __IR_H extern uint32_t Data; void IR_Init(void); uint8_t IR_GetDataFlag(void); uint8_t IR_GetRepeatFlag(void); uint16_t IR_GetCounter(void); #endif
main.c
/*这里只是简单的测试了下是否获取到键位码,后面的业务逻辑,可以根据需要来进行设计*/ #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "IR.h" #include "Timer.h" #include "LED.h" int main() { OLED_Init(); LED_Init(); IR_Init(); Timer_Init(); while(1) { if(IR_GetDataFlag()==1) { OLED_ShowHexNum(1,1,Data,8); } } }
参考文章:江科协的51单片机教学,在这里对江科协表示感谢。