记录一下4《红外(含引导码)》

项目场景:

通过红外(NEC格式)控制外设,需要对引导码进行验证,并对客户码和键码进行记录


问题描述

如下是未处理引导码的初始代码

#ifndef _MAIN_C_
#define _MAIN_C_

#include "include/ca51f155_config.h"		
#include "include/ca51f155sfr.h"
#include "include/ca51f155xsfr.h"
#include "include/gpiodef_f155.h"
#include "include/system_clock.h"

#include "include/uart.h"
#include "include/delay.h"
#include "include/pwm.h"
#include "include/lcd_led.h"
#include <intrins.h>

typedef unsigned int uint16;	  //对数据类型进行声明定义
typedef unsigned char uint8;

uint8 irtime;
uint8 startflag;
uint8 bitnum;
uint8 irdata[33];
uint8 irreceok;	
uint8 ircode[4];
uint8 irprosok;
uint8 keynum;
uint8 keynum2;
uint8 dat1,dat2;
uint8 gsmg_code[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};
uint8 g_code[]={0x04,0x02,0x01};
uint16 j=0;
int k=0;

enum 
{
	P10_INDEX = 0,
	P11_INDEX = 1,
	P12_INDEX = 2,
	P13_INDEX = 3,
	P14_INDEX = 4,
	P15_INDEX = 5,
	P16_INDEX = 6,
	P17_INDEX = 7,
	
	P20_INDEX = 8,
	P21_INDEX = 9,
	P22_INDEX = 10,
	P23_INDEX = 11,
	P24_INDEX = 12,
	P25_INDEX = 13,
	P26_INDEX = 14,
	P27_INDEX = 15,
	
	P30_INDEX = 16,
	P31_INDEX = 17,
	P32_INDEX = 18,
	P33_INDEX = 19,
	P34_INDEX = 20,
	P35_INDEX = 21,
	P36_INDEX = 22,
	P37_INDEX = 23,

	P40_INDEX = 24,
	P41_INDEX = 25,
	P42_INDEX = 26,
	P43_INDEX = 27,
	P44_INDEX = 28,
	P45_INDEX = 29,
	P46_INDEX = 30,
	P47_INDEX = 31,
	
	P50_INDEX = 32,
	P51_INDEX = 33,
	P52_INDEX = 34,
	P53_INDEX = 35,
	P54_INDEX = 36,
	P55_INDEX = 37,
	
	P04_INDEX = 38,
	P05_INDEX = 39,
	P06_INDEX = 40,
	P07_INDEX = 41,
};

#define EPIE(n)			(n<<7)                          //外部中断4
#define EPPL(n)			(n<<5)
#define EPPSEL_L(n)		((n&0x1F)<<0)
#define EPPSEL_H(n)		(n>>5)

#define PWMDIV_V				(12000000/12000)		//当PWM时钟为其他时钟频率时,需相应修改参数
#define PWMDUT_V				(PWMDIV_V/2)			//占空比为50%

//P3.0~P3.7端口为大灌电流管脚
//寄存器PnxC
#define SMIT_EN(N)		(N<<6)		//N=0~1, 1:SMIT使能,0:反相器使能
#define SINK(N)			(N<<3)		//灌电流强度选择;P30C~P37C的SINK设置位为两位(SINK[1:0] = 0~3)
#define SINK_EN(N)		(N<<2)		//N=0~1,大灌电流使能	
#define DRV(N)			(N<<1)		//N=0~1,输出强度选择	
#define SR(N)			(N<<0)		//N=0~1,输出斜率控制		0:最慢斜率控制   1:最快斜率控制

#define INT_TIME			10000	//定时时间,单位为us
#define	TH_VAL				(unsigned char)((0x10000 - (INT_TIME*(FOSC/1000))/12000)>>8)
#define	TL_VAL				(unsigned char)((0x10000 - (INT_TIME*(FOSC/1000))/12000))

bit  int4_flag;

void PWM_Init(void)
{	
	INDEX = PWM_CH5;												//设置INDEX值对应PWM5
	PWMCON = TIE(0) | ZIE(0) | PIE(0) | MS(0) | CKS_IH ;	//设置PWM时钟源为IRCH  	
	INDEX = PWM_CH5;
	PWMCON = TIE(0) | ZIE(0) | PIE(0) | MS(0) | MOD(0);		
	PWMCFG = TOG(0) | 0;
	//设置PWMDIV、PWMDUT
	PWMDIVH	= (unsigned char)(PWMDIV_V>>8);			
	PWMDIVL	= (unsigned char)(PWMDIV_V);			
	PWMDUTH	= (unsigned char)(PWMDUT_V>>8);		
	PWMDUTL	= (unsigned char)(PWMDUT_V);	

	P51F = P51_PWM5_SETTING;										//设置P5.1为PWM1输出引脚	

  PWMUPD |= (1<<PWM_CH5);											//PWMDIV、PWMDUT更新使能
  while(PWMUPD);											
}
void LED_Init(void)	
{
	uint8 a;
	//初始化LCD驱动引脚
	P31F = 2;//3&8
	P30F = 2;P34F = 2;P17F = 2;P35F = 2;P13F = 2;P14F = 2;P15F = 2;P16F = 2;//other
	P43F = 2;P43(0);P42F=2;P41F=2;P37F=2;//3LED
	for(a = 0; a < 9; a++)
	{
		INDEX = a;
		LXDAT = 0;
	}
	LXDIVH = 0;															//设置LED时钟分频
	LXDIVL = 112;	
	LXCFG =	 COMHV(COM_L) | SEGHV(SEG_H) | BLNK(0) | LDRV(LDRV_7);		//设置LED COM、SEG有效电平,亮度级别
	LXCON =  LEN(LEN_IRCH) | LMOD(LMOD_led);	 						//设置LED时钟源,选择LED模式
}
void Timer0_Init(void)													//256us
{
	TMOD = (TMOD&0xFC)|0x02; 		//模式选择: 定时器0,模式2。
	TH0 = 0x00;    				//装载重载值
	TL0 = 0x00;    				//装载计数初值
	
	TR0 = 1;       					//定时器0使能  
	ET0 = 1;       					//定时器0中断使能
	
//	PT0 = 1;       					//设置定时器0中断优先级为高优先级	
}
void Timer1_Init(void)													//10ms
	{
		TMOD = (TMOD&0xCF)|0x10; 		//模式选择: 定时器0,模式1。
		TH1 = TH_VAL;    				//高8位装初值
		TL1 = TL_VAL;    				//低8位装初值	
		//TR0 = 1;       					//定时器1使能  
		ET1 = 1;       					//定时器1中断使能
	}

/*外部中断4控制例程****************************************************************************************************/

void Ext_Int4_Init(void)
{
	P36F = INPUT;					//INT4可选择除P0.0~ P0.3外的其它任意I/O引脚为中断触输入引脚	
	EP1CON |= EPPSEL_H(P36_INDEX);  //EPPS2[5]放在EP1CON[0]
	EP2CON  = EPIE(1) | EPPL(1) | EPPSEL_L(P36_INDEX); //使能外部中断,并选择下降沿触发, 设置P36为INT4中断引脚
	INT4EN = 1;						//外部中断4中断使能
}

void irpros() //红外接收数据处理 ,区分是数据0还是1
{
	uint8 i,j,value;
	uint8 k=1;	//引导码去掉,所以令k=1;
	for(j=0;j<4;j++)  //取出了一帧数据中的四个字节数据
	{
		for(i=0;i<8;i++)	//取出了一个字节的数据		
		{
			value>>=1;
			if(irdata[k]>6)
			{
				value=value|0x80;
			}
			k++;
		}
		ircode[j]=value;
	}
	irprosok=1;
}
void irwork()  //对接收到的四个字节的数据进行处理转化成十进制
{
	
	dat1=ircode[2]/16;
	dat2=ircode[2]%16;
	keynum++;
	if((dat1==8)&&(dat2==2)){keynum2++;}
}
void impros()
{
	if((dat1==8)&&(dat2==0)&&(keynum)) //蜂鸣器 
	{			
		dat1=0;
		dat2=0;
		PWMEN  |= (1<<PWM_CH5);Delay_ms(500);PWMEN  &= (0<<PWM_CH5);
	}

	if((dat1==8)&&(dat2==1)&&(keynum)) //数码管显示加1
	{			
		dat1=0;
		dat2=0;
		k++;
	}
	if((dat1==8)&&(dat2==2)&&(keynum2%2))  //数码管自动循环累加
	{			
		dat1=0;
		dat2=0;
		TR1=1;
	}
	if((dat1==8)&&(dat2==2)&&(!(keynum2%2)))  //再按一次数码管停止累加
	{			
		dat1=0;
		dat2=0;
		TR1=0;
	}
	if((dat1==8)&&(dat2==3)&&(keynum))  //数码管显示减1
	{			
		dat1=0;
		dat2=0;
		k--;
	}
	if((dat1==8)&&(dat2==4)&&(keynum))  //3个小灯循环点亮
	{			
		dat1=0;
		dat2=0;
		j++;
	}
	
}

void lighten (unsigned char a)          //点灯
{
	P37=a&(1<<0);P41(a&(1<<1));P42(a&(1<<2));
	return ;
}
void lighten_duan (unsigned char a)     //点亮数码管
{
	P35=a&(1<<0);P30=a&(1<<1);P17=a&(1<<2);P13=a&(1<<3);P14=a&(1<<4);P34=a&(1<<5);P15=a&(1<<6);P16=a&(1<<7);
}
/*********************************************************************************************************************/

void System_Init(void)
{
	LVDCON = 0xE2;					//开启LVD,设置为低电压复位模式,检测电压为2.7V
	LOAD_CLK_CFG();				   
#ifdef SYSCLK_12MHZ					
	CKDIV = 0;						//系统时钟上电默认为IRCH的二分频(6MHz),运行12MHz,则CKDIV设置为0
#endif
#ifdef UART0_EN
	Uart0_Initial(UART0_BAUTRATE);
#endif		
#ifdef UART1_EN
	Uart1_Initial(UART1_BAUTRATE);
#endif	
#ifdef UART2_EN
	Uart2_Initial(UART2_BAUTRATE);
#endif
}
void main(void)
{
	
	System_Init();
	CKCON |= IHCKE;
	EA = 1;							//开全局中断
	Timer0_Init();
	Timer1_Init();
	Ext_Int4_Init();
	PWM_Init();
	LED_Init();
	while(1)
	{
		//int4_flag = 0;
			if(irreceok)
		{
			irreceok=0;
			irpros();
			if(irprosok)
			{
				irwork();
			}
		}
		impros();
		if(j>=3){j=0;}
		lighten(g_code[j]);
		if(k>=10){k=0;}
		if(k<0){k=9;}
		lighten_duan(gsmg_code[k]);
	}
}
void TIMER0_ISR (void) interrupt 1 	
{
	irtime++;;
}
void EXT_INT4_ISR(void)				//函数在INT4_ISR中调用
{	
	if(EPIF & 0x04)					//判断外部中断4中断标志
	{
		EPIF = 0x04;				//中断标志写1清0		
		if(startflag)
		{
			if(irtime>32)//检测引导码,求法是用引导码时间除以一次计数时间,看看要多少次
			{
				bitnum=0;	
			}
			irdata[bitnum]=irtime;
			irtime=0;
			bitnum++;
			if(bitnum==33)
			{
				bitnum=0;
				irreceok=1;//一帧红外数据接收完成标志
			}
		}
		else
		{
			startflag=1;//将开始标志位置1,等到下次进入中断即可进入if语句
			irtime=0;//将开始之前的计数器时间清零。等到下次进入中断的时候才是引导码真正的时间
		}	
	}
}
void TIMER1_ISR (void) interrupt 3 	
	{	
		static unsigned int i;
		TH1 = TH_VAL;
		TL1 = TL_VAL;
		i++;
		
		if(i==50){
			k++;			//500ms执行一次
			i=0;
		}
	}
#endif


一开始选择红外接收的输出口P36作为外部中断4的输入引脚,设置下降沿触发。根据NEC协议规定,逻辑1时长2.25ms,经历两次电平翻转,逻辑0时长1.125ms,同样两次电平翻转,思路对每一次下降沿到来之前的时间进行记录,判断时长即可区分0或1.对引导码的处理想的是在中断函数中直接对P36判断低电平是否持续9ms,再判断高电平是否持续4.5ms,若否,直接return。但是不行,用keil调试试图分析原因,奈何菜鸡一个,没捣鼓出来。具体情况是到判断低电平这一步根本不进去if语句,有点懵逼,可惜代码当时没保存,大概如下

if(IRED==0)
{
	time_cnt=1000;
	while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
	{
		delay_10us(1);//延时约10us
		time_cnt--;
		if(time_cnt==0)return;		
	}
	if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
	{
		time_cnt=500;
		while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
		{
			delay_10us(1);
			time_cnt--;
			if(time_cnt==0)return;	
		}
		......
	}
}

解决方案:

在马哥的指导下,改变中断4的触发方式为双边沿触发,包括引导码,对每一次电平翻转的时间进行记录,判断每一段电平的持续时间,这样也会方便引导码的处理,主要修改的地方如下

uint8 xdata irdata[67];
#define EPPLH(n)			(n<<6)

void Ext_Int4_Init(void)
{
	P36F = INPUT;					//INT4可选择除P0.0~ P0.3外的其它任意I/O引脚为中断触输入引脚	
	EP1CON |= EPPSEL_H(P36_INDEX);  //EPPS2[5]放在EP1CON[0]
	EP2CON  = EPIE(1) | EPPLH(1)|EPPL(1)| EPPSEL_L(P36_INDEX); //使能外部中断,并选择双边沿触发, 设置P36为INT4中断引脚
	INT4EN = 1;						//外部中断4中断使能
}

void irpros() //红外接收数据处理 ,区分是数据0还是1
{
	uint8 i,j,value;
	uint8 k=0;	//
	if((irdata[k]<34)||(irdata[k]>37)){		
				if((irdata[k+1]<16)||(irdata[k+1]>19)){			//不符合直接return
					return;
				}
	}		
			k=k+2;
	for(j=0;j<4;j++)  //取出了一帧数据中的四个字节数据
	{
		for(i=0;i<8;i++)	//取出了一个字节的数据		
		{
			
				value>>=1;
			
			if((irdata[k]<=3)&&(irdata[k+1]>=6))		//逻辑1
			{
				value=value|0x80;
			}
			k=k+2;
		}
		ircode[j]=value;
	}
	irprosok=1;
}

void EXT_INT4_ISR(void)				//函数在INT4_ISR中调用
{	
	if(EPIF & 0x04)					//判断外部中断4中断标志
	{
		EPIF = 0x04;				//中断标志写1清0		
		if(startflag)
		{
			
			if(irtime>30){		//判断是不是引导码
				bitnum=0;	
			}
			irdata[bitnum]=irtime;
			irtime=0;
			bitnum++;
			if(bitnum==67)
			{
				bitnum=0;
				irreceok=1;//一帧红外数据接收完成标志
			}
		}
		else
		{
			startflag=1;//将开始标志位置1,等到下次进入中断即可进入if语句
			irtime=0;//将开始之前的计数器时间清零。等到下次进入中断的时候才是引导码真正的时间
		}	
	}
}

需要重点注意的地方在于irdata这个数组,在简单版本中,irdata的数组大小为33,这无关紧要,但是修改之后要记录的电平数变多了,当数组大小到达64的时候,就会出现 ERROR L107: ADDRESS SPACE OVERFLOW,解决方法只需要加上地址类型xdata即可,将其存储到片外内存空间。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值