STM32——红外接收和红外发射

这里没有解析红外的函数,毕竟考虑到学习的红外有几种协议(如:NEC).同时代码存在一些问题(有部分遥控是学习不了的(如:空调、DVD)).

红外数据接收:定时器捕获红外信号(还有一个是通过外部中断的,这里不列出)
红外数据发射:使用PWM输出
下面直接上代码

remote.c

/*remote.c*/
#include "remote.h"
#include "delay.h"
#include "usart.h"

/**
发射:PA7-TIM3-CH2
接收:PC8-TIM8-CH3

TIM8:
下面代码中捕获中断和定时中断分开,不在同一中断函数,所以需要配置两次中断优先级(应该可以合在一起配置)
*/

/**
 *  @name			void Remote_Init(void) 
 *	@description	红外遥控初始化 设置IO以及定时器8的输入捕获
 *                  Infrared remote initializer sets IO and timer 8 input capture
 *  @notice			
 */
void Remote_Init(void)    			  
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;  

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE); //使能PORTB时钟 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE);                      //TIM8 时钟使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;                                //PC8 输入 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                    //浮空输入 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_8);                                          //初始化GPIOC.8

	TIM_TimeBaseStructure.TIM_Period = 9999;                                 //设定计数器自动重装值 最大10ms溢出  
	TIM_TimeBaseStructure.TIM_Prescaler =(72-1);                             //预分频器,1M的计数频率,1us加1.	   	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                  //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;              //TIM向上计数模式
	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);                          //根据指定的参数初始化TIMx

	TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;                         // 选择输入端 IC4映射到TI4上
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;              //上升沿捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;                    //配置输入分频,不分频 
	TIM_ICInitStructure.TIM_ICFilter = 0x03;                                 //IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
	TIM_ICInit(TIM8, &TIM_ICInitStructure);                                  //初始化定时器输入捕获通道
	TIM_Cmd(TIM8,ENABLE );                                                   //使能定时器4

	NVIC_InitStructure.NVIC_IRQChannel = TIM8_CC_IRQn;                       //TIM8TIM8捕获比较中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;                //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                       //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                          //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);                                          //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	
	TIM_ITConfig( TIM8,TIM_IT_CC3,ENABLE);                                   //允许更新中断 ,允许CC4IE捕获中断		
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn;                       //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;                //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                       //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                          //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);                                          //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	
	TIM_ITConfig( TIM8,TIM_IT_Update,ENABLE);                                //允许更新中断 ,允许CC4IE捕获中断								 
}
/**
 *  @name			void TIM3_PWM_Init(u16 arr,u16 psc)
 *	@description	初始化定时器3的设置,将定时器3用于PWM调制,PWM输出口为 PA.7
 *	@param			arr --	u16,定时器重装值
					psc --	u16,定时器分频值							
 *	@return		
 *  @notice			PWM频率 = 72M/((arr+1)*(psc+1)),这里用作红外发射的载波,需要生成38kHz的方波,故取arr = 1895,psc = 0。
 */
void TIM3_PWM_Init(u16 arr,u16 psc)
{
	/* 初始化结构体定义 */
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseInitStructure;                        //定时器基本设置
	TIM_OCInitTypeDef 	TIM_OCInitStructure;                                  //定时器比较输出配置

	/* 使能相应端口的时钟 */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);                      //使能定时器2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设时钟
	
	/* GPIOA.7初始化 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;                                 // TIM3 CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                           // PA.7 复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_7);

	/* TIM3 初始化*/
	TIM_TimeBaseInitStructure.TIM_Period = arr;                               //下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;                            //作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;                          //时钟分割:TDTS = Tck_tim
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;           //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

	/* 定时器TIM3 Ch2 PWM模式初始化 */
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                         //选择定时器模式:TIM PWM1   TIM_OCMode_PWM1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;             //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = (arr+1)/3;                                //占空比1:3 (arr+1)/10  3
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                 //输出极性:TIM输出比较极性高   TIM_OCPolarity_High
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);

	/* 使能TIM3在CCR1上的预装载寄存器 */
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); 
}


u8 	RmtSta=0;                    //红外学习上升或下降标志位
u16 Dval;                        //上升沿、下降沿的计数器的值
u8  RmtCnt=0;                    //定时器红外学习计数超时标志位	  
u8  PulseTabCnt=0;               //上升沿下降沿计数器的值
u16 PulseTab[MAX_PULSE_LEN]={0}; //红外学习存储数据
u8  Flag_LearnState = 0;         //红外学习标志位

/**
  *@name  void TIM8_UP_IRQHandler(void)
  *@brief TIM8的定时计数中断函数
  *       The timing count interrupt function of TIM8
  *@note
  */
void TIM8_UP_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM8,TIM_IT_Update)!=RESET)
	{
		if(RmtCnt++>50)
		{
			RmtCnt = 0;

			if(RmtSta)
			{
				RmtSta = 0;
				Flag_LearnState = 1;
			}
		}
	}
	TIM_ClearITPendingBit(TIM8,TIM_IT_Update);	 	    
}

/**
  *@name  void TIM8_CC_IRQHandler(void)
  *@brief 捕获红外载波的高低电平宽度,记录到数组PulseTab
  *       Capture the high and low level width of the infrared carrier and record it to the array PulseTab
  *@note
  */
void TIM8_CC_IRQHandler(void)
{ 		    	 
	if(TIM_GetITStatus(TIM8,TIM_IT_CC3)!=RESET)                 //获取上升沿或下降沿状态
	{	  
		if(RDATA)                                               //上升沿捕获
		{
			Dval=TIM_GetCapture3(TIM8);                         //读取CCR4也可以清CC4IF标志位
			
			TIM_OC3PolarityConfig(TIM8,TIM_ICPolarity_Falling); //CC4P=1	设置为下降沿捕获
			
			TIM_SetCounter(TIM8,0);                             //清空定时器值
			
			if(RmtSta&0X01)
			{
				PulseTab[PulseTabCnt++] = Dval;
				
				RmtSta = 0x10;
			}
			else
			{
				RmtSta = 0X10;                                  //标记上升沿已经被捕获
			}
		}
		else                                                    //下降沿捕获
		{
			Dval=TIM_GetCapture3(TIM8);                         //读取CCR4也可以清CC4IF标志位

			TIM_OC3PolarityConfig(TIM8,TIM_ICPolarity_Rising);  //CC4P=0	设置为上升沿捕获
			
			TIM_SetCounter(TIM8,0);                             //清空定时器值
			
			if(RmtSta&0X10)                                     //完成一次高电平捕获 
			{
				PulseTab[PulseTabCnt++] = Dval;
				
				RmtSta = 0x01;
			}
			else 
			{
				RmtSta = 0x01;
			}
		}				 		     	    					   
	}
	TIM_ClearITPendingBit(TIM8,TIM_IT_CC3);	 	    
}

/**
  *@name            void Infrared_Send_IR1(u16 *irdata,u32 irlen)
  *@description     红外信号发射函数
  *@param           irdata --   u16,红外数据
                    irlen  --   u32,红外数据长度							
  *@return		
  *@notice			
  */
void Infrared_Send(u16 *irdata,u32 irlen)
{
	u32 i;                                      //用于下面的for循环

	for(i=0; i<irlen && irdata[i]!=0xffff; i++) //循环,从i=0开始,当i
	{
		if(i%2 == 0)                            //偶数的下标的数组成员延时拉高电平
		{
			TIM_Cmd(TIM3,ENABLE);
			
			delay_us(irdata[i]);
			
			TIM_Cmd(TIM3,DISABLE);
			
			GPIO_SetBits(GPIOA,GPIO_Pin_7);			
		}
		else
		{
			GPIO_SetBits(GPIOA,GPIO_Pin_7);
			
			delay_us(irdata[i]);
		}
	}
	
	delay_us(555);
	
	GPIO_ResetBits(GPIOA,GPIO_Pin_7);
	
	return ;
}

remote.h

/*remote.h*/
#ifndef __RED_H
#define __RED_H 
#include "sys.h"   

#define RDATA 	PCin(8)	 	//红外数据输入脚

//红外遥控识别码(ID),每款遥控器的该值基本都不一样,但也有一样的.
//我们选用的遥控器识别码为0
#define REMOTE_ID 0      		   
#define MAX_PULSE_LEN 400 //500  300

extern u8  RmtCnt;			//按键按下的次数
extern u16 PulseTab[MAX_PULSE_LEN];
extern u8  Flag_LearnState ;
extern u8  PulseTabCnt;//上升沿下降沿计数器的值

void Remote_Init(void);    	//红外传感器接收头引脚初始化
void TIM3_PWM_Init(u16 arr,u16 psc);
void Infrared_Send(u16 *irdata,u32 irlen);

#endif

main.c

/*main.c*/
#include "delay.h"
#include "sys.h"
#include "usart.h"	 
#include "remote.h"
#include "string.h"


/**
  *@name   int main(void)
  *@brief
  *@retval return 0
  *@note
  */
int main(void)
{	 
	delay_init();                                   //延时函数初始化	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	
	uart_init(115200);                              //串口初始化为115200

	Remote_Init();                                  //红外接收初始化		 	

	TIM3_PWM_Init(1895,0);                          //tim3 pwm initialize
	
	printf("init ok\r\n");
	
	while(1)
	{
		if(Flag_LearnState)
		{
			printf("%s\r\n",(char *)PulseTab);
			
			Infrared_Send(PulseTab,PulseTabCnt);
			
			PulseTabCnt = 0;
			
			Flag_LearnState=0;

			memset((void *)PulseTab,0,MAX_PULSE_LEN);
		}
	}
}

最后
实验现象:
本实验开机之后,即进入等待红外触发,如过接收到正确的红外信号,则500ms后自动从红外发射棒发射红外,同时通过串口1,将接受的红外信号以16进制打印出来.

注意事项:
1,可以用最小开发板调试,红外接收头和发射棒可以外接.
2,当使用的接收或发射引脚不一样是,注意将定时器修改.
3,本实验为万能学习红外遥控器,属于半成品,未做存储处理,需要储存需使用芯片的flash或外接eeprom.

你可能感兴趣的:(STM32——红外接收和红外发射)