少占鱼-STC12C2052AD单片机-万能学习遥控器-记录波形方式

来源:互联网 发布:oracle sql优化案例 编辑:程序博客网 时间:2024/04/27 22:16


图片


// 中断函数注意养成指定寄存器组的习惯
//不同优先级的中断程序绝对不能使用同一组寄存器
/*****编程时防止中断把寄存器中的数据改变的解决方法是给中断指定寄存器,同优先级的使用同一组没事。
     1、写中断程序一定要用using语句指定寄存器组。第1、2、3组都可以,不能是0.
    2、51单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。 
 这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,高优先级的中断都用 using 2 。这样不会冲突。 
    下面是一个正常的例子:  
    C程序: void int0() interrupt 0 using 1
 默认5个中断时同级的,不会冲突,但是最好养成好习惯
 不指定中断要使用的寄存器,每次都要入栈保护数据,中断完还要出栈,代码会增加32字节
********************************************************************/
#include <stc12c2052ad.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
//少占鱼制作  河北正定欢迎您   长沙航空职业技术学院  2010 年QQ:411656434


//定义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 xdata a[111]={121,1,3,4,44,55,24,156,35};//间接寻址的高128RAM,内部256RAM高128只能间接寻址
uint j=0;
uint zu=0,addr=0; //扇区地址
uchar k;//按键代号
uchar m=0;//写EEPROM时用来移动数组的 
uchar b[6];//用来存储每一组数据的总字节数
/**********************************************************************/
/****************************************************************/
 void main()
{   
  //默认STC12是1T运行模式。时钟没有分频,为了兼容8051,定时器可以分频12.但是如果时钟也分频,就会影响他了。
 AUXR=0x00;//定时器T0 T1,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);} //for end 
   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();
  }
   }  
}
/********************************************************************/
//接收函数初始化
void init1()//接收初始化
{
  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;
}
//



/************************************************************************/
// 红外接收子程序
void jieshou()
{
      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;
  }
/***********************************************/
/********************************************************************/
//发射函数初始化
void init2()//发射初始化
{
  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;
}


// 红外发射子程序
void fashe() //发射程序里没有安排推出操作,所以只有重启才能重新进入选择模式
{
 
  while(1)
  {
  ADC();
  switch(k)
  {
  case 1:for(j=0;j<b[0];j++){a[j]=Byte_Read(j);}        init2();led(b[0]);break; //必须先读EEPROM再开定时器中断
  case 2:for(j=0;j<b[1];j++){a[j]=Byte_Read(j+0x200);}  init2();led(b[1]);break;
  case 3:for(j=0;j<b[2];j++){a[j]=Byte_Read(j+0x400);}  init2();led(b[2]);break;
  case 4:for(j=0;j<b[3];j++){a[j]=Byte_Read(j+0x600);}  init2();led(b[3]);break;
  case 5:for(j=0;j<b[4];j++){a[j]=Byte_Read(j+0x800);}  init2();led(b[4]);break;
  case 6:for(j=0;j<b[5];j++){a[j]=Byte_Read(j+0xa00);}  init2();led(b[5]);break;


  default:k=0;break;
  }
  TR0=0;//发射完毕要关闭38K载波
  TR1=0;
  }
}
 //发射高电平。接收端就是低电平。而载波就是高电平有38KHZ载波,二极管反复38K亮灭。低电平什么也没有,二极管不通。
/**********************************************************************/
 void led(uchar x)
 {
 
  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单片机的中断有两个优先级。一个中断不会打断另一个相同优先级的中断。 
 这样相同级别中断可以使用同一个组。比如:低优先级的中断函数都用 using 1,高优先级的中断都用 using 2 。这样不会冲突。 
    下面是一个正常的例子:  
    C程序: void int0() interrupt 0 using 1
 默认5个中断时同级的,不会冲突,但是最好养成好习惯
 不指定中断要使用的寄存器,每次都要入栈保护数据,中断完还要出栈,代码会增加32字节
********************************************************************/
//中断函数要指定使用那组寄存器,使用同一组时可能会破坏了上次寄存器中的数据   
//同一优先级的中断可以使用同一组寄存器
  void time0() interrupt 1 using 1//定时器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);} //for end


     if(zu<0xa00)//第一组代码完毕后,转到第二组,每组都是200个空间
      zu+=0x200; //测完一组 ,扇区地址指向下一个扇区
      else
    { zu=0x000;} //超过6组代码,内存重新指向第1组
  flag=0;
  receive=0;//用完接收启动标志要清0
      end=1;//退出接收函数最外层循环


  }
/******************************************************/
// 发射频率38khz由T1产生
  void time1() interrupt 3 using 1 //定时器1中断 ,因为默认是同优先级,所以可以使用同一组寄存器
  {
   contrl=!contrl;//38K翻转产生
  }
/******************************************************/
// 外部中断 存储高电平长度
void interint0()  interrupt 0 using 1 //外部中断0  
{
 
  if (0==flag)//flag=0表示是首次接收到脉冲
   {
     TH0=0;
  TL0=10;//前面延时函数消耗的时间补上
     TR0=1;
  EX0=0;//关闭外部中断0,以后的计数都在接收函数里
     flag=1;//表示启动T0


    }


}
//
/************************************************************/
// 外部按键中断 1
void 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.3
    delayms(1);                       //使输入电压达到稳定
    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( voltage<40)
    {
  k=1; //对应0X000扇区内容
    }
   if(voltage>=40&&voltage<80)
    {
  k=2; //对应0X200扇区内容
    }


  if(voltage>=80&&voltage<110)
    {
   k=3;
    }
  if(voltage>=110&&voltage<130)
    {
   k=4;
    }
  if(voltage>=130&&voltage<148)
    {
  k=5;
    }
  if(voltage>=148&&voltage<160)//注意:默认是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(i=0;i<k;i++)
  {
    for(j=0;j<125;j++)
     {;}
   }
}