现在的位置: 首页 > 综合 > 正文

单片机练习-RC-5红外遥控器程序及简单制造DIY PC遥控器

2011年08月03日 ⁄ 综合 ⁄ 共 7410字 ⁄ 字号 评论关闭
本程序采用的芯片为SAA3010, 参考资料有:
1. 常用红外遥控接收头引脚图解
2. 红外遥控编码资料
3. RC-5红外遥控程序
4. GIRDER中文教程与电脑遥控器制作资料
5. Girder网站 (一个需要钱买的遥控)
6. 再度出击,20元打造经典PC遥控器!
7. SAA3010 DataSheet

这次主要是完成单片机接收红外摇控器发出的数据. 在此基础上, 我们可DIY出自己的PC摇控器, 也可自行发挥一些, 做出类似于很多Club里的点歌系统的硬件外红信号转换装置....

本次实验依旧采用TX-1B实验板, 只需增加一个一体化红外接收头, 如下图:

本程序中, DataOut引脚接到P3.2口. 在接收头的电源和地端接上滤波电容会使误码率更加低....

SAA3010芯片:


(图中位时间1.688ms, 而根据DataSheet里描述的位时间为1.788ms,  但实际中我使用的时间为1.651ms )

我使用的键盘编码如下:

键盘外观:

编码:

一体化红外接收头的数据输出的判断思路: (手画, 太难看也别见怪:) )
Tb = 1.778ms = 1.780ms (实际中测试为1.651ms)
T1/4b = 445us (实际中413ms)
T3/4b = 1.335ms (实际1.239ms)

在等待第一次低电平到来后, 定时器开始第一次定时T1/4b时间, 然后到达第一次数据的T3/4b时刻, 读取该位电平状态;
以后定时器每次定时Tb时间, 这样到达对应每位的T3/4时刻, 并可读取该位电平状态.

具体逻辑请看代码中的详细注释:

 采用程序查询方式:

SAA3010_RC5
//接收红外遥控器(Infrared remote control transmitter)发出的数据
//芯片型号: SAA3010, 它采用RC-5协议
//芯片资料: http://www.alldatasheet.com/datasheet-pdf/pdf/18953/PHILIPS/SAA3010.html

//利用与P1口相连的发光二极管输出接收到的按键对应码, 并将它输出到串口, 用于遥控
//PC使用了Girder来触发自定义的功能

#include 
"reg51.h"
#include 
" intrins.h "

#define uchar unsigned char

/*********************IRC RC-5****************************************/

uchar YKDatas[3];   //遥控码(一帧14位),YKDatas[0]: 0,1为start bits, 2 为control bit, YKtype=0
                    
//YKDatas[1]: 3~7为system bits, YKtype=1
                    
//YKDatas[2]: 8~13为command bits, YKtype=2
sbit YKIn = P3^2;     //数据输入位

uchar YKcount, YKtype;            
//遥控已接收位数, 一帧的各段标志
bit YKend; //接收结束标志

//延时 a * 1ms
void delayMs(unsigned int a)
{
    unsigned 
int i, j;
    
for(i = a; i > 0; i--)
        
for(j = 100; j > 0; j--);
}

void YKInit()                  //遥控接收初始化
{
    YKDatas[
0= YKDatas[1= YKDatas[2= 0;
    YKcount 
= 0;
    YKtype 
= 0;
    YKend 
= 0;
}

void time0() interrupt 1 
{
    
//第一次进入中断前, 定时1/4位的时间:445us, 以后则定时一位时间1.778ms
    
//即在3/4位时间时, 判断该位是1还是0
    
//实际测试中, 位时间只在1.651ms(+- 1ms), 定时1/4位的时间:413us
    bit in = ~YKIn; //一体化解码后, 有载频部分变为低电平, 即低电平实际为1, 高电平实际为0
    
//设置定时器初值
    
//模式1: TH0 = (2^16 - (1651/1.085)) / 2^8 = (65536 - 1651/1.085) / 256 =  250;
    
//TL0 = (65536 - 1651/1.085) % 256 = 14
    TH0 = 250;
     TL0 
= 14;
    
    YKDatas[YKtype] 
= YKDatas[YKtype] | in//将数据放入最低位
    YKcount++;
    
if(YKcount == 3//获取完Start bits 和control bit, 共3位
    {
        YKtype 
= 1;
    }
    
else if(YKcount == 8//获取完system bits, 共5位
    {
        YKtype 
= 2;
    }
    
else if(YKcount == 14//获取完commond bits, 共6位
    {
        YKtype 
= 3;
    }
    
else if(YKtype == 3//等待最后1/4位时间结束, 实际延时1位时间
    {
        YKend 
= 1;
        YKcount 
= 0;
        YKtype 
= 0;
        TR0 
= 0//接收结束, 停止定时器0
        return
    }
    
else  //将数据左移一位, 以便将一下位数据并于最低位
    {
        YKDatas[YKtype] 
= YKDatas[YKtype] << 1;
    }
}

/*********************IRC RC-5****************************************/

/*********************RS232****************************************/
//初始化串口
void RSInit()
{
    TMOD 
|= 0x20//T1工作方式2
    TH1 = TL1 = 0xfd//装入初值, 以后是自动重载的8位计数器
    TR1 = 1//启动T1
    SM0 = 0;
    SM1 
= 1//方式1
    REN = 1//允许接收
    EA = 1;    //开中断
    ES = 1//允许串口中断
    PCON = 0x00//串口波特率不加倍. 即设置SMOD = 0;
}

//写一字节数据到串口, 使用程序查询方式检测发送情况
void RsWriteByte(unsigned char byte)
{
    ES 
= 0//关中断
    SBUF = byte;
    
while(!TI); //检测是否发送完
    TI = 0//清0发送中断标志
    ES = 1//开中断, 以允许接收数据时使用中断方式
}

void serial() interrupt 4 //串口中断是4
{
    P1 
= SBUF;
    RI 
= 0//清0接收中断标志
}

/*********************RS232****************************************/
void display() //显示接收的数据
{
    P1 
= ~YKDatas[2];
/*    switch(YKDatas[2])
    {
        case 0x3f :
        case 0x0c : RsWriteByte(YKDatas[0]); break;
        default : break;
    }
*/
//    RsWriteByte(YKDatas[1]);
    RsWriteByte(YKDatas[2]);
}

void main(void)
{
    TMOD
=0x01;                //T0选用方式1(16位定时)
    IE = 0x82//开总中断, 开定时器0中断 
    YKIn = 1;
    RSInit();
    
while(1)                
    {    
        YKInit();    
        
//模式1: TH0 = (2^16 - (413/1.085)) / 2^8 = (65536 - 381) / 256 =  255;
        
//TL0 = (65536 - 381) % 256 = 131
        TH0 = 255;
        TL0 
= 131;
        
while(YKIn); //等待低电平, 一帧开始
        TR0 = 1//启动定时器0, 接收红外遥控器发来的数据
        while(!YKend); //等待接收结束
        display();
        delayMs(
200);
    }
}

改进代码: 由原来的程序查询方式, 换成中断方式:  成功接收到数据后, 会自动调用YKSuccess()方法.

IRC_RC5_Interrupt_module
//接收红外遥控器(Infrared remote control transmitter)发出的数据
//芯片型号: SAA3010, 它采用RC-5协议
//芯片资料: http://www.alldatasheet.com/datasheet-pdf/pdf/18953/PHILIPS/SAA3010.html

//利用与P1口相连的发光二极管输出接收到的按键对应码, 并将它输出到串口, 用于遥控
//PC使用了Girder来触发自定义的功能

#include 
"reg51.h"
#include 
" intrins.h "

#define uchar unsigned char

void display();

/*********************IRC RC-5****************************************/

uchar YKDatas[3];   //遥控码(一帧14位),YKDatas[0]: 0,1为start bits, 2 为control bit, YKtype=0
                    
//YKDatas[1]: 3~7为system bits, YKtype=1
                    
//YKDatas[2]: 8~13为command bits, YKtype=2
sbit YKIn = P3^2;     //数据输入位
uchar YKcount, YKtype;            //遥控已接收位数, 一帧的各段标志

//延时 a * 1ms
void delayMs(unsigned int a)
{
    unsigned 
int i, j;
    
for(i = a; i > 0; i--)
        
for(j = 100; j > 0; j--);
}

//重置所有数据
void YKReset()
{
    delayMs(
200); //去重复
    YKDatas[0= YKDatas[1= YKDatas[2= 0;
    YKcount 
= 0;
    YKtype 
= 0;
    EX0 
= 1//开外部中断0, 检测是否有数据输入
}

void YKInit()                  //遥控接收初始化
{
    TMOD 
= 0x01;                //T0选用方式1(16位定时)
    IE = 0x82;                  //开总中断, 开定时器0中断 
    YKIn = 1;
    YKReset();
}

void YKSuccess()  //接收到数据后会自动被调用
{
    display();
    YKReset();
}

void ex0() interrupt 0
{
    EX0 
= 0;  //关闭外部中断0
    
//模式1: TH0 = (2^16 - (413/1.085)) / 2^8 = (65536 - 381) / 256 =  255;
    
//TL0 = (65536 - 381) % 256 = 131
    TH0 = 255;
    TL0 
= 131;
    TR0 
= 1;  //启动定时器0, 定时1/4位周期
}

void time0() interrupt 1 
{
    
//第一次进入中断前, 定时1/4位的时间:445us, 以后则定时一位时间1.778ms
    
//即在3/4位时间时, 判断该位是1还是0
    
//实际测试中, 位时间只在1.651ms(+- 1ms), 定时1/4位的时间:413us
    bit in = ~YKIn; //一体化解码后, 有载频部分变为低电平, 即低电平实际为1, 高电平实际为0
    
//设置定时器初值
    
//模式1: TH0 = (2^16 - (1651/1.085)) / 2^8 = (65536 - 1651/1.085) / 256 =  250;
    
//TL0 = (65536 - 1651/1.085) % 256 = 14
    TH0 = 250;
    TL0 
= 14;
    
    YKDatas[YKtype] 
= YKDatas[YKtype] | in//将数据放入最低位
    YKcount++;
    
if(YKcount == 1 || YKcount == 2)
    {
        
if(in == 0)  //起始两位必须都为1
        {
            TR0 
= 0;
            EX0 
= 1;
            
return;
        }
    }
    
if(YKcount == 3//获取完Start bits 和control bit, 共3位
    {
        YKtype 
= 1;
    }
    
else if(YKcount == 8//获取完system bits, 共5位
    {
        
if(YKDatas[1!= 0//系统码全为0才是正确的
        {
            TR0 
= 0;
            EX0 
= 1;
            
return;
        }
        YKtype 
= 2;
    }
    
else if(YKcount == 14//获取完commond bits, 共6位
    {
        YKtype 
= 3;
    }
    
else if(YKtype == 3//等待最后1/4位时间结束, 实际延时1位时间
    {
        TR0 
= 0//接收结束, 停止定时器0
        YKSuccess();
        
return
    }
    
else  //将数据左移一位, 以便将一下位数据并于最低位
    {
        YKDatas[YKtype] 
= YKDatas[YKtype] << 1;
    }
}

/*********************IRC RC-5****************************************/

/*********************RS232****************************************/
//初始化串口
void RSInit()
{
    TMOD 
|= 0x20//T1工作方式2
    TH1 = TL1 = 0xfd//装入初值, 以后是自动重载的8位计数器
    TR1 = 1//启动T1
    SM0 = 0;
    SM1 
= 1//方式1
    REN = 1//允许接收
    EA = 1;    //开中断
    ES = 1//允许串口中断
    PCON = 0x00//串口波特率不加倍. 即设置SMOD = 0;
}

//写一字节数据到串口, 使用程序查询方式检测发送情况
void RsWriteByte(unsigned char byte)
{
    ES 
= 0//关中断
    SBUF = byte;
    
while(!TI); //检测是否发送完
    TI = 0//清0发送中断标志
    ES = 1//开中断, 以允许接收数据时使用中断方式
}

void serial() interrupt 4 //串口中断是4
{
    P1 
= SBUF;
    RI 
= 0//清0接收中断标志
}

/*********************RS232****************************************/

void display() //显示接收的数据
{
    P1 
= ~YKDatas[2];
    RsWriteByte(YKDatas[
2]);
}

void main(void)
{
    YKInit(); 
    RSInit();
    
while(1);
}

效果图:

按下9键:

PC上利用Girder响应事件:

抱歉!评论已关闭.