STM32制作红外遥控和红外接收装置

声明

        .这是一部分读取遥控器的键值然后再模拟出该键值的红外信号并发射的代码,可以读到键值,以及设备对应的识别码,收到的2进制转16进制的数据,不是每个遥控器都可以复制的哟,一些简单的遥控,比如电扇的,电视的,大多数还是可以的,但是像空调遥控,尤其是大牌子的,就不行了哦~

        .所有的数据在OLED屏上显示,4针脚I2C的,最好有块OLED啦。

        .我也是自学的,所以肯定会有很多地方的不足,希望大家见谅,勿喷!

没有放所有文件,代码仅供参考和提供思路哦~,欢迎大家来一起交流,我也是第一次发文,多多支持啦。

使用的外设


这里使用的外设有

1.STM32F103C8T6

2.GY-032红外发射和接收

3.HX1838(要接收头)

4.4针脚IIC OLED显示屏(有最好,没有的话就是串口) 

 

 

接线部分


GY-032接线==================

  vcc--- 3.33V

  out---A0

  EN---C14

  拔掉跳线帽

OLED接线===================

SCL--A5

SDA--A7

按键接线====================

A1和A2

红外接收头接线===============

data---PB9

红外接收部分的代码



下面是红外的发射部分,借鉴正点原子的,把验证设备识别码的部分省了,原理教程还是蛮多,大家可以去搜一下,重点是一定要弄懂位操作的过程,以及编程的思路,

//红外遥控初始化
//设置IO以及定时器4的输入捕获
void Remote_Init(void)    			  
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;  
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB时钟 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);	//TIM4 时钟使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;				 //PB9 输入 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 		//上拉输入 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_9);	//初始化GPIOB.9
	
						  
 	TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大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(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;  // 选择输入端 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(TIM4, &TIM_ICInitStructure);//初始化定时器输入捕获通道

  TIM_Cmd(TIM4,ENABLE ); 	//使能定时器4
 
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_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( TIM4,TIM_IT_Update|TIM_IT_CC4,ENABLE);//允许更新中断 ,允许CC4IE捕获中断								 
}

//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留	
//[4]:标记上升沿是否已经被捕获								   
//[3:0]:溢出计时器
u8 	RmtSta=0;	  	  
u16 Dval;		//下降沿时计数器的值
u32 RmtRec=0;	//红外接收到的数据	  
u32 RmtRecSEND=0;
u8  RmtCnt=0;	//按键按下的次数	  
//定时器4中断服务程序	 
void TIM4_IRQHandler(void)
{ 		    	 
 
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)//更新中断
	{	
		if(RmtSta&0x80)								//上次有数据被接收到了
			//1000 0000
		{	
			RmtSta&=~0X10;							//取消上升沿已经被捕获标记
			//               0001 0000
			//取反之后      1110 1111
			//此时的RmtSta  1000 0000
			//按位与后      1000 0000
 			if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;	//标记已经完成一次按键的键值信息采集
			//0000 1111  ; 0000 0000
			if((RmtSta&0X0F)<14)RmtSta++;
			//0000 1111  ; 0000 1110
			else
			{
				RmtSta&=~(1<<7);					//清空引导标识
				RmtSta&=0XF0;						//清空计数器	
				//1111 0000
			}								 	   	
		}							    
	}
	if(TIM_GetITStatus(TIM4,TIM_IT_CC4)!=RESET)//捕获中断
	{	  
		if(RDATA)//上升沿捕获
		{
  		TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Falling);						//CC4P=1	设置为下降沿捕获
			TIM_SetCounter(TIM4,0);							//清空定时器值
			RmtSta|=0X10;							//标记上升沿已经被捕获
			//0001 0000
		}else //下降沿捕获
		{
			Dval=TIM_GetCapture4(TIM4);					//读取CCR4也可以清CC4IF标志位
  		TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Rising);				//CC4P=0	设置为上升沿捕获
			if(RmtSta&0X10)							//完成一次高电平捕获 如果之前捕获到了上升沿 
				//0001 0000
			{
 				if(RmtSta&0X80)//接收到了引导码
					//1000 0000
				{
					
					if(Dval>300&&Dval<800)			//560为标准值,560us
					{
						RmtRec<<=1;					//左移一位. 
					//	printf("RmtRec  %d\r\n",RmtRec);
						RmtRec|=0;					//接收到0	   
					}else if(Dval>1400&&Dval<1800)	//1680为标准值,1680us
					{
						RmtRec<<=1;					//左移一位.
						RmtRec|=1;					//接收到1
					}else if(Dval>2200&&Dval<2600)	//得到按键键值增加的信息 2500为标准值2.5ms
					{
						RmtCnt++; 					//按键次数增加1次
						RmtSta&=0XF0;				//清空计时器		
						//1111 0000
					}
 				}else if(Dval>4200&&Dval<4700)		//4500为标准值4.5ms
				{
					RmtSta|=1<<7;					//标记成功接收到了引导码
					RmtCnt=0;						//清除按键次数计数器
				}						 
			}
			RmtSta&=~(1<<4);
		}				 		     	    					   
	}
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update|TIM_IT_CC4);	 	    
}

//处理红外键盘
//返回值:
//	 0,没有任何按键按下
//其他,按下的按键键值.
u8 Remote_Scan(void)
{        
	u8 sta=0;       
    u8 t1,t2;  
	if(RmtSta&(1<<6))//得到一个按键的所有信息了
	{ 
	    t1=RmtRec>>16;			//得到地址码
	    t2=(RmtRec>>16)&0xff;	//得到地址反码'
		//1111 1111
			printf("RmtRec:%x\r\n",RmtRec);			
			printf("t1:%d;t2:%d\r\n",t1,t2);
			printf("%d\r\n",(u8)~t2);
		RmtRecSEND=RmtRec;
 	    //if((u8)~t2==REMOTE_ID)//检验遥控识别码(ID)及地址 
	    //{ 
	        t1=RmtRec>>8;
	        t2=RmtRec; 	
	       sta=t2;//键值正确	 
				printf("识别到\r\n");
		//}   
		if((sta==0)||((RmtSta&0X80)==0))//按键数据错误/遥控已经没有按下了
		{
		 	RmtSta&=~(1<<6);//清除接收到有效按键标识
			RmtCnt=0;		//清除按键次数计数器
			printf("错误\r\n");
		}
	}  
    return sta;
}

       开始读取信息的IO口的模式为下拉输入(默认为高电平)方便检测低电平,因为引导码开始是低电平。

 

       检测到低电平后进入捕获中断函数,设置捕获中断为上升沿捕获,并读取定时器记录的低电平的时长,如果时长在4.5ms左右,则标记为第一次下降沿,也就是一段数据中的引导码部分

       紧接着肯定只会有高电平的出现,也就是等待着上升沿的捕获中断。检测到高电平后,清空计时器值并重新开始,同时标记上升沿已被捕获,此时只需等待下一次的低电平的到来。

       检测到低电平后,若判断到上升沿已被捕获,说明此段数据已不为引导码部分,为后续部分,然后读取定时器时长,判断时长即可判断出该段数据是“1”还是“0”。

        保存完数据后,进行数据的解析。

下面是红外发射部分

 TIM2初始化

void TIM2_PWM_Init(u16 arr,u16 psc)
{
	/* 初始化结构体定义 */
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef 	TIM_OCInitStructure;
	/* 使能相应端口的时钟 */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能定时器2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //使能GPIO外设时钟
	
	/* GPIOA.0初始化 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;			// TIM2 CH1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 	// PA.0 复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_0);
	/* TIM2 初始化*/
	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(TIM2, &TIM_TimeBaseInitStructure);
	/* 定时器TIM2 Ch1 PWM模式初始化 */
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  //选择定时器模式:TIM PWM1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//比较输出使能
	//TIM_OCInitStructure.TIM_Pulse = (arr+1)/2;	  //占空比 50%
	TIM_OCInitStructure.TIM_Pulse = (arr+1)/3;	  //占空比1:3
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;	//输出极性:TIM输出比较极性高
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);

	/* 使能TIM2在CCR1上的预装载寄存器 */
	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); 
	/* 使能定时器 */
	TIM_Cmd(TIM2, ENABLE); 			
}

下面这是红外数据的发射部分,还是很简单的,这是最简单的写法,利用延时做的。

void TR_SendData(u32 data)
{
	int t;
	//引导码
	IR_SEND = 0;
	delay_us(9000 - 2);
	IR_SEND = 1;
	delay_us(4500 - 2);
	//数据
	for (t = 31; t >= 0; t--)
	{
		IR_SEND = 0;
		delay_us(560 - 2);
		IR_SEND = 1;
		if ((data >> t) % 2)
		{
			delay_us(1680 - 2);
		}
		else
		{
			delay_us(560 - 2);
		}
	}
	//关闭输出
	IR_SEND = 0;
	delay_us(560 - 2);
	IR_SEND = 1;
}

这是我的main函数



/*main.c*/
#include "delay.h"
#include "sys.h"
#include "usart.h"	 
#include "send.h"
#include "string.h"
#include "remote.h"
//#include "smg.h"
#include "key.h"
#include "led.h"
#include "oled.h"


int main()
{	u8 key;
	u8 t=0;	
 	u8 *str=0;
	u8 x=0;
	u8 k;
	u8 RmtCnt;
	 char RmtRec_C[32]={0};
	 char T2_C[32]={0};
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级	
	delay_init();
	Remote_Init();
	uart_init(115200);
	TIM2_PWM_Init(1895,0);
	IR_SendPort_Init();
	KEY_Init();
	LED_Init();
	OLED_Init();
	OLED_Clear();//清屏 提前显示一遍
						OLED_ShowString(0,4,"Key:",16);
						sprintf((char *)RmtRec_C,"RmtRec:%x   ",RmtRec);
						OLED_ShowString(0,0,RmtRec_C,16);
						OLED_ShowNum(32,4,x,3,16);
						sprintf((char *)T2_C,"Code:%d       ",t22);
						OLED_ShowString(0,2,T2_C,16);
	while (1)
	{			
		k=KEY_Scan();//按键检测
		
		switch(k)
		{
			case 3:
					LED0=!LED0;;//就绪提示
					key=Remote_Scan();//读取红外	
					x=key;//赋值给X
					if(key){
						strcpy(RmtRec_C, "");//清空数组
						OLED_ShowString(0,6,"Reading...",16);//显示状态
						delay_ms(500);//延时,防止多次接收
						sprintf((char *)RmtRec_C,"RmtRec:%x   ",RmtRec);//16进制数据
						OLED_ShowString(0,0,RmtRec_C,16);
						OLED_ShowNum(32,4,x,3,16);//键值
						sprintf((char *)T2_C,"Code:%d       ",t22);//识别码
						OLED_ShowString(0,2,T2_C,16);		
					}
				break;
			
			case 2:
					TR_SendData(RmtRecSEND);//发射数据
					printf("RmtRecSEND已发送:%x\r\n",RmtRecSEND);	
					OLED_ShowString(0,6,"Transmitting...",16);
					delay_ms(500);//防止多次发射
					LED0=0;
				break;
		}
		OLED_ShowString(0,6,"===============",16);
		LED0=1;
		
		//TR_SendData(0x2FDB04F);
		//TR_SendData(0xFf38c7);
		
		
}
}

 按键部分


#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"
#include "usart.h"
=
void KEY_Init(void)
{
	
	GPIO_InitTypeDef GPIO_InitStructure;

 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//??PORTA??

	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//??jtag,??SWD,???SWD????
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;//PA15
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //???????
 	GPIO_Init(GPIOA, &GPIO_InitStructure);//???GPIOA15
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2;//PC5
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //???????
 	GPIO_Init(GPIOA, &GPIO_InitStructure);//???GPIOC5
 }

u8 KEY_Scan(void)
{	 
	if(KEY0==0)
	{
		return 3;
	}
		else if(KEY1==0)
	{printf("KEY1");
		return 2;
	}
  
 	return 0;// 无按键按下
}

按住KEY0进入读红外模式,OLED显示reading后松开就行

KEY1是发射红外,按一次就行。

结尾

有啥问题欢迎来评论啊各位❤


  • 8
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
STM32外部中断红外遥控解码是指利用STM32微控制器的外部中断功能对红外遥控信号进行解码的过程。 首先,我们需要了解红外遥控信号的工作原理。红外遥控信号是通过红外线传输的,信号的编码通常采用NEC、RC5等协议。主要包括起始位码、地址码和命令码等。 解码的第一步是利用红外接收器将红外遥控信号转化为电信号。常用的红外接收器有红外管、红外模块等。电信号经过红外接收器的解调,得到了红外遥控信号的脉冲宽度调制(PWM)信号。 接下来,我们需要利用STM32的外部中断功能对红外遥控信号进行解码。首先,需要配置STM32的GPIO引脚为外部中断模式,并设置中断触发方式。常用的触发方式有上升沿触发、下降沿触发和双边沿触发等。 当红外接收到PWM信号后,触发外部中断,将中断引脚与STM32的外部中断输入管脚连接。当外部中断触发时,STM32会跳转到相应的中断服务函数进行处理。 在中断服务函数中,我们可以利用定时器和输入捕获功能来测量PWM信号的高电平时间和低电平时间。利用这些时间信息,我们可以解析出红外遥控信号的起始位码、地址码和命令码。 解码完成后,我们可以根据实际需求对红外遥控信号进行相应的处理。比如,可以控制某些设备的开关状态、调节音量、切换频道等。 需要注意的是,在实际应用中,还需要考虑红外遥控干扰、多个按键同时按下等问题,以提高系统的稳定性和抗干扰能力。 综上所述,STM32外部中断红外遥控解码是一种使用STM32微控制器的外部中断功能,对红外遥控信号进行解码的技术。通过这种技术,我们可以实现对红外遥控信号的解析和响应,从而控制不同设备的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值