⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fun.h

📁 从零开始学51单片机源代码
💻 H
字号:
#include <reg52.h> 
#include "intrins.h"

//定义端口寄存器	 
sfr P0M0 = 0X93;
sfr P0M1 = 0X94;
sfr P1M0 = 0X91;
sfr P1M1 = 0X92;
sfr P2M0 = 0X95;
sfr P2M1 = 0X96;  
sfr P3M0 = 0Xb1;
sfr P3M1 = 0Xb2; 

//定义ADC寄存器 
sfr P1ASF = 0X9D;
sfr ADC_CONTR = 0XBC;
sfr ADC_RES = 0XBD;
sfr ADC_RESL = 0XBE;

//定义EEPROM寄存器 
sfr IAP_DATA = 0xc2;
sfr IAP_ADDRH = 0xc3;
sfr IAP_ADDRL = 0xc4;
sfr IAP_CMD = 0xc5;
sfr IAP_TRIG = 0xc6;
sfr IAP_CONTR = 0xc7;

//看门狗BRT寄存器
sfr BRT = 0x9c;

sfr AUXR= 0x8e;
sfr AUXR1 = 0xa2;

//定义看门狗寄存器 
sfr WDT_CONTR = 0xc1;  //STC12C5A60S2的看门狗控制寄存器地址0xc1

#define fosc 11059200L
#define T0_16ms (65536 - 16000 * ( fosc / 12000000L))  //16ms定时参数
#define T1_50ms (65536 - 50000 * ( fosc / 12000000L))  //50ms定时参数 

//定义按键 
sbit s1 = P3^4; 
sbit s2 = P3^3;
sbit s3 = P3^2;

//定义LED
sbit d1 = P3^7; 
sbit d2 = P3^6;
sbit d3 = P3^5;
sbit d_vote = P2^0;

//模块电源 
sbit m_power = P1^0;

unsigned char key0,key1;	  //key0记录按下的键,key1进入记录工厂模式的步骤 
unsigned int ad_result10;			//10位精度的电压转换值
float vin; 
bit f_adconverting; //正在AD转换标志 
 
//低地址,高地址  
unsigned char addrL,addrH;
unsigned int addr;	//地址 
unsigned int me;//排队发送次序 

int cnt; //cnt计数 
unsigned char rec[5],sent[3];//接收,发送数据缓存 
unsigned char recnum=0;  //接收缓存当前数据量  
unsigned int checkXOR,f_start;  //异或校验,投票开始标记 
char time,time_m;//按键计时 ,进入工厂模式计时  
int time_f;   //空闲计时 
char add=0;  //地址正确标志 
char f_sleep=0,f_volt; //休眠标志,低电压标志
char ty,qq,fd;  //同意 弃权 反对标志 

 /*****************************************************
        				函数 
 *****************************************************/
  void usdelay(int t)
 {
  	  int i;
	  t=t>>1;
  	  for (i=0;i<t;i++);
 }
 //毫秒级延时 
 void msdelay(int t)
 {
  	  int i;
  	  for (i=0;i<t;i++)
	  {
	    usdelay(1000);
	  }
 }

//串口发送数据 
void putchar(char c)
{
  SBUF = c;
  while(!TI);//等待发完 

}

//AD转换电压 
void checkpower()
{
	unsigned int temp;
	f_adconverting = 1;

	//第一次测基准电压 
	P1ASF |= 0x07; //选择ADC7
	ADC_CONTR |= 0X08;	//ADC_START=1,启动ad转换
	//等待转换完成 
	while (f_adconverting);
	//第二次测基准电压 
	ADC_CONTR |= 0X08;	//ADC_START=1,启动ad转换
	//等待转换完成 
	while (f_adconverting);
	//保存3.3V基准电压转换值 
	temp = ad_result10;

	//第一次测分压电源电压 
	P1ASF &= 0xf8; //选择ADC0
	ADC_CONTR |= 0X08;	//ADC_START=1,启动ad转换
	//等待转换完成 
	while (f_adconverting);
	//第二次测分压电源电压  
	ADC_CONTR |= 0X08;	//ADC_START=1,启动ad转换
	//等待转换完成 
	while (f_adconverting);

	//求分压电压 
	vin = 3.3 * ad_result10 / temp;

   /*
	分压电压vin=(VCC-Vce)/2
   其中Vce是三极管ce端的压降,为0.1V
   分压电压Vin分2个门槛,1.95V、1.75V
   对应模块电压,         3.9V、3.5V
   对应VCC,             4V、  3.6V
   */
   //分压电压>1.95V,VCC>4V
	if (vin>=1.95)
	{
		sent[0]=0x80 | addrH;
	}
	//分压电压 >1.75V,VCC>3.6V
	else if (vin>=1.75) 
	{
		sent[0]=0x90 | addrH;
	}
	else  //分压电压<1.75V,VCC<3.6V 
	{
		sent[0]=0xe0 | addrH;
	}

}

//读写EEPROM
unsigned char IAP_read (unsigned char addrh, unsigned char addrl)
{
	unsigned char edata;
	IAP_ADDRH = addrh; //送地址
	IAP_ADDRL = addrl;
	IAP_CMD = 0x01;  //读指令
	IAP_CONTR = 0x81; //允许操作,设置等待时间为01,20MHz以内
	IAP_TRIG = 0x46;  //触发指令
	IAP_TRIG = 0xb9;
	_nop_();
	edata = IAP_DATA;  //取数据
	return edata;
}

void  IAP_erase  (unsigned char addrh, unsigned char addrl)
{
	IAP_ADDRH = addrh; //送地址
	IAP_ADDRL = addrl;
	IAP_CMD = 0x03;  //扇区擦除指令
	IAP_CONTR = 0x81; //允许操作,设置等待时间为01,20MHz以内
	IAP_TRIG = 0x46;  //触发指令
	IAP_TRIG = 0xb9;
	_nop_();
}

void  IAP_write  (unsigned char addrh, unsigned char addrl, unsigned char edata)
{
	IAP_ADDRH = addrh; //送地址
	IAP_ADDRL = addrl;
	IAP_DATA = edata; //送数据
	IAP_CMD = 0x02;  //写指令
	IAP_CONTR = 0x81; //允许操作,设置等待时间为01,20MHz以内
	IAP_TRIG = 0x46;  //触发指令
	IAP_TRIG = 0xb9;
	_nop_();
}


//喂狗 
void WDR()
{
 WDT_CONTR = 0x10;//喂狗 
}

void recieve()
{
   int i;

   if (key0<8)  //普通按键模式 
   {

	    /**************************************
	          投票开始
	  ***************************************/
	  if ((rec[0]==0x22) && (rec[1]==0xdd)) 
		{
	       f_start=1;
		   d_vote=0;//点亮投票状态灯 
 	    }  
	    /**************************************
	         依次投票 
	  ***************************************/
	    if ((rec[0]==0x21) && (rec[1]==0xde)) 
		{
		  cnt=0;
		  me=addr;
		  f_start=1;
		  d_vote=0;//点亮投票状态灯 
		  //准备数据 
		  switch (key0)
		  {
	  	   	case 1:
	     		 sent[0]=0xa0|addrH;
		 		 break;
	  		case 2:
	     		 sent[0]=0xb0|addrH;
		 		 break;
	  		case 3:
	     		 sent[0]=0xc0|addrH;
		 		 break;
			default:
			     sent[0]=0xd0|addrH;
				 key0=4;
			}
	       sent[1]=addrL;
	       sent[2]=sent[0]^sent[1];
		   m_power=0;  //开模块电源
		   TR0=1; //启动定时器0
		   
		}
		/**************************************
	          投票结束
	  ***************************************/
	  if ((rec[0]==0x23) && (rec[1]==0xdc)) 
		{
	       f_start=0;
		   key0=0;
		  d_vote=1;//关投票状态灯 
 	    }  
		/**************************************
	         询问电源
	  ***************************************/
		 if ((rec[0]==0x31) && (rec[1]==0xce) && (f_start==0)) 
		{
		  cnt=0;
		  me=addr;
		  checkpower();
		  WDR();  //喂狗
		  checkpower();
		  WDR();
		  sent[1]=addrL;
		  sent[2]=sent[0]^sent[1];
		  key0=4;  //按键给一个任意值

		   m_power=0;  //开模块电源
		   TR0=1; //启动定时器0
		  
		  if (vin<1.75)  //如果需要充电
		  {
		     f_volt=1;
		  }  
		  
		}
		/**************************************
	             电源自检
	  ***************************************/
		 if ((rec[0]==0x32) && (rec[1]==0xcd) && (f_start==0)) 
		{
		  cnt=0;
		  checkpower();
		  WDR();
		  checkpower();//该函数影响到sent[0]的值 
		  WDR();
		  if (sent[0]==(0xe0 | addrH))  //如果需要充电
		  {
		     f_volt=1;
		  }
		}
		/**************************************
	               全体休眠
	  ***************************************/
	  if ((rec[0]==0x11) && (rec[1]==0xEE)) 
		{
		   f_sleep=1;
 	    }  
		/**************************************
	               个体休眠
	  ***************************************/
	  if ((rec[0]&0xfc)==0x00 && (rec[0]&0x03)==addrH && (rec[1]==addrL))
		{
		   f_sleep=1;
 	    }  
	  /**************************************
	               空寻呼
	  ***************************************/
	  if ((rec[0]==0x33) && (rec[1]==0xcc)) 
		{
		
 	    }  
		

	    /**************************************
	               询问状态
	  ***************************************/
	  
	    if ((rec[0]==0x60) && ((rec[0]&0x03)==addrH) && (rec[1]==addrL))
		{
		  switch (key0)
		  {
	  	   	case 1:
	     		 sent[0]=0xa0|addrH;
		 		 break;
	  		case 2:
	     		 sent[0]=0xb0|addrH;
		 		 break;
	  		case 3:
	     		 sent[0]=0xc0|addrH;
		 		 break;
		    default:
			     sent[0]=0xd0|addrH;
				 key0=4;
			}
	       sent[1]=addrL;
	       sent[2]=sent[0]^sent[1];
		   m_power=0;  //开模块电源

           msdelay(16);

           putchar(sent[0]);
	       putchar(sent[1]);
	       putchar(sent[2]);
		}

	    /**************************************
	               寻找设备
	  ***************************************/
	  if ((rec[0]&0xf0)==0x50 && (rec[0]&0x03)==addrH && (rec[1]==addrL))  
	   {
	     checkpower();
		 WDR();
		 checkpower();
		 WDR();
		 //sent[0]已经在checkpower()里面准备好了 
		 sent[1]=addrL;
	     sent[2]=sent[0]^sent[1];
		 
	     m_power=0;  //开模块电源

         msdelay(16);
		 //返回本机地址 
         putchar(sent[0]);
	     putchar(sent[1]);
	     putchar(sent[2]);
		 
	     for (i=0;i<5;i++)
		 {
		 	//灯全亮 
		  	 d_vote=0;
			 P3 &= 0x1f;
			 msdelay(500);
			 WDR();
			 //灯全灭 
			 d_vote=1;
			 P3 |= 0xe0;
			 msdelay(500);
			 WDR();  //喂狗
		 }
	   }
   } //结束普通按键模式
   else //key0>8,工厂模式
   {
	    //灯全灭 
			 d_vote=1;
			 P3 |= 0xe0;
	   /**************************************
	             设置地址
	  ***************************************/
	  	  if ((rec[0]&0xfc )==0x70) 
		  {
		   	 addrH=rec[0]&0x03;
			 addrL=rec[1];
			 EA=0;

			 IAP_erase(0,0);//擦除扇区 
			    for (i=0;i<5;i++)
			 	{
			   	 IAP_write(0,i,addrL);
			 	}
			 	for (i=5;i<10;i++)
			 	{
			   	 IAP_write(0,i,addrH);
			 	}
			 EA=1;
			 WDR();
			 key0=0;
			 time_m=0;
			 msdelay(500);
			 WDR();  //喂狗

 			 m_power=0;  //开模块电源 
			  //灯全灭 
			 d_vote=1;
			 P3 |= 0xe0;

			 checkpower();
			 WDR();
			 checkpower();
			 WDR();
			 sent[1]=addrL;
	       	 sent[2]=sent[0]^sent[1];

		   	 m_power=0;  //开模块电源
           	 msdelay(16);

           	 putchar(sent[0]);
	       	 putchar(sent[1]);
	       	 putchar(sent[2]);

			 addr=addrH*256+addrL;
		  }
   } //结束工厂模式
}
//---------------------------

/*cpu power down模式休眠*/
void sleep(void) 
{
 int i;
 checkpower();
 WDR();
 checkpower();//会改变sent[0]的值 
 WDR();
 if (sent[0]==(0xe0 | addrH))  //如果需要充电 
 {
	//灯全关 
	  d_vote = 1;
	  P3 |= 0xe0;
	for (i=0;i<5;i++)
    {
		//闪烁5次d_vote
		d_vote=0;	 //亮d_vote
	    msdelay(500);
		WDR();
		d_vote=1;  //灭d_vote
		msdelay(500);
		WDR();  //喂狗 
	}
 }
 //灯全亮,然后依次熄灭 
 d_vote=0;
 P3 &= 0x1f;
 msdelay(500);
 WDR();
 d_vote=1; //关d_vote
 msdelay(500);
 WDR();  //喂狗 
 s1=1; //关s1
 msdelay(500);
 WDR();
 s2=1;//关s2
 msdelay(500);
 WDR();  //喂狗
 s3=1;//关s3

 EA=0; //关中断 
 //关串口 
 REN=0;
 ES=0;
 //关定时器
TR0=0;
TR1=0;


 //关模块电源 
 m_power=1;
 //按键置高 
 s1 = 1;
 s2 = 1;
 s3 = 1;
 
 WDR();  //喂狗
 //关狗使能、清狗标志、空闲不计数 
 WDT_CONTR &= 0x07;	 //	EN_WDT =0,CLR_WDT=0,IDLE_WDT=0

 //开外部中断0、外部中断1
 IE |= 0x05;  //0000 0101
 EA=1;//开中断
 
 //休眠,进入power down模式 
 PCON |= 0x02; //PD=1
 _nop_(); 
}



 //点亮Dn,关闭其它LED
void offled(unsigned char p)
{
 if(p==1)
 {
 	d1 = 0;//点亮d1
	d2 = 1;//关闭d2
	d3 = 1;	//关闭d3
 }
 if(p==2)
 {
  	d1 = 1;
	d2 = 0;
	d3 = 1;	
 }
 if(p==3)
 {
  	d1 = 1;
	d2 = 1;
	d3 = 0;
 }
} 


/*************************************
为了安全起见,在EEPROM区存放了5次地址值, 
如果有3个或3个以上是相同的,则认为是正确的地址 
 ***************************************/
void readset()
{
 unsigned char AtempH[5],AtempL[5],max;
 int i,j;

EA=0;
 //读地址
for (i=0;i<5;i++)
{//低位
	AtempL[i]= IAP_read(0,i);
}
for (i=5;i<10;i++)
{//高位
	AtempH[i]= IAP_read(0,i);
}

//判断地址
for (i=0;i<=2;i++)
{
  max=0;
  for (j=i+1;j<=4;j++)
  {
    if (AtempL[i]==AtempL[j])
	{
	  max++;
	}
  }
  if (max>=2) //5个里面有3个相同的
  {
    addrL=AtempL[i];
  }
  else
  {
    addrL=0;
  }
}

for (i=0;i<=2;i++)
{
  max=0;
  for (j=i+1;j<=4;j++)
  {
    if (AtempH[i]==AtempH[j])
	{
	  max++;
	}
  }
  if (max>=2) //5个里面有3个相同的
  {
    addrH=AtempH[i];
  }
  else
  {
    addrH=0;
  }
}

addr=addrH*256+addrL;
//判断地址是否正确
if (addr==0 || addr>1023) //不正确
{
   	addr=0;
    //关闭所有的灯 
	d_vote=1;
	P3 |= 0xe0;

	for (i=0;i<10;i++)
	{ //d_vote闪10次 
	    d_vote = !d_vote;
	 	msdelay(500);
		WDR();
	}
	//关闭所有的灯 
	d_vote=1;
	P3 |= 0xe0;
}
else //正确,回写 
{
	IAP_erase(0,0);//擦除扇区 
  for (i=0;i<5;i++)
  {
	IAP_write(0,i,addrL);
  }
  for (i=5;i<10;i++)
  {
	IAP_write(0,i,addrH);
  }
}
EA=1;
}	  

void disable()
{
	TR0 = 0;//关闭定时器0
	TR1 = 0;//关闭定时器1
	IE &= 0xdf;//关闭ADC终端,EADC=0
}

void enable()
{
	TR1 = 1;//打开定时器1
} 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -