【STM32】電能表抄表功能實現|自學筆記


一、抄表的原理

抄表就是讀電能表的測量參數,一般有電能,電壓,電流,實時功率,功率因數等,用單板抄表其實就是讀電能表種相應寄存器中的值。智能電表目前主流協議有DLT645國標各家通用,MODBUS各家有自己協議。這里就拿DLT645為例,MODBUS很簡單一條指令可以讀連續的地址空間,收發一次抄到所有數據。645就得一條一條讀。

DL/T645-1997 通訊規約通信規約

這里主要需要配置串口時按照協議要求配置,我的另一篇隨筆種有串口的配置方法http://www.cnblogs.com/VergilYang/p/8902264.html

協議中最關鍵的部分就是幀格式了,別看這么多如果只是抄表的話用到的就只有讀數據這一種控制碼。

我們試着組織一條完整報文

幀起始符沒有問題,地址怎么確定,一般表的銘牌上會寫地址,如果串口上只掛了這一個設備,可以廣播一般為全9或者全A,這樣我們就能得到68 AA AA AA AA AA AA 68

接下來是控制碼,我現在想讀一個電壓,那么控制碼就是01,數據得看各家的寄存器地址,查出來為B6 11,但是發送的時候要加33H而且高位在右低位在左,即44 E9,可以得到 68 AA AA AA AA AA AA 68 01 02 44 E9

最后是校驗碼,ADD8可以寫一個專門計算校驗的函數,這里算出來是FC,所以完整的報文就是68 AA AA AA AA AA AA 68 01 02 44 E9 FC 16

這時電表回一條68 AA AA AA AA AA AA 68 81 04 44 E9 33 55 06 16 可以看出返回了電壓數據是 33 55,前后顛倒再減33H救得到2200,電表這位精度是0.1那么就是220.0V

 二、軟件的實現方法

定義一個幀的結構體

typedef struct DLT645_CmdFrame
{
	u32 BaudRate;
	u8 TxBuf[256];
	u8 RxBuf[256];
	u8 add[6];			//地址域
	u8 len;					//數據長度
	u8 cmd;					//控制碼
	u8 data[2];			//數據域
	
}DLT645_CmdFrame;

我這里用了兩路485分別讀一個電能表,這里配合一個定時器,發送一條在500ms內回一條,算一條收發成功,然后發送下一條。
發送抄表指令 -> 等待返回數據 -> 返回數據直接保存在Charger中 -> 讀下一項數據
                                    |-> 累積十個5秒報故障(報故障功能沒寫)

#include "App_Com_DLT645.h"
#include "Drv_UARTx.h"

#define A_Electical_Meter 1
#define B_Electical_Meter 0

DLT645_CmdFrame EMFrame[2];
extern Charger st_Charger[2];
extern Com_Manager Com[7];

/*******************************************************************************
* Function Name  : EMFrame_Packager
* Description    : 封裝645幀
* Last Eidt Time : 2018年4月18日09:49:57
* Author				 :Vergil Yang
* Notice				 : 
*******************************************************************************/
void EMFrame_Packager(DLT645_CmdFrame *frame,u8 cmd,u8 datalen,u8 data[])//添加幀頭長度校驗碼
{
	frame->TxBuf[8] = cmd;	//控制碼
	frame->TxBuf[9] = datalen;	//數據域長度
	memcpy(&frame->TxBuf[10],data,datalen);	//數據域
	frame->TxBuf[10+datalen] = CalcSum(frame->TxBuf,10+datalen);//ADD8校驗碼
	frame->TxBuf[11+datalen] = 0x16;//結束碼
}
/*******************************************************************************
* Function Name  : EMFrame_UnPackager
* Description    : 解析645幀
* Last Eidt Time : 2018年4月18日09:49:57
* Author				 :Vergil Yang
* Notice				 : 
*******************************************************************************/
u8 EMFrame_UnPackager(u8 *buf,u16 cnt)
{
	u16 add8;
	add8 = CalcSum(buf,cnt-2);
	
	if((buf[0] != 0x68)||(buf[7] != 0x68))
	{
		return 1;//幀頭錯誤
	}
	else if(buf[8] != 0x01)
	{
		return 2;//控制碼錯誤
	}
	else if((buf[9] + 12) != cnt)
	{
		return 3;//長度錯誤
	}
	else if(buf[cnt-2] != add8)
	{
		return 4;//ADD8錯誤
	}
	else 
	{
		return 0;
	}
}
/*******************************************************************************
* Function Name  : DataConvert
* Description    : 解析645數據轉換
* Last Eidt Time : 2018年4月18日09:49:57
* Author				 :Vergil Yang
* Notice				 : 
*******************************************************************************/
u32 DataConvert(u8 *buf)
{
	u8 cnt;
	u32 temp;
 	cnt = buf[9]-2;//數據個數 - 指令碼(2字節) = 數據字節個數 
	temp=0;
	while(cnt)
	{
		temp *=100;
		temp=temp+(((buf[11+cnt]-0x33)>>4)*10)|((buf[11+cnt]-0x33)&0x0F);
		cnt--;
	}
	return temp;
}

/*******************************************************************************
* Function Name  : EMTask_Deal
* Description    : 抄表處理
* Last Eidt Time : 2018年4月18日09:49:57
* Author				 :Vergil Yang
* Notice				 : 
*******************************************************************************/
void EMTask_Deal()
{
	u8 i;
	u16 framelen;
	u32 temp;
#if B_Electical_Meter 
	for(i = 0;i < 2;i++)
#else
	for(i = 0;i < 1;i++)
#endif
	{
		if(st_Charger[i].EM_Data.status == Need_Send)
		{//發送
			switch(st_Charger[i].EM_Data.step)
			{
				case 0:EMFrame_Packager(&EMFrame[i],0x01,2,"\x44\xE9");break;
				case 1:EMFrame_Packager(&EMFrame[i],0x01,2,"\x45\xE9");break;
				case 2:EMFrame_Packager(&EMFrame[i],0x01,2,"\x46\xE9");break;
				case 3:EMFrame_Packager(&EMFrame[i],0x01,2,"\x54\xE9");break;
				case 4:EMFrame_Packager(&EMFrame[i],0x01,2,"\x55\xE9");break;
				case 5:EMFrame_Packager(&EMFrame[i],0x01,2,"\x56\xE9");break;
				case 6:EMFrame_Packager(&EMFrame[i],0x01,2,"\x63\xE9");break;
				case 7:EMFrame_Packager(&EMFrame[i],0x01,2,"\x64\xE9");break;
				case 8:EMFrame_Packager(&EMFrame[i],0x01,2,"\x65\xE9");break;
				case 9:EMFrame_Packager(&EMFrame[i],0x01,2,"\x66\xE9");break;
				case 10:EMFrame_Packager(&EMFrame[i],0x01,2,"\x43\xC3");break;
				default:break;
			}//裝載完成
			if(i==0)Drv_Uart_Async_Send(&Com[4],EMFrame[i].TxBuf,14);
			else{Drv_Uart_Async_Send(&Com[5],EMFrame[i].TxBuf,14);}
			Tick_SetTimerDisable(EM_AskOverTime_L+i);
			Tick_SetTimerEnable(EM_AskOverTime_L+i,500,TIMER_ms);//開始計時
			st_Charger[i].EM_Data.status = Recving;
		}
		else
		{//接收
			if(i==0)
			{
				framelen = Drv_Uart_Async_Recv(&Com[4],EMFrame[i].RxBuf);
			}
			else if(i==1)
			{
				framelen = Drv_Uart_Async_Recv(&Com[5],EMFrame[i].RxBuf);
			}
			if(Tick_GetRemainTime(EM_AskOverTime_L+i)>0)
			{
				if(framelen != 0)
				{	//收到數據
					if(EMFrame_UnPackager(EMFrame[i].RxBuf,framelen)==0)
					{
						temp = DataConvert(EMFrame[i].RxBuf);//接收解析
						switch(st_Charger[i].EM_Data.step)
						{
							case 0:st_Charger[i].EM_Data.PhaseA_Voltage = temp;break;
							case 1:st_Charger[i].EM_Data.PhaseB_Voltage = temp;break;
							case 2:st_Charger[i].EM_Data.PhaseC_Voltage = temp;break;
							case 3:st_Charger[i].EM_Data.PhaseA_Current  = temp;break;
							case 4:st_Charger[i].EM_Data.PhaseB_Current = temp;break;
							case 5:st_Charger[i].EM_Data.PhaseC_Current = temp;break;
							case 6:st_Charger[i].EM_Data.Total_Power = temp;break;
							case 7:st_Charger[i].EM_Data.PhaseA_Power = temp;break;
							case 8:st_Charger[i].EM_Data.PhaseB_Power = temp;break;
							case 9:st_Charger[i].EM_Data.PhaseC_Power = temp;break;
							case 10:st_Charger[i].EM_Data.Total_Energy = temp;break;
							default:st_Charger[i].EM_Data.step = 0;break;
						}
						st_Charger[i].EM_Data.cnt_overtime = 0;
						st_Charger[i].EM_Data.step++;//接收成功發下一條
						if(st_Charger[i].EM_Data.step>10)st_Charger[i].EM_Data.step = 0;
						st_Charger[i].EM_Data.status = Need_Send;
					}
					else
					{
						st_Charger[i].EM_Data.status = Need_Send;//幀錯誤
					}
				}//沒收到數據等待什么都不做
			}
			else
			{
				st_Charger[i].EM_Data.status = Need_Send;//超時
				st_Charger[i].EM_Data.cnt_overtime++;
				if(st_Charger[i].EM_Data.cnt_overtime>10)
				{
					//報故障
				}
			}
		}
	}
}
/*******************************************************************************
* Function Name  : InitEM
* Description    : 初始化電表通訊
* Last Eidt Time : 2018年4月18日09:49:57
* Author				 :Vergil Yang
* Notice				 : 
*******************************************************************************/
void InitEM()
{
	//A表---EMFrame[0]
#if A_Electical_Meter
	EMFrame[0].BaudRate  = 9600;
  memcpy(EMFrame[0].TxBuf,"\x68\x99\x99\x99\x99\x99\x99\x68",8);
	
	InitUart4(EMFrame[0].BaudRate);
	//SyncBaudrate(0,9600);///波特率同步成功即初始化成功
#endif
	//B表---EMFrame[1]
#if B_Electical_Meter
	EMFrame[1].BaudRate  = 9600;
  memcpy(EMFrame[1].TxBuf,"\x68\x99\x99\x99\x99\x99\x99\x68",8);
	
	InitUART5(EMFrame[1].BaudRate);
	QueueInit(&EMQueue[1]);
	SyncBaudrate(0,9600);///波特率同步成功即初始化成功
#endif
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM