新闻中心

EEPW首页>嵌入式系统>设计应用> STC12C2052AD单片机做的红外学习遥控器

STC12C2052AD单片机做的红外学习遥控器

作者: 时间:2012-08-22 来源:网络 收藏

点击浏览下一页

// 中断函数注意养成指定寄存器组的习惯//不同优先级的中断程序绝对不能使用同一组寄存器/*****编程时防止中断把寄存器中的数据改变的解决方法是给中断指定寄存器,同优先级的使用同一组没事。1、写中断程序一定要用using语句指定寄存器组。第1、2、3组都可以,不能是0.2、51单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。 这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,高优先级的中断都用 using 2 。这样不会冲突。 下面是一个正常的例子: C程序: void int0() interrupt 0 using 1默认5个中断时同级的,不会冲突,但是最好养成好习惯不指定中断要使用的寄存器,每次都要入栈保护数据,中断完还要出栈,代码会增加32字节完整代码下载:http://www.51hei.com/f/hwxx52.rar********************************************************************/#include stc12c2052ad.h>#include intrins.h>#define uchar unsigned char#define uint unsigned int//少占鱼制作 河北正定欢迎您 长沙航空职业技术学院 2010 年QQ:41165643////定义Flash 操作等待时间及允许IAP/ISP/EEPROM 操作的常数//#define ENABLE_ISP 0x80 //系统工作时钟30MHz 时,对ISP_CONTR 寄存器设置此值 //#define ENABLE_ISP 0x81 //系统工作时钟24MHz 时,对ISP_CONTR 寄存器设置此值 #define ENABLE_ISP 0x82 //系统工作时钟20MHz 时,对ISP_CONTR 寄存器设置此值 //#define ENABLE_ISP 0x83 //系统工作时钟12MHz 时,对ISP_CONTR 寄存器设置此值 //#define ENABLE_ISP 0x84 //系统工作时钟6MHz 时,对ISP_CONTR 寄存器设置此值 //#define ENABLE_ISP 0x85 //系统工作时钟3MHz 时,对ISP_CONTR 寄存器设置此值 //#define ENABLE_ISP 0x86 //系统工作时钟2MHz 时,对ISP_CONTR 寄存器设置此值 //#define ENABLE_ISP 0x87 //系统工作时钟1MHz 时,对ISP_CONTR 寄存器设置此值 union union_temp16{uint un_temp16;uchar un_temp8[2];}my_unTemp16;uchar Byte_Read(uint add); //读一字节,调用前需打开IAP 功能 void Byte_Program(uint add, uchar ch); //字节编程,调用前需打开IAP 功能 void Sector_Erase(uint add); //擦除扇区 void IAP_Disable(); //关闭IAP 功能 sbit JIESHOU=P1^0; //接收指示灯sbit FASHE=P1^1; //发射指示灯sbit KEY=P3^5;sbit cin=P3^2; //接收端sbit contrl=P3^0;//发射控制端sbit khz=P3^1;//38KHZ产生 ,由T1设置/****************************************************************/void delayms(uint); void ADC();void InitADC();void init1();void init2();void fashe();void jieshou();void delayus(uchar i);void led(uchar x);/******************************************************/uint voltage;bit receive=0;//接收标志bit flag=0;//低电平记录完成标志 bit end=0;bit finish=1;uchar a[43]={121,1,3,4,44,55,24,156,35};
//间接寻址的高128RAM,内部256RAM高128只能间接寻址uchar j=0;uint zu=0,addr=0; //扇区地址uchar k;//按键代号uchar m=0;//写EEPROM时用来移动数组的 uchar b[6];//用来存储每一组数据的总字节数/**********************************************************************//****************************************************************/void main(){ delayus(5);delayms(1000);InitADC();//这里对其他用到P1口的地方有影响,尽量放前面contrl=0;//关闭38K输出KEY=1;Sector_Erase(0x0000);//擦除扇区 1Sector_Erase(0x200);Sector_Erase(0x400);Sector_Erase(0x600);Sector_Erase(0x800);Sector_Erase(0xa00);Sector_Erase(0xc00);//擦除扇区 7addr=0xc00;FASHE=0;JIESHOU=0;FASHE=1;delayms(4900);for(j=6;j>0;j--)//j是数据总长度,如此判断,不会存储多余的空位{ Byte_Program(addr,a??);//从本组数据对应扇区首地址开始写EEPROMm++; //数组下移addr++; //地址下移delayms(1);} //for endm=Byte_Read(0xc00);if(m==121){JIESHOU=1;delayms(6000);}m=0;//下面还要用,所以清0 addr=0;zu=0;FASHE=0;KEY=1;JIESHOU=0;delayms(1000);Sector_Erase(0x0000);//擦除扇区 1Sector_Erase(0x200);Sector_Erase(0x400);Sector_Erase(0x600);Sector_Erase(0x800);Sector_Erase(0xa00);Sector_Erase(0xc00);//擦除扇区 7EX1=1; //开外部中断1IT1=1;//外部中断1边沿触发,不然按住的时候一直中断EA=1;delayms(1);//等待按键时两个灯灭while(1){if(receive)//外部按键中断1,正式进入接收函数{FASHE=0;//发射指示灯JIESHOU=1; //接收指示灯delayms(3000);JIESHOU=0;delayms(2);FASHE=1;delayms(3000);FASHE=0;jieshou(); //接收函数是T0定时开始后计数满溢出跳出的 }if(!KEY) //KEY为0时进入发射模式{JIESHOU=0;delayms(200);FASHE=1;delayms(200);KEY=1;fashe();}} }/********************************************************************///接收函数初始化void init1()//接收初始化{finish=1;EA=0; //因为下面要写EEPROM,必须关闭EATMOD=0x01;//T0方式1TH0=0x00;TL0=0x00;TR0=0;EX0=1;EX1=0;//关闭外部中断1按键 ,一旦进入接收函数,就关闭按键防止干扰 ET0=1; //开T0中断IT0=1; //外部中断边沿触发EA=0;}///********************************************************************///发射函数初始化void init2()//发射初始化{contrl=0;//关闭发射端,由于它与38K输出端并联,所以拉低不输出TMOD=0x21;//T0方式1,外部INTO唤醒 ,T1方式2TH1=-(13/256);//定时13us翻转一次,即38KHZ (26us)TL1=-(13%256);ET1=1; //T1中断TR1=1;EA=1;}/************************************************************************/// 红外接收子程序void jieshou(){init1(); //接收初始化delayms(3000);flag=0;finish=1;JIESHOU=1;//接收灯亮才可以开始按遥控EA=1;//开中断 EX0=1;//接收灯亮等待接收while(finish) //退出接收循环检测{while(flag)//T0已启动标志,用完记得清0,由外部中断0启动,初次启动检测{//第一次低电平测宽已经开始while(!cin);//等待高电平到来,T0中断不会在这里发生,因为低电平宽度不会有65MS这么长{TR0=0; a[j]=TH0; //低电平宽度 先存高8位数据j++;a[j]=TL0;//存储的是低电平宽度j++;//数组下移 TH0=0;//重装T0TL0=9;//补偿前面消耗的时间TR0=1;//重新启动T0,计时高电平 }//高电平测宽开始while(cinflag);
//等待cin低电平到来。T0中断就是在这里等待的时候发生的,
因为最后一个电平必然是高电平(无信号就是高)//flag=1表示T0还没中断,还是接收有效if(flag)//flag为1才表示计时有效,flag=0表示最后高电平很长结束了{ //加个flag才能退出这个等待TR0=0;a[j]=TH0; //先存高8位数据j++;a[j]=TL0;//存储的是低电平段j++;//数组下移 TH0=0; //重装T0TL0=0;TR0=1;//重新启动T0,计时低电平}}//判断是否退出接收if(end){receive=0;//用完接收启动标志要清0flag=0;FASHE=1;delayms(122);JIESHOU=1;//亮两个灯表示接收成功 j=0;finish=0;end=0;}}//接收完亮两个灯finish=1;EX1=1;//开外部按键中断1EA=1;}
/***********************************************/// 红外发射子程序void fashe() //发射程序里没有安排推出操作,所以只有重启才能重新进入选择模式{while(1){ADC();switch(k){case 1:for(j=0jb[0];j++){a[j]=Byte_Read(j);}led(b[0]); break;case 2:for(j=0jb[1];j++){a[j]=Byte_Read(j+0x200);}led(b[1]);break;case 3:for(j=0jb[2];j++){a[j]=Byte_Read(j+0x400);}led(b[2]);break;case 4:for(j=0jb[3];j++){a[j]=Byte_Read(j+0x600);}led(b[3]);break;case 5:for(j=0jb[4];j++){a[j]=Byte_Read(j+0x800);}led(b[4]);break;case 6:for(j=0jb[5];j++){a[j]=Byte_Read(j+0xa00);}led(b[5]);break;default:k=0;break;}init2();//必须先读EEPROM再开定时器中断,不然会无法读EEPROM}}///**********************************************************************/void led(uchar x){j=0;x=x/2;//2个数组是一段电平,而且肯定是偶数个数组 2*N 是偶数嘛while(x){TH0=a[j];j++;TL0=a[j];j++;TR0=1;//while(!TF0);//等待T0溢出,因为没有采用T0中断 contrl=!contrl;x--;}}///***************************************************************//*编程时防止中断把寄存器中的数据改变的解决方法是给中断指定寄存器,
同优先级的使用同一组没事。1、写中断程序一定要用using语句指定寄存器组。第1、2、3组都可以,不能是0.2、51单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。 这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,
高优先级的中断都用 using 2 。这样不会冲突。 下面是一个正常的例子: C程序: void int0() interrupt 0 using 1默认5个中断时同级的,不会冲突,但是最好养成好习惯不指定中断要使用的寄存器,每次都要入栈保护数据,中断完还要出栈,代码会增加32字节********************************************************************///中断函数要指定使用那组寄存器,使用同一组时可能会破坏了上次寄存器中的数据 //同一优先级的中断可以使用同一组寄存器void time0() interrupt 1 using 1//定时器0中断{EA=0;EX0=0;EX1=0;ET0=0;FASHE=0;delayms(200);JIESHOU=0; //接收指示灯delayms(1000);//有65MS以上了,表示接收完毕b[zu/0x200]=j; //j是从0开始的,最后一次电平存完j自加1了,总长度正好是当前值 addr=zu;//因为下面zu值还要用,所以这里转移其数据 //zu是每个存储空间的起始地址m=0;for(;j>0;j--)//j是数据总长度,如此判断,不会存储多余的空位{ Byte_Program(addr,a??);//从本组数据对应扇区首地址开始写EEPROMm++; //数组下移addr++; //地址下移delayms(1);} //for endi(zu0xa00)//第一组代码完毕后,转到第二组,每组都是200个空间zu+=0x200; //测完一组 ,扇区地址指向下一个扇区else{ zu=0x000;} //超过6组代码,内存重新指向第1组flag=0;receive=0;//用完接收启动标志要清0end=1;//退出接收函数最外层循环}/******************************************************/// 发射频率38khz由T1产生void time1() interrupt 3 using 1 //定时器1中断 ,因为默认是同优先级,
所以可以使用同一组寄存器{khz=!khz;}/******************************************************/// 外部中断 存储高电平长度void interint0() interrupt 0 using 1 //外部中断0 {if (flag==0)//flag=0表示是首次接收到脉冲{TH0=0;TL0=10;//前面延时函数消耗的时间补上TR0=1;EX0=0;//关闭外部中断0,以后的计数都在接收函数里flag=1;//表示启动T0}}///************************************************************//******************************************************/// 外部按键中断 1void interint1() interrupt 2 using 1 //外部中断1 {receive=1;delayms(122);//等过抖动时间EA=0;}/******************************************************///AD转换初始化 ----打开ADC电源void InitADC(){P1=0xff;//这里对其他用到P1口的地方有影响ADC_CONTR|=0x80;delayms(30);//这两个寄存器用来设置 P1口四种状态,每一位对应一个P1引脚 ,按状态组合操作P1M0=0x08;//这两个寄存器用来设置 P1口四种状态,每一位对应一个P1引脚 ,按状态组合操作P1M1=0x08;//设置 P1.3做AD }/******************************************************************/// AD转换程序void ADC(){ ADC_DATA = 0; //清除结果ADC_CONTR = 0x60; //转换速度设置 0x60 最快速度ADC_CONTR = 0xE0; //1110,0000 清 ADC_FLAG,
ADC_START 位和低 3 位ADC_CONTR |= 0x03; //选择 A/D 当前通道 P1.3delayus(100); //使输入电压达到稳定ADC_CONTR |= 0x08; //0000,1000 令 ADCS = 1, 启动A/D转换, while(!(ADC_CONTR 0x10)); //!的优先级比高太多了 /***************这里while 不能改成while(ADC_CONTR 0x10==0) ;就错误了,因为优先级 ==比高 ,
所以要加括号while( (ADC_CONTR 0x10) ==0) 或者非一下 while(!(ADC_CONTR 0x10));
//!的优先级比高太多了 ******************************/ADC_CONTR = 0xE7; //1111,0111 清 ADC_FLAG 位, 关闭A/D转换, voltage=ADC_DATA;if( volage40){k=1; //对应0X000扇区内容}if(voltage>=40volage80){k=2; //对应0X200扇区内容}if(voltage>=80volage110){k=3;}if(voltage>=110volage130){k=4;}if(voltage>=130volage148){k=5;}if(voltage>=148volage160)//注意:默认是165 电压AD值{k=6;}}/******************************************//*--- STC International Limited ----------------一个完整的EEPROM 测试程序,用宏晶的下载板可以直接测试STC12C52xxAD 系列单片机 EEPROM/IAP 功能测试程序演示 STC11xx 系列单片机 EEPROM/IAP 功能测试程序演示STC10xx 系列单片机 EEPROM/IAP 功能测试程序演示--- STC International Limited --------------------- 宏晶科技 设计 2009/1/12 V1.0 --------------***********************************************///读一字节,调用前需打开IAP 功能,入口:DPTR = 字节地址,返回:A = 读出字节 uchar Byte_Read(uint add){ISP_DATA = 0x00;ISP_CONTR = ENABLE_ISP; //打开IAP 功能, 设置Flash 操作等待时间 ISP_CMD = 0x01; //IAP/ISP/EEPROM 字节读命令 my_unTemp16.un_temp16 = add; //联合体变量赋值 ,
这里是俩字节,因为公用内存,所以下面数组也是此内容ISP_ADDRH = my_unTemp16.un_temp8[0]; //设置目标单元地址的高8 位地址 ISP_ADDRL = my_unTemp16.un_temp8[1]; //设置目标单元地址的低8 位地址 EA = 0;ISP_TRIG = 0x46; //先送 5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此 ISP_TRIG = 0xB9; //送完A5h 后,ISP/IAP 命令立即被触发起动_nop_();//EA = 1;IAP_Disable(); //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关 return (ISP_DATA); //数据在ISP_DATA寄存器中}//字节编程,调用前需打开IAP 功能,入口:DPTR = 字节地址, A= 须编程字节的数据 void Byte_Program(uint add, uchar ch){ISP_CONTR = ENABLE_ISP; //打开 IAP 功能, 设置Flash 操作等待时间 ISP_CMD = 0x02; //IAP/ISP/EEPROM 字节编程命令 my_unTemp16.un_temp16 = add;
//联合体变量赋值 ,这里是俩字节,因为公用内存,所以下面数组也是此内容ISP_ADDRH = my_unTemp16.un_temp8[0]; //设置目标单元地址的高8 位地址 ISP_ADDRL = my_unTemp16.un_temp8[1]; //设置目标单元地址的低8 位地址 ISP_DATA = ch; //要编程的数据先送进ISP_DATA 寄存器 EA = 0;//必须关中断,不然没法写ISP_TRIG = 0x46; //先送 46h,再送B9h 到ISP/IAP 触发寄存器,每次都需如此 ISP_TRIG = 0xb9; //送完B9h 后,ISP/IAP 命令立即被触发起动 _nop_();//EA = 1;IAP_Disable(); //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关 }//擦除扇区, 入口:DPTR = 扇区地址void Sector_Erase(uint add){ISP_CONTR = ENABLE_ISP; //打开IAP 功能, 设置Flash 操作等待时间 ISP_CMD = 0x03; //IAP/ISP/EEPROM 扇区擦除命令 my_unTemp16.un_temp16 = add;ISP_ADDRH = my_unTemp16.un_temp8[0]; //设置目标单元地址的高8 位地址 ISP_ADDRL = my_unTemp16.un_temp8[1]; //设置目标单元地址的低8 位地址 EA = 0;//必须关中断ISP_TRIG = 0x46; //先送 46h,再送B9h 到ISP/IAP 触发寄存器,每次都需如此 ISP_TRIG = 0xB9; //送完B9h 后,ISP/IAP 命令立即被触发起动 _nop_();//EA = 1;IAP_Disable(); //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关 }void IAP_Disable(){//关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,//一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关 ISP_CONTR = 0; //关闭IAP 功能ISP_CMD = 0; //清命令寄存器,使命令寄存器无命令,此句可不用 ISP_TRIG = 0; //清命令触发寄存器,使命令触发寄存器无触发,此句可不用 ISP_ADDRH = 0;ISP_ADDRL = 0;}//void delayus(uchar i ){while(i--);}//延时函数void delayms(uint k){uint data i,j;for(i0;ik;i++){for(j0;j200;j++){;}}}


评论


技术专区

关闭