📄 fun.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 + -