发新帖本帖赏金 0.10元(功能说明)我要提问
12下一页
返回列表
打印

【MCU方案】+M430F147实现三相交流智能数显仪表

[复制链接]
3776|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 stu_deepblue 于 2015-7-10 16:03 编辑

三相交流能数显仪表, 广泛适用于各行业供配电场所、能源管理、自动化以及智能化网络监控系统等。

此款仪表适用于220/380V低压系统,产品提供电压、电流、有功功率、无功功率、功率因数、频率、有功电度、无功电度等电参数的组合测量,扩展两路外部有源开关量输入与控制开关动作的两路继电器报警输出功能,通过RS485/MODBUS总线通讯,对仪表进行组网管理,实现自动控制。

下表为参数以及相关电磁参数:

  
参  数
  
指   标
  
精度等级
  
电压、电流0.2级,功率0.5级,有功电度1级,无功电度2级
  
适用网络
  
三相四线
  
显示数据
  
电流
四位,根据数值大小切换小数点位,单位安培。
电压
显示相/线电压,单位伏特。
有功功率
四位,根据数值大小切换小数点位。为正时,无符号,四位有效数字;为负时,有符号,三位有效数字。单位为千瓦。
无功功率
四位,根据数值大小切换小数点位。为正时,无符号,四位有效数字;为负时,有符号,三位有效数字。单位为千乏。
功率因数
为正时,无符号,四位有效数字,三位小数。
  
为负时,有符号,三位有效数字,两位小数。
频率
四位,两位小数点,单位赫兹。
有功电度
最大可以到达9位,一位小数点,分行显示,数码管第三行显示千位以下的数,第二行显示千位以上的数,第一行显示最高位,有功电度单位千瓦时,无功电度单位千乏时。
无功电度
  
输入
  
额定电流
AC5A
额定电压
AC220V
过载
持续电流1.2倍,瞬间电流10倍/1秒,电压2倍/1秒
频率
50Hz
开关量
220Vac±25%,220Vdc±25%
  
继电器输出
  
220VAC/5A,30VDC/5A
  
工作电源
  
宽压型
交流85V ~ 265V;直流80V ~ 300V
功耗
小于10VA
  
通讯
  
波特率
支持4800bps和9600bps两种波特率
协议
标准Modbus-RTU协议
  
绝缘强度
  
2KV(测试电压为交流有效值)
  
绝缘电阻
  
≥ 50MΩ
  
MTBF
  
≥ 50000h
  
工作条件
  
工作温度:-10℃~+55℃  
极限工作温度:-25℃~+55℃  
存储温度:-25℃~+70℃
相对湿度:5%~95%,无凝露
电磁特性
静电抗扰性试验:IEC61000-4-2,Level4
辐射抗扰性试验:IEC61000-4-3,Level3
快速瞬变脉冲群抗扰性试验:IEC61000-4-4,Level4
浪涌抗扰性试验:IEC61000-4-5,Level4
传导射频干扰试验:EN 55022,Class B
辐射射频干扰试验:EN 55022,Class B
  

打赏榜单

王七七 打赏了 0.10 元 2015-07-14

相关帖子

沙发
stu_deepblue|  楼主 | 2015-7-10 16:04 | 只看该作者
本帖最后由 stu_deepblue 于 2015-7-10 17:41 编辑

本楼为硬件楼层
首先是MCU部分整体

MCU局部放大

MCU主板

电流测量部分

电流PCB

电压测量

模拟量,开关量

继电器部分

485部分,其中485是用公司现有模块,所以没有电路图

MCU部分原理图
625.pdf (53.23 KB)



使用特权

评论回复
板凳
stu_deepblue|  楼主 | 2015-7-10 16:04 | 只看该作者
本帖最后由 stu_deepblue 于 2015-7-10 17:59 编辑

本楼为程序楼层,限于保密需要,只有部分:ad.h文件,relay.c部分,main.c文件

ad.h文件

#ifndef AD_H
#define AD_H

//定义硬件通道对应cpu的采样通道
#define  VA_CH    INCH_4
#define  VB_CH    INCH_5
#define  VC_CH    INCH_6
#define  VN_CH    INCH_7
#define  IC_CH    INCH_3
#define  IB_CH    INCH_2
#define  IA_CH    INCH_1

//定义电度的宏定义
//内部电度保存提供一个小数点
#define  ENERGY_BASE    3600000
#define  ENERGY_MAX     999999999


typedef struct{              
               unsigned  int  Va;          //四线星形为相电压
               unsigned  int  Vb;          //三相三线为线电压
               unsigned  int  Vc;
               unsigned  int  Vab;
               unsigned  int  Vbc;
               unsigned  int  Vca;
               unsigned  int  Ia;
               unsigned  int  Ib;
               unsigned  int  Ic;
               long           Pavg;         //三相总有功功率
               long           Qavg;         //三相总无功功率
               int            PF;   //总的功率因数
               int            Pa;   //a相有功功率
               int            Pb;   //b相有功功率
               int            Pc;   //c相有功功率
               int            Qa;   //a相无功功率
               int            Qb;   //b相无功功率
               int            Qc;   //c相无功功率
               int            PFa;      //a相功率因数
               int            PFb;          //b相功率因数
               int            PFc;   //c相功率因数
               unsigned int   Hz; //频率
               unsigned int   KVAa;  //a相视在功率
               unsigned int   KVAb; //b相视在功率
               unsigned int   KVAc; //c相视在功率

               unsigned long  ImpKwH;
               unsigned long  ExpKwH;
               unsigned long  ImpKvarH;
               unsigned long  ExpKvarH;
               
               unsigned long  ImpKwHLeft;
               unsigned long  ExpKwHLeft;
               unsigned long  ImpKvarHLeft;
               unsigned long  ExpKvarhLeft;

               int Pa2,Pb2,Pc2;             //功率修正
               int Qa2,Qb2,Qc2;
               
               unsigned char  IoStatus;
}POWERMETER;
            
            
//  硬件乘法  无符号 16 乘 16位   060214
#define UMul16_16(a,b) _DINT();_NOP();_NOP();MPY = a; OP2 = b; _NOP();_NOP();_EINT();   //060214

//  硬件乘法  有符号 16 乘 16位   060223
#define Mul16_16(a,b) _DINT();_NOP();_NOP();MPYS = a; OP2 = b; _NOP();_NOP();_EINT();   //060223

extern POWERMETER PowerMeter;
extern unsigned char AdFlag;
extern unsigned char VoltMode;

extern unsigned int I0_Value;
extern void InitAdc(void);
extern void AdCal(void);
extern void InitEnergy(void);

#endif

relay.c部分
#include "flash.h"
#include "relay.h"
#include "port.h"
#include "ad.h"
#include  "mymath.h"

extern CONFIG_DEVICE Config_Device;
extern CONFIG_SYSTEM Config_System;
extern unConfig      Config_Function;

StrRelay strRelay[2];//定义一个继电器结构体数组
unsigned int Relaytime[2];

//初始化继电器部分程序
//在系统初始化过程调用
void InitRelay(void);

//继电器闭合函数
void CloseRelay(unsigned char Num);
//继电器断开函数
void OpenRelay(unsigned char Num);

//继电器越限判断函数
//函数在主程序循环中调用
//一秒钟AD数值计算完毕后执行
void RelayLimit(unsigned char Num);

//继电器动作判断函数
//函数在10毫秒定时器中进行调用
void RelayOperate(unsigned char Num);

//060222  该函数用于继电器参数被修改之后重新调入参数
void ReInitRelay(void);

//初始化继电器部分程序
//初始化端口
//初始化设置数据
//初始化定时器
//初始化继电器状态
//初始化相关标志
void InitRelay(void)
{
   unsigned int iTemp;
   //程序调用前必须判断是否已经激活了继电器功能
   //如果没有的话,则不可以初始化端口和获取设置数值
   //但相应变量仍然需要得到初始化
   if(Config_Function.Info.bRelay == 1)
   {
      //初始化端口
      OpenRelay1;
      OpenRelay2;

      //初始化设置数值
      strRelay[0].Mode      = Config_System.Relay1_Mode;
      strRelay[0].Object    = Config_System.Relay1_Object;
      strRelay[0].HILimit   = Config_System.Relay1_HILimit;
      strRelay[0].LOWLimit  = Config_System.Relay1_LOWLimit;
      strRelay[0].DelayTime = Config_System.Relay1_DelayTime;
      strRelay[1].Mode      = Config_System.Relay2_Mode;
      strRelay[1].Object    = Config_System.Relay2_Object;
      strRelay[1].HILimit   = Config_System.Relay2_HILimit;
      strRelay[1].LOWLimit  = Config_System.Relay2_LOWLimit;
      strRelay[1].DelayTime = Config_System.Relay2_DelayTime;
      
      iTemp = strRelay[0].DelayTime;              
      UMul16_16(50,iTemp) ;                        
      Relaytime[0] = RESLO ;
      
      iTemp = strRelay[1].DelayTime;              
      UMul16_16(50,iTemp) ;                        
      Relaytime[1] = RESLO ;
      
   }
   else
   {
      //没有继电器功能,则应当都清零
      strRelay[0].Mode      = 0;
      strRelay[0].Object    = 0;
      strRelay[0].HILimit   = 0;
      strRelay[0].LOWLimit  = 0;
      strRelay[0].DelayTime = 0;
      strRelay[1].Mode      = 0;
      strRelay[1].Object    = 0;
      strRelay[1].HILimit   = 0;
      strRelay[1].LOWLimit  = 0;
      strRelay[1].DelayTime = 0;
   }
   
   //初始化状态
   strRelay[0].Status  = 0;
   strRelay[1].Status  = 0;
   
   //初始化定时器
   strRelay[0].Counter = 0;
   strRelay[1].Counter = 0;
   
   //初始化标志
   strRelay[0].Flag    = 0;
   strRelay[1].Flag    = 0;
}

//060222  该函数用于继电器参数被修改之后重新调入参数
void ReInitRelay(void)
{
   unsigned int iTemp;
   
   if(Config_Function.Info.bRelay == 1)
   {
      //初始化设置数值
      strRelay[0].Mode      = Config_System.Relay1_Mode;
      strRelay[0].Object    = Config_System.Relay1_Object;
      strRelay[0].HILimit   = Config_System.Relay1_HILimit;
      strRelay[0].LOWLimit  = Config_System.Relay1_LOWLimit;
      strRelay[0].DelayTime = Config_System.Relay1_DelayTime;
      strRelay[1].Mode      = Config_System.Relay2_Mode;
      strRelay[1].Object    = Config_System.Relay2_Object;
      strRelay[1].HILimit   = Config_System.Relay2_HILimit;
      strRelay[1].LOWLimit  = Config_System.Relay2_LOWLimit;
      strRelay[1].DelayTime = Config_System.Relay2_DelayTime;
      
      iTemp = strRelay[0].DelayTime;              
      UMul16_16(50,iTemp) ;                        
      Relaytime[0] = RESLO ;
      
      iTemp = strRelay[1].DelayTime;              
      UMul16_16(50,iTemp) ;                        
      Relaytime[1] = RESLO ;
   }
   else
   {
      //没有继电器功能,则应当都清零
      strRelay[0].Mode      = 0;
      strRelay[0].Object    = 0;
      strRelay[0].HILimit   = 0;
      strRelay[0].LOWLimit  = 0;
      strRelay[0].DelayTime = 0;
      strRelay[1].Mode      = 0;
      strRelay[1].Object    = 0;
      strRelay[1].HILimit   = 0;
      strRelay[1].LOWLimit  = 0;
      strRelay[1].DelayTime = 0;
   }
}
//继电器闭合函数
//传入参数为继电器通道号
void CloseRelay(unsigned char Num)
{
   //闭合相应通道继电器
   //修改状态
   //清除时间计数器
   if(Num == 0)
   {
      if(strRelay[0].Status  == 0)
      {
         CloseRelay1;
         strRelay[0].Status  = RELAY_CLOSE;
         strRelay[0].Counter = 0;
      }
   }
   else
   {
       if(strRelay[1].Status  == 0)
      {
         CloseRelay2;
         strRelay[1].Status  = RELAY_CLOSE;
         strRelay[1].Counter = 0;
      }
   }
}

//继电器断开函数
//传入参数为继电器通道号
void OpenRelay(unsigned char Num)
{
   //断开相应通道继电器
   //修改状态
   //清除时间计数器
   if(Num == 0)
   {
      if(strRelay[0].Status  == 1)
      {
         OpenRelay1;
         strRelay[0].Status  = RELAY_OPEN;
         strRelay[0].Counter = 0;
      }
   }
   else
   {
       if(strRelay[1].Status  == 1)
      {
         OpenRelay2;
         strRelay[1].Status  = RELAY_OPEN;
         strRelay[1].Counter = 0;
      }
   }
}

//继电器越限判断函数
//传入参数为继电器通道号
void RelayLimit(unsigned char Num)
{
   unsigned int TempValue;
   
   //判断继电器功能是否被激活
   //未被激活则不执行操作
   if(Config_Function.Info.bRelay == 0)
      return;
   
   //越限判断主体
   //控制模式判断
   if(strRelay[Num].Mode == RELAY_LOCAL_MODE)
   {
      //继电器对象是否有效
      if(strRelay[Num].Object != RELAY_OBJECT_NULL)
      {
         //根据继电器对象确定有效参数百分比
         //除需要确定对象外还需要确定额定测量参数
         if(strRelay[Num].Object <= RELAY_OBJECT_VC)
         {
            //电压对象
            if(Config_Function.Info.bRate >= 2)
            {
               //高压网络采用线电压
               TempValue = *((unsigned int *)&PowerMeter.Vab + strRelay[Num].Object -1 );
               TempValue /= 100;
            }
            else
            {
               //低压网络采用相电压
               TempValue = *((unsigned int *)&PowerMeter.Va + strRelay[Num].Object -1 );
               TempValue /= 220;
            }
         }
         else if(strRelay[Num].Object <= RELAY_OBJECT_IC)
         {
            //电流对象
            TempValue = *((unsigned int *)&PowerMeter.Ia + strRelay[Num].Object -4 );
            
            if(Config_Function.Info.bRate & 0x01)
            {
               //1A电流额定
               TempValue /= 10;
            }
            else
            {
               //5A电流额定
               TempValue /= 50;
            }
         }
         else
         {
            //零序电流
            TempValue = I0_Value ;
            
            if(Config_Function.Info.bRate & 0x01)
            {
               //1A电流额定
               TempValue /= 10;
            }
            else
            {
               //5A电流额定
               TempValue /= 50;
            }
         }         
         
         //上下限值判断     // 临界值应该 不动作   
         if((TempValue <= strRelay[Num].HILimit) && (TempValue >= strRelay[Num].LOWLimit))
         {
            strRelay[Num].Flag = 0;
         }
         else
         {
            //发生参数越限   //060330_yjg
            if(TempValue > strRelay[Num].HILimit)     //越上限   
            {
               if(strRelay[Num].Flag != 1)
               {
                 strRelay[Num].Flag = 1;
                 strRelay[Num].Counter = 0;
               }
            }
            else                                     //越下限      
            {
               if(strRelay[Num].Flag != 2)
               {
                 strRelay[Num].Flag = 2;
                 strRelay[Num].Counter = 0;
               }              
            }
         }
      }
      else
      {
         strRelay[Num].Flag = 0;
      }      
   }
   else
   {
      strRelay[Num].Flag = 0;
   }   
}

//继电器动作判断函数
//传入参数为继电器通道号
void RelayOperate(unsigned char Num)
{
   //判断继电器功能是否被激活
   //未被激活则不执行操作
   if(Config_Function.Info.bRelay == 0)
      return;
   
   //继电器模式判断
   if(strRelay[Num].Mode == RELAY_LOCAL_MODE)
   {
      //本地控制模式
      if(strRelay[Num].Flag)
      {
         //如果继电器已经闭合,则不需要判断时间
         if(strRelay[Num].Status == RELAY_OPEN)
         {
            strRelay[Num].Counter++;
         
            //延时要求满足判断
            //这里的时间判断需要依赖函数调用的周期
            if(strRelay[Num].Counter >= Relaytime[Num])
               CloseRelay(Num);
         }
      }
      else
      {
         OpenRelay(Num);
      }     
   }
   else
   {
      //远程控制模式
      //只有在继电器闭合状态下判断是否返回
      if(strRelay[Num].Status == RELAY_CLOSE)
      {
         //只有设定了延时复归时间才进行判断
         //这里的时间判断需要依赖函数调用的周期
         if(strRelay[Num].DelayTime != 0)
         {
            strRelay[Num].Counter++;

            if(strRelay[Num].Counter >= Relaytime[Num])
               OpenRelay(Num);
         }
      }
   }
}



main.c部分

#include <msp430x14x.h>
#include "port.h"
#include "timer.h"
#include "ad.h"
#include "adint.h"
#include "comm.h"  
#include "mymath.h"
#include "watchdog.h"
#include "display.h"
#include "flash.h"
#include "relay.h"
#include "analog.h"
extern void WriteDD_NVROM(void);
extern void InitDisp(void);    //060223       初始化显示

void InitClock(void);
void InitSystem(void);
void scanLED(void);

void InitClock(void)
{
   unsigned int i;
   DCOCTL  =  0x60;
   BCSCTL1 = 0 ;
   for(i=0;i<8000;i++)
   {
      _NOP();
   }
   BCSCTL2 = SELM_2  | DIVM_0 | DIVS_0 |SELS ;
   while(IFG1 & OFIFG)
   {
      IFG1 = 0 ;
      BCSCTL2 = SELM_2  | DIVM_0 | DIVS_0 |SELS ;
   }
}
  
void InitSystem(void)
{
   InitClock();
   InitFlash();
   InitPort();   
   InitTimerA();
   InitTimerB();
   InitRelay();
   
   //初始化模拟量输出
   //add code here
   InitAnalog();           //060228
   
   InitAdc();
   InitUart0();  
   //060223  初始化显示
   InitDisp() ;
}

void main(void)
{
   unsigned int EnergyTimer = 0;
   
  unsigned char i=0;
   _DINT();
   StopWDT;      
   InitSystem();
   AdFlag |=  HZ_SAMPLE;
   StartWDT ;
   _EINT();

   for(;;)
   {
      ClrWDT;
      
      //按键检测并且刷新显示
     KeyResponse();
      
      //通讯检测并且响应
      CommDeal();
      
      //自动刷新显示
      if(AutoDispFlag)
      {
         if(DispStatus == 0)
         {
            DispIndex++;
            RealDataSubIndex = 0;
            //显示目录自动跳到下一有效数据           
            if(DispIndex > (DISP_DATA_NUM - 1))
              DispIndex = 0;
            DispIndex = DispGetNextIndex(DispIndex);
            AutoDispFlag = 0;
         }
      }     

      //AD数据计算并且刷新显示
     if(AdFlag&1)
      {
        AdCal();//PowerMeter.Pa = 11000;PowerMeter.Pb = 11000;PowerMeter.Pc = 11000;
        AdFlag &= ~1;
         EnergyTimer++;
         if(EnergyTimer > 10)
         {
            WriteDD_NVROM();
            EnergyTimer = 0;
         }
         
        RelayLimit(0);
         RelayLimit(1);
         
         //模拟量输出
         //add code here  //060228
         Ana**ut(0);
        Ana**ut(1);
         
         Display();
         
      }
   }  
}

使用特权

评论回复
地板
stu_deepblue|  楼主 | 2015-7-10 16:05 | 只看该作者
本帖最后由 stu_deepblue 于 2015-7-10 18:17 编辑

此楼为实物样品照片楼层,前4张为PCBA,后面的是挂在展厅的展示样机,实际接入三相电压电流后照片
采样板背面

主板背面

主板正面

采样板正面

相电压界面

电流界面

有功功率界面

无功功率界面

无功电能界面

有功电能界面

频率界面

功率因素界面

线电压界面

编程界面

20150710_161738.jpg (2.28 MB )

20150710_161738.jpg

使用特权

评论回复
5
dirtwillfly| | 2015-7-10 16:17 | 只看该作者
感谢分享

使用特权

评论回复
6
vivilzb1985| | 2015-7-11 11:53 | 只看该作者
此款仪表适用于220/380V低压系统,产品提供电压、电流、有功功率、无功功率、功率因数、频率、有功电度、无功电度等电参数的组合测量,扩展两路外部有源开关量输入与控制开关动作的两路继电器报警输出功能,通过RS485/MODBUS总线通讯,对仪表进行组网管理,实现自动控制。

使用特权

评论回复
7
vivilzb1985| | 2015-7-11 11:54 | 只看该作者
先看其功能用途的还是挺多的,输出的参数比较详细的。

使用特权

评论回复
8
comeon201208| | 2015-7-11 13:16 | 只看该作者
用147的做这个的,资源上比较紧张吧?

使用特权

评论回复
9
comeon201208| | 2015-7-11 13:16 | 只看该作者
看这个这个实现的功能还是蛮多的。

使用特权

评论回复
10
stu_deepblue|  楼主 | 2015-7-11 15:18 | 只看该作者
comeon201208 发表于 2015-7-11 13:16
用147的做这个的,资源上比较紧张吧?

还好,这是第一代产品,已经停产了,最新产品已经不用147了,这款产品曾经撑起公司半边天的

使用特权

评论回复
11
Thor9| | 2015-7-11 21:55 | 只看该作者
这款产品的确是比较经典,有很多是可以借鉴的

使用特权

评论回复
12
冰河w| | 2015-7-12 13:01 | 只看该作者
三相电的功是怎么算的

使用特权

评论回复
13
stu_deepblue|  楼主 | 2015-7-14 10:02 | 只看该作者
冰河w 发表于 2015-7-12 13:01
三相电的功是怎么算的

和单相的一样,只不过要乘3

使用特权

评论回复
14
JY-DX-JY| | 2015-7-14 17:26 | 只看该作者
顶一个。

使用特权

评论回复
15
王七七| | 2015-7-14 20:13 | 只看该作者
要是打包发上去就好了

使用特权

评论回复
16
shenmu2012| | 2015-7-15 19:41 | 只看该作者
产品提供电压、电流、有功功率、无功功率、功率因数、频率、有功电度、无功电度等电参数的组合测量。

使用特权

评论回复
17
shenmu2012| | 2015-7-15 19:41 | 只看该作者
这个实现的功能还是蛮细致的,,跟电相关的参数参量的都检测到了。

使用特权

评论回复
18
firstblood| | 2015-7-15 21:55 | 只看该作者
这个方案是非常不错的,硬件设计上不是很复杂的,重点在软件部分的。

使用特权

评论回复
19
stu_deepblue|  楼主 | 2015-7-16 10:16 | 只看该作者
firstblood 发表于 2015-7-15 21:55
这个方案是非常不错的,硬件设计上不是很复杂的,重点在软件部分的。

嗯,硬件的确不是很难,关键是软件部分,特别是电度方面,有段时间电度一直出问题

使用特权

评论回复
20
lefeng| | 2015-7-16 22:45 | 只看该作者
这里面用到什么算法了吗

使用特权

评论回复
发新帖 本帖赏金 0.10元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

23

主题

336

帖子

1

粉丝