[/free][paragraph]
51单片机红外遥控器读码发射程序码
[free]
本主题需向作者支付 2 枚金币 才能浏览 购买主题
kkkkkkkk 51单片机红外遥控器读码发射程序码
STC的51单片机红外遥控器读码、发射程序,已试成功 | |
有一台DVD机没有遥控器,正好别的遥控器有的键可以用,但功能不一样。 | |
于是开展本工程,程序原为网上摘的,经过修改均已全部成功。 | |
采用STC的51单片机,STC12C5A60S2,可以直接串口编程,而且是1T的,非常方便。 | |
一、红外遥控器读码 | |
读码程序没怎么修改就成功了。 | |
注意:这里的延时程序是STC12C5A60S2的,如果用别的单片机,需要修改。 | |
#include <STC\STC12C5A60S2.H> | |
#include <INTRINS.h> | |
//采用1T周期的STC12C5A60S2单片机,11.0592MHZ | |
//WXL:一体化接收头默认是输出高电平,有信号时输出低电平;接P3.2脚。 | |
//WXL:这里按“低位在先” | |
/******************************************************************/ | |
/* 本程序的蓝本从网上搜集,经修改并注释,万能遥控器解码成功 */ | |
/* 晶振:11.0592MHz */ | |
/* 整理与测试:单片机教程网 胡琴 2012.5.15 */ | |
/************************* 说 明 *********************************/ | |
/* 以一个9ms的低电平和4.5ms的高电平为引导码,后跟32位二进制代码 */ | |
/* 前16位为8位用户码及其反码,后16位为8位的操作码及其反码 */ | |
/* 以脉宽为低电平0.565ms、间隔高电平0.56ms、周期为1.125ms的组合表示"0"; */ | |
/* 以脉宽为低电平0.565ms、间隔高电平1.685ms、周期为2.25ms的组合表示"1"。 */ | |
/* 注意:接收码的脉宽与间隔是对发射码取反的,即间隔是0.565ms */ | |
/* 解码后共有四个十六进制码,本程序取第三个作为识别码 */ | |
/*******************************************************************/ | |
#define uchar unsigned char | |
uchar data IRcode[4]; //定义一个4字节的数组用来存储代码 | |
uchar CodeTemp; //编码字节缓存变量 | |
uchar i,j,k; //延时用的循环变量 | |
sbit IRsignal=P3^2; //HS0038接收头OUT端直接连P3.2(INT0) | |
sbit P0_0=P0^0; //P0连接到 LED 上 | |
sbit P0_1=P0^1; | |
sbit P0_2=P0^2; | |
/**************************延时0.6ms子程序**********************/ | |
void Delay0_6ms(void) //@11.0592MHz | |
{ | |
unsigned char i, j; | |
_nop_(); | |
_nop_(); | |
i = 7; | |
j = 112; | |
do | |
{ | |
while (--j); | |
} while (--i); | |
} | |
/**************************延时0.9ms子程序**********************/ | |
void Delay0_9ms(void) //@11.0592MHz | |
{ | |
unsigned char i, j; | |
_nop_(); | |
_nop_(); | |
_nop_(); | |
i = 10; | |
j = 170; | |
do | |
{ | |
while (--j); | |
} while (--i); | |
} | |
/***************************延时1ms子程序**********************/ | |
void Delay1ms(void) | |
{ | |
unsigned char i, j; | |
_nop_(); | |
i = 11; | |
j = 190; | |
do | |
{ | |
while (--j); | |
} while (--i); | |
} | |
/***************************延时4ms子程序**********************/ | |
void Delay4ms(void) | |
{ | |
unsigned char i, j; | |
_nop_(); | |
_nop_(); | |
_nop_(); | |
i = 44; | |
j = 3; | |
do | |
{ | |
while (--j); | |
} while (--i); | |
} | |
/**************************** 延时子程序 ************************/ | |
void Delay(void) | |
{ | |
uchar i,j,k; | |
for(i=200;i>0;i--) | |
for(j=200;j>0;j--) | |
for(k=3;k>0;k--) ; | |
} | |
/******************** 中断0解码服务子程序 ********************/ | |
void int0(void) interrupt 0 using 2 | |
{ | |
EA = 0; //??? 可以这样,跳入中断,但仍可对P3.2(INT0)进行电平变化的读取 | |
for(k=0;k<10;k++) | |
{ | |
Delay0_9ms(); | |
if (IRsignal==1) //如果0.9ms后IRsignal=1,说明不是引导码,退出中断 | |
{ | |
k=10; | |
break; | |
} | |
else if(k==9) //如果 持续了10×0.9ms=9ms的低电平,说明是引导码。WXL:一定是从引导码开始 | |
{ | |
while(IRsignal==0); // WXL:因为红外头默认输出是高电平,故用while(IRsignal==0)很安全,而用while(IRsignal==1)则可能会进入死循环 | |
Delay4ms(); //跳过持续4.5ms的高电平 WXL:要超过4.5ms更好 | |
Delay0_6ms(); | |
for(i=0;i<4;i++) //分别读取4个字节 | |
{ | |
for(j=1;j<=8;j++) //每个字节8个bit的判断 | |
{ | |
while(IRsignal==0); //等待上升沿,此处用得很好:因为0.56ms的低电平(接收时)是代码0与1的相同部分 | |
Delay0_9ms(); //从上升沿那一时刻开始延时0.9ms(因为0.9介于0.56(=1.125-0.56)与1.69(=2.25-0.56)之间),再判断IRsignal | |
if(IRsignal==1) // 如果IRsignal是"1",高位置"1",并向右移一位 | |
{ | |
Delay1ms(); //为什么要延时1ms呢?因为要使IRsignal跳至低电平(即0.56ms的0与1相同部分上) | |
CodeTemp=CodeTemp | 0x80; //此处的算法很好 |
if(j<8) CodeTemp=CodeTemp>>1; | |
} | |
else // 如果IRsignal是"0",高位置"0",并向右移一位 | |
if(j<8) CodeTemp=CodeTemp>>1; //如果IRsignal是"0",则直接向右移一位,自动补"0" | |
} | |
IRcode=CodeTemp; | |
CodeTemp=0; | |
} //end for | |
for(i=0;i<4;i++) //通过串口将代码发出 | |
{ | |
SBUF=IRcode; | |
while(!TI); //等待一个字节发送完毕 | |
TI=0; | |
} | |
Delay(); | |
} //end else | |
} //END for | |
EA = 1; | |
} | |
/***********************串口初始化程序*********************/ | |
void initUart(void) | |
{ | |
TMOD | = 0x20; // |
SCON = 0x50; // | |
PCON | = 0x80; // |
TH1 = 250; // 9600 bps @ 11.0592MHz | |
TL1 = 250; | |
TR1 = 1; | |
} | |
/**************************主程序*************************/ | |
void main() | |
{ | |
//P0=0XFF; | |
initUart(); | |
IT0 = 1; //INT0为负边沿触发, (1:负边沿触发,0:低电平触发) | |
EX0 = 1; //外部中断INT0开, (1:开, 0:关 ) | |
EA = 1; //开所有中断 | |
CodeTemp = 0; //初始化红外编码字节缓存变量 | |
Delay(); | |
while(1) | |
{ | |
} | |
} | |
二、红外遥控发射 | |
网上的程序是http://gudeng614.blog.163.com/blog/static/818017420101545648734/ | |
做发射程序费了很大波折,因为网上的程序不好用。 | |
后来不得不用计算机的并口采集了发射数据,发现数据有异常,终于找到了问题所在。 | |
原因是count变量是int的,对其赋值或比较时,汇编语句一句完不成,会被中断服务程序中断,造成count变量赋值或比较出现问题。 | |
解决方法是必须在操作时屏蔽中断。而flag变量是bit的,一句汇编即可完成赋值,故不会有问题。 | |
其间还发现别的遥控器会在起始码前加一个前脉冲,以为是这个问题,其实不是。 | |
注意:由于13us会中断一次,这里是采用1T的单片机。如果采用普通的51单片机,由于是12T的,不知道能不能成功。 | |
//程序从网上修改而来 | |
//由于中断需要13us中断一次,即中断要在几us处理完,因此需要单片机速度比较快,用24MHZ晶振才能保证正常 | |
//但24MHZ晶振,用串口不方便 | |
//这里采用1T周期的STC12C5A60S2单片机,11.0592MHZ,可以兼顾。 | |
//STC12C5A60S2 引脚可灌入20mA电流,直接从正电源→红外LED→串1K电阻→P0.0脚。 | |
//串口1默认选T1作为波特率发生器 | |
//TO用于中断 | |
//发送时,低比特位优先 | |
#include <STC\STC12C5A60S2.H> | |
#include <INTRINS.h> | |
sbit P0_0 = P0^0; | |
static bit g_OP; //红外发射管的亮灭 | |
static unsigned int g_count; //延时计数器 | |
static unsigned int g_endcount; //终止延时计数 | |
static bit g_flag; //红外发送标志 | |
unsigned char g_iraddr1; //十六位地址的第一个字节 | |
unsigned char g_iraddr2; //十六位地址的第二个字节 | |
//定时器0中断处理 | |
void timeint(void) interrupt 1 | |
{ | |
g_count++; | |
if (g_flag) g_OP=~g_OP; | |
else g_OP = 1; //LED不点亮 | |
P0_0 = g_OP; | |
} | |
///////////////////////////////////////////////////// | |
void SendIRdata_38KHZ(unsigned int temp1, bit temp2) | |
{ | |
g_endcount=temp1; | |
g_flag=temp2; | |
EA=0; g_count=0; EA=1; //避免中断影响count置数 | |
while(1) | |
{ | |
EA=0; | |
if( g_count < g_endcount ) EA=1; //避免中断影响count比较 | |
else | |
{ | |
EA=1; | |
break; | |
} | |
} | |
} | |
///////////////////////////////////////////////////// | |
void SendIRdata_BYTE(unsigned char irdata) | |
{ | |
unsigned char i; | |
for(i=0;i<8;i++) | |
{ | |
//先发送0.56ms的38KHZ红外波(即编码中0.56ms的高电平) | |
SendIRdata_38KHZ(43, 1); //13.02*43=0.56ms | |
//停止发送红外信号(即编码中的低电平) | |
if(irdata & 1) //判断最低位为1还是0。 低位先发送!! | |
SendIRdata_38KHZ(130, 0); //1为宽电平,13.02*130=1.693ms | |
else SendIRdata_38KHZ(43, 0); //0为窄电平,13.02*43=0.560ms | |
irdata=irdata>>1; | |
} | |
} | |
///////////////////////////////////////////////////// | |
void SendIRdata(unsigned char p_irdata) | |
{ | |
//有的遥控器会发一个前脉冲,如果不灵,可试试加上前脉冲 | |
//发送起始码前脉冲,高电平有38KHZ载波 | |
//SendIRdata_38KHZ(18, 1); | |
//发送起始码前脉冲,低电平无38KHZ载波 | |
//SendIRdata_38KHZ(18, 0); | |
//发送9ms的起始码,高电平有38KHZ载波 | |
SendIRdata_38KHZ(692, 1); //13.02*692=9.010ms | |
//发送4.5ms的结果码,低电平无38KHZ载波 | |
SendIRdata_38KHZ(346, 0); //13.02*346=4.505ms | |
//发送十六位地址的前八位 | |
SendIRdata_BYTE(g_iraddr1); | |
//发送十六位地址的后八位 | |
SendIRdata_BYTE(g_iraddr2); | |
//发送八位数据 | |
SendIRdata_BYTE(p_irdata); | |
//发送八位数据的反码 | |
SendIRdata_BYTE(~p_irdata); | |
//发送总的结束位1bit | |
SendIRdata_38KHZ(43, 1); //13.02*43=0.56ms | |
/* //后面这些可以不用发 | |
g_endcount=1766; | |
g_flag=0; | |
EA=0; g_count=0; EA=1; | |
while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; } } | |
//发送9ms的起始码,高电平有38KHZ载波 | |
g_endcount=692; //13.02*692=9.010ms | |
g_flag=1; | |
EA=0; g_count=0; EA=1; | |
while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; } } | |
//发送4.5ms的结果码,低电平无38KHZ载波 | |
g_endcount=346; //13.02*346=4.505ms | |
g_flag=0; | |
EA=0; g_count=0; EA=1; | |
while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; } } | |
//发送总的结束位1bit | |
g_endcount=43; //13.02*43=0.56ms | |
g_flag=1; | |
EA=0; g_count=0; EA=1; | |
while(1){EA=0; if(g_count<g_endcount) EA=1; else { EA=1; break; } } | |
*/ | |
g_flag=0; | |
} | |
/////////////////////////////////////////////////////////// | |
void main(void) | |
{ | |
unsigned char com_data; //数据字节 | |
g_count = 0; | |
g_flag = 0; | |
g_OP = 1; | |
P0_0 = g_OP; //LED接电源正极,不点亮 | |
SCON=0x50; //串口方式1 01 0 1 0 0 00 模式1,非多机,允许接收,无数据位8,清中断标识TI和RI | |
TMOD = 0x22; //(定时器0和1:方式2,自动重装,8位) | |
TH1=253; //11.0592MHZ,9600bps。没有设置SMOD,故波特率没有加倍。即:11.0592/12/3/32=9600bps | |
TL1=253; | |
TR1=1; //启动定时器 | |
TH0 = 244; | |
TL0 = 244; //(WXL:即计数12次中断一次,即11.0592MHZ晶振,机器周期是1.085us,12次*1.085=13.02us,这样达38KHZ。 13us一次中断,时间太短了,所以单片机要快) | |
ET0 = 1; //定时器0中断允许 | |
EA = 1; //允许CPU中断 | |
TR0 = 1; //开始计数 | |
g_iraddr1=0; //地址码 | |
g_iraddr2=255; //地址反码 | |
RI=0; | |
while(1) | |
{ | |
if(RI==1) | |
{ | |
com_data =SBUF; | |
RI=0; //要人工清RI | |
SendIRdata(com_data); //发送红外数据 | |
TI=0; | |
SBUF = com_data; //输出字符 | |
while(!TI) ; //空语句判断字符是否发完,TI=1表示发完 | |
TI = 0; //要人工清TI | |
} | |
} | |
} |
来源网络,仅供学习交流 |
页:
[1]