基于stm32f103zet6之红外遥控解码的学习

不得不吐槽一下,我真的好挫,真的真的非常差劲儿。。。

一、红外遥控解码部分从昨天开始整,一直到现在才解码成功!中途遇到了不少问题,结果出来后还是觉得有必要总结一下,唉!

1、首先我又是怀疑我硬件电平不兼容德问题,后来给接上3.3V的电压,还是不行,好吧,算失败了,在网上查阅了比较多的帖子,也找了比较多的资料,最终还是决定用原本那个生了锈的遥控来解码!

2、然后准备参照着原来51的思想来移植代码,也确实找到类似的代码貌似使用的2.0的库写的,单步调试了半天,总感觉在延时部分出了点问题,所以比较郁闷,好吧,分析来分析去的,结果真的是没有半点现象啊!果断网上求助,游荡了一会,压根没人理,高手不屑一顾呀!!偶然间让我遇到了原子哥的那段红外的代码,拖出来分析,所以就有了今晚解码成功的结果!

3、我照着原子的移植,我用的是自己的延时,也就是系统定时器,MTD,单步调试的时候,发现居然死在了systick那里,进不了中断,一步步观察,好像导致进不了中断的原因就是:我已经进了外部中断,心想,没道理啊,系统定时器的优先级不应该是高于外部中断的么,因为他是核决定的呀(至少我是这么想的),然后又查了相关资料,据说系统定时器的中断优先级是最低的,这时候我才恍然大悟!

现在开始分析代码,虽然说原子的代码风格不怎么样,但是个人觉得他真的好牛逼,库函数是人家ST公司搞出来的,我想,原子的这套代码,应该基本上是他自己一个人整出来的吧!

二、所谓红外遥控!(针对我手上的红外遥控)

1、红外解码一直是单片机中应用较多的,需要设备加装专用解码芯片,这就大大减轻了单片机的负担。需要单片机样例使用延时做红外解码,比较容易理解,
下面通过TC9012和uPD6121芯片为例大致了解解码原理:
先看一接收头产生的波形图,这是原子的一张图


         % U, K" ?3 K2 _( j' a! e: K: o
从上图可以看出 9.0ms高电平+4.5ms低电平称为头码,用于识别是否遥控码开始,这是一张连续发射码的波形图(就是一直按下某一遥控器按键)。; n5 [
+ z; ^4 d( T# L) h" Y6 B5 j3 T
头码过后会出现4个8位的数据,我们最终目的就是要把这个 32位(4x8)从一体化红外接收头提取出来,并转换成16进制数,用于区分不同按键按下得出的不同数值。
在遥控器发射波形中,可以知道,8位数中的0或者1不是用高低电平表示,而是用不同的低电平的宽度表示,0.565ms表示0,1.69ms表示1,2个位中间还会有一个0.56ms的高电平
     看到如上图波形,表示单片机引脚可以接收到的波形,我们只要通过单片机读取波形并分析波形的宽度,然后分辨出是头码,还是0或者1,最后整理出这组码的16进制组合。正确的解码结果是按同一个按键得出的16进制数值是不变化的。通过这个原理,我们可以分辨出每个按键的键值。
! z7 B/ `2 Q: z
基本原理分析如下,如接收到头码是4.5ms低电平+4.5ms高电平,我们分析 第一个下降沿到第二个下降沿的宽度是 9ms,我们判断这个头码可以给定一个范围,只要数据在这个范围内则认为头码是正确的,检测头码正确后接着检测剩下的32位数值。

2、用自己的话概括就是:平常是高电平 --->按键按下 --->产生引导码(9+4.5)ms--->然后判断是不是连续发送--->1还是0--->存储码值--->转换码值!简单就是这样!

3、首先是我的主程序,代码注释都非常详细,不解释了!注意码值需要依据自己的遥控而定,我就是单步测试出来的!

#include "stm32f10x.h"    
#include "Usart.h"
#include "stdio.h"
#include "Remote_Control.h"
#include "Delay.h"

/**************************PA1接红外接收端************************************/
/************由于没有做外设测试的程序是:按键PA0仅一个LED灯*******************/
/*******由于没有做外设测试的程序是:串口采用的是PA9->(T<->T),PA9->(R<->R)*****/


int main(void)
{
	u8 key;
	USART1_Config();
	delay_init(72);	     							//延时初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	printf("\r\n ("__DATE__ " - " __TIME__ ") \r\n");
	Remote_Init();
	while(1)
	{	  
		if(Remote_Rdy)
		{
			key=Remote_Process();
			switch(key)
			{
			 case 0x68:printf("0\n");break;//0 	  
			 case 0x30:printf("1\n");break;//1 
			 case 0x18:printf("2\n");break;//2
			 case 0x7a:printf("3\n");break;//3
			 case 0x10:printf("4\n");break;//4
			 case 0x38:printf("5\n");break;//5
			 case 0x5a:printf("6\n");break;//6
			 case 0x42:printf("7\n");break;//7
			 case 0x4a:printf("8\n");break;//8
			 case 0x52:printf("9\n");break;//9
             default:break;
			}
		}
	}
}

4、然后是驱动程序

/*-------------------------协议--------------------------
开始拉低9ms,接着是一个4.5ms的高脉冲,通知器件开始传送数据了
接着是发送4个8位二进制码,第一二个是遥控识别码(REMOTE_ID),第一个为
正码(0),第二个为反码(255),接着两个数据是键值,第一个为正码
第二个为反码.发送完后40ms,遥控再发送一个9ms低,2ms高的脉冲,
表示按键的次数,出现一次则证明只按下了一次,如果出现多次,则可
以认为是持续按下该键.
---------------------------------------------------------*/	
#include "Remote_Control.h"
#include "Delay.h"
	  		  
u32 Remote_Odr=0;    //命令暂存处		 	
u8  Remote_Cnt=0;    //按键次数,此次按下键的次数
u8  Remote_Rdy=0;    //红外接收到数据    
/************************初始化红外接收引脚的设置**********************************/
/******************选择PA1脚作为外部中断,用于红外输入*****************************/
void Remote_Init(void)
{							 
	GPIO_InitTypeDef GPIO_InitStructure;	//GPIO
	NVIC_InitTypeDef NVIC_InitStructure;	//中断
	EXTI_InitTypeDef EXTI_InitStructure;	//外部中断线
 		 
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE );	  
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;  			//注意需要上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);  
 
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);  		//选择PA1所在的GPIO管脚用作外部中断线路EXIT1		
 
	EXTI_InitStructure.EXTI_Line = EXTI_Line1;				//外部线路EXIT1
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//设外外部中断模式:EXTI线路为中断请求  EXTI_Mode_Event ;//设置 EXTI线路为事件请求 
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  		//外部中断触发沿选择:设置输入线路下降沿为中断请求
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;				//使能外部中断新状态
	EXTI_Init(&EXTI_InitStructure);						//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器	
 
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; 			//使能按键所在的外部中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; 		//先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 			//从优先级1级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 			//使能外部中断通道
	NVIC_Init(&NVIC_InitStructure);	 					//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}   
/*功能	:检测脉冲宽度*****************************************/
/*说明	:最长脉宽为5ms
/*返回值: t代表脉宽为t*20us(T=1~250); *************************/
u8 Pulse_Width_Check(void)
{
    u8 t=0;	 
    while(RDATA)
    {	 
		t++;
		delay_us(20);					 
        if(t==250)
		return t; 			//超时溢出
    }
    return t;
}			   
 
	 
/*功能	: 中断函数*****************************************/
/*说明	:检测是否有中断
/*返回值: 无************************************************/  
void EXTI1_IRQHandler(void)
{       
	u8 res=0;
    u8 OK=0; 
    u8 RODATA=0; 
	EXTI_ClearITPendingBit(EXTI_Line1);  	//清除EXTI1线路挂起位           		 
	while(1)
    {        
        if(RDATA)//有高脉冲出现
        {
            res=Pulse_Width_Check();		//获得此次高脉冲宽度       
            if(res==250)break;			//非有用信号
            if(res>=200&&res<250)OK=1;  	//获得前导位(4.5ms)
            else if(res>=85&&res<200)   	//按键次数加一(2ms)
            {  							    		 
                Remote_Rdy=1;			//接受到数据
                Remote_Cnt++;			//按键次数增加
                break;
            }
            else if(res>=50&&res<85)RODATA=1;//1.5ms
            else if(res>=10&&res<50)RODATA=0;//500us	  
            if(OK)
            {
                Remote_Odr<<=1;							
                Remote_Odr+=RODATA; 	//这里得到的是一个32位的码值,	//地址码、地址反码、控制码、控制反码
                Remote_Cnt=0; 		//按键次数清零
            }   
        }			 						 
    } 	 	    
}  

/*功能	: 处理红外键盘*****************************************/
/*说明	:无
/*返回值: 键值************************************************/
u8 Remote_Process(void)
{               
    u8 t1,t2;   
    t1 = ((Remote_Odr >> 8)&(0xff)); 	//得到控制码
    t2=(Remote_Odr >> 0)&0xff;		//得到控制反码 
    Remote_Rdy=0;			//清除标记 		      
//    if(t1==(u8)~t2&&t1==REMOTE_ID)	//检验遥控识别码(ID)及地址 
//    { 
//        t1=Remote_Odr>>8;
//        t2=Remote_Odr; 	
//    }     
    if(t1==(u8)~t2)
		return t1; 		//处理键值 
	return 0xff; 
}
5、延时函数也采用用原子的!有时间自己改成定时器的!

//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; //时间加载	  		 
	SysTick->VAL=0x00;        //清空计数器
	SysTick->CTRL=0x01 ;      //开始倒数 	 
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//等待时间到达   
	SysTick->CTRL=0x00;       //关闭计数器
	SysTick->VAL =0X00;       //清空计数器	 
}
6.算是要注意的地方吧,有些地方还是不习惯原子的代码风格,比如:

#define RDATA GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)	 //红外数据输入脚

当然,这只是个人感觉!

就弄到这里吧!!!

  • 11
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值