STM32红外串口接收

1.NEC协议
现有的红外遥控包括两种方式: PWM(脉冲宽度调制)和PPM(脉冲位置调制)。
两种形式编码的代表分别为NEC 和 PHILIPS的RC-5、RC-6以及将来的RC-7。

PWM(脉冲宽度调制):以发射红外载波的占空比代表"0”和"1"。为了节省能量,一般情况下,发射红外载波的时间固定,通过改变不发射载波的时间来改变占空比。例如常用的电视遥控器,TOSHIBA的TC9012,其引导码为载波发射4. 5ms,不发射4.5ms,其"0"为载波发射0.56ms,不发射0.565ms,其"1"为载波发射0.56ms,不发射1.69ms。

PPM(脉冲位置调制)∶以发射载波的位置表示"0"和"1"。从发射载波到不发射载波为"0",从不发射载波到发射载波为"1"。其发射载波和不发射载波的时间相同,都为0.68ms,也就是每位的时间是固定的。

2.电路图:三条线,VCC 、GND、DATA
在这里插入图片描述

3.通信协议图,总共4*8=32位数据。
在这里插入图片描述

4.地址码,持续时间1690us为数据"1",持续时间560us为数据"0"。
他们低电平部分560us相同,可以省略判断,直接根据高电平持续时间来判断是数据0还是数据1。
通过测量不同的高电平持续时间,就能够知道当前的信号是引导码、比特0、比特1。
地址码
在这里插入图片描述

思路:

红外发射头发射红外,相当于按下按键,变为高电平。 红外发射头停止发射,相当于松开按键,变为高电平,默认的时候是高电平。
所以采用外部中断来触发。

5.代码

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include "stdio.h"

static GPIO_InitTypeDef  	GPIO_InitStructure;
static USART_InitTypeDef 	USART_InitStructure;
static NVIC_InitTypeDef 	NVIC_InitStructure;		
static EXTI_InitTypeDef   	EXTI_InitStructure;

static volatile uint8_t 	g_ir_data[4]={
    
    0};
static volatile uint32_t	g_ir_event=0;


//重定义fputc函数 
int fputc(int ch, FILE *f)
{
    
     	
	USART_SendData(USART1,ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  
	
	return ch;
}   

void delay_us(uint32_t nus)
{
    
    		
	uint32_t temp;	    	 
	SysTick->LOAD =SystemCoreClock/8/1000000*nus; 	//时间加载	  		 
	SysTick->VAL  =0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; 		//使能滴答定时器开始倒数 	 
	do
	{
    
    
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));			//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; 		//关闭计数器
	SysTick->VAL =0X00;       						//清空计数器 
}

void delay_ms(uint16_t nms)
{
    
    	 		  	  
	uint32_t temp;		   
	SysTick->LOAD=SystemCoreClock/8/1000*nms;		//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;    	//能滴答定时器开始倒数 
	do
	{
    
    
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));			//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    	//关闭计数器
	SysTick->VAL =0X00;     		  				//清空计数器	  	    
} 



void USART1_Init(uint32_t baud)
{
    
    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 							//使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);							//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); 						//GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); 						//GPIOA10复用为USART1
	
	//USART1端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 						//GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;									//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;								//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 									//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 									//上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); 											//初始化PA9,PA10

	//USART1 初始化设置
	USART_InitStructure.USART_BaudRate = baud;										//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;						//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;							//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;								//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;					//收发模式
	USART_Init(USART1, &USART_InitStructure); 										//初始化串口1
	
	USART_Cmd(USART1, ENABLE);  													//使能串口1 
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);									//开启相关中断

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;								//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;							//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;								//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;									//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);													//根据指定的参数初始化VIC寄存器
}


void ir_init(void)
{
    
    

	/* GPIOA硬件时钟使能 */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	/* Enable SYSCFG clock ,使能系统配置时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);


	/* 配置PA8引脚为输入模式  */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;			//第8根引脚	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;		//输入模式,能够检测外部电平状态
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//GPIO最大的速度为100MHz
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	//不需要上下拉电阻
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* Configure EXTI Line8 ,配置外部中断控制线8*/
	EXTI_InitStructure.EXTI_Line 	= EXTI_Line8;			//使能外部中断控制线8
	EXTI_InitStructure.EXTI_Mode 	= EXTI_Mode_Interrupt;	//中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发,能够检测到红外信号的到达
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;				//中断控制线使能,让它工作
	EXTI_Init(&EXTI_InitStructure);
	
	/* Connect EXTI Line8 to PA8 pin */
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource8);

	
	/* Enable and set EXTI Line8 Interrupt to the lowest priority ,使能并设置外部中断控制线8中断,优先级是最低*/
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;				//外部中断控制线9_5触发中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;	//抢占优先级为0xF
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;			//响应优先级为0xF
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//允许外部中断控制线9_5触发中断
	NVIC_Init(&NVIC_InitStructure);


}


int main(void)
{
    
     
	LED_Init();		

	//系统定时器初始化,时钟源来自HCLK,且进行8分频,
	//系统定时器时钟频率=168MHz/8=21MHz
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
		
	//设置中断优先级分组2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//串口1,波特率115200bps,开启接收中断
	USART1_Init(115200);
	
	ir_init();
	
	while(1)
	{
    
    

		if(g_ir_event)
		{
    
    
			printf("ir data:%02X%02X%02X%02X\r\n",g_ir_data[0],g_ir_data[1],g_ir_data[2],g_ir_data[3]);
			
			
			g_ir_event=0;
		
		
		}
	}
}
//分析红外信号,总结出规律,通过测量不同的高电平持续时间,就能够知道当前的信号是引导码、比特0、比特1
uint8_t IR_PluseHighTime(void)
{
    
    
	uint8_t t=0;
	
	//高电平
	while(PAin(8) == 1)
	{
    
    
		t++;
		delay_us(20);
		
		//超时溢出
		if(t > 250)
			return t;
	
	}

	return t;
}


void EXTI9_5_IRQHandler(void)
{
    
    
	uint8_t t=0,ir_vaild=0,ir_bit=0;
	uint32_t ir_data=0,ir_bit_cnt=0;
	
	//检查当前外部中断控制线8是否触发中断
	if(EXTI_GetITStatus(EXTI_Line8) != RESET)
	{
    
    
		//添加代码
		while(1)
		{
    
    
			//若出现高电平,就进行测量
			if(PAin(8)==1)
			{
    
    
				t = IR_PluseHighTime();
				
				//当前信号是非法信号
				if(t >=250)
					break;
				
				//判断当前信号是引导码
				if(t>=200 && t<250)		//4ms ~ 5ms
				{
    
    
					ir_vaild=1;
					
					continue;
				
				}
				//收到bit1
				else if(t>=60 && t<90)	//1.2ms~1.8ms
				{
    
    
					ir_bit = 1;
				
				}
				//收到bit0
				else if(t>=10 && t<50)  //0.2ms ~ 1 ms
				{
    
    
					ir_bit = 0;
				}
				
				//获取bit数据
				if(ir_vaild)
				{
    
    
					ir_data|=ir_bit<<ir_bit_cnt;
				}
				
				ir_bit_cnt++;
				
				if(ir_bit_cnt >=32)
				{
    
    
					g_ir_data[0] = (uint8_t)((ir_data>>24)&0xFF);
					g_ir_data[1] = (uint8_t)((ir_data>>16)&0xFF);				
					g_ir_data[2] = (uint8_t)((ir_data>>8)&0xFF);
					g_ir_data[3] = (uint8_t)(ir_data&0xFF);

					//进行数据校验判断,检查当前接收到的红外数据是否正确
					if(g_ir_data[0] == (0xFF - g_ir_data[1]))
					{
    
    
						if(g_ir_data[2] == (0xFF - g_ir_data[3]))
						{
    
    
							g_ir_event = 1;
							
						}
					
					}
					
					
					
					break;

				}
				
				
			}
		
		}
		
		/* Clear the EXTI line 8 pending bit,清空中断标志位,就代表说我已经完成中断处理 */
		EXTI_ClearITPendingBit(EXTI_Line8);
	 }
}

void USART1_IRQHandler(void)                				//串口1中断服务程序
{
    
    
	uint8_t d;
	

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  	//接收中断
	{
    
    
		d = USART_ReceiveData(USART1);
				 		
	} 
} 


猜你喜欢

转载自blog.csdn.net/ABCisCOOL/article/details/115144371