一、原理
1、红外发射协议
2、定时器计数
3、实现方法
二、实现
1、配置 GPIO 口下降沿触发中断
EXTI_Line7
,在库函数中对该中断源定义的服务函数为 EXTI9_5_IRQHandler()
,也就是说外部中断 5 到 9 是 共用一个中断服务函数的。void IR_Pin_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStructure);
EXTI_ClearITPendingBit(EXTI_Line7);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7);
EXTI_InitStructure.EXTI_Line=EXTI_Line7;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
2、配置定时器计数值
Tim2_UPCount_Init(SystemCoreClock/1000000-1,100-1); //0.1ms
进行初始化,可以每 0.1ms 产生一次中断。void Tim2_UPCount_Init(u16 Prescaler,u16 Period)
{
TIM_TimeBaseInitTypeDef TIM_StructInit;
NVIC_InitTypeDef NVIC_StructInit;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_StructInit.TIM_Period=Period;
TIM_StructInit.TIM_Prescaler=Prescaler;
TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up;
TIM_StructInit.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2, &TIM_StructInit);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
NVIC_StructInit.NVIC_IRQChannel=TIM2_IRQn;
NVIC_StructInit.NVIC_IRQChannelCmd=ENABLE;
NVIC_StructInit.NVIC_IRQChannelPreemptionPriority=0;
NVIC_StructInit.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_StructInit);
}
3、定时器中断函数统计时间
ucTim2Flag
加 1,意味着时间过去了 0.1ms。uint16_t ucTim2Flag;
。void TIM2_IRQHandler(void)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
ucTim2Flag++;
}
4、GPIO 中断函数中接收 32 位数据
uint8_t irdata[33]; //用于记录两个下降沿之间的时间
bool receiveComplete; //接收完成标志位
uint8_t idx; //用于索引接收到的数值
bool startflag; //表示开始接收
void EXTI9_5_IRQHandler(void)
{
uint16_t ir_time;
if(startflag)
{
ir_time = ucTim2Flag;
if(ucTim2Flag < 150 && ucTim2Flag >= 50 ) // 接收到同步头
{
idx=0; // 数组下标清零
}
irdata[idx] = ucTim2Flag; // 获取计数时间
ucTim2Flag = 0; // 清零计数时间,以便下次统计
idx++; // 接收到一个数据,索引加1
if(idx==33) // 如果接收到33个数据,包括32位数和以一个同步头
{
idx=0;
ucTim2Flag = 0;
receiveComplete = TRUE;
}
}
else // 下降沿第一次触发
{
idx = 0;
ucTim2Flag = 0;
startflag = TRUE;
}
EXTI_ClearITPendingBit(EXTI_Line7); // 清除中断标志
}
5、判断控制码值
receiveComplete
来实现。uint8_t Ir_Server()
{
uint8_t i,j,idx=1; //idx 从1 开始表示对同步头的时间不处理
uint8_t temp;
for(i=0; i<4; i++)
{
for(j=0; j<8; j++)
{
if(irdata[idx] >=8 && irdata[idx] < 15) //表示 0
{
temp = 0;
}
else if(irdata[idx] >=18 && irdata[idx]<25) //表示 1
{
temp = 1;
}
remote_code <<= 1;
remote_code |= temp;
idx++;
}
}
return remote_code[2]; // 该数组中记录的是控制码,每个按键不一样
//for(idx=0; idx<4; idx++)
//{
// printf("remote_code[%d] = %#x\n",idx,remote_code[idx]);
//}
}
6、主函数
void main()
{
...
IR_Pin_init();
Tim2_UPCount_Init(SystemCoreClock/1000000-1,100-1);
while(1)
{
if(repeatEnable)
{
repeatEnable = FALSE;
Ir_Server();
printf("key_code = %#x\n",remote_code[2]);
}
}
}
三、演示
如下图为串口出接收息:
说明1:这只是实现红外接收的其中一种方法,网上还有一种比较常见的方法是利用下降沿触发,在中断中进行延迟,判断高电平持续时间以此来判断信号类别。个人感觉这不是一种很好的方法,因为在中断中进行延时会导致主函数得不到及时的处理。
说明2:在调试时,不要在中断处理中加入过多无关语句,例如打印语句,这会导致结果出错。
本文档基于 STM32 F1 系列 MCU,固件库版本 3.5。其他 MCU 及固件库仅需要对库函数略作修改。
文章评论(0条评论)
登录后参与讨论