红外遥控在我们生活中随处可见,以前,还没学电子之前觉得它是多么不可思议,而真正接触它,却是大二的时候。上专业选修课《无线遥控》,选题做基于MCU的红外编码与解码。看了老师课件的时序图,感觉并不难,而真正自己敲代码和调试,却花上好几天的功夫。
首先做完的部分是接收程序,拿遥控器测试成功后,觉得协议有点繁琐,于是自己改写协议,只用9ms+4.5ms引导码,不再用标准的NEC或者飞利浦了,做完了接收。开始做我的发射。发射最让人头疼的是失败后我们不知道自己编码不成功还是发射管不工作。如果我们没有把载波频率调到38K左右,接收管根本无法接收到发射数据。另外,假如我们已经有了38K的频率,但是我们接收端还是接收不到数据,那就有可能是接收端的协议和发射端的协议不一致造成的。
冥思苦想了好久,还好师兄告诉了一个诀窍:拿手机照相机拍摄红外发射管。当发射管工作的时候,人的眼睛虽然看不到红外线,但是照相机却能捕捉到红外线。当红外发射管工作的时候,照相机显示出来的图像和我们肉眼见到的发光二极管基本一样。有了这一方法,继续调了不久,终于完成了红外发射与接收的全部工作。以下是发射与接收的代码,是基于51单片机写的。
发射部分:
/******************************************
** 红外发送 MCU 编码
** 9ms 高电平 + 5ms 低电平 引导码
** 8位键值 一位一位发送
** 为1 高低电平比为1:1 ms
** 为0 高低电平比为1:3 ms
** 5ms 的结束码 高电平
******************************************/
#include <reg52.h>
#include "keyscan.c"
sbit TR_DATA=P2^0;
void delay(void);
void delay_ms(unsigned int n);
void SentData(unsigned char);
//------------------------------------------------
//主函数
//------------------------------------------------
void main()
{
unsigned char value;
TMOD=0x02; //定时器0方式2
TH0=0xcb; //定时26.3uS,产生38K的方波
TL0=0xcb; //自动重载
EA=1; //总中断允许
ET0=1; //允许T0中断
//TR0=1; //开始定时
while(1)
{
// TR_DATA=1;
while(!KeyOn()); //等待按下按键
{ value=KeyScan();
// if(value == 20) continue;
SentData(value);
while(KeyOn());
//等按键松开
}
}
}
//------------------------------------------------
//发送数据
//------------------------------------------------
void SentData(unsigned char s_data)
{
unsigned char i;
TR0=1;
TR_DATA= 0; //启动38K发生器
delay_ms(9); //9ms的启动高电平
TR0=0; //关闭38K发生器
TR_DATA= 0; //输出0
delay_ms(5); //5ms的启动低电平
for(i=0;i<8;i++)
{
TR0=1; //启动38K发生器
TR_DATA= 0;
delay_ms(1); //产生1ms的38k脉冲代表高电平时间
TR0=0; //关闭38K发生器
TR_DATA= 1; //输出0
if(s_data&(1<<i))delay_ms(1);//如果发送1 则高低电平比为1:1
else delay_ms(3); //如果发送0 则高低电平比为1:3
}
TR0=1; //启动38K发生器
TR_DATA= 0;
delay_ms(5); //5ms的结束高电平
TR0=0; //关闭38K发生器
TR_DATA= 1; //输出0
}
//定时器零产生38K方波
void T0_interrupt(void) interrupt 1
{
TR_DATA=~TR_DATA;
TR0=1;
}
//------------------------------------------------
//功 能:延迟1000us子程序
//误差 -0.651041666667us
//------------------------------------------------
void delay(void)
{
unsigned char a,b;
for(b=102;b>0;b--)
for(a=3;a>0;a--);
}
//------------------------------------------------
// 延迟Nms子程序
//------------------------------------------------
void delay_ms(unsigned int n)
{
while(n--)
delay();
}
//====================================
/************** 宏定义 ***********/
#ifndef uint8
#define uchar unsigned char
#define uint unsigned int
#endif
code uchar key_char[16]={0xe7,0xd7,0xb7,0x77,
0xeb,0xdb,0xbb,0x7b,
0xed,0xdd,0xbd,0x7d,
0xee,0xde,0xbe,0x7e
};
#define KEYPORT P1
//------------------------------------------------
// 键盘延迟
//------------------------------------------------
/*void key_delay(uchar n)
{
uint a=5000;
for(;n>0;n--)
{
while(a--);
a=5000;
}
}
*/
//10ms矩阵键盘防抖延迟函数
/*void key_delay(void)
{
unsigned char a,b;
for(b=151;b>0;b--)
for(a=29;a>0;a--);
}
*/
//5ms矩阵键盘防抖延迟函数
void key_delay(void) //误差 0us
{
unsigned char a,b;
for(b=19;b>0;b--)
for(a=130;a>0;a--);
}
//------------------------------------------------
// 有按键按下返回1
//------------------------------------------------
bit KeyOn()
{
uchar temp;
bit flag;
KEYPORT=0xf0;
temp=KEYPORT;
if(temp!=0xf0)
{
key_delay() ; //去抖
KEYPORT=0xf0;
temp=KEYPORT;
if(temp!=0xf0)
{
key_delay() ; //去抖
KEYPORT=0xf0;
temp=KEYPORT;
if(temp!=0xf0) flag=1;
else flag=0;
}
else flag=0;
}
else flag=0;
return (flag);
}
//------------------------------------------------
// 键盘扫描获取键值
//------------------------------------------------
uchar KeyScan()
{
uchar key1,key0,key;
uchar n;
KEYPORT=0xf0;
key1=KEYPORT&0xf0;
KEYPORT=0x0f;
key0=KEYPORT&0x0f;
key=key0|key1;
for(n=0;n<16;n++)
{
if(key==key_char[n])
return(n);
}
// n=20;
// return (n);
}
接收部分:
/******************************************
** 红外接收 MCU 解码
**发送: 9ms 高电平 + 5ms 低电平 引导码
** 8位键值 一位一位发送
** 为1 高低电平比为1:1 ms
** 为0 高低电平比为1:3 ms
** 5ms 的结束码 高电平
**接收:相反
**
******************************************/
#include "reg52.h"
#include "infrared.h"
sbit RE_DATA=P3^2;
sbit SDATA_595=P0^1; //串行数据输入
sbit SCLK_595 =P0^2; //移位时钟脉冲
sbit RCK_595 =P0^3; //输出锁存器控制脉冲
code char led_code[10]={0x18,0xdd,0x31,0x91,0xd4,0x92,0x12,0xd8,0x10,0x90};//0--9代码
code char data_code[10]={0,1,2,3,4,5,6,7,8,9};
unsigned char Recived_Data;
unsigned char dat1,dat2;
//------------------------------------------------
// 延迟500us子程序
// 误差 -0.868055555556us
//------------------------------------------------
void delay500us(void)
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=227;a>0;a--);
}
//------------------------------------------------
// 延迟0.5 * n MS子程序
//------------------------------------------------
void delay_ms_re(unsigned int n)
{
while(n--)
delay500us();
}
//------------------------------------------------
// 红外解码
//------------------------------------------------
unsigned char CheckRE()
{
unsigned char i;
Recived_Data = 0;
while(RE_DATA);
delay_ms_re(3); //延迟1.5ms
if(RE_DATA==1)return 0; //没有出现足够的9ms低电平出错
delay_ms_re(14); //延迟7 ms,再加上前面的1.5ms达到延迟8.5ms
if(RE_DATA==1)return 0; //高电平没达到9ms的低电平时间,出错
while(!RE_DATA); //等待出现5ms高电平的开始信号,即等待RE_DATA=1
delay_ms_re(3); //延迟1.5ms
if(RE_DATA==0)return 0; //没到5 ms就出现高低电平,出错返回
delay_ms_re(6); //延迟3 ms,加上前面的1.5ms达到延迟4.5ms
if(RE_DATA==0)return 0; //高电平没达到5ms出错
while(RE_DATA); //等待出现低电平
for(i=0;i<8;i++)
{
while(!RE_DATA); //等待出现高电平
delay_ms_re(3); //延迟1.5ms
if(RE_DATA==0) //如果该位是1则为1ms低电平1ms高电平
{
Recived_Data|=(1<<i);
}
else //如果该位是0则为1ms低电平3ms高电平
{
while(RE_DATA);//等待出现低电平即等待数据0发送完毕
}
}
while(!RE_DATA); //当数据为0的时候,即数据已经发送完成
// P1 = Recived_Data;
return Recived_Data;
}
//------------------------------------------------
// 中断初始化
//------------------------------------------------
void Inter0_Init(void)
{
EA = 1;//打开总中断
EX0 = 1;//开外部中断0
IT0 = 1;//设置为低电平中断方式 IT0=1为下降沿触发
//IO_inter0 = 1;
RE_DATA=1;//先把中断引脚拉高
}
//------------------------------------------------
// 中断查询红外按键,
//------------------------------------------------
void Inter0(void) interrupt 0 //中断的作用只是说明有红外需要接收
{
EA=0; //必须禁止所有中断
EX0=0;
CheckRE(); //这里才是真正判断红外的码
EX0=1;
EA=1;
}
/*//------------------------------------------------
void Close_Ex0(void)
{
EX0=0;
}
//------------------------------------------------
void Open_Ex0(void)
{
EX0=1;
}
*/
//================显示部分设置=======================
void display(unsigned char J)
{
seperate(J);
RCK_595=0;
shift(dat1);
shift(dat2);
shift(0);
shift(0);
RCK_595=1;
}
//---------------------------------------------------
void seperate(unsigned char n)
{
dat1=n%10;
dat2=n/10;
}
//---------------------------------------------------
void shift(unsigned char n )
{
unsigned char m,i;
m=led_code[n];
for(i=0;i<8;i++)
{
SDATA_595=m&0x80;
m<<=1;
WR_595();
}
}
//--------------------------------------------------
void WR_595(void)
{
SCLK_595=0;
SCLK_595=1;
}
//------------------------------------------------
void main(void)
{
unsigned char dat ;
// unsigned char J=1;
// display(J);
while(1)
{
dat=CheckRE( );
if(dat!=0)
{
// display(data_code[dat]);
display(dat);
}
}
}
#ifndef __INFRARED_H
#define __INFRARED_H
extern unsigned char Recived_Data;
void delay500us(void); //延时500US
void delay_ms_re(unsigned int n);
unsigned char CheckRE(void);
void Inter0_Init(void);
void Close_Ex0(void);
void Open_Ex0(void);
void display(unsigned char);
void seperate(unsigned char);
void shift(unsigned char);
void WR_595(void);
#endif