這裡沒有解析紅外的函數,畢竟考慮到學習的紅外有幾種協定(如: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.