STM32模块学习之红外遥控

本文介绍了如何使用STM32单片机通过红外遥控原理,特别是NEC协议进行数据通信,并通过状态机和定时器实现红外信号的捕捉和解码。作者详细解释了NEC协议的数据帧结构和代码实现,包括TIM2定时器的配置和中断处理过程。
摘要由CSDN通过智能技术生成

一、红外遥控的原理

红外遥控是利用红外光进行通信的设备,由红外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单片机教学,在这里对江科协表示感谢。

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值