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

📄 51

📁 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 + -