“实战演练:使用51单片机进行红外遥控技术实验”
文章目录
这一节来介绍一种无线通信技术–红外遥控通信。我们开发板标配了一个一体化红外接收头和红外遥控器,我们来学习如何使用
51
单片机解码红外遥控器的信号。本节使用
51
单片机的
外部中断功能来解码红外遥控器的编码信号。本节要实现的功能是:使用外部中断功能将遥控器键值编码数据解码后通过数码管显示。
一、红外遥控介绍
1.红外线简介
人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫。其中红光的波长范围为 0.62~0.76μm
;紫光的波长范围为 0.38~0.46μm
。比紫光波长还短的光叫紫外线,比红光波长还长的光叫红外线。红外线遥控就是利用波长为0.76~1.5μm
之间的近红外线来传送控制信号的。
2.红外遥控的原理
红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。
由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方便。由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。
红外遥控通信系统一般由红外发射装置和红外接收设备两大部分组成。
2.1 红外发射装置
红外发射装置,也就是通常我们说的红外遥控器是由键盘电路、红外编码电路、电源电路和红外发射电路组成。红外发射电路的主要元件为红外发光二极管。它实际上是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前,大量的使用的红外发光二极管发出的红外线波长为 940nm
左右,外形与普通发光二极管相同。红外发光二极管有透明的,还有不透明的,在我们的红外遥控器上可以看到这个红外发光二极管。红外遥控器和红外发光二极管如下图所示:
通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为 38kHz
,这是由发射端所使用的 455kHz
晶振来决定的。在发射端要对晶振进行整数分频,分频系数一般取 12
,所以455kHz÷12≈37.9kHz≈38kHz
。也有一些遥控系统采用 36kHz
、 40 kHz
、 56 kHz
等,一般由发射端晶振的振荡频率来决定。所以,通常的红外遥控器是将遥控信号(二进制脉冲码)调制在 38KHz
的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。
二进制脉冲码的形式有多种,其中最为常用的是 NEC Protocol
的 PWM
码(脉冲宽度调制)和 Philips RC-5 Protocol
的 PPM
码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。如果要开发红外接收设备,一定要知道红外遥控器的编码方式和载波频率,我们才可以选取一体化红外接收头和制定解码方案。我们配套的红外遥控器使用的是 NEC
协议,其特征如下:
- 8 位地址和 8 位指令长度;
- 地址和命令 2 次传输(确保可靠性)
PWM
脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;- 载波频率为
38Khz
; - 位时间为 1.125ms 或 2.25ms;
NEC
码的位定义:一个脉冲对应 560us
的连续载波,一个逻辑 1 传输需要2.25ms
(560us 脉冲+1680us 低电平),一个逻辑 0 的传输需要 1.125ms
(560us 脉冲+560us 低电平)。而红外接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。所以可以通过计算高电平时间判断接收到的数据是 0 还是 1。NEC
码位定义时序图如下图所示:
NEC
遥控指令的数据格式为:引导码、地址码、地址反码、控制码、控制反码。引导码由一个 9ms
的低电平和一个 4.5ms
的高电平组成,地址码、地址反码、控制码、控制反码均是 8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。数据格式如下:
NEC
码还规定了连发码(由 9ms 低电平+2.5m 高电平+0.56ms 低电平+97.94ms 高电平组成),如果在一帧数据发送完毕之后,红外遥控器按键仍然没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短或次数。
2.2 红外接收设备
红外接收设备是由红外接收电路、红外解码、电源和应用电路组成。红外遥控接收器的主要作用是将遥控发射器发来的红外光信号转换成电信号,再放大、限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。近几年不论是业余制作还是正式产品,大多都采用成品红外接收头。成品红外接收头的封装大致有两种:一种采用铁皮屏蔽;一种是塑料封装。均有三只引脚,即电源正( VDD
)、电源负(GND
)和数据输出(VOUT
)。其外观实物图如下图所示:
正对接收头的凸起处看,从左至右,管脚依次是 1:VOUT
,2:GND
,3:VDD
。
由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平,所以可以通过外部中断的下降沿触发中断,在中断内通过计算高电平时间来判断接收到的数据是 0 还是 1。
为什么配置外部中断?
因为遥控器什么时候按下是未知的,但是遥控器一按下就会产生下降沿,如果配置外部中断的下降沿功能,只要一按下就会产生下降沿触发进入外部中断,再进行解码。
二、硬件设计
本实验使用到硬件资源如下:
动态数码管电路在前面章节都介绍过,这里就不再重复。红外遥控器和接收头是一体的,内部结构不用去管它。下面来看下开发板上红外接收模块电路,如下图所示:
从上图中可知,红外接收头的输出管脚接至单片机 P3.2
管脚上,为了保证红外接收头输出管脚默认为高电平,需外接一个 10K
上拉电阻,可在上图并没有看到有上拉电阻,在前面介绍最小系统时已知道,单片机 IO
口都增加了 10K
上拉电阻,所以此处可省略。
三、软件设计
本节所要实现的功能是:数码管上显示红外解码遥控器键值。
程序框架如下:
编写公共文件
打开public.c
,代码如下:
#include "public.h"
/*
函 数 名 : delay_10us
函数功能 : 延时函数,ten_us=1 时,大约延时 10us
输 入 : ten_us
输 出 : 无
*/
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
/*
函 数 名 : delay_ms
函数功能 : ms 延时函数,ms=1 时,大约延时 1ms
输 入 : ten_us
输 出 : 无
*/
void delay_ms(u16 ms)
{
u16 i,j;
for (i=ms;i>0;i--)
{
for (j=110;j>0;j--);
}
}
其头文件public.h
,代码如下:
/*
头文件作用如下:包含头文件,定义全局变量,声明函数
*/
#ifndef _public_H
#define _public_H
#include "reg52.h"
//对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned int u16;
//函数声明
void delay_10us(u16 ten_us);
void delay_ms(u16 ms);
#endif
编写数码管显示功能
打开smg.c
,代码如下:
#include "smg.h"
//使用数组保存共阴极数码管0-F的段码数据
u8 gsmg_code[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
/*
函 数 名 : smg_display
函数功能 : 动态数码管显示
输入 : 无
输出 : 无
*/
void smg_display(u8 dat[],u8 pos)
{
u8 i = 0;
u8 pos_temp = pos - 1; //位置与位选进行统一
for (i=pos_temp;i<8;i++)
{
//位选
switch (i)
{
case 0 : LSC = 1;LSB = 1;LSA = 1;break;
case 1 : LSC = 1;LSB = 1;LSA = 0;break;
case 2 : LSC = 1;LSB = 0;LSA = 1;break;
case 3 : LSC = 1;LSB = 0;LSA = 0;break;
case 4 : LSC = 0;LSB = 1;LSA = 1;break;
case 5 : LSC = 0;LSB = 1;LSA = 0;break;
case 6 : LSC = 0;LSB = 0;LSA = 1;break;
case 7 : LSC = 0;LSB = 0;LSA = 0;break;
}
//给当前位传送段选数据
SMG_A_DP_PORT = dat[i-pos_temp];
delay_10us(100); //延时1ms,循环一轮8ms,小于肉眼可区分的24ms
SMG_A_DP_PORT =0x00; //消音,消除上一次数码管段选数据对下一次的影响
}
}
其头文件smg.h
,代码如下:
#ifndef _smg_H
#define _smg_H
#include "public.h"
//使用宏定义数码管段码口
#define SMG_A_DP_PORT P0
//定义数码管位选信号控制管脚
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
extern u8 gsmg_code[17];
void smg_display(u8 dat[],u8 pos);
#endif
编写红外解码函数
编写主函数
1.红外初始化函数
打开 ired.c
文件,红外初始化代码如下:
/*
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断 0 配置
* 输 入 : 无
* 输 出 : 无
*/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断 0 允许
EA=1; //打开总中断
IRED=1; //初始化端口
}
因为我们使用外部中断 0 来解码红外遥控数据,所以需初始化配置外部中断0。初始化配置在外部中断实验章节已详细介绍,此处不再重复。
2.红外解码函数
初始化外部中断后,中断就已经开启了,当 P32
引脚来一个下降沿,就会触发一次中断,在中断内我们可以计算高电平时间,通过高电平时间判断是否进入引导码及数据 0 和 1。具体代码如下:
#include "ired.h"
u8 gired_data[4]; //保存地址码、地址反码、控制码、控制反码
/*
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断 0 配置
* 输 入 : 无
* 输 出 : 无
*/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断 0 允许
EA=1; //打开总中断
IRED=1; //初始化端口
}
/*
* 函 数 名 : ired
* 函数功能 : 当 P32 引脚来一个下降沿,就会触发一次中断,在中断内我们可以计算高电平时间,通过高电平时间判断是否进入引导码及数据 0 和 1。
* 输 入 : 无
* 输 出 : 无
*/
void ired() interrupt 0 //外部中断0服务函数
{
u8 ired_high_time = 0; //高电平时间计数
u16 time_cnt = 0; //时间计数
u8 i=0;
u8 j=0;
if (IRED == 0)//引导码低电平的判断
{
time_cnt = 1000;
//等待引导信号 9ms 低电平结束,若超过 10ms,强制退出
while ((!IRED) && (time_cnt))
{
delay_10us(1); //延时10us
time_cnt--;
if (time_cnt == 0) return;
}
//引导信号 9ms 低电平已过,进入 4.5ms 高电平
if (IRED)
{
time_cnt = 500;
//等待引导信号 4.5ms 高电平结束,若超过 5ms 强制退出
while (IRED && time_cnt)
{
delay_10us(1);
time_cnt--;
if (time_cnt == 0)return;
}
//根据高电平的时间,循环判断每一位数据是0还是1,
for (i=0;i<4;i++) //循环 4 次,读取 4 个字节数据
{
for (j=0;j<8;j++) //循环 8 次读取每位数据即一个字节
{
time_cnt = 600;
//等待数据 1 或 0 前面的 0.56ms结束,若超过 0.6ms 强制退出
while ((IRED == 0) && time_cnt)
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt = 20;
//等待数据 1 或 0 后面的高电平结束,若超过 2ms 强制退出
while (IRED)
{
delay_10us(10);//约 0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位,需要移位
if(ired_high_time>=8)//如果高电平时间大于 0.8ms,数据则为1,否则为 0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
//校验控制码与反码,错误则返回
if (gired_data[2] != ~gired_data[3])
{
for (i=0;i<4;i++)
gired_data[i] = 0;
return;
}
}
}
进入中断函数,表示已来下降沿,然后判断管脚是否为低电平,如果为低电平则首先判断引导信号,根据前面 NEC
协议可知,引导信号有 9ms 的低电平和4.5ms 的高电平,因此通过 time_cnt
赋值 1000,然后在 while
循环内判断,time_cnt
每递减一次约 10us,1000 次则为 10ms,在解码时,这个时间要适当放宽一点范围,因为不同传感器性能会有差异,所以此处以 10ms 的低电平为界限,如果超过 10ms 则强制退出,防止系统死机。判断完引导信号的低电平,接着判断高电平,实现方法一样。当引导信号判断完成后进入地址码、地址反码、控制码及控制反码共 4 个字节的数据判断,也就是数据 0 和 1 的判断,实现方法也是和前面判断引导信号一样,这里使用到了嵌套循环,外层循环次数是 4,表示读取 4 个字节,内层循环次数是 8,表示读取每个字节的 8 位。注意,红外遥控解码数据是从低位开始,最后是高位。最后将读取的 4 个字节数据存储在全局变量数组 gired_data
中,外部可直接使用这四个字节。
3.主函数
打开 main.c
文件,代码如下:
/*
实验名称:红外遥控实验
接线说明:
实验现象:下载程序后,数码管上显示数码管上显示红外解码遥控器键值
注意事项:红外接收头凸起处要与 PCB 板接口凸起丝印处对应
*/
#include "public.h"
#include "smg.h"
#include "ired.h"
/*
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*/
void main(void)
{
u8 ired_buf[3];
//红外初始化
ired_init();
while (1)
{
ired_buf[0]=gsmg_code[gired_data[2]/16];//将控制码高 4 位转换为数码管段码
ired_buf[1]=gsmg_code[gired_data[2]%16];//将控制码低 4 位转换为数码管段码
ired_buf[2]=0X76;//显示 H 的段码
smg_display(ired_buf,6);
}
}
四、实验现象
使用 USB
线将开发板和电脑连接成功后(电脑能识别开发板上 CH340
串口),把编译后产生的.hex
文件烧入到芯片内,实现现象如下:数码管上显示红外解码遥控器键值。
注意:红外接收头的凸起位置需对到 PCB
板丝印凸出位置,否则功能无法正常实现。出厂时的遥控器电池盖内都有一张绝缘片,使用时必须将此绝缘片拿掉。还有键值码不是大家所想的按键上的数字字符码,这个要区分开。
实验说明:我们配的遥控器所有键的地址码与反码都是相同的,不同的是控制码和控制反码,其实知道了控制码就知道了控制反码,所以如果要使用红外遥控控制其他设备,可以通过区分控制码来实现。
如果程序下载了,能显示“00H”
,按键时却没有反应,首先检查遥控器是否拔下绝缘片。如果要检查遥控器好坏,可以在光线暗的地方,打开手机摄像机,将遥控器前方的发射灯对准手机摄像头,按下按键,在手机上是否能看见灯闪,来判断遥控器好坏。