
红外收发基本原理
1、红外通信的基本原理
1.1 、红外通信的框架
红外通信由发送端和接收端两部分组成,发送端对数据进行编码,然后调制成一系列的脉冲信号,然后通过带有红外发射管的发射电路发送脉冲信号,即红外信号。接收端完成对脉冲信号的接收、放大、检波、整形,然后解调出编码信号,对其解码获取到发送的数据。具体的如图1.1的通信框架。
下面通过示例明说通信的整个过程是如何工作的。
a.数据:假设发送的数据为0xA5,二进制表示:10100101。
b.编码:就是用一种符号表示码元0,另一种符号表示码元1,然后用两种符号分别代表码元0和码元1。现做如下规定,如图1.2所示。
码元0用560us的高电平和560us的低电平代替;码元1用560us的高电平和1680us的低电平代替,因此数据0xA5的编码结果如图1.3所示.
c.调制:就是把编码数据放到一定频率的载波上面,即使用数据调制载波,形成一串脉冲信号。具体的定义在视频中由详细的解释。下面通过图1.4详细的说明调制的过程。
由图可以观察出,当有高电平的时候,形成载波信号,当为低电平的时候,无载波信号,最终通过高低电平的变化,产生一串脉冲信号,大家可以通过示波器观察自己产生的脉冲信号是否正确。
d.发送电路:主要是对脉冲信号的放大,和红外管对脉冲信号的发送,电路的设计有许多种,这里只简单列举一种,如图1.5所示.
如果想发送的在远一些,信号更加强一些,建议电路选择成二级放大。
e.接收电路与解调:为了减少干扰,采用便宜可靠地一体化接收头HS0038,接收的频率是38khz左右,周期为26us,使用它完成对调制后的脉冲信号进行放大、检波、整形得到TTL电平的编码信号,即图1.3所示。具体电路如下。
f.解码:通过对高低电平的时间判断就会得到二进制的01序列,具体的判断算法有许多种,下面说明常见的高电平时间判断法
。
g.数据:对二进制的01序列进行判断,就可以得到数据0xA5。
1.2NEC红外协议
该协议是由NEC开发的红外协议,其特征是:
-
8位地址码、8位命令码;
-
完整发射两次地址码和命令码,提高可靠性
-
脉冲时间长短调制方式
-
38khz的载波频率
-
位时间1.12ms或2.25ms
调制方式:
NEC协议根据脉冲时间长短解码,每个脉冲位560us长的38Khz载波(约21个载波周期),逻辑“1”脉冲时间为2250us,逻辑“0”脉冲时间为1120us,推荐的载波周期为1/3或者1/4。
协议格式:
-
引导码:9ms的高电平,4.5ms的低电平,用于判断数据帧的开始;
-
地址码:注意小端在前,如例子中发送的数据是0x59;
-
地址反码:地址码取反获得,如0x59的反码是0xA6,用于判断数据的正确
-
命令码:0x16
-
命令反码:0xe9
如果一直按着某个键值,发送的则是以110ms为周期的重复码,重复码由9ms的高电平,和4.5ms的低电平,以及一个560us的高电平组成,具体如下图
2、程序
2.1 接收程序
#include <MSP430X14X.h>
typedef unsigned char uchar;
typedef unsigned int uint;
#define t_3ms5 2300
#define t_1ms 666
uchar get_code[4] = {0};
uchar dat_code=0;
uint timer;
#include "msp430.c"
#include "disp_4led.c"
void delay(uint time) //10ms--10000
{
while(time--);
}
void dat_high()
{
P1DIR |= BIT5;
P1OUT |= BIT5;
}
void dat_low()
{P1DIR |= BIT5;
P1OUT &= ~BIT5;
}
uchar rd_dat()
{
uchar stat;
P1DIR |= BIT5;
P1OUT |= BIT5;
P1DIR &= ~BIT5;
stat = P1IN;
return (stat);
}
uchar get_num()
{
uchar i,j,rd,dat=0;
_DINT(); //关闭中断
for(j=0;j<4;j++)
{
for(i=8;i>0;i--)
{
dat>>=1;
do
rd=rd_dat();
while(!(rd & BIT5)); // wait high
set_timer_b0(); //检测高电平时间
do
rd=rd_dat();
while(rd & BIT5); // wait low
timer=TBR;
stop_timer_b0();
if(timer>t_1ms)dat=dat|0x80;
else dat=dat&0x7f;
}
get_code[j]=dat;
}
_EINT();
return(get_code[2]);
}
void main( void )
{
uchar rd;
WDTCTL = WDTPW + WDTHOLD;
dat_high();
set_timer_a0();
judge:
do
rd=rd_dat();
while(rd & BIT5); //wait low
delay(100);
rd=rd_dat();
if(rd & BIT5) goto judge; //xiao dou
do
rd=rd_dat();
while(!(rd & BIT5)); //wait high
set_timer_b0();
do
rd=rd_dat();
while(rd & BIT5); //wait low
timer=TBR;
stop_timer_b0();
if(timer<t_3ms5)
{
delay(1000);
goto judge;
}//连续按下,重复标志
dat_code=get_num();
goto judge;
}
2.2 发送程序
#include <MSP430X14X.h>
#include"Timer.h"
#include"UART.h"
typedef unsigned char uchar;
typedef unsigned int uint;
#define t_3ms5 2300
#define t_1ms 666
uchar get_code[4] = {0};
uchar dat_code=0;
uint timer;
#include "msp430.c"
#include "disp_4led.c"
#include "Keypad.h"
/****************************************************************************
* 名 称:TAPwmInit
* 功 能:TA定时器作为PWM发生器的初始化设置函数
* 入口参数:Clk:时钟源 'S'=SMCLK; 'A'=ACLK ; _____
'E'=TACLK(外部输入) 'e'= TACLK(TACLK取反)
Div:时钟分频系数: 1/2/4/8
Mode1:通道1的输出模式 'P'设为高电平输出,'N'低电平 ,0=禁用
Mode2:通道2的输出模式 'P'设为高电平输出,'N'低电平 ,0=禁用
* 出口参数:1表示设置成功,0表示参数错误,设置失败。
* 说 明: 在调用PWM相关函数之前,需要调用该函数设置TA的模式和时钟源。
* 范 例: TAPwmInit('A',1,'P','P')TA时钟设为ACLK,通道1和通道2均为高电平输出
TAPwmInit('S',4,'N','N')TA时钟设为SMCLK/4, 通道1、2均为低电平输出
TAPwmInit('A',1,'P',0)TA时钟设为ACLK,通道1高电平输出,通道2不用,被
禁用的PWM通道的输出管脚仍可作为普通IO口使用。
****************************************************************************/
char TAPwmInit(char Clk,char Div,char Mode1,char Mode2)
{
TACTL = 0; //清除以前设置
TACTL |= MC_1; //定时器TA设为增计数模式
switch(Clk) //选择时钟源
{
case 'A': case 'a': TACTL|=TASSEL_1; break; ACLK
case 'S': case 's': TACTL|=TASSEL_2; break; //SMCLK
case 'E': TACTL|=TASSEL_0; break; //外部输入(TACLK)
case 'e': TACTL|=TASSEL_3; break; //外部输入(TACLK取反)
default : return(0); //参数有误
}
switch(Div) //选择分频系数
{
case 1: TACTL|=ID_0; break; //1
case 2: TACTL|=ID_1; break; //2
case 4: TACTL|=ID_2; break; //4
case 8: TACTL|=ID_3; break; //8
default : return(0); //参数有误
}
switch(Mode1) //设置PWM通道1的输出模式。
{
case 'P':case 'p': //如果设置为高电平模式
TACCTL1 = OUTMOD_7; //高电平PWM输出
// P1SEL |= BIT2; //从P1.2输出 (不同型号单片机可能不一样)
// P1DIR |= BIT2; //从P1.2输出 (不同型号单片机可能不一样)
P2SEL |= BIT3; //P2.2:IO P2.3:TA0
P2DIR |= BIT3;//P2.2, P2.3输出
break;
case 'N':case 'n': //如果设置为低电平模式
TACCTL1 = OUTMOD_3; //低电平PWM输出
// P1SEL |= BIT2; //从P1.2输出 (不同型号单片机可能不一样)
// P1DIR |= BIT2; //从P1.2输出 (不同型号单片机可能不一样)
P2SEL |= BIT3; //P2.2:IO P2.3:TA0
P2DIR |= BIT3;//P2.2, P2.3输出
break;
case '0':case 0: //如果设置为禁用
// P1SEL &= ~BIT2; //P1.2恢复为普通IO口
P2SEL &= ~BIT3;
break;
default : return(0); //参数有误
}
switch(Mode2) //设置PWM通道1的输出模式。
{
case 'P':case 'p': //如果设置为高电平模式
TACCTL2 =OUTMOD_7; //高电平PWM输出
// P1SEL |= BIT3; //从P1.3输出 (不同型号单片机可能不一样)
// P1DIR |= BIT3; //从P1.3输出 (不同型号单片机可能不一样)
break;
case 'N':case 'n': //如果设置为低电平模式
TACCTL2 =OUTMOD_3; //低电平PWM输出
// P1SEL |= BIT3; //从P1.3输出 (不同型号单片机可能不一样)
// P1DIR |= BIT3; //从P1.3输出 (不同型号单片机可能不一样)
break;
case '0':case 0: //如果设置为禁用
// P1SEL &= ~BIT3; //P1.3恢复为普通IO口
break;
default : return(0); //参数有误
}
return(1);
}
void delay(uint time) //10ms--10000
{
while(time--);
}
void SendData(unsigned char data)
{
unsigned char i=0;
unsigned char temp=0;
unsigned char datac=0;
datac=~data;
TAPwmInit('s',1,'P','P'); //引导码
delay(10000);//延时9ms
delay(2000);
TAPwmInit('s',1,'0','0');
delay(5000);//延时4.5ms
for(i=0;i<8;i++)//用户码 0x00
{
TAPwmInit('s',1,'P','P');
delay(600);//延时ms
TAPwmInit('s',1,'0','0');
delay(600);//延时ms
}
for(i=0;i<8;i++)//用户码 反码0xff
{
TAPwmInit('s',1,'P','P');
delay(600);//延时0.56ms
TAPwmInit('s',1,'0','0');
delay(1900);//延时1690ms
}
for(i=0;i<8;i++)
{
temp=data>>i;
temp=temp&0x01;
if(0x01==temp)
{
TAPwmInit('s',1,'P','P');
delay(600);
TAPwmInit('s',1,'0','0');
delay(1900);
}
else
{
TAPwmInit('s',1,'P','P');
delay(600);//延时ms
TAPwmInit('s',1,'0','0');
delay(600);//延时ms
}
}
for(i=0;i<8;i++)
{
temp=datac>>i;
temp=temp&0x01;
if(0x01==temp)
{
TAPwmInit('s',1,'P','P');
delay(600);
TAPwmInit('s',1,'0','0');
delay(1900);
}
else
{
TAPwmInit('s',1,'P','P');
delay(600);//延时ms
TAPwmInit('s',1,'0','0');
delay(600);//延时ms
}
}
}
void main( void )
{
unsigned char i=0;
uchar temp,keyval = 0;
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
TimerAClkInit();
TAPwmInit('s',1,'0','0'); //将定时器TA初始化成为PWM发生器
//时钟源=SMCLK ; 无分频; 通道1和通道2均设为高电平模式。
TAPwmSetPeriod(25); //通道1/2的PWM方波周期均设为500个时钟周期
TAPwmSetDuty(1,13); //1通道 有效200个时钟周期
TAPwmSetPermill(2,50); //2通道 20.0%
#if 1
Uart_main();
#endif
Init_Keypad(); //初始化键盘端口
_EINT(); //打开全局中断
while(1)
{
Key_Event();
if(key_Flag == 1)
{
key_Flag = 0;
if(key_val>=0&&key_val<=9)
{
SendData(key_val);
}
}
}
}
至此红外的收发设计完成,按照此过程便可以实现红外的双向通信。