天天看點

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<irlen 并且 irdata[i] != 0xffff 時成立,當其中一個不成立,退出循環
	{
		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.