学习STM32模块红外遥控技术
一、红外遥控的原理
•红外遥控是利用红外光进行通信的设备,由红外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单片机教学,在这里对江科协表示感谢。
作者:heibai_emily