📄 51
字号:
#include <reg51.h>
#include <absacc.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#define ADP2 P2
#define ADP0 P0
#define CD4051 P1
#define fosc 11.0592 // 晶振频率
#define time0 2000 // 定时2000us
#define jishu 1000 // 假设AD输入电压与对应瞬时功率的基数
// 1V对应1000w
uint idata jisuandu; // 临时变量,用于计算电度数
uint idata time0_0; // 临时变量,用于计算定时
sbit STAT7135= P1^7; // 7135的启动端
sbit busy = P2^6; // 7135的忙端
sbit st = P2^5; // 7135的选通端
sbit CS7221 = P1^5; // 7221的片选
sbit DIN7221 = P1^4; // 7221的数据端
sbit CLK7221 = P1^6; // 7221的时钟端
sbit SDA=P3^4; // 2416的数据端
sbit SCL=P3^5; // 2416的时钟端
uchar DISPBUF[8]={0,1,2,3,4,5,6,7}; //显示缓冲区
uchar ADBUF[40]=0; //AD缓冲区 (万千百十个)*8
uchar TIME[2]=0; //用于定时
uchar BUF[5]=0; //数据处理缓冲区
uchar key[2]={0xaa,0xbb}; //通信密码2位
uchar key_FLAG=0; //密码标志位
sbit PinSD = P3^3; //zHX1010的使能端
void InitSerial(uint temp); //初始化串口
void ZHX1010_Send(uchar dat); //ZHX1010发送一字节数据
void Send(void); //发送电度数据
uchar ZHX1010_Recieve(void);//ZHX1010接收一字节数据函数
void delay(uint n); //延时子程序
void Initial7221(void); //MAX7221初始化
void WR7221(uchar addr,uchar Data); //MAX7221写程序
void Max7221Display(uchar *buffer); //MAX7221显示程序
void time2ms(void); //定时器0初始化程序
void time0_int(void); //定时器0中断服务程序
void ICL7135(void); //ICL7135 8路信号AD转换程序
void SAVE(void); //电量存储 转电度程序
void start_bit(void); //IIC开始条件
void stop_bit(void); //IIC停止条件
void mast_ack(void); //IIC应答
bit write_8bit(uchar ch); //IIC写8位数据
uchar page_wr(uint firstw_ad,uint counter,uchar *firstr_ad);//IIC页写
uchar page_rd(uint firstrd_ad,uint count,uchar *firstwr_ad);//IIC页读
main()
{
//while(page_wr(0,120,0)==0); //初次使用时 清电量数
Initial7221(); //初始化7221
Max7221Display(&DISPBUF[0]); //开机默认显示0~7
delay(40); //延时
time2ms(); //启动定时器
InitSerial(0xfdfd); //初始化串口 9600 Bds
while (1)
{
if (TIME[1]%10==0) //5秒时间到
{
ICL7135(); //启动8路AD转换
SAVE(); //存储电能
}
if(key_FLAG==2) //抄表密码正确
{
key_FLAG=0; //清标志位
Send(); //发送电度
}
}
}
//串行通信初始化函数:InitSerial()
void InitSerial(uint temp)
{
SCON = 0x50; //串行通信工作在模式1,8 位, REN=1
TMOD = TMOD | 0x20 ; //定时器1工作在模式2
TH1 = (temp & 0xff00) >> 8; //定时时间常数高位
TL1 = temp & 0xff; //定时时间常数低位
IP = 0X10; //串行中断为较高优先级
RI = 0; //清接收标志位
TI = 0; //清发送标志位
TR1 = 1; //定时器1运行
ES = 1; //允许串行中断
PinSD= 0; //串行通信使能
EA = 1; //开总中断
}
//ZHX1010发送一字节数据函数:ZHX1010_Send()
//函数功能:ZHX1010发送一字节数据
//说明:参数dat为要发送的数据
void ZHX1010_Send(uchar dat)
{
while (RI==1); //如果正在接收则等待
TI = 0; //清发送标志位
SBUF = dat; //把要发送的数据打入发送寄存器 启动发送
while (!TI); //等待发送完成
}
void ZHX1010_int(void) interrupt 4 //串行中断服务程序
{
uchar key1;
if (RI) //如果为接收中断
{
key1= SBUF; //读接收寄存器数据
RI = 0 ; //清接收标志位
if ((key_FLAG==0)&&(key1==key[0])) //第1位密码正确时,置标志位key_FLAG为1
{
key_FLAG=1;
}
else if((key_FLAG==1)&&(key1!=key[1])) //第1位密码正确第二位错误时 清标志位
{
key_FLAG=0;
}
else if((key_FLAG==1)&&(key1==key[1])) //2位密码正确时 置标志位key_FLAG为2
{
key_FLAG=2;
}
}
}
void Send(void) //发送电度程序
{
uchar k,j,sum=0;
ES = 0; //不允许串行中断
ZHX1010_Send(0xcc); //固定发cc 表示开始
do
{
for(k=0;k<8;k++) //8户
{
while (page_rd(50+k*5,5,&BUF[0])==0);//读电度数
for(j=0;j<5;j++) //5位电度数
{
ZHX1010_Send(BUF[j]); //发送电度数
sum=sum+BUF[j]; //和校验
}
}
ZHX1010_Send(sum); //发送校验字
}while(ZHX1010_Recieve()==0xdd); //直到正确
ZHX1010_Send(0xdd); //固定发DD 表示结束
ES = 1; //允许串行中断
}
//ZHX1010接收一字节数据函数:ZHX1010_Recieve()
uchar ZHX1010_Recieve(void)
{
uchar temp;
while(RI!=1);
RI = 0;
temp = SBUF;
return(temp);
}
void WR7221(uchar addr,uchar Data) //MAX7221的写子程序
{
uchar i;
CS7221 = 0; //片选有效
for (i=0;i<8;i++) //写8位地址
{
CLK7221 = 0; //时钟低
DIN7221 = (addr&(0x80>>i)) ? 1:0; //先发高位 依次到低位
_nop_();
_nop_();
CLK7221 = 1; //时钟高 上升沿锁数据
_nop_();
_nop_();
}
for (i=0;i<8;i++) //写8位数据
{
CLK7221 = 0; //时钟低
DIN7221 = (Data&(0x80>>i)) ? 1:0; //先发高位 依次到低位
_nop_();
_nop_();
CLK7221 = 1; //时钟高 上升沿锁数据
_nop_();
_nop_();
}
CS7221 = 1; //片选无效
}
void Initial7221(void) //MAX7221初始化
{
WR7221(0x0A,0x0A); //亮度地址0AH,0x00~0x0F,0x0F最亮
WR7221(0x0B,0x07); //扫描LED个数地址0BH,0x00~0x07,最多扫描8个数码管
WR7221(0x0C,0x01); //工作模式地址0x0C. 0x00:关断;0x01:正常
WR7221(0x09,0xFF); //编码模式地址0x09. 0x00~0xFF:哪一位为1,哪一位就支持编码
}
void Max7221Display(uchar *buffer) //MAX7221显示子程序
{
uchar i;
for (i=0;i<8;i++) //MAX7221的8个数码管显示
{
WR7221(i+1,*(buffer+i)); //调MAX7221的写子程序
}
}
void delay(uint n) //延时程序
{
uint i,j;
for (i=0;i<n;i++)
for (j=0;j<1140;j++);
}
void time2ms(void) //T0定时器初始化
{
TMOD=0x01; // T0工作方式1
/* 2ms 定时设置 */
time0_0 = 65536-time0*fosc/12; //计算初值
TH0=(time0_0/256); //装定时器0初值
TL0=(time0_0%256);
TR0=1; //启动定时器0
ET0=1; //打开定时器0中断
EA=1; //打开总中断
}
/* 定时器0中断服务子程序,定时用于AD转换
1s约转换3次,8路信号约3s时间
为了时间充裕 5s采集一次电能信号 */
void time0_int(void) interrupt 1
{
TH0=(time0_0/256); //重装定时器0初值
TL0=(time0_0%256);
TIME[0]++;
if (TIME[0]==250) //250*2ms=500ms=0.5s时间到
{
TIME[0]=0; //到0.5s时 TIME[0]清0
TIME[1]++; //TIME[1]加1 内存的0.5秒的整数倍
}
}
void ICL7135(void) //启动8路AD转换
{
uchar i,j;
STAT7135=1; //7135启动端使能 启动AD转换
CD4051=CD4051&0xf0; //设置CD4051的第一路信号输入AD
for (j=0;j<=7;j++) //8路循环测量
{
i=CD4051&0xf0; //读P1口的状态 保护高位
CD4051=j|i; //通过j调节 多路开关的转换
STAT7135=1; //7135启动端使能 启动AD转换
i=busy; //读7135的正在转换 忙端
do{i=busy;}while(busy==0); //忙端为0时 等待 直到开始转换
do{i=busy;}while(busy==1); //忙端为1时 正在转换 等待
STAT7135=0; //7135 禁止AD转换
do{i=ADP2;}while((ADP2&0x010)!=0x010); //读7135的D5,直到D5为1
if ((ADP2&0x010)==0x010) //D5为1开始读AD转换结果
{
//STAT7135=0;
ADBUF[j*5]=ADP0&0x0f; //读7135的万位
do{i=ADP2;}while((ADP2&0x08)!=0x08);//读7135的D4,直到D4为1
ADBUF[1+j*5]=ADP0&0x0f; //读7135的千位
do{i=ADP2;}while((ADP2&0x04)!=0x04);//读7135的D3,直到D3为1
ADBUF[2+j*5]=ADP0&0x0f; //读7135的百位
do{i=ADP2;}while((ADP2&0x02)!=0x02);//读7135的D2,直到D2为1
ADBUF[3+j*5]=ADP0&0x0f; //读7135的十位
do{i=ADP2;}while((ADP2&0x01)!=0x01);//读7135的D1,直到D1为1
ADBUF[4+j*5]=ADP0&0x0f; //读7135的个位
//ADBUF[0]=j+1; //路号
//Max7221Display(&ADBUF[j*5]); //当频率慢时可以显示AD转换的结果
}
}
}
void SAVE(void) //电能处理 保存
{
uchar k,i;
ulong kk,kk1,kk2;
if (TIME[1]<120) //小于1分钟时 120*0.5=60s 简单加
{
for (k=0;k<=7;k++) //8路电能循环存储
{
while (page_rd(k*5,5,&BUF[0])==0); //读原来的电能 各路5位数字
for (i=0;i<=4;i++)
{
BUF[i]=BUF[i]+ADBUF[i+k*5]; //本次的电能和原来的电能求和
}
while (page_wr(k*5,5,&BUF[0])==0); //存新的总电能
}
}
if (TIME[1]==120) //等于1分钟时 做电度数的处理
{
TIME[1]=0;
for (k=0;k<=7;k++) //8路电能循环转换成电度数 存储
{
while (page_rd(k*5,5,&BUF[0])==0); //读原来的电能 各路5位数字
for (i=0;i<=4;i++)
{
BUF[i]=BUF[i]+ADBUF[i+k*5]; //本次的电能和原来的电能求和
}
while (page_wr(k*5,5,0)==0); //清寄存的电能
kk=(ulong)BUF[0]*10000+(ulong)BUF[1]*1000+(ulong)BUF[2]*100+(ulong)BUF[3]*10+(ulong)BUF[4];
// 把电能转化为千瓦时 即度
while(page_rd(100+k*2,2,&BUF[0])==0); //读上次余数
kk=kk*jishu+(ulong)BUF[0]*100+(ulong)BUF[1];// (jishu*1000)/(10000*12*60)=jishu/7200
kk1=kk/7200; //取电度数
kk2=kk%7200; //kk1为电度数 kk2 余数
BUF[0]=(uchar)(kk2/100); //分两部分存储电度的余数 100为界
BUF[1]=(uchar)(kk2%100);
while(page_wr(100+k*2,2,&BUF[0])==0); //电度的存余数
while (page_rd(50+k*5,5,&BUF[0])==0); //读原来电度数
kk1=kk1+(ulong)BUF[0]*10000+(ulong)BUF[1]*1000+(ulong)BUF[2]*100+(ulong)BUF[3]*10+(ulong)BUF[4];
//原来的电度和新的电度数相加
BUF[0]=(uchar)(kk1/10000); //万
BUF[1]=(uchar)((kk1%10000)/1000); //千
BUF[2]=(uchar)(((kk1%10000)%1000)/100); //百
BUF[3]=(uchar)((kk1%100)/10); //十
BUF[4]=(uchar)((kk1%100)%10); //个
while(page_wr(50+k*5,5,&BUF[0])==0); //存新电度数
DISPBUF[0]=k+1; //显示户号
DISPBUF[1]=0xf; //显示2个F
DISPBUF[2]=0xf;
DISPBUF[3]=BUF[0]; //显示此时电度数
DISPBUF[4]=BUF[1];
DISPBUF[5]=BUF[2];
DISPBUF[6]=BUF[3];
DISPBUF[7]=BUF[4];
Max7221Display(&DISPBUF[0]); //送显示
delay(20); //延时
}
}
}
/*-----------------------------------------------
调用方式:void start_bit(void)
函数说明:开始位
-----------------------------------------------*/
void start_bit(void)
{
SCL=1;_nop_();
SDA=1;_nop_();
SDA=0;_nop_();
SCL=0;_nop_();
}
/*-----------------------------------------------
调用方式:void stop_bit(void)
函数说明:停止位
-----------------------------------------------*/
void stop_bit(void)
{
SDA=0;_nop_();
SCL=1;_nop_();
SDA=1;_nop_();
}
/*-----------------------------------------------
调用方式:void mast_ack(void)
函数说明:主答函数
-----------------------------------------------*/
void mast_ack(void)
{
SCL=0;_nop_();
SDA=0;_nop_();
SCL=1;_nop_();
SCL=0;_nop_();
SDA=1;_nop_();
}
/*-----------------------------------------------
调用方式:write_8bit(uchar ch)
函数说明:写一个字节(8位)数据
-----------------------------------------------*/
bit write_8bit(uchar ch)
{
uchar i=8;
bit fan_w;
SCL=0;_nop_();
while (i--)
{
SDA=(bit)(ch&0x80);_nop_();
ch<<=1;
SCL=1;_nop_();
SCL=0;_nop_();
}
SDA=1;_nop_();
SCL=1;_nop_();
fan_w=SDA;
SCL=0;_nop_();
return(fan_w);
}
uchar page_wr(uint firstw_ad,uint counter,uchar *firstr_ad)
{
uchar data *ufirstr_ad;
ufirstr_ad=firstr_ad;
start_bit();
if (write_8bit(0xA0)!=0){
stop_bit();return(0);
}
if (write_8bit(firstw_ad)!=0){
stop_bit();return(0);
}
while (counter--)
{
if (write_8bit(*ufirstr_ad)!=0){
stop_bit();return(0);
}
ufirstr_ad++;
}
stop_bit();
return(1);
}
/*-----------------------------------------------
调用方式:void page_rd(uint firstrd_ad,uint count,uint firstwr_ad)
函数说明:页面读函数,firstrd-ad为所读字节首地址,count为读字节数
*ufirstwr-ad为读出数据存储首地址指针
-----------------------------------------------*/
uchar page_rd(uint firstrd_ad,uint count,uchar *firstwr_ad)
{
uchar j=8;
uchar data *ufirstwr_ad;
ufirstwr_ad=firstwr_ad;
start_bit();
if (write_8bit(0xA0)!=0){
stop_bit();return(0);
}
if (write_8bit(firstrd_ad)!=0){
stop_bit();return(0);
}
start_bit();
if (write_8bit(0xA1)!=0){
stop_bit();return(0);
}
while (count--)
{
uchar i=8;
while (i--)
{
(*ufirstwr_ad)<<=1;
SCL=1;_nop_();
if (SDA) (*ufirstwr_ad)|=0x01;
SCL=0; _nop_();
}
ufirstwr_ad++;
mast_ack();
}
while (j--)
{
(*ufirstwr_ad)<<=1;
SCL=0;_nop_();_nop_();SCL=1;
if (SDA) (*ufirstwr_ad)|=0x01;
}
stop_bit();
return(1);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -