38KHz,NEC红外模拟发送和接收程序
/*************************************************************************************************/ //38k NEC 编码接收和模拟发射 //完整的信号构成:引导码+8位的客户码+8位客户码的补码+8位的按键值+8位按键值的补码+结束码 //接收使用外部中断0,发射管低电平触发 //STC15F104W@24MHz // //为了尽可能的简化代码量,没有引入按键,程序运行起来为每250ms一个周期,每250ms通过红外发射管发射一次数据,同 //时若检测到接收管接收到了完整的数据则把这个数据通过串口发出 // //由于发送和接收数据肯定无法同时在一片单片机上完成,所以若需测试代码的完整功能应准备两个单片机或者其他设备。 // //网上能找到的模拟红外发射的代码多为通过定时器实现,本代码则统统使用软件延时实现。一来使用定时器太浪费资源,二 //来对于1T单片机来说,用软件延时马马虎虎输出38K的波形还是挺简单的(这都0202年了,估计以后很难由机会再用到12T //的51单片机了吧) /*************************************************************************************************/ #include "config.h" #include "delay.h" #include "suart.h" sbit IRIN =P3^2; //红外接收端口 sbit IROUT=P3^3; //红外发射端口 unsigned char IrValue[4]; //红外键值.四个数据分别为用户码、用户补码、键值、键值补码 bit flag_Ir; //红外接收完成标志位 void Ir_Init(void); //红外初始化(外部中断0和发送接收端口初始化) void Ir_Out(unsigned char u, unsigned char x); //发送数据,两个参数分别为用户码和键值 void Ir_Out_Frame(unsigned char y); //发送一帧数据,由 Ir_Out() 函数调用 void Delay7us(); //7us延时 @24.000MHz_STC-Y5 void Delay19us(); //19us延时@24.000MHz_STC-Y5 void Delay100us(); //100us延时@24.000MHz_STC-Y5 /************************************************************************************************** * 主函数 */ void main(void) { Ir_Init(); //红外初始化 SUART_INIT(); //串口初始化 TxString("38KHz NEC 红外接收测试程序\r\n"); while (1) { delay_ms(250); //每250ms通过红外发送一个数据 Ir_Out(0x42,0x5A); //参数为用户码、键值 if(flag_Ir) //如果接收到完整数据,则把数据通过串口打印出来 { flag_Ir=0; //必须清空接收完成标志 TxByte(IrValue[0]); TxByte(IrValue[1]); TxByte(IrValue[2]); TxByte(IrValue[3]); } } } /************************************************************************************************** * 红外接收初始化 */ void Ir_Init(void) { IT0 = 1; //下降沿触发 EX0 = 1; //打开中断0允许 EA = 1; //打开总中断 IRIN = 1; //初始化端口 IROUT = 1; flag_Ir = 0; //初始化接收完成标志位 } /************************************************************************************************** * 延时7us @ 24MHz STC-Y5 * 生成38k波使用,占空比在1/3时效果最好。i的取值供参考,使用时视波形表现调整。 */ void Delay7us() //@24.000MHz { unsigned char i; _nop_(); _nop_(); i = 38; while (--i); } /************************************************************************************************** * 延时19us @ 24MHz STC-Y5 * 生成38k波使用,占空比在1/3时效果最好。i的取值供参考,使用时视波形表现调整。 */ void Delay19us() //@24.000MHz { unsigned char i; _nop_(); _nop_(); i = 105; while (--i); } /************************************************************************************************** * 延时100us @ 24MHz STC-Y5 */ void Delay100us() //@24.000MHz { unsigned char i, j; i = 3; j = 82; do { while (--j); } while (--i); } /************************************************************************************************** * 红外发射程序 */ void Ir_Out(unsigned char u, unsigned char x) { int tt; EA=0; //关闭所有中断 /*发送引导码*/ tt=350; //输出9ms 1(有38k信号输出表示 1. 38k每个周期约26us,9ms大概tt个周期,视实际情况调整) do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt); tt=45; //输出4.5ms 0 (无38k信号输出表示 0. 同样,tt的取值看实际情况调整) do {IROUT=1; Delay100us();} while(--tt); /*发送用户码和用户补码*/ Ir_Out_Frame(u); Ir_Out_Frame(~u); /*发送键值和键值补码*/ Ir_Out_Frame(x); Ir_Out_Frame(~x); /*发送结束码*/ tt=25; //输出0.65ms 1 do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt); tt=5; //输出40ms 0 do {IROUT=1; Delay100us();} while(--tt); /*重复码*/ //9ms 1 //2.25ms 0 //0.56ms 1 //40ms 0 //56ms 0 EA=1; // 打开中断 } /************************************************************************************************** * 红外单帧发射程序 */ void Ir_Out_Frame(unsigned char y) { char num; int tt; for (num=0; num<8; num++) //循环8次移位 { tt=25; //输出0.65ms 1 do {IROUT=0; Delay19us(); IROUT=1; Delay7us();} while(--tt); if(y&0x01) //if为1 { tt=16; //输出1.65ms 0 do {IROUT=1; Delay100us();} while(--tt); } else //否则 { tt=5; //输出0.56ms 0 do {IROUT=1; Delay100us();} while(--tt); } y >>= 1; //右移一位 } } /************************************************************************************************** * 读取红发外值 * 外部中断的服务程序 */ void ReadIr() interrupt 0 { unsigned int err; unsigned int tt; unsigned char Time; unsigned char j, k; EA=0; //关闭总中断 Time=0; tt=80; //延时8ms去干扰,引导码为9ms的低电平和4.5ms的高电平 do { Delay100us(); } while(--tt); if(IRIN==0) //确认是否真的接收到正确的信号 { err=500; //超时判断 while((IRIN==0)&&(err>0)) //等待高电平,最多等待50ms { Delay100us(); err--; } if(IRIN==1) //收到高电平 { err=500; //超市判断 while((IRIN==1)&&(err>0)) //等待4.5ms的高电平过去 { Delay100us(); err--; } for(k=0; k<4; k++) //开始接收数据,共有4个数据 { for(j=0; j<8; j++) //每个8位 { err=60; while((IRIN==0)&&(err>0)) //等待信号前面的560us低电平过去 { Delay100us(); err--; } err=500; while((IRIN==1)&&(err>0)) //计算高电平的时间长度。 { Delay100us(); Time++; err--; if(Time>50) //超过5ms说明接收到重复码。重复码:9ms高电平+2.25ms低电平+560us高电平 { EA=1; //打开总中断 return; //退出函数 } } IrValue[k]>>=1; //k表示第几组数据 if(Time>=8) //若高电平出现大于0.8ms,为1(1.65ms为1,0.56ms为0,这里取中间值) IrValue[k]|=0x80; Time=0; //用完时间要重新赋值 } } } if(IrValue[2]=~IrValue[3]) //这里一般只判断键值而不判断用户码,因为对于一些用户码是0的遥控无论怎样都是0 { flag_Ir = 1; //如果按键值与按键值的补码的取反相同,则接收完成标志置位 } } EA=1; //打开总中断 }