基于51单片机+红外遥控解码+LCD1602显示

基本介绍

什么是红外线?

人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。其中红光的波长范围为0.62~0.76μm紫光的波长范围为0.38~0.46μm。比紫光波长还的光叫紫外线,比红光波长还的光叫红外线.红外线遥控就是利用波长为0.76~1.5μm之间的近红外线来传送控制信号的。

红外线系统的组成

红外线系统一般由红外发射装置和红外接收设备两大部分组成。红外发射装置又可由键盘电路、红外编码芯片、电源和红外发射电路组成。红外接收设备可由红外接收电路、红外解码芯片、电源和应用电路组成。通常为了使信号更好的被发射端发送出去,经常会将二进制数据信号调制成为脉冲信号,通过红外发射管发射。常用的有通过脉冲宽度来实现信号调制的脉宽调制(PWM)和通过脉冲串之间的时间间隔来实现信号调制的脉时调制(PPM)两种方法。

发射管和接收管

发送
是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前大量的使用的红外发光二极管发出的红外线波长为940nm左右,外形与普通φ5发光二极管相同。

在这里插入图片描述
在这里插入图片描述
接收
红外遥控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。在这里插入图片描述
在这里插入图片描述

红外遥控发射(载波频率)

通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHz,这是由发射端所使用的455kHz晶振来决定的。

重要介绍

NEC协议

NEC功能特点包含:

1.地址码与地址反码,以及命令码与命令反码,具有数据校验作用,增强结果的准确性

2.脉冲距离调制,调制占空比

3.载波频率38KHZ

4.逻辑位时间为逻辑0:1.125ms或逻辑1:2.25ms

5.发送的信号与接收到的信号是反向的

数据格式(必看)

数据格式包括了引导码、用户码、用户反码、数据码和数据码反码,编码总占32位。 数据反码是数据码反相后的编码,编码时可用于对数据的纠错。注意:第二段的用户码也可以在遥控应用电路中被设置成第一段用户码的反码。
在这里插入图片描述

位定义(必看)

用户码或数据码中的每一个位可以是位 ‘1’ ,也可以是位 ‘0’。区分 ‘0’和 ‘1’是利用脉冲的时间间隔来区分,这种编码方式称为脉冲位置调制方式,英文简写PPM。
在这里插入图片描述

编写程序思路(2种)

1.这个红外一般都是用在中断的,像在我的板子是P3.2口,是外部中断0,中断我们知道可以设定为下降沿触发或者低电平触发。

方式一

第一种方式就是直接模拟数据格式的图来编写程序。(一次中断做完一帧的数据)
一开始 是9ms低电平 ——> 4.5高电平 ——> 0.56ms低电平——>位1/位0的高电平时间…

void ReadIr() interrupt 0
{
	u8 j,k;
	u16 err;
	Time=0;					 
	delay(700);	//7ms
	if(IRIN==0)		//确认是否真的接收到正确的信号
	{	 
		
		err=1000;				//1000*10us=10ms,超过说明接收到错误的信号
		/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时
		侯,程序死在这里*/	
		while((IRIN==0)&&(err>0))	//等待前面9ms的低电平过去  		
		{			
			delay(1);
			err--;
		} 
		if(IRIN==1)			//如果正确等到9ms低电平
		{
			err=500;
			while((IRIN==1)&&(err>0))		 //等待4.5ms的起始高电平过去
			{
				delay(1);
				err--;
			}
			for(k=0;k<4;k++)		//共有4组数据
			{				
				for(j=0;j<8;j++)	//接收一组数据
				{

					err=60;		
					while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去
					{
						delay(1);
						err--;
					}
					err=500;
					while((IRIN==1)&&(err>0))	 //计算高电平的时间长度。
					{
						delay(10);	 //0.1ms  
						Time++;
						err--;
						if(Time>30)	//大于3ms  退出程序
						{
							return;
						}
					}
					IrValue[k]>>=1;	 //k表示第几组数据
					if(Time>=8)			//如果高电平出现大于0.8ms,那么是1
					{
						IrValue[k]|=0x80;
					}
					Time=0;		//用完时间要重新赋值							
				}
			}
		}
		if(IrValue[2]!=~IrValue[3]) //对比 数据码 和数据反码 是否成立 不对则退出
		{
			return;
		}
	}			
}
方式二

第二种是每触发中断就记录一次时间,通过记录时间的长短来确定是引导位还是数据位 位1还是位0(只记下降沿到下一次下降沿的时间)

void readIr() interrupt 0
{
	count++;
	if(count == 1)
	{
		irTime=0;
	}
	
	if(irTime > 40)   //判断前导码是否接近9ms
	{
		bitNum = 0;	
	}
	irData[bitNum] = irTime;
	irTime = 0;
	bitNum++;

	if(bitNum == 33)
	{
		irDataOk = 1;   //32位数据接收完标志位
		count = 0;		//次数清零
	}
	
}

程序代码(在1602显示)

方式一

main.c

#include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器
#include "lcd1602.h"	

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


sbit IRIN=P3^2; //红外引脚
u8 IrValue[4] = {'\0'};
u8 Time;

/*延时函数,i=1时,大约延时10us*/
void delay(u16 i)
{
	while(i--);	
}


/*显示函数*/
void DigDisplay()
{
	u8 i;
	write_com(0x83);
	for(i=0;i<4;i++)
	{
			if(IrValue[i]/16<10)
			{
				write_data(IrValue[i]/16 + 0x30);	
			}
			else
			{
				write_data(IrValue[i]/16 + 0x37);	
			}

			if(IrValue[i]%16<10)
			{
				write_data(IrValue[i]%16 + 0x30);	
			}
			else
			{
				write_data(IrValue[i]%16 + 0x37);	
			}
	}		
}


/*配置外部中断0(初始化红外线接收)*/
void IrInit()
{
	IT0=1;//下降沿触发
	EX0=1;//打开中断0允许
	EA=1;	//打开总中断

	IRIN=1;//初始化端口
}


/*主函数*/
void main()
{	
	IrInit(); //调用中断0(红外初始化)
	init_lcd(); //初始化1602
	while(1)
	{	
		DigDisplay();	//显示	
	}		
}


/*读取红外数值的中断函数*/
void ReadIr() interrupt 0
{
	u8 j,k;
	u16 err;
	Time=0;					 
	delay(700);	//7ms
	if(IRIN==0)		//确认是否真的接收到正确的信号
	{	 
		
		err=1000;				//1000*10us=10ms,超过说明接收到错误的信号
		/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时
		侯,程序死在这里*/	
		while((IRIN==0)&&(err>0))	//等待前面9ms的低电平过去  		
		{			
			delay(1);
			err--;
		} 
		if(IRIN==1)			//如果正确等到9ms低电平
		{
			err=500;
			while((IRIN==1)&&(err>0))		 //等待4.5ms的起始高电平过去
			{
				delay(1);
				err--;
			}
			for(k=0;k<4;k++)		//共有4组数据
			{				
				for(j=0;j<8;j++)	//接收一组数据
				{

					err=60;		
					while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去
					{
						delay(1);
						err--;
					}
					err=500;
					while((IRIN==1)&&(err>0))	 //计算高电平的时间长度。
					{
						delay(10);	 //0.1ms  
						Time++;
						err--;
						if(Time>30)	//大于3ms  退出程序
						{
							return;
						}
					}
					IrValue[k]>>=1;	 //k表示第几组数据
					if(Time>=8)			//如果高电平出现大于0.8ms,那么是1
					{
						IrValue[k]|=0x80;
					}
					Time=0;		//用完时间要重新赋值							
				}
			}
		}
		if(IrValue[2]!=~IrValue[3]) //对比 数据码 和数据反码 是否成立 不对则退出
		{
			return;
		}
	}			
}

方式二

main.c

#include <reg52.h>
#include "lcd1602.h"

unsigned char irTime = 0;  //电平时间
unsigned char bitNum = 0;  //位数
unsigned char count = 0; //记录次数
unsigned char irDataOk = 0; //红外数据接收标志位
unsigned char irValueOk = 0; //红外编码完成标志位
unsigned char irValue[4] = {'\0'}; //存放键值
unsigned char irData[33] = {'\0'}; //存放电平时间

/*配置外部中断0  和 定时器0*/
void interrupt0Config()
{
	EX0 = 1; //触发方式为负跳变
	IT0 = 1; //打开中断0允许位

	TMOD = 0x02;  //定时器0 方式2(8位自动重装)
    TH0 = 0x00;	  //200us
    TL0 = 0x48;
    ET0 = 1;	  //打开定时器0 允许位
    TR0 = 1;	  //启动定时器0

	EA = 1; //打开总开关
}


/********红外数据处理函数*********/
void irDataProcessing()
{
	unsigned char i,j;
	unsigned char k = 1;
	unsigned char value = 0;

	for(i=0;i<4;i++)
	{
		for(j=0;j<8;j++)
		{
			value>>=1; //右移一次	
			if(irData[k] > 7 )	// 8X200us=1.6ms  大于1.6则记为1
			{
				value |= 0x80;
			}
			k++;
		}
		irValue[i] = value;
		
	}
		irValueOk = 1;
		irDataOk = 0;
}


/*********显示函数**********/
void display()
{
	unsigned char i;
	write_com(0x83);
	if(irValueOk == 1 && irValue[2]==~irValue[3])//判断是否接收完和数据码有无错
	{
		for(i=0;i<4;i++)
		{

			if(irValue[i]/16<10)
			{
				write_data(irValue[i]/16 + 0x30);	
			}
			else
			{
				write_data(irValue[i]/16 + 0x37);	
			}

			if(irValue[i]%16<10)
			{
				write_data(irValue[i]%16 + 0x30);	
			}
			else
			{
				write_data(irValue[i]%16 + 0x37);	
			}
		}
			irValueOk=0;
			
	}
}
/*主函数*/
void main()
{
	interrupt0Config(); //调用中断函数
	init_lcd();	//初始化1602
	while(1)
	{
		if(irDataOk == 1)
		{
			irDataProcessing(); //红外数据处理函数
		}
		display();	   //显示函数
	}
}

/*红外接收函数*/
void readIr() interrupt 0
{
	count++;
	if(count == 1)
	{
		irTime=0;
	}
	
	if(irTime > 40)   //判断前导码是否接近9ms
	{
		bitNum = 0;	
	}
	irData[bitNum] = irTime;
	irTime = 0;
	bitNum++;

	if(bitNum == 33)
	{
		irDataOk = 1;   //32位数据接收完标志位
		count = 0;		//次数清零
	}
	
}

void time0() interrupt 1
{
	irTime++;	
}
LCD1602相关代码

lcd1602.c

#include <reg52.h>
#include "lcd1602.h"

#define LCD P0
sbit E = P2^7;
sbit RS = P2^6;
sbit RW = P2^5;

/******延迟5毫秒函数********/
void delay5ms()   //误差 -0.000000000001us
{
    unsigned char a,b;
    for(b=15;b>0;b--)
        for(a=152;a>0;a--);
}


/******LCD1602写命令函数********/
void write_com(unsigned char command)
{
	RS = 0; 
	RW = 0; 	//高读低写
	LCD = command;
	delay5ms(); //这里延时最低要30纳秒 我们直接给5ms
	E = 1;		//使能拉高 
	delay5ms(); //最低要求延迟150纳秒 我们直接给5ms
	E = 0;
}

/******LCD1602写数据函数********/
void write_data(unsigned char dat)
{
	RS = 1;
	RW = 0;
	LCD = dat;
	delay5ms(); //这里延时最低要30纳秒 我们直接给5ms
	E = 1;		//使能拉高 
	delay5ms(); //最低要求延迟150纳秒 我们直接给5ms
	E = 0;
}
/******初始化LCD1602********/
void init_lcd()
{	
	write_com(0x06); //写入数据后光标自动右移 整屏不移动。 0x40(光标左移 整屏不移动)0x05(左移 整屏右移)0x07(右移 整屏右移)
	write_com(0x0c); //开显示功能 无光标 不闪烁
	write_com(0x38); //数据总线8位 16X2显示 5*7点阵
	write_com(0x01); //清屏 0000 0001
	
}

lcd1602.h

#ifndef _LCD1602_H_
#define _LCD1602_H_

void write_com(unsigned char command);	//写命令函数
void write_data(unsigned char dat);		//写数据函数
void init_lcd();  //初始化LCD1602函数
void delay5ms();  //延时5ms函数	

#endif

项目展示

在这里插入图片描述

  • 22
    点赞
  • 141
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
#include "remote.h" #include "delay.h" #include "usart.h" ////////////////////////////////////////////////////////////////////////////////// //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK战舰STM32开发板 //红外遥控解码驱动 代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //修改日期:2012/9/12 //版本:V1.0 //版权所有,盗版必究。 //Copyright(C) 广州市星翼电子科技有限公司 2009-2019 //All rights reserved ////////////////////////////////////////////////////////////////////////////////// u8 g_IR_RecFlag = 0; //红外接收到标志 //红外遥控初始化 //设置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 =(35-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_Falling; //上升沿捕获..改为下降沿捕获 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.NV

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮卡丘吉尔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值