STC12CAD单片机万能学习遥控器记录波形方式.docx
《STC12CAD单片机万能学习遥控器记录波形方式.docx》由会员分享,可在线阅读,更多相关《STC12CAD单片机万能学习遥控器记录波形方式.docx(14页珍藏版)》请在冰豆网上搜索。
STC12CAD单片机万能学习遥控器记录波形方式
//中断函数注意养成指定寄存器组的习惯
//不同优先级的中断程序绝对不能使用同一组寄存器
/*****编程时防止中断把寄存器中的数据改变的解决方法是给中断指定寄存器,同优先级的使用同一组没事。
1、写中断程序一定要用using语句指定寄存器组。
第1、2、3组都可以,不能是0.
2、51单片机的中断有两个优先级。
一个中断不会打断另一个相同优先级的中断。
这样相同级别中断可以使用同一个组。
比如:
低优先级的中断函数都用using1,高优先级的中断都用using2。
这样不会冲突。
下面是一个正常的例子:
C程序:
voidint0()interrupt0using1
默认5个中断时同级的,不会冲突,但是最好养成好习惯
不指定中断要使用的寄存器,每次都要入栈保护数据,中断完还要出栈,代码会增加32字节
********************************************************************/
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
//少占鱼制作河北正定欢迎您长沙航空职业技术学院2010年QQ:
411656434
//定义Flash操作等待时间及允许IAP/ISP/EEPROM操作的常数
//#defineENABLE_ISP0x80//系统工作时钟<30MHz时,对ISP_CONTR寄存器设置此值
//#defineENABLE_ISP0x81//系统工作时钟<24MHz时,对ISP_CONTR寄存器设置此值
#defineENABLE_ISP0x82//系统工作时钟<20MHz时,对ISP_CONTR寄存器设置此值
//#defineENABLE_ISP0x83//系统工作时钟<12MHz时,对ISP_CONTR寄存器设置此值
//#defineENABLE_ISP0x84//系统工作时钟<6MHz时,对ISP_CONTR寄存器设置此值
//#defineENABLE_ISP0x85//系统工作时钟<3MHz时,对ISP_CONTR寄存器设置此值
//#defineENABLE_ISP0x86//系统工作时钟<2MHz时,对ISP_CONTR寄存器设置此值
//#defineENABLE_ISP0x87//系统工作时钟<1MHz时,对ISP_CONTR寄存器设置此值
unionunion_temp16
{
uintun_temp16;
ucharun_temp8[2];
}my_unTemp16;
ucharByte_Read(uintadd);//读一字节,调用前需打开IAP功能
voidByte_Program(uintadd,ucharch);//字节编程,调用前需打开IAP功能
voidSector_Erase(uintadd);//擦除扇区
voidIAP_Disable();//关闭IAP功能
/******************************************************************/
sbitJIESHOU=P1^0;//接收指示灯
sbitFASHE=P1^1;//发射指示灯
sbitKEY=P3^5;
sbitcin=P3^2;//接收端
sbitcontrl=P3^0;//发射控制端
sbitkhz=P3^1;//38KHZ产生,由T1设置
/****************************************************************/
voiddelayms(uint);
voidADC();
voidInitADC();
voidinit1();
voidinit2();
voidfashe();
voidjieshou();
voiddelayus(uchari);
voidled(ucharx);
/******************************************************/
uintvoltage;
bitreceive=0;//接收标志
bitflag=0;//低电平记录完成标志
bitend=0;
bitfinish=1;
ucharxdataa[111]={121,1,3,4,44,55,24,156,35};//间接寻址的高128RAM,内部256RAM高128只能间接寻址
uintj=0;
uintzu=0,addr=0;//扇区地址
uchark;//按键代号
ucharm=0;//写EEPROM时用来移动数组的
ucharb[6];//用来存储每一组数据的总字节数
/**********************************************************************/
/****************************************************************/
voidmain()
{
//默认STC12是1T运行模式。
时钟没有分频,为了兼容8051,定时器可以分频12.但是如果时钟也分频,就会影响他了。
AUXR=0x00;//定时器T0T1,12分频。
兼容8051
delayus(5);
delayms(1000);
InitADC();//这里对其他用到P1口的地方有影响,尽量放前面
contrl=0;//关闭38K输出
KEY=1;
delayms(500);
Sector_Erase(0x0000);//擦除扇区1
delayms(500);
Sector_Erase(0x200);
delayms(500);
Sector_Erase(0x400);
delayms(500);
Sector_Erase(0x600);
delayms(500);
Sector_Erase(0x800);
delayms(500);
Sector_Erase(0xa00);
delayms(500);
Sector_Erase(0xc00);//擦除扇区7
delayms(500);
addr=0xc00;
FASHE=0;
delayms(1000);
JIESHOU=0;
delayms(4900);
for(j=6;j>0;j--)//j是数据总长度,如此判断,不会存储多余的空位
{Byte_Program(addr,a
);//从本组数据对应扇区首地址开始写EEPROM
m++;//数组下移
addr++;//地址下移
delayms
(1);}//forend
m=Byte_Read(0xc00);
/************************************************/
//C编程技巧:
判断恒等于==时,把常量写前面可以防止写成赋值语句
//把“==”写成“=”,会报错,常量不能赋值
/************************************************/
if(121==m)
{
JIESHOU=1;
delayms(6000);
}
m=0;//下面还要用,所以清0
addr=0;
zu=0;
delayms(500);
Sector_Erase(0x0000);//擦除扇区1
delayms(500);
Sector_Erase(0x200);
delayms(500);
Sector_Erase(0x400);
delayms(500);
Sector_Erase(0x600);
delayms(500);
Sector_Erase(0x800);
delayms(500);
Sector_Erase(0xa00);
delayms(500);
Sector_Erase(0xc00);//擦除扇区7
delayms(500);
FASHE=0;
delayms(1000);
JIESHOU=0;
delayms(1000);
KEY=1;
EX1=1;//开外部中断1
IT1=1;//外部中断1边沿触发,不然按住的时候一直中断
EA=1;
delayms(1000);
//等待按键时两个灯灭
while
(1)
{
if(receive)//外部按键中断1,正式进入接收函数
{
receive=0;//只用一次,先清0,免得忘了
FASHE=0;//发射指示灯
JIESHOU=1;//接收指示灯
delayms(3000);
delayms
(2);
FASHE=1;
delayms(3000);
jieshou();//接收函数是T0定时开始后计数满溢出跳出的
}
if(!
KEY)//KEY为0时进入发射模式
{
JIESHOU=0;
delayms(200);
FASHE=1;
delayms(200);
KEY=1;
fashe();
}
}
}
/********************************************************************/
//接收函数初始化
voidinit1()//接收初始化
{
finish=1;
flag=0;
end=0;
j=0;
EA=0;//因为下面要写EEPROM,必须关闭EA
TMOD=0x01;//T0方式1
TH0=0x00;
TL0=0x00;
TR0=0;
EX0=1;
EX1=0;//关闭外部中断1按键,一旦进入接收函数,就关闭按键防止干扰
ET0=1;//开T0中断
IT0=1;//外部中断0边沿触发
EA=0;
}
//
/************************************************************************/
//红外接收子程序
voidjieshou()
{
delayms(3000);
flag=0;
finish=1;
JIESHOU=1;//接收灯亮才可以开始按遥控
FASHE=0;
init1();//接收初始化
EX0=1;
EA=1;//开中断
cin=1;
//接收灯亮等待接收
while(finish)//退出接收循环检测
{
while(flag)//T0已启动标志,用完记得清0,由外部中断0启动,初次启动检测
{//第一次低电平测宽已经开始
while(!
cin);//等待高电平到来,T0中断不会在这里发生,因为低电平宽度不会有65MS这么长
{
TR0=0;
_nop_();_nop_();_nop_();_nop_();
a[j]=TH0;//低电平宽度先存高8位数据
j++;
_nop_();_nop_();_nop_();_nop_();
a[j]=TL0;//存储的是低电平宽度
j++;//数组下移
_nop_();_nop_();_nop_();_nop_();
TH0=0;//重装T0
TL0=9;//补偿前面消耗的时间
_nop_();_nop_();_nop_();_nop_();
TR0=1;//重新启动T0,计时高电平
}
//高电平测宽开始
while(cin&&flag);//等待cin低电平到来。
T0中断就是在这里等待的时候发生的,因为最后一个电平必然是高电平(无信号就是高)
_nop_();_nop_();_nop_();_nop_();
//flag=1表示T0还没中断,还是接收有效
if(flag)//flag为1才表示计时有效,flag=0表示最后高电平很长结束了
{//加个flag才能退出这个等待
TR0=0;
a[j]=TH0;//先存高8位数据
_nop_();_nop_();_nop_();_nop_();
j++;
a[j]=TL0;//存储的是低电平段
_nop_();_nop_();_nop_();_nop_();
j++;//数组下移
TH0=0;//重装T0
TL0=0;
_nop_();_nop_();_nop_();_nop_();
TR0=1;//重新启动T0,计时低电平
}
}
//判断是否退出接收
if(end)
{
receive=0;//用完接收启动标志要清0
flag=0;
FASHE=1;
delayms(122);
JIESHOU=1;//亮两个灯表示接收成功
j=0;
finish=0;
end=0;
}
}
//接收完亮两个灯
EA=0;
finish=1;
EX1=1;//开外部按键中断1
IT1=1;
EA=1;
}
/***********************************************/
/********************************************************************/
//发射函数初始化
voidinit2()//发射初始化
{
contrl=0;//关闭发射端,由于它与38K输出端并联,所以拉低不输出
TMOD=0x21;//T0方式1,T1方式2
TH1=-(13%256);//定时13us翻转一次,即38KHZ(26us)
TL1=-(13%256);
ET1=1;//T1中断
ET0=0;//禁止T0中断
TR0=0;
TR1=1;//T1启动38K载波开始
EA=1;
}
//红外发射子程序
voidfashe()//发射程序里没有安排推出操作,所以只有重启才能重新进入选择模式
{
while
(1)
{
ADC();
switch(k)
{
case1:
for(j=0;j
case2:
for(j=0;j
case3:
for(j=0;j
case4:
for(j=0;j
case5:
for(j=0;j
case6:
for(j=0;j
default:
k=0;break;
}
TR0=0;//发射完毕要关闭38K载波
TR1=0;
}
}
//发射高电平。
接收端就是低电平。
而载波就是高电平有38KHZ载波,二极管反复38K亮灭。
低电平什么也没有,二极管不通。
/**********************************************************************/
voidled(ucharx)
{
j=0;
x=x/2;//2个数组是一段电平,而且肯定是偶数个数组2*N是偶数嘛
while(x)
{
_nop_();_nop_();_nop_();_nop_();//空操作不废时序。
就是没指令
//因为共有2N个数据。
第一个肯定是发射的高电平。
因为接收端首位肯定是0.
//红外二极管亮表示发射高电平。
接收的就是低电平。
高电平就是有38K载波,低电平什么也没有,二极管不通。
//所以,高电平发射38K波形,低电平不发射。
时间由contrl控制
/**********数组里存的是要定时的时间。
这里不能用256-a[j],因为对没有存入数据的EEPROM,
读出来数据都是1,即a[j]=0xff,用减法会定时65MS的,用负号就不会,已启动他就会溢出中断
************************/
TH0=-a[j];//数组里存的是要定时的时间。
这里不能用256-a[j]
j++;
TL0=-a[j];
j++;
contrl=!
contrl;//首次发射肯定是高电平,所以要翻转。
每个电平发完到第二个开始前多了一次翻转。
TR0=1;//
while(!
TF0);//等待T0溢出,因为没有采用T0中断
_nop_();_nop_();_nop_();_nop_();//加空操作是为了防止在执行指令的时候发生中断,破坏38Khz频率。
_nop_();_nop_();_nop_();_nop_();//空操作不废时序。
就是没指令
x--;
TR0=0;//必须在清0溢出位TF之前关闭定时器。
不然会自动启动的
_nop_();_nop_();_nop_();_nop_();//空操作不废时序。
就是没指令
TF0=0;//溢出位清0
}
//发射完毕,T0关闭
EA=0;
TR1=0;
TR0=0;
}
//
/***************************************************************/
/*****编程时防止中断把寄存器中的数据改变的解决方法是给中断指定寄存器,同优先级的使用同一组没事。
1、写中断程序一定要用using语句指定寄存器组。
第1、2、3组都可以,不能是0.
2、51单片机的中断有两个优先级。
一个中断不会打断另一个相同优先级的中断。
这样相同级别中断可以使用同一个组。
比如:
低优先级的中断函数都用using1,高优先级的中断都用using2。
这样不会冲突。
下面是一个正常的例子:
C程序:
voidint0()interrupt0using1
默认5个中断时同级的,不会冲突,但是最好养成好习惯
不指定中断要使用的寄存器,每次都要入栈保护数据,中断完还要出栈,代码会增加32字节
********************************************************************/
//中断函数要指定使用那组寄存器,使用同一组时可能会破坏了上次寄存器中的数据
//同一优先级的中断可以使用同一组寄存器
voidtime0()interrupt1using1//定时器0中断
{
EA=0;//有65MS以上了,表示接收完毕
EX0=0;
EX1=0;
ET0=0;
FASHE=0;
delayms(200);
JIESHOU=0;//接收指示灯
delayms(1000);
b[zu/0x200]=j;//j是从0开始的,最后一次电平存完j自加1了,总长度正好是当前值
addr=zu;//因为下面zu值还要用,所以下面不直接用ZU
//zu是每个存储空间的起始地址
m=0;
for(;j>0;j--)//j是数据总长度,如此判断,不会存储多余的空位
{Byte_Program(addr,a
);//从本组数据对应扇区首地址开始写EEPROM
m++;//数组下移
addr++;//地址下移
delayms
(1);}//forend
if(zu<0xa00)//第一组代码完毕后,转到第二组,每组都是200个空间
zu+=0x200;//测完一组,扇区地址指向下一个扇区
else
{zu=0x000;}//超过6组代码,内存重新指向第1组
flag=0;
receive=0;//用完接收启动标志要清0
end=1;//退出接收函数最外层循环
}
/******************************************************/
//发射频率38khz由T1产生
voidtime1()interrupt3using1//定时器1中断,因为默认是同优先级,所以可以使用同一组寄存器
{
contrl=!
contrl;//38K翻转产生
}
/******************************************************/
//外部中断存储高电平长度
voidinterint0()interrupt0using1//外部中断0
{
if(0==flag)//flag=0表示是首次接收到脉冲
{
TH0=0;
TL0=10;//前面延时函数消耗的时间补上
TR0=1;
EX0=0;//关闭外部中断0,以后的计数都在接收函数里
flag=1;//表示启动T0
}
}
//
/************************************************************/
//外部按键中断1
voidinterint1()interrupt2using1//外部中断1
{
receive=1;
delayms(122);//等过抖动时间
EA=0;
}
/******************************************************/
//AD转换初始化----打开ADC电源
voidInitADC()
{
P1=0xff;//这里对其他用到P1口的地方有影响
ADC_CONTR|=0x80;
delayms(30);
//这两个寄存器用来设置P1口四种状态,每一位对应一个P1引脚,按状态组合操作
P1M0=0x08;//这两个寄存器用来设置P1口四种状态,每一位对应一个P1引脚,按状态组合操作
P1M1=0x08;//设置P1.3做AD
}
/******************************************************************/
//AD转换程序
voidADC()
{
ADC_DATA=0;//清除结果
ADC_CONTR=0x60;//转换速度设置0x60最快速度
ADC_CONTR=0xE0;//1110,0000清ADC_FLA