目次

ArduinoMega2560Modbus処理

 ArduinoMega2560は、シリアルインタフェースを
 0から3の4チャネル持ちます。

 チャネル2を、RS485上の通信プロトコルであるModbusに
 よる処理で、測定機器と情報交換してみました。

 通信相手は、電力計モニタのKM50C。



 Modbusを使うために、ArduinoMega2560はマスターで
 KM50Cはスレーブとします。

 通信プロトコルの諸元は、次のように指定。

データ転送速度 38.4kbps
データ長    8ビット
パリティ    EVEN
ストップビット 1ビット

 デフォルトでは、データ転送速度は9600bpsですが
 最高の38.4kbpsにし、ArduinoMega2560が反応可能
 かを調べることを目標にしました。

 Arduinoには、Modbusマスター用ライブラリが
 存在するので、使わせて貰います。

 ArduinoIDEから、ModbusMasterライブラリをダウン
 ロードすると、使えるようになりました。

 ModbusMasterライブラリ関係ソースコードを、GitHubから
 ダウンロードして解析。



 ディレクトリsrcにC++のソースコードが含まれています。

 Modbusはシーケンサ(Programmable Logic Circuit)で利用
 することを想定しているので、相手の計測制御機器のIDと
 内蔵してるレジスタへの書込み、読込みをフレームで処理
 しています。

 フレームの構成は、以下のようになってます。

 コマンドフレーム構成



 レスポンスフレーム構成



 どちらのフレームでも、CRCの計算が必要になりますが
 ModbusMasterライブラリで対応するので、使うだけなら
 気にしないで済みます。

 RS485による通信は、平衡かつ半二重になるのでICを使い
 ハードウエア上の面倒な処理を肩代わりして貰います。

 今回は、秋月電子で入手できるインタフェース基板を利用。



 6ピンをArduinoMega2560に接続します。
 6ピンの内容は、以下。

 Vccは、5V、3.3Vのどちらでもよく
 TxD、RxDはチャネル2に接続。
 nRE、DEは受信と送信を入れ替えるときに利用
 するので、通常GPIOで使っているピンに接続。

 nRE、TEの扱い方は、使っているICのブロック図を
 見ると、動作を理解できます。



 平衡伝送のため、次の様にAとY、BとZをショートし
 終端抵抗を接続して使います。



 ハードウエアの用意ができたなら、ModbusMasterライブラリを
 Arduinoスケッチで、どう利用するのかを見ていきます。

 ライブラリ利用宣言とピンアサイン

  Arduinoスケッチの先頭では、ライブラリの利用を
  宣言すると同時に、RS485のICのピンアサイン指定。

#include <ModbusMaster.h>

/* Power Controller */
ModbusMaster node_power;

#define MPOWER_TDE   6
#define MPOWER_RDE   7

  今回は、DEとnREをデジタル6、7ピンに接続します。



 初期化

  関数setupの中で、シリアルチャネルとModbusに関連する
  指定をして、初期化します。
  利用する関数として定義。

void init_power()
{
  /* set control pin directions */
  pinMode(MPOWER_TDE,OUTPUT);
  pinMode(MPOWER_RDE,OUTPUT);
  /* Main Controller */
  Serial2.begin(38400,SERIAL_8E1);
  /* Modbus slave ID 1 */
  node_power.begin(1, Serial2);
  /* Callbacks allow us to configure the RS485 transceiver correctly */
  node_power.preTransmission(preTransmission);
  node_power.postTransmission(postTransmission);
}

  preTransmission、postTransmissionは、サイレント
  インターバルを生成するために使います。
  2関数は、次のように定義。

void preTransmission()
{
  digitalWrite(MPOWER_TDE,HIGH);
  digitalWrite(MPOWER_RDE,HIGH);
}
 
void postTransmission()
{
  digitalWrite(MPOWER_TDE,LOW);
  digitalWrite(MPOWER_RDE,LOW);
}

  コマンド送信前に、RS485のICの送信部分をイネーブルにし
  受信部分をディセーブルにします。

  コマンド送信が終了したなら、送信部分をディセーブルにし
  受信部分をイネーブルにします。

  この2つの関数を利用することで、半二重を実現。

 通信処理

  コマンドは、スレーブIDと機能番号を連らねて
  レジスタアドレスを並べた後、CRCで締めます。



  ModbusMasterライブラリでは、機能番号を指定する関数は
  決められていて、次のように対応。

0x03 readHoldingRegisters
0x04 readInputRegisters

  また、電力計の内部レジスタアドレスはデータシートに
  一覧表にされています。



  電力計から計測値を取得する関数を定義。

byte get_power_ppm(unsigned long *ptr)
{
  byte ii ;
  byte jj ;
  word dh ;
  word dl ;
  unsigned long tmp ;
  byte flag ;
  byte result ;
  /* default */
  flag = 0 ;
  /* 0x03 voltage and current => 0x0000 - 0x0005 */
  result = node_power.readHoldingRegisters(0,12);
  if ( result == node_power.ku8MBSuccess ) {
    for ( ii = 0 ; ii < 13 ; ii++ ) {
      /* calculate pointer */
      jj = 2 * ii ;
      /* get word */
      dh = node_power.getResponseBuffer(jj);
      dl = node_power.getResponseBuffer(jj+1);
      /* concatenate */
      tmp = dh * 65536 + dl ;
      /* store */
      *(ptr+ii) = tmp ;
    }
  } else {
    flag |= (1 << 0) ;
  }
  delay(10);
  /* 0x03 voltage and current => 0x0006 - 0x000B */
  result = node_power.readHoldingRegisters(6,12);
  if ( result == node_power.ku8MBSuccess ) {
    for ( ii = 0 ; ii < 13 ; ii++ ) {
      /* calculate pointer */
      jj = 2 * ii ;
      /* get word */
      dh = node_power.getResponseBuffer(jj);
      dl = node_power.getResponseBuffer(jj+1);
      /* concatenate */
      tmp = dh * 65536 + dl ;
      /* store */
      *(ptr+ii+6) = tmp ;
    }
  } else {
    flag |= (1 < 1) ;
  }
  delay(10);
  /* 0x03 voltage and current => 0x000C */
  result = node_power.readHoldingRegisters(12,2);
  if ( result == node_power.ku8MBSuccess ) {
    /* get word */
    dh = node_power.getResponseBuffer(0);
    dl = node_power.getResponseBuffer(1);
    /* concatenate */
    tmp = dh * 65536 + dl ;
    /* store */
    *(ptr+12) = tmp ;
  } else {
    flag |= (1 << 2) ;
  }

  return flag ;
}

  32ビットの整数値を配列の要素として保存する
  仕様としました。配列は、次のように指定。

unsigned long ppmx[13];

  コーリングシーケンスは、次のように単純。

    /* get informations from METER */
    xx = get_power_ppm( ppmx );
    /* judge */
    if ( xx ) {
      rs_puts(" power getting is failed ");
      crlf();
    }
    show_power_ppm( ppmx );

  取得値を表示するために、次の関数を定義。

void show_power_ppm(unsigned long *ptr)
{
  char msg[11];
  char scode ;
  byte ii ;
  byte jj ;
  unsigned long tmp ;
  boolean sflag ;
  /* loop */
  for ( ii = 0 ; ii < 13 ; ii++ ) {
    sflag = OFF ;
    /* heading */
    switch ( ii ) {
      case 0  : 
                rs_puts("Voltage1    ");
                break ;
      case 1  : 
                rs_puts("Voltage2    ");
                break ;
      case 2  : 
                rs_puts("Voltage3    ");
                break ;
      case 3  : 
                rs_puts("Current1    ");
                break ;
      case 4  : 
                rs_puts("Current2    ");
                break ;
      case 5  : 
                rs_puts("Current3    ");
                break ;
      case 6  : 
                rs_puts("Factor      ");
                break ;
      case 7  : 
                rs_puts("Frequency   ");
                break ;
      case 8  : 
      case 9  : 
                rs_puts("Efficient    ");
                break ;
      case 10 : 
      case 11 : 
                rs_puts("No Efficient ");
                break ;
      case 12 : 
                rs_puts("Total        ");
                break ;
      default : 
                break ;
    }
    /* get value */
    tmp = *(ptr+ii) ;
    if ( tmp & 0x80000000 ) {
      sflag = ON ;
      tmp ^= 0xffffffff ;
      tmp++ ;
    }
    /* seperate */
    *(msg+10) = '\0' ;
    for ( jj = 0 ; jj < 10 ; jj++ ) {
      *(msg+9-jj) = get_asc( tmp % 10 );
      tmp /= 10 ;
    }
    /* zero surpress */
    if ( *(msg+0) == '0' ) {
      *(msg+0) = ' ' ;
      if ( *(msg+1) == '0' ) {
        *(msg+1) = ' ' ; 
        if ( *(msg+2) == '0' ) {
          *(msg+2) = ' ' ;
          if ( *(msg+3) == '0' ) {
            *(msg+3) = ' ' ; 
            if ( *(msg+4) == '0' ) {
              *(msg+4) = ' ' ; 
              if ( *(msg+5) == '0' ) { *(msg+5) = ' ' ; }
            }
          }
        }
      }
    }
    /* show */
    scode = ' ' ;
    if ( sflag ) { scode = '-' ; }
    rs_putchar( scode );
    rs_puts( msg );
    /* unit */
    switch ( ii ) {
      case 0  : 
      case 1  : 
      case 2  : 
                rs_puts(" x 0.1V");
                break ;
      case 3  : 
      case 4  : 
      case 5  : 
                rs_puts(" x 0.001A");
                break ;
      case 6  : 
                rs_puts(" x 0.01");
                break ;
      case 7  : 
                rs_puts(" x 0.1Hz");
                break ;
      case 8  : 
                rs_puts(" x 0.1 W");
                break ;
      case 9  : 
                rs_puts(" x 0.01kW");
                break ;
      case 10 : 
                rs_puts(" x 0.1 var");
                break ;
      case 11 : 
                rs_puts(" x 0.01 kvar");
                break ;
      case 12 : 
                rs_puts(" x 0.1 kWh");
                break ;
      default :
                break ;
    }
    /* new line */
    crlf();
  }
}

  実際に端末ソフトを使って、電力計から計測値を
  入力すると、次のように表示します。

M get power from METER
M
Voltage1           2051 x 0.1V
Voltage2           2073 x 0.1V
Voltage3           2097 x 0.1V
Current1           2937 x 0.001A
Current2           2888 x 0.001A
Current3           2935 x 0.001A
Factor      -      0096 x 0.01
Frequency          0500 x 0.1Hz
Efficient    -      0232 x 0.1 W
Efficient    -      0002 x 0.01kW
No Efficient        0063 x 0.1 var
No Efficient        0000 x 0.01 kvar
Total               0160 x 0.1 kWh

M
Voltage1           2050 x 0.1V
Voltage2           2073 x 0.1V
Voltage3           2095 x 0.1V
Current1           2935 x 0.001A
Current2           2887 x 0.001A
Current3           2932 x 0.001A
Factor      -      0096 x 0.01
Frequency          0499 x 0.1Hz
Efficient    -      0224 x 0.1 W
Efficient    -      0002 x 0.01kW
No Efficient        0063 x 0.1 var
No Efficient        0000 x 0.01 kvar
Total               0160 x 0.1 kWh

 項目と数字の間に「−」があると、その数値は
 負の数になっていると読みます。


目次

inserted by FC2 system