单片机练习-RC-5红外遥控器程序及简单制造DIY PC遥控器
本程序采用的芯片为SAA3010, 参考资料有:
1. 常用红外遥控接收头引脚图解
2. 红外遥控编码资料
3. RC-5红外遥控程序
4. GIRDER中文教程与电脑遥控器制作资料
5. Girder网站 (一个需要钱买的遥控)
6. 再度出击,20元打造经典PC遥控器!
7. SAA3010 DataSheet
这次主要是完成单片机接收红外摇控器发出的数据. 在此基础上, 我们可DIY出自己的PC摇控器, 也可自行发挥一些, 做出类似于很多Club里的点歌系统的硬件外红信号转换装置....
本次实验依旧采用TX-1B实验板, 只需增加一个一体化红外接收头, 如下图:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SM0038.jpg)
本程序中, DataOut引脚接到P3.2口. 在接收头的电源和地端接上滤波电容会使误码率更加低....
SAA3010芯片:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_1.JPG)
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_2.JPG)
(图中位时间1.688ms, 而根据DataSheet里描述的位时间为1.788ms, 但实际中我使用的时间为1.651ms )
我使用的键盘编码如下:
键盘外观:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_Infrared_remote_control_transmitter.jpg)
编码:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_4.jpg)
一体化红外接收头的数据输出的判断思路: (手画, 太难看也别见怪:) )
Tb = 1.778ms = 1.780ms (实际中测试为1.651ms)
T1/4b = 445us (实际中413ms)
T3/4b = 1.335ms (实际1.239ms)
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_Infrared_remote_control_transmitter_5.jpg)
在等待第一次低电平到来后, 定时器开始第一次定时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来触发自定义的功能![](https://www.cnblogs.com/Images/dot.gif)
#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);
}
}
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);
}
1. 常用红外遥控接收头引脚图解
2. 红外遥控编码资料
3. RC-5红外遥控程序
4. GIRDER中文教程与电脑遥控器制作资料
5. Girder网站 (一个需要钱买的遥控)
6. 再度出击,20元打造经典PC遥控器!
7. SAA3010 DataSheet
这次主要是完成单片机接收红外摇控器发出的数据. 在此基础上, 我们可DIY出自己的PC摇控器, 也可自行发挥一些, 做出类似于很多Club里的点歌系统的硬件外红信号转换装置....
本次实验依旧采用TX-1B实验板, 只需增加一个一体化红外接收头, 如下图:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SM0038.jpg)
本程序中, DataOut引脚接到P3.2口. 在接收头的电源和地端接上滤波电容会使误码率更加低....
SAA3010芯片:
(图中位时间1.688ms, 而根据DataSheet里描述的位时间为1.788ms, 但实际中我使用的时间为1.651ms )
我使用的键盘编码如下:
键盘外观:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_Infrared_remote_control_transmitter.jpg)
编码:
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_4.jpg)
一体化红外接收头的数据输出的判断思路: (手画, 太难看也别见怪:) )
Tb = 1.778ms = 1.780ms (实际中测试为1.651ms)
T1/4b = 445us (实际中413ms)
T3/4b = 1.335ms (实际1.239ms)
![](https://www.cnblogs.com/images/cnblogs_com/fengmk2/SAA3010_Infrared_remote_control_transmitter_5.jpg)
在等待第一次低电平到来后, 定时器开始第一次定时T1/4b时间, 然后到达第一次数据的T3/4b时刻, 读取该位电平状态;
以后定时器每次定时Tb时间, 这样到达对应每位的T3/4时刻, 并可读取该位电平状态.
具体逻辑请看代码中的详细注释:
采用程序查询方式:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
//接收红外遥控器(Infrared remote control transmitter)发出的数据
//芯片型号: SAA3010, 它采用RC-5协议
//芯片资料: http://www.alldatasheet.com/datasheet-pdf/pdf/18953/PHILIPS/SAA3010.html
//利用与P1口相连的发光二极管输出接收到的按键对应码, 并将它输出到串口, 用于遥控
//PC使用了Girder来触发自定义的功能
![](https://www.cnblogs.com/Images/dot.gif)
#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()方法.
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
//接收红外遥控器(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响应事件: