发一个红外线解码程序+仿真文件(可定义任意I/O作接收脚,支持长/短按,自适应主频6MHz~40
http://cache.amobbs.com/bbs_upload782111/files_47/ourdev_689712TW2D7X.jpg(原文件名:TP0.jpg)
//*********************【 NEC解码头文件 】*******************
//
// 简介:本程序适用于NCE解码:(9ms+4.5ms)引导码+32位编码。
// 兼容STC所有型号(包括 1T 和 12T 系列),可以定义任意I/O作红外接收脚,
// 自适应解码主频:6MHz ~ 40MHz。
//
// 使用条件:占用系统定时器0,开启定时器0中断(如使用其它定时器请自改IR_Init();初始化函数)
//
// 使用说明:填相关宏定义:USER_H、USER_L、Check_EN、CPU_Fosc、IR,
// 上电初始化函数IR_Init(),
// 在定时器0中断中调用IR_NEC()解码函数,
// 解码有效时,IR_BT=2即短按,IR_BT=3即长按,由用户清0,
// 解码存放:用户码高8位NEC,用户码低8位NEC,操作码NEC,操作码反码NEC。
//
//【供用户调用的函数】
// IR_Init(); //接收初始化,开启定时器0中断400us
// IR_NEC(); //红外线解码(解NEC编码)
//
//***************************************************************/
#ifndef __IR_NEC_H__
#define __IR_NEC_H__
//【用户必填项:USER_H、USER_L、Check_EN、CPU_Fosc、IR】
#defineUSER_H 0x80 //用户码高8位
#defineUSER_L 0x7F //用户码低8位
#defineCheck_EN 0 //是否要校验16位用户码:不校验填0,校验则填1
#defineCPU_Fosc 12000000L //输入主频,自适应解码(单位:Hz,范围:6MHz ~ 40MHz)
#defineCA_S 8 //长按时间设置,单位:108mS(即 108mS整数倍,10倍以上为宜)
sbit IR = P3^6; //红外线接口(任意引脚)
#defineStep 400 //红外采样步长:400us
#defineTH_H ((65536-Step*(CPU_Fosc/300)/40000)/256)//定时器高8位基准赋值
#defineTH_L ((65536-Step*(CPU_Fosc/300)/40000)%256)//定时器低8位基准赋值
uint8 IR_BT; //解码效果返回:0无效,1有效,2短按,3长按
uint8 NEC; //解码存放:16位用户码、操作码正反码
uint8 cntCA; //长按计数
uint16cntStep; //步数计
bit IRa,IRb; //接收脚电位状态保存
bit IRsync; //同步标志
uint8 BitN; //位码装载数
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线解码初始化
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void IR_Init()
{
TMOD &= 0xF0; //清定时器0
TMOD |= 0x01; //定时器0:16位定时器
TL0 = TH_L; //每步时间
TH0 = TH_H;
ET0 = 1;
EA= 1;
TR0 = 1;
}
/*┈┈┈┈┈┈┈┈┈┈ 基准 ┈┈┈┈┈┈┈┈┈┈┈*/
#define Boot_Limit ((9000+4500 +1000)/Step) //引导码周期上限
#define Boot_Lower ((9000+4500 -1000)/Step) //引导码周期下限
#define Bit1_Limit ((2250 +800)/Step) //“1”周期上限
#define Bit0_Limit ((1125 +400)/Step) //“0”周期上限
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线NEC周期采样解码法(定时中断,下降沿查询周期时间)
全局变量:IR_BT = 0无效
1有效,待继续判断长、短按(如不需要判断长、短按,则直接使用)
2短按
3长按
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void IR_NEC()
{
TL0 = TH_L; //重赋值
TH0 = TH_H;
cntStep++; //步数累加
if(IR_BT==1)if(cntStep>300)IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按
IRb = IRa; //保存上次电位状态
IRa = IR; //保存当前电位状态
if(IRb && !IRa) //是否下降沿(上次高,当前低)
{
if(cntStep > Boot_Limit) //超过同步时间?
{
if(IR_BT==1)if(++cntCA>CA_S)IR_BT=3; //解码有效后,继续按住遥控>CA_S即长按
IRsync=0; //同步位清0
}
else if(cntStep > Boot_Lower){ IRsync=1; BitN=32; } //同步位置1,装载位码数32
else if(IRsync) //如果已同步
{
if(cntStep > Bit1_Limit)IRsync=0;
else
{
NEC >>= 1;
if(cntStep > Bit0_Limit)NEC |= 0x80; //“0”与“1”
if(--BitN == 0)
{
IRsync = 0; //同步位清0
#if (Check_EN == 1)
if((NEC==USER_H)&&(NEC==USER_L)&&(NEC==~NEC)) //校验16位用户码、操作码正反码
{IR_BT=1; cntCA=0;} //解码有效,接下来判断:短按?长按?
#else
if(NEC==~NEC){ IR_BT=1; cntCA=0; }//只校验操作码正反码
#endif
}
else if((BitN & 0x07)== 0) //NEC每装满8位,移动保存一次(即 BitN%8 == 0)
{ NEC=NEC; NEC=NEC; NEC=NEC; }
}
}
cntStep = 0; //步数计清0
}
}
//取消相关宏定义
#undef CPU_Fosc
#endif
主程序
#include "INC\STC89C52RC.H"
#include "INC\MY_SET.H"
#include "INC\IR_NEC.H" //调用NEC解码头文件
sfr SE = 0x80; //数码管段选 P0:0x80 P1:0x90
sbitWX1= P2^0; //数码管位显
sbitWX2= P2^1;
sbitWX3= P2^2;
sbitWX4= P2^3;
sbitWX5= P2^4;
sbitWX6= P2^5;
sbitWX7= P2^6;
sbitWX8= P2^7;
uint8c tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0X88,0X83,0XC6,0XA1,0X86,0X8E,0xFF};
uint8Xn,X1,X2,X3,X4,X5,X6;
void KZ0(); //短按处理
void KZ1(); //长按处理
/***************** 主函数 ********************/
void main(void)
{
IR_Init(); //红外线解码初始化
while(1)
{
//遥控检测
if((IR_BT==2)||(IR_BT==3))
{
if(IR_BT==2)KZ0(); //短按处理
else KZ1(); //长按处理
IR_BT =0; //清有效标志
X1 = NEC/16; //更新显示
X2 = NEC%16;
X3 = NEC/16;
X4 = NEC%16;
X5 = NEC/16;
X6 = NEC%16;
}
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控短按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ0()
{
switch(NEC)
{
case 0x12: P10 = !P10; break;
case 0x05: break;
case 0x1E: break;
case 0x55: break;
case 0x01: break;
case 0x1B: break;
case 0x03: break;
case 0x6B: break;
case 0x07: break;
case 0x08: break;
case 0x09: break;
case 0x68: break;
case 0x22: break;
case 0xE6: break;
case 0x33: break;
case 0xE2: break;
default:break;
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控长按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ1()
{
switch(NEC)
{
case 0x12: P14 = !P14; break;
case 0x05: break;
case 0x1E: break;
case 0x55: break;
case 0x01: break;
case 0x1B: break;
case 0x03: break;
case 0x6B: break;
case 0x07: break;
case 0x08: break;
case 0x09: break;
case 0x68: break;
case 0x22: break;
case 0xE6: break;
case 0x33: break;
case 0xE2: break;
default:break;
}
}
/*********************数码管扫描*************************/
void XS(void)
{
if(++Xn > 7)Xn=0;
switch(Xn)
{
case 0: WX8=1;NOP; //屏蔽上个位显
SE=tab; //送段码
WX1=0; //开位显
break;
case 1: WX1=1; NOP; SE=tab; WX2=0; break;
case 2: WX2=1; NOP; SE=tab; WX3=0; break;
case 3: WX3=1; NOP; SE=tab; WX4=0; break;
case 4: WX4=1; NOP; SE=tab; WX5=0; break;
case 5: WX5=1; NOP; SE=tab; WX6=0; break;
case 6: WX6=1; NOP; SE=tab; WX7=0; break;
case 7: WX7=1; NOP; SE=tab; WX8=0; break;
default:break;
}
}
/********************** 定时器0中断函数************************/
void time0(void) interrupt 1
{
IR_NEC();
XS();
}
接收源程序+仿真
点击此处下载 ourdev_689713KCBR6N.rar(文件大小:111K) (原文件名:NEC(任意接收引脚,支持长短按).rar)
遥控器源程序
点击此处下载 ourdev_689745J4Z85P.rar(文件大小:94K) (原文件名:遥控器源程序.rar)
/***************************************************************
作品:红外线遥控发射(NEC编码)
单片机:STC89C52RC
晶振:12M
***************************************************************/
//
// 发射引脚(接PNP三极管b极)
// PNP三极管e极接2Ω电阻,c极接红外发射管
#include <REG51.h>
#include "INC\MY_SET.h"
#include "INC\LCD1602_6IO.h"
sbitIR= P3^6; //发射引脚(接PNP三极管基极)
#defineUSER_H P2 //用户码高8位
#defineUSER_L P0 //用户码低8位
uint8c tab = { //操作码
0x12,0x05,0x1e,0x55,
0x01,0x1b,0x03,0x6b,
0x07,0x08,0x09,0x68,
0x22,0xE6,0x33,0xe2};
#define m9 (65536-9000) //9mS
#define m4_5(65536-4500) //4.5mS
#define m1_6(65536-1650) //1.65mS
#define m_56(65536-560) //0.56mS
#define m40 (65536-40000) //40mS
#define m56 (65536-56000) //56mS
#define m2_25 (65536-2250) //2.25mS
voidSanZhuan();
uint8 KEY(void);
voidZZ(uint8 x); //NEC编码发送程序
voidZ0(uint8 temp); //单帧(8位数据)发送程序
voidTT0(bit BT,uint16 x); //38KHz载波发射 + 延时程序
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:主程序
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void main(void)
{
TMOD = 0x01; //T0 16位工作方式
IR=1; //发射端口常态为高电平
L1602_Init();
L1602_clr();
L1602_xy(0,0);
L1602_ZIFUC("UserCode :0x");
L1602_xy(0,1);
L1602_ZIFUC("Opcode :0x");
while(1)
{
L1602_xy(12,0);
L1602_JZ(USER_H,16,1);
L1602_JZ(USER_L,16,1);
SanZhuan();
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:4×4矩阵键盘
【 线翻转法键值表 】
P1.0P1.1P1.2P1.3P1.4P1.5P1.6P1.7
│ │ │ │ │ │ │ │
│ │ │ └──7e be de ee
│ │ └─────7d bd dd ed
│ └────────7b bb db eb
└─────────── 77 b7 d7 e7
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
uint8 KEY(void)
{
uint8 Key = 0;
P1 = 0xf0; //键盘初始:行值=0,列值=1
NOP; //缓冲,待IO端口电位稳定
Key = P1&0xf0; //得到行标志
P1= 0x0f; //翻转键盘接口输出
NOP;
Key |= (P1&0x0f); //列标志 + 行标志
return Key; //返回键值
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:散转程序
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void SanZhuan()
{
uint8 v;
v = KEY(); //键盘检测
switch(v)
{
case 0x7e:ZZ(tab);break;
case 0xbe:ZZ(tab);break;
case 0xde:ZZ(tab);break;
case 0xee:ZZ(tab);break;
case 0x7d:ZZ(tab);break;
case 0xbd:ZZ(tab);break;
case 0xdd:ZZ(tab);break;
case 0xed:ZZ(tab);break;
case 0x7b:ZZ(tab);break;
case 0xbb:ZZ(tab);break;
case 0xdb:ZZ(tab);break;
case 0xeb:ZZ(tab);break;
case 0x77:ZZ(tab);break;
case 0xb7:ZZ(tab);break;
case 0xd7:ZZ(tab);break;
case 0xe7:ZZ(tab);break;
default:break;
}
v=0;
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:NEC编码发送程序
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void ZZ(uint8 Value)
{
L1602_xy(12,1);
L1602_JZ(Value,16,1); //更新显示
TT0(1,m9); //高电平9mS
TT0(0,m4_5); //低电平4.5mS
/*┈ 发送4帧数据┈*/
Z0(USER_H); //用户码高8位
Z0(USER_L); //用户码低8位
Z0(Value); //操作码
Z0(~Value); //操作码反码
/*┈┈ 结束码 ┈┈*/
TT0(1,m_56);
TT0(0,m40);
/*┈┈ 重复码 ┈┈*/
while(KEY() != 0xFF)
{
TT0(1,m9);
TT0(0,m2_25);
TT0(1,m_56);
TT0(0,m40);
TT0(0,m56);
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:单帧(8位数据)发送程序
入口:temp
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void Z0(uint8 temp)
{
uint8 v;
for(v=0;v<8;v++)
{
TT0(1,m_56); //高电平0.65mS
if(temp&0x01) TT0(0,m1_6);//发送最低位
else TT0(0,m_56);
temp >>= 1; //右移一位
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:38KHz载波发射 + 延时程序
入口:(是否发射载波,延时约 x (uS))
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void TT0(bit BT,uint16 x)
{
TH0 = x>>8; //输入定时值
TL0 = x;
TF0=0; //溢出标志位清0
TR0=1; //启动定时器0
if(BT == 0) while(!TF0); //BT=0时,不发射38KHz载波只延时;
else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比5:26
{
IR = 0;
if(TF0)break;if(TF0)break;
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
}
TR0=0; //关闭定时器0
IR =1; //载波停止后,发射端口常态为高
} mark 有空试试 发送部分代码看一下呀,只有解码 很不错 不错顶一下! 多任务的思想,不错 不错,mark一下 mark mark!!! 谢谢! 不错 好啊 还可以 实物怎么运行不了啊,1602无显示。 回复【13楼】pangjineng
-----------------------------------------------------------------------
1602的程序只是临时加进去,没经过实物验证 mark.有空再玩。学无止境啊 THX 不错,谢谢分享 回复【楼主位】BXAK
-----------------------------------------------------------------------
试了一下发射的,蛮不错的,测了发射的频率为38.6473K(低电平4.9375uS,脉宽25.875uS),请问一下要微调发射的频率,软件要咋改啊?谢谢 回复【18楼】tzsteel
-----------------------------------------------------------------------
else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
IR = 0;
if(TF0)break;if(TF0)break;
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
}
修改大括号里面的就可以,
频率有些高,降低添加适量的空操作“_nop_();”(注:不要放在大括号两侧)
http://cache.amobbs.com/bbs_upload782111/files_51/ourdev_715650M2F4ZX.jpg
(原文件名:TP0.jpg)
比如改成:
else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
IR = 0;
if(TF0)break;if(TF0)break;
_nop_();
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
} 新人,表示有压力! mark 回复【19楼】BXAK
-----------------------------------------------------------------------
谢谢楼主解答,试了一下加NOP;是可以减少频率,加if(TF0)break;也是一样的,但加或减这些语句低电平4.9375uS是一直不变的,变的只是后面高电平的时间,请问一下要增加低电平的时间要咋改软件?谢谢 红外遥控 MARK 回复【22楼】tzsteel
-----------------------------------------------------------------------
调整低/高时间比例,比如
else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
IR = 0;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
_nop_();
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
} mark谢谢 好贴,好人,好思路! mark 谢了,下载试试 回复【24楼】BXAK
-----------------------------------------------------------------------
非常感谢 回复【24楼】BXAK
-----------------------------------------------------------------------
非常感谢 多谢分享,先留名再看 mark mark! 回复【24楼】BXAK
-----------------------------------------------------------------------
请问一下楼主,else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比约5:26
{
IR = 0;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
_nop_();
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
}这段程序中,是怎么产生38KHz脉冲的?比如9mS时,定时器装的初值是65536-9000,计数满9000次TF0才为1,要产生5:26的38KHz载波不能理解,但用数字示波器看波型,9mS确实是由一串等分的38KHz载波组成.谢谢 回复【34楼】tzsteel
-----------------------------------------------------------------------
通过软件产生啊,你通过反汇编查看这段C的汇编代码看看,对照“51指令执行所用时钟表”就知道了
那9mS区间一直循环着while(1)
{ //12MHz时指令执行时间
IR = 0; //1us
if(TF0)break; //2us
if(TF0)break;
if(TF0)break;if(TF0)break;
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
}
上面一个循环正好26us(传统51@12MHz晶振,比如STC89系列),周期26us则频率38.461KHz 回复【35楼】BXAK
-----------------------------------------------------------------------
谢谢,非常好的思路. 学习收藏,谢谢楼主 红外解码多任务思路 要学习 mark留用! 收藏,学习。 想搞红外很久了,以前学习的时候也试过,但是没有成功。楼主程序写的很不错,虽然看不太懂,但是楼主的解释很详细。
先收藏了,等有时间了,好好研究。非常感谢楼主分享。
还有个问题想请教楼主
发射程序中
else while(1) //BT=1时,发射38KHz脉冲+延时;38KHz载波(低电平)占空比5:26
{
IR = 0;
if(TF0)break;if(TF0)break;
IR = 1;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
if(TF0)break;if(TF0)break;
}
TR0=0; //关
占空比非得5:26左右吗?
只要占空比小于1,能行吗?
这个软件实现载波实在是不懂。 回复【42楼】vp110
-----------------------------------------------------------------------
晕,没规定一定是5:26,低占空比是为了节省能量的同时,通过接小限流电阻(2.2Ω ~ 4.7Ω)获得高峰值电流,
比如:像电视遥控器之类一般都是(高电平)占空比约1:3,用NPN三极管驱动。
这也是为什么不用可编程时钟输出 或者 定时器中断 产生38K载波的原因之一,而普通的51没有PWM,想获得低占空比的38K载波只好用软件实现了。
你看看下面的信息,会有收获的:
二、红外发射(接收)距离与哪些因素有关?怎样提高红外发射管的发射距离?
常用的红外发射管,它的外形与普通的发光二极管(LED)相似,发出人眼不可见的近红外线,约0.93μm 。工作电流小于20ma,为了使其在不同的电路中正常使用,回路中常串有限流电阻。 使用红外线控制时,其控制的距离与发射功率成正比。
红外发射管工作于脉冲状态,因为脉动光(调制光)的有效传送距离与脉冲的峰值电流成正比,只需尽量提高峰值Ip,就能增加红外光的发射距离。
提高Ip 的方法:尽量减小脉冲占空比,即压缩脉冲的宽度т,比如家电红外遥控中,红外发射管的工作脉冲占空比约为1/4~1/3;一些电气产品红外遥控器,其占空比是1/10。减小脉冲占空比可使红外发射管的发射距离增大。
常见的红外发射管,其功率分为小功率(1mW~10mW)、中功率(20mW~50mW)和大功率(50mW~100mW以上)三大类。要使红外发光二极管产生调制光,只需在驱动管上加上一定频率的脉冲电压。
用红外发光二极管发射红外线去控制受控装置时,受控装置中均有相应的红外接收元件,现在常用的是一体化的红外接收头,一体化的红外接收头相关材料看这里:图解红外遥控的发射和接收原理。
红外线发射与接收的方式有两种,其一是直射式,其二是反射式。直射式指红外发射管和红外接收头相对安放在发射与受控物的两端,中间相距一定距离,比如常见的红外遥控;反射式是指红外发射管和红外接收头在同一平面上,红外发射管发出的红外光经过物体反射回来,被红外接收头接收。
三、红外发射、接收遥控距离,轻松达到10米以上:
(1)发射载波一定要接近接收头的中心频率(最关键);
(2)提高发射电流峰值Ip:使用三极管驱动发射管,占空比1:3(或者1:4……等等),小限流电阻(2欧姆):
①使用NPN型三极管驱动时,用“高电平”占空比(如果是没有推挽输出的51,应加3K左右的上拉电阻
);
②使用PNP型三极管驱动时,用“低电平”占空比(没有推挽输出的51推荐用PNP)。
(3)选用 脉冲型 红外接收头。 基本上是了解了
谢谢楼主./emotion/em020.gif 感谢楼主的分享!我非常喜欢你的思路
在这里提一个问题:按说STC的1T单片机的定时器默认是12T分频的,所以这个程序应该可以直接移植到STC的1T单片机上,但我把程序移植到了STC11F04上,用红外一体头接受没有信号,吧红外LED换成普通LED,能看到闪烁,和正常遥控器遥控一体头的信号看不出什么区别,因为手头上没有示波器,所以无法抓红外的波形。
求解,楼主能不能放个STC1T的红外发射程序,就这个在网上还比较稀缺,谢谢! mark,留名 回复【45楼】wangyz1997
-----------------------------------------------------------------------
因为那个发射程序的38K载波使用软件指令生成,所以上面的发射程序不兼容1T系列,
STC 1T系列换成下面的( 另:如果不喜欢用“goto”,就用“if(TF0)break;” )
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:38KHz载波发射 + 延时
入口:(是否发射载波,约 X (uS))
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void TT0(bit BT,uint16 X)
{
uint8 t;
TH0 = ((uint8 *)&X); //X高8位
TL0 = ((uint8 *)&X); //X低8位
TF0=0; //清0
TR0=1; //启动定时器0
if(BT) //38KHz载波,(低电平)占空比约 9:(9+28)【12M: 1t <=> 1/12(uS),38KHz周期26.316uS <=> 316t 】
{
while(1)
{
IR_Out = 0; // 4t
for(t=9;t;t--)if(TF0)goto TN; // 2t + (4t+4t)*x
if(TF0)goto TN; // 4t
IR_Out = 1; // 4t
for(t=28;t;t--)if(TF0)goto TN; // 2t + (4t+4t)*x
if(TF0)goto TN; // 4t
}
}
else while(!TF0);
TN:TR0 =0; //关闭定时器0
IR_Out =1; //38KHz载波停止后,发射端口常态=1
} mark 研读。。。 回复【47楼】BXAK
-----------------------------------------------------------------------
BXAK大侠,你太厉害了!让我这个新手佩服不已啊!
就是注释里的// 2t + (4t+4t)*x 是什么意思?软延时计算吗?求教! 回复【49楼】wangyz1997
-----------------------------------------------------------------------
业余爱好罢了,闲时就折腾折腾
是延时计算,
你可以用keil反汇编看代码 对照 指令执行时间表 计算,
也可以用keil断点调试直接查看指令执行时间(适合STC 12T系列),
也可以用TKStudio断点调试直接查看指令执行时间(适合STC 1T、12T系列)
STC 12T系列(传统51) STC 1T系列 指令执行时间查询:点击此处下载 ourdev_721603SOZVXC.pdf(文件大小:1.38M) (原文件名:STC 1T 12系列 指令执行时间查询.pdf) 谢谢!长见识了! 回复【楼主位】BXAK
-----------------------------------------------------------------------
mark mark 学习,谢谢分享。 能遥控电视吗?遥控电视需要改哪里?谢谢。 回复【55楼】chinasy519
-----------------------------------------------------------------------
是NEC编码格式(9ms高 + 4.5ms低 + 16位用户码 + 8位操作码正/反码)的就可以,换上你遥控器的用户码、操作码 回复【56楼】BXAK
回复【55楼】chinasy519
-----------------------------------------------------------------------
是nec编码格式(9ms高 + 4.5ms低 + 16位用户码 + 8位操作码正/反码)的就可以,换上你遥控器的用户码、操作码
-----------------------------------------------------------------------
谢谢!遥控电视实验成功。 强大,实物简单测试了一下,发射成功,1602有显示,用户码控制码可以控制电视。
还有个问题,就是如果我用STC11F104E 1T单片机,没有P0 P2口,如何设置用户码?
比如用户码固定为 0xcced ,如何改呢? 回复【58楼】b60885262
-----------------------------------------------------------------------
把
#defineUSER_H P2 //用户码高8位
#defineUSER_L P0 //用户码低8位
改成
#defineUSER_H 0xcc //用户码高8位
#defineUSER_L 0xed //用户码低8位
实物直接去掉1602显示得了(把"INC\LCD1602_6IO.h"去掉,有关于1602的函数也去掉),
加入掉电模式,按键中断唤醒,一副电池用一年都可以,再加个指示灯,
http://cache.amobbs.com/bbs_upload782111/files_45/ourdev_672303W3V3QD.jpg
(原文件名:20110819099.jpg) 回复【59楼】BXAK
-----------------------------------------------------------------------
谢谢指导,明天我试试!
楼主注意休息啊,呵呵! 回复【59楼】BXAK
-----------------------------------------------------------------------
刚看到你的图片,能不能把你的实物程序发给我 60885262@163.com
那么多按键,估计不好拿吧?我打算只用5个按键就可以了。常用的换台加减,音量加减,和静音键就好了。这样体积可以更小。 好思路学习一下。 顶一个!! mark一下。 过了一遍 感觉很好,下次仔细研究下 谢谢 这么多人都说好!那好吧,我也马克吧。 以前写的有缺陷啊,
【1】
像:
uint16cntStep; //步数计
uint16 即 unsigned int ,其实用 uint8 (即 unsigned char)就够了,这样省了些运算提高了速度
【2】
像:
{
TL0 = TH_L; //重赋值
TH0 = TH_H;
cntStep++; //步数累加
if(IR_BT==1)if(cntStep>300)IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按
IRb = IRa; //保存上次电位状态
IRa = IR; //保存当前电位状态
……
接收端的电平查询应该放在前面合理些:
{
TL0 = TH_L; //重赋值
TH0 = TH_H;
IRb = IRa; //保存上次电位状态
IRa = IR; //保存当前电位状态
cntStep++; //步数累加
if(IR_BT==1)if(cntStep>300)IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按
……
楼主给个联系方式 {:biggrin:}{:biggrin:} 谢谢哦。是个好贴,学习了 {:smile:}强悍! 强悍啦,不错,顶顶 请教,为何要这个样
#defineTH_H ((65536-Step*(CPU_Fosc/300)/40000)/256)//定时器高8位基准赋值
#defineTH_L ((65536-Step*(CPU_Fosc/300)/40000)%256)//定时器低8位基准赋值
进行/300 再/40000?????????????? 有空试试!!! 值得学习啊。。。 这么好的资料,对新手来说太好了。谢谢! 本帖最后由 BXAK 于 2012-4-11 15:10 编辑
hpdell 发表于 2012-4-11 12:38 static/image/common/back.gif
请教,为何要这个样
#defineTH_H ((65536-Step*(CPU_Fosc/300)/40000)/256)//定时器高8位基准赋值 ...
编译软件是Keil,其它的不知有没有这种情况
其实原是:Step*CPU_Fosc/120000000 ①
为什么用:Step*(CPU_Fosc/300)/40000 ②
unsigned long范围: 0 ~ 4,294,967,295
如果Step = 400,4294967295/400 = 10737418.2375,
当 CPU_Fosc < 10,737,418 时,用①没问题,因为 Step*CPU_Fosc < 4,294,967,295;
当 CPU_Fosc > 10,737,418 时,用①就会有溢出问题,因为 Step*CPU_Fosc > 4,294,967,295;
②缩小300倍后计算, Step*(CPU_Fosc/300) / (120000000/300) →Step*(CPU_Fosc/300)/40000
保证CPU_Fosc < 40,000,000 范围内,Step*(CPU_Fosc/300)< 4,294,967,295,这样就不会有溢出问题
不过用 Step*(CPU_Fosc/100)/120000 应该够了,具体的,你可以编译后反汇编看看代码就知道其中的差异了 好人!赞! 本帖最后由 hpdell 于 2012-4-11 16:45 编辑
BXAK 发表于 2012-4-11 15:09 static/image/common/back.gif
编译软件是Keil,其它的不知有没有这种情况
其实原是:Step*CPU_Fosc/120000000 ①
谢谢你的回复,现在有些明白了。兄台考虑的很全面啦。 学习了 楼主的态度很值得学习.
软件也很棒. 学习收藏,谢谢楼主 效果不错哦 不错啊!有空试一下 你好:BXAK
红外接收程序在51单片机试过效果很好,可是我把这思路移植到AVR上怎么就不行了,帮忙参考。
#include<avr\io.h>
#include<avr\interrupt.h>
#include<util\delay.h>
/*==================================
遥控代码处理
==================================*/
unsigned char i,TEST_flag;
unsigned int TEST=0;
#defineUSER_H 0x02 //用户码高8位
#defineUSER_L 0xfd //用户码低8位
#defineCheck_EN 0 //是否要校验16位用户码:不校验填0,校验则填1
#defineCA_S 8 //长按时间设置,单位:108mS(即 108mS整数倍,10倍以上为宜)
#defineIR (PINB&_BV(PINB7)) //PB4//红外线接口(任意引脚)
#defineStep 400 //红外采样步长:400us
//#defineTH_H (65536-(8000000*0.0004)/256)//定时器高8位基准赋值
//#defineTH_L (65536-(8000000*0.0004)%256)//定时器低8位基准赋值
unsigned char IR_BT; //解码效果返回:0无效,1有效,2短按,3长按
unsigned char NEC; //解码存放:16位用户码、操作码正反码
unsigned char cntCA; //长按计数
unsigned int cntStep; //步数计
unsigned charIRa , IRb; //电位状态保存
unsigned charIRsync ; //同步标志
unsigned char BitN; //位码装载数
/*┈┈┈┈┈┈┈┈┈┈ 基准 ┈┈┈┈┈┈┈┈┈┈┈*/
#define Boot_Limit (((9000+4500) +2000)/Step) //引导码周期上限
#define Boot_Lower (((9000+4500) -2000)/Step) //引导码周期下限
#define Bit1_Limit ((2250 +800)/Step) //“1”周期上限800
#define Bit0_Limit ((1125 +400)/Step) //“0”周期上限400
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线NEC周期采样解码法(定时中断,下降沿查询周期时间)
全局变量:IR_BT = 0无效
1有效,待继续判断长、短按(如不需要判断长、短按,则直接使用)
2短按
3长按
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控短按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ0(void)
{
switch(NEC)
{
case 0x0D: //音量加
break;
case 0x0E: //音量减
break;
case 0x0A: //上曲
break;
case 0x15: //下曲
break;
case 0x0C: PORTC=0XFF; //播放
break;
case 0x66: //单曲
break;
case 0x12: //循环
break;
case 0x09: //电源
break;
default:
break;
}
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:遥控长按处理
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void KZ1(void)
{
switch(NEC)
{
case 0x0D: //音量加
break;
case 0x0E: //音量减
break;
case 0x0A: //上曲
break;
case 0x15: //下曲
break;
case 0x0C: PORTC=0XFF; //播放
break;
case 0x66: //单曲
break;
case 0x12: //循环
break;
case 0x09: //电源
break;
default:
break;
}
}
/*=================================
红外遥控译码
=================================*/
void IR_NEC(void)
{
cntStep++; //步数采样
if(IR_BT==1)if(cntStep>300)
{
IR_BT=2; //解码有效后,如果无长按,120ms(400us×300)后默认短按
}
IRb = IRa; //上次电位状态
IRa = IR; //当前电位状态
if((IRb==1) &&(IRa==0)) //是否下降沿(上次高,当前低)
{
if(cntStep > Boot_Limit) //超过同步时间?38.75
{
if(IR_BT==1)if(++cntCA>CA_S)
{
IR_BT=3; //解码有效后,继续按住遥控>CA_S即长按
}
IRsync=0; //同步位清0
}
else if(cntStep > Boot_Lower)//28.75
{
IRsync=1; //同步位置1,装载位码数
BitN=32;
}
else if(IRsync) //如果已同步
{
if(cntStep >Bit1_Limit)//7.6
{if(TEST_flag==0){TEST=2;TEST_flag=1;}
IRsync=0;
}
else
{
NEC >>= 1;
if(cntStep >Bit0_Limit)//3.8
{
NEC |= 0x80; //“0”与“1”
}
if(--BitN == 0)
{
IRsync = 0; //同步位清0
#if (Check_EN == 1)
if((NEC==USER_H)&&(NEC==USER_L)&&(NEC==~NEC)) //校验16位用户码、操作码正反码
{
IR_BT=1; //解码有效,接下来判断:短按?长按?
cntCA=0;
}
#else
if(NEC==~NEC)
{
IR_BT=1; //校验操作码正反码
cntCA=0;
}
#endif
}
else if((BitN & 0x07)== 0) //NEC每装满8位,移动保存一次(即 BitN%8 == 0)
{
NEC=NEC;
NEC=NEC;
NEC=NEC;
}
}
}
cntStep = 0; //步数计清0
}
}
/*==================================
主程序
===================================*/
int main()
{
DDRC=0XFF;
cli(); //关总中断
TCCR1B= 0x00;//停止定时器
OSCCAL=0XBF;//内部时钟校准补尝
TCNT1H =0XF3; //225;//初始值
TCNT1L=0X80; //225;//初始值
TIMSK1 |= 0x01;//中断允许
TCCR1B= 0x01;//03启动定时器
DDRB&=~_BV(DDB7);//PIN7输入
PORTB&=~_BV(PORTB7);//PORTB7配置上拉电阻
sei(); //开总中断
for(;;)
{
if((IR_BT==2)||(IR_BT==3))
{
if(IR_BT==2)
{
KZ0(); //短按处理
}
else
{
KZ1(); //长按处理
}
IR_BT=0;
}
/***********************************
for(;TEST>0;)
{
PORTC=0XFF;
_delay_ms(1000); 调试用计算步
PORTC=0X00;
_delay_ms(1000);
TEST--;
if(TEST==0){TEST_flag=0;}
}
}
return 0;
}
************************/
/*==================================
定时中断
===================================*/
ISR(TIMER1_OVF_vect)
{
TCNT1H=0XF3;//定时400US
TCNT1L=0X80;
IR_NEC();
PORTC^=_BV(PORTC1);
} 本帖最后由 BXAK 于 2012-5-4 22:08 编辑
xyr 发表于 2012-5-4 18:25 static/image/common/back.gif
你好:BXAK
红外接收程序在51单片机试过效果很好,可是我把这思路移植到AVR上怎么就不行了,帮 ...
才开始学AVR,暂时帮不了,
有条件的话,你看看AVR定时器是否每400us定时中断,是否正确捕捉接收脚的电平……
补充:
我觉得问题可能是
51有可位寻址区,所以用 位 来保存接收脚状态很方便,
bit IRa,IRb;
if((IRb==1) &&(IRa==0))下降沿判断没问题
但在AVR中用unsigned char IRa,IRb保存电位状态,
if((IRb==1) &&(IRa==0))应该不适合了 用位域定义过位 IRb IRa if(IRb&&!IRa)不行,
在这测试(PORTC^=_BV(PORTC1);)频率为1.24K符正常的。 xyr 发表于 2012-5-5 09:08 static/image/common/back.gif
用位域定义过位 IRb IRa if(IRb&&!IRa)不行,
在这测试(PORTC^=_BV(PORTC1);)频率为1.24K符正常的。 ...
这样改应该可以了,试试:
if( (IRb!=0) && (IRa==0) ) //是否下降沿(上次高,当前低)
不错啊,这么强! 不错不错!!!!!!!正在学习中 谢谢分享 本帖最后由 TIANKUANG52 于 2012-5-6 20:34 编辑
其中cnt的时间是怎么算的,9ms的cnt是多久,怎么算的,X1到X4的值怎么算的
我不懂,教下,谢谢
void exint0() interrupt 0
{
uint cnt;
uchar i;
EX0 = 0;
cnt = 0;
while(!IR) cnt++; //记录引导码时间
if(cnt < 1000){EX0=1;return;} //9ms的计数值(12MHz:1000< cnt <1500)
cnt = 0;
while(IR) if(cnt++ > 400){EX0=1;return;} //防卡死,超时保护(12MHz: > 300)
if(cnt < 200){EX0=1;return;} //(12MHz不分频: <260)
for(i=0; i<32; i++) //读取32位位码
{
cnt = 0;
while(!IR);
while(IR) if(cnt++ > 200){EX0=1;return;}//超时保护(12MHz:>=200)
N >>= 1;
if(cnt>60) N |= 0x80; //0和1的计数界线(12MHz:< 109)
}
if(N == ~N && N == ~N) //校验识别码,操作码
{
X1 = N/16;
X2 = N%16;
X3 = N/16;
X4 = N%16;
}
EX0 = 1;
}
本帖最后由 xyr 于 2012-5-7 14:04 编辑
BXAK 发表于 2012-5-5 09:15 static/image/common/back.gif
这样改应该可以了,试试:
if( (IRb!=0) && (IRa==0) ) //是否下降沿(上次高,当前 ...
不是软件的问题,我由原来AVR Studio 4改为ICC编译就好了,具体AVR Studio 4为什么不行我还未查清,
谢谢! 本帖最后由 BXAK 于 2012-5-7 21:31 编辑
TIANKUANG52 发表于 2012-5-6 20:29 static/image/common/back.gif
其中cnt的时间是怎么算的,9ms的cnt是多久,怎么算的,X1到X4的值怎么算的
我不懂,教下,谢谢
void exint0 ...
好像是以前写的,但此法解码区间占用掉CPU,所以实用性有限。
原版的已经没了,修改版倒的还在,也贴出来算了,至于9ms的cnt是多久,怎么算,这涉及汇编,如果你不懂汇编……
while(!IR)cnt++;//查询低电平时间 【1T系列指令周期】【12T系列指令周期】
C:0x000F JB IR(0xB0.2),C:0019 4 t 2T
C:0x0012 INC R7 3 t 1T
C:0x0013 CJNE R7,#0x00,C:0017 4 t 2T
C:0x0016 INC R6 3 t 1T
C:0x0017 SJMP C:000F 3 t 2T
【STC 1T系列】
time*12t = 14t*cnt + 3t*(cnt/256)
time*12 = 14*cnt + 3*cnt/256 = (14 + 3/256)*cnt
cnt= (time*12)/(14 + 3/256)≈7708 (12MHz @ 9000us,time=9000)
【STC 12T系列】
time*1T = 7T*cnt + 1T*(cnt/256)
time = 7*cnt + cnt/256 = (7+ 1/256)*cnt
cnt= time/(7 + 1/256)≈1285 (12MHz @ 9000us,time=9000)
修改版的已经将计数值公式化,直接使用就行了
以下计算公式只适合STC 1T系列 以下计算公式适合STC 12T系列 以及 其它传统51指令速度的MCU/*┈┈┈┈┈┈ 红外线NEC解码函数 ┈┈┈┈┈┈┈
检测接收端高电平时,采用超时保护,防信号终断卡死。
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
#definems10 8571 //ms10 = 10000*CPU工频/14000000 //ms10 = 10000*CPU工频/12000000/7
#definems8 6857 //ms8=8000*CPU工频/14000000 //ms8=8000*CPU工频/12000000/7
#definems5 3000 //ms5=5000*CPU工频/20000000 //ms5=5000*CPU工频/12000000/12
#definems4 2400 //ms4=4000*CPU工频/20000000 //ms4=4000*CPU工频/12000000/12
#definems2 1200 //ms2=2000*CPU工频/20000000 //ms2=2000*CPU工频/12000000/12
#definems1 600 //ms1=1000*CPU工频/20000000 //ms1=1000*CPU工频/12000000/12
//以上计算公式只适合STC 1T系列 //以上计算公式适合STC 12T系列 以及 其它传统51指令速度的MCU
void IR_NEC(void)
{
uintcnt=0;
uchar i;
while(!IR)cnt++;
if((cnt < ms8)||(cnt > ms10))return; //<8ms || >10ms,则返回
cnt = 0;
while(IR)if(++cnt > ms5)return; //超时保护,>5ms,则返回
if(cnt < ms4)return; //<4ms,则返回
for(i=32;i;) //读取32编码
{
cnt = 0;
while(!IR);
while(IR)if(++cnt > ms2)return; //超时保护,>2ms,则返回
NEC >>= 1;
if(cnt > ms1)NEC |= 0x80; //1ms,“0”与“1”
if((--i)&&(i%8==0)){ NEC=NEC; NEC=NEC; NEC=NEC; }
}
if((NEC==YHM_H)&&(NEC==YHM_L)&&(NEC==~NEC))//校验16位用户码,操作码正反码
{IR_BT = 1; } //解码有效标志
} 好东西啊这下省事了 本帖最后由 TIANKUANG52 于 2012-5-7 21:47 编辑
BXAK 发表于 2012-5-7 11:38 static/image/common/back.gif
好像是以前写的,但此法解码区间占用掉CPU,所以实用性有限。
原版的已经没了,修改版倒的还在,也贴出来 ...
我用的是stc89c52的单片机,能不能麻烦你帮我算下cnt的时间,晶振是12m的,不是一个指令是1us的吗?
那个9ms的cnt和4ms等一些时间的cnt怎么算,汇编我懂一些,谢了
我用的是stc89c52的用的是下面的公式吗?我上面的9ms的cnt就是1285左右也就是在1000到1500之间
那 if(cnt < 200){EX0=1;return;} //(12MHz不分频: <260) 是4.5ms的判断吗?
按公式4ms的cnt不是571左右吗怎么小于200是什么意思
【STC 12T系列】
time*1T = 7T*cnt + 1T*(cnt/256)
time = 7*cnt + cnt/256 = (7+ 1/256)*cnt
cnt= time/(7 + 1/256)≈1285 (12MHz @ 9000us,time=9000)
TIANKUANG52 发表于 2012-5-7 20:46 static/image/common/back.gif
我用的是stc89c52的单片机,能不能麻烦你帮我算下cnt的时间,晶振是12m的,不是一个指令是1us的吗?
那个 ...
晕,回复够详细,看的确不认真,
不是已经列成公式帖出来了,直接套用公式计算就可以了
看看上面给你的回复:
……
//以上计算公式只适合STC 1T系列 //以上计算公式适合STC 12T系列 以及 其它传统51指令速度的MCU
……
BXAK 发表于 2012-5-7 21:26 static/image/common/back.gif
晕,回复够详细,看的确不认真,
不是已经列成公式帖出来了,直接套用公式计算就可以了
我用的是stc89c52的用的是下面的公式。我上面的9ms的cnt就是1285左右也就是在1000到1500之间
那 if(cnt < 200){EX0=1;return;} //(12MHz不分频: <260) 是4.5ms的判断吗?
按公式4ms的cnt不是571左右吗怎么小于200是什么意思
while(IR) if(cnt++ > 400){EX0=1;return;} //防卡死,超时保护(12MHz: > 300)
这是怎么防卡死的
【STC 12T系列】
time*1T = 7T*cnt + 1T*(cnt/256)
time = 7*cnt + cnt/256 = (7+ 1/256)*cnt
cnt= time/(7 + 1/256)≈1285 (12MHz @ 9000us,time=9000)
谢了
本帖最后由 BXAK 于 2012-5-8 16:25 编辑
TIANKUANG52 发表于 2012-5-8 14:12 static/image/common/back.gif
我用的是stc89c52的用的是下面的公式。我上面的9ms的cnt就是1285左右也就是在1000到1500之间
那 if(cnt < ...
原版是 while(IR) if(cnt++ > ……
修改版是 while(IR)if(++cnt > ……
“cnt++” “++cnt ”指令执行时间的计算方式是不同的
用修改版的吧,修改版的都有公式,
原版的没了,也懒得重新反汇编计算
另:
也不一定要反汇编计算那么麻烦,
你可以先不加限制,按一下遥控将每个时间段cnt计数值保存下来后发送串口显示,就知道当前晶振下9ms、4ms……各时间段的cnt计数值