外部インターフェース

USBカメラ

概要

レシピ言語ではUSBカメラを制御するAPIを準備しています。本サンプルコードではこのUSBカメラのAPIについて説明します。

USBカメラ機能の特徴

USBカメラ機能の概要を以下に示します。

※ UVC対応のUSBカメラでもカメラ機器によって動作しない場合があります。
※ 動作確認済みのUSBカメラは「ハードウェアリファレンスの動作確認済みUSBカメラ」を参照ください。
※USB Type-AはUSBカメラ専用となり、他のUSB機器を動かすことは出来ません。

USBカメラ制御

システム概要

USBカメラのAPIとシステム動作のイメージ図を以下に示します。

カメラ

USBカメラ機能はアプリケーションにて画像データを直接扱わない作りとなっており、カメラタスク側のカメラバッファに保存されている画像データをモデムタスクが参照して、HTTPボディーとして直接クラウドに送信する作りとなっています。

[カメラバッファについて]

受信した動画はJPEG画像として内蔵メモリのカメラバッファに保存します。
カメラバッファに保存できる枚数は解像度により異なります。

解像度 保存可能枚数
QVGA 190枚
VGA 46枚
HD 14枚
FlulHD 6枚

カメラバッファは最大数を超えた場合には、古い画像から上書きします。

USBカメラはサンプリングレート(撮影したJPEG画像をカメラバッファに記録する周期)を設定せずに撮影開始した場合、カメラバッファへの記録する周期はカメラ撮影のフレームレートに従います。サンプリングレートを事前に設定して撮影開始した場合、指定された周期でカメラバッファに画像を記録します。
細かい周期で記録する必要がない場合や、極力長期間の画像を記録したい場合はサンプリングレートを設定することを推奨します。

カメラバッファの基本動作

カメラバッファに記録される画像は、記録上限に達すると古い画像を消去して上書きするリングバッファ方式です。
カメラバッファに記録する速度よりLTE送信速度の方が遅い場合は全ての画像データを送信することができません。
例えばドライブレコーダーのように一定期間内の画像データのまとまりを欠落なくLTE送信したい場合は、USBカメラの撮影を停止してから、後にLTE送信することで実現できます。

画像データをLTE送信する際には、カメラバッファ内の「最新の画像」「最古の画像」「n番目に古い画像」のようにどの画像データを送信するか任意に指定することができます。
画像送信に成功すると、その画像より古い画像データは全て削除されます。もしカメラバッファ内の全てのデータを送信したい場合は古い画像から順番に送信するようにしてください。
全ての画像データを送信したい場合は「最古の画像」を指定して送信を複数回実行することで実現可能です。


USBカメラの制御

USBカメラ機能は以下の制御が可能です。
・撮影開始要求(解像度、フレームレートを実行時に指定する)
・撮影停止要求
・サンプリングレート(撮影したJPEG画像をカメラバッファに記録する周期)の変更要求
・USB接続状態の監視用リスナー登録要求
・USB接続状態の監視用リスナー登録解除要求

※USBカメラの設定変更要求や撮影開始/停止要求は単一タスクからのみ利用可能
※サンプリングレートは撮影するフレームレートとは別に最大60秒周期まで指定可能 (例えば、30fpsでUSBカメラ撮影を行いながら、カメラバッファへの記録は0.5秒に1枚という指定も可能)


カメラ制御のAPI

API 説明
1 UVC_VideoStreamCtrl() 撮影開始や停止、解像度やフレームレートの設定をする
2 UVC_BufCtrlSmpRate() カメラバッファに記録する周期を設定するためのAPI
3 UVC_RegistUsbVideoStatusListener() USB接続状態の監視リスナー登録要求

UVC_VideoStreamCtrl()

USBカメラを制御/設定を行うAPIです。

UVC_VideoStreamCtrl(uint8 start, uint8 format, uint16 h_size, uint16 v_size, uint16 fps) 
    start : USBカメラの撮影開始/停止設定 (0:停止, 1:開始)
    format : USBカメラの画像形式設定 (UVC_STREAM_CODEC_MJPEG固定)
    h_size : 水平方向の解像度設定 (ex:1280)
    v_size : 垂直方向の解像度設定 (ex:720)
    fps : カメラのfps設定 ([0.1fps]単位で利用可)
        (例えば30.0fpsの場合は300、7.5fpsの場合は75を設定する)
[撮影の開始]

USBカメラの撮影開始は以下のように実施します。

#incliude "uvcAPI.h"
   ~
func any_func() {

    // カメラ撮影スタート(解像度:HD, フレームレート:30[fps])
    UVC_VideoStreamCtrl(UVC_STREAM_CTRL_START, UVC_STREAM_CODEC_MJPEG, 1280, 720, 300);

    return(0);
}

開始要求によりUSBカメラ機器との接続処理が行われます。カメラの起動時間後(5~10秒)後に最初の画像がUSBカメラから送らてきます。

USBカメラ機器からの応答が無い場合は、USBカメラとの接続保証の観点で、接続のリトライ処理を数十秒周期で繰り返します。リトライ処理を止めたい場合は後述の撮影停止要求を実施ください。

USBケーブルの接続状況を監視したい場合は、後述の「USB接続状態の監視リスナー登録API」を利用することで監視することが可能です。例えば「USBカメラが接続されておらずUSB接続に失敗したこと」や「その後のリトライ処理を開始したこと」などをイベントで取得することができます。

[撮影の停止]

USBカメラの撮影停止は以下のように実施します。

#incliude "uvcAPI.h"
   ~
func any_func() {

    // カメラ撮影ストップ
    UVC_VideoStreamCtrl(UVC_STREAM_CTRL_STOP, 0, 0, 0, 0);

    return(0);
}

撮影を停止しただけではカメラバッファの画像データは削除されず、保持された状況を継続します。次の撮影開始要求や端末電源OFFやシステムリセットの発生にて、カメラバッファの画像データは全て削除されます。


UVC_BufCtrlSmpRate()

カメラバッファに記録する周期を設定を行うAPIです。

UVC_BufCtrlSmpRate(uint32 smp_rate)
    smp_rate : カメラバッファに記録する周期設定
      (0=fpsに従う、または1~60000[msec]で指定)
[サンプリングレートの変更方法]

サンプリングレート(撮影したJPEG画像をカメラバッファに記録する周期)変更は以下のように撮影を開始する前に設定を行います。

#incliude "uvcAPI.h"
   ~
func any_func() {

    // サンプリングレートを2秒に1枚の設定に変更
    UVC_BufCtrlSmpRate(2000);
    // カメラ撮影スタート(解像度:HD, フレームレート:30[fps])
    UVC_VideoStreamCtrl(UVC_STREAM_CTRL_START, UVC_STREAM_CODEC_MJPEG, 1280, 720, 300);

    return(0);
}

UVC_RegistUsbVideoStatusListener()

USB制御ステータス変化通知のリスナー登録を行うAPIです。

UVC_RegistUsbVideoStatusListener(uint8 event_id, uint8 mode_sw)
    event_id : イベント通知ID (1-254)
    mode_sw: 登録設定 (0:登録解除, 1:登録設定)

USBバスの状態監視リスナー登録APIに対応 (例えば、USBカメラ抜去やストリーム開始や失敗といった状態変化をイベント通知で取得可能)
USBバスの状態監視リスナー登録APIは複数タスクから利用可能
※通知されるイベントは4章にて後述します。


画像のクラウド送信

カメラバッファから画像をクラウドに送信をする場合は、HTTP送信のAPIにて該当の画像を指定する形で送信を実施します。

MMG_SendHTTP()に対して、以下の引数を渡します。
 第5引数:MMG_MOVIE_BUF_IDを代入
 第6引数:カメラバッファの画像の選択方式
  0x0000:最新の画像
  0xFFFF:最古の画像、
  それ以外の数値:nを指定した場合は最新の画像からn番手前に保存されている画像
            ※n=1の場合は、最新の画像のひとつ前の画像になります。

#incliude "modemMgrTask.h"
#incliude "uvcAPI.h"
   ~
   ※ 事前にHTTPに関する基本設定を済ませておく(ここでは詳細割愛します) ※
   ~
func any_func() {

    // カメラバッファ内の最新の画像を送信する(第5引数と第6引数で画像指定を行う)
    MMG_SendHTTP(
      timeout, 
      sync, 
      ex_result, 
      send_id, 
      MMG_MOVIE_BUF_ID,  // カメラバッファを意味する設定値
      0,                 // 今回は「最新の画像」を設定
      burst, 
      session, 
      retain, 
      http_rsp_code, 
      rcv_buff_id, 
      send_success_num
      );

    return(0);
}

USBの接続状態を監視

USBとの接続状態の監視をするには、APIのUVC_RegistUsbVideoStatusListener()を利用することで、下記のような状態を知ることができます。

define名 数値 通知される契機
UVC_RESULT_STOP 1 撮影停止制御完了
UVC_RESULT_START 2 撮影開始制御後最初の画像を取得
UVC_RESULT_NO_STREAM_TIMEOUT 3 タイムアウトによる撮影失敗
UVC_RESULT_STARTED_RETRY 4 撮影のリトライ処理開始
UVC_RESULT_P0_VBUS_ON 100 カメラ以外の情報
UVC_RESULT_P1_VBUS_ON 101 カメラの電源供給開始
UVC_RESULT_P2_VBUS_ON 102 カメラ以外の情報
UVC_RESULT_P0_ENUMERATION_COMPLETED 103 カメラ以外の情報
UVC_RESULT_P1_ENUMERATION_COMPLETED 104 カメラのUSB接続完了
UVC_RESULT_P2_ENUMERATION_COMPLETED 105 カメラ以外の情報
UVC_RESULT_P0_VBUS_OFF 106 カメラ以外の情報
UVC_RESULT_P1_VBUS_OFF 107 カメラの電源供給停止
UVC_RESULT_P2_VBUS_OFF 108 カメラ以外の情報
UVC_RESULT_P1_DISCONNECT_HUB 109 カメラ抜去を検出
UVC_RESULT_P2_DISCONNECT_HUB 110 カメラ以外の情報
UVC_RESULT_P0_DISCONNECT_H7 111 カメラ以外の情報
UVC_RESULT_CALLER_DIFFERENCE 200 撮影中の別タスクからのAPI実行
UVC_RESULT_EXCLUSIVE 201 同一タスクからのAPI重複実行
UVC_RESULT_BUFF_SETUP_ERROR 202 カメラバッファの初期化に失敗
UVC_RESULT_NOT_APPLICABLE_SMP_RATE 203 カメラバッファのサンプリングレートの範囲外指定
UVC_RESULT_NOT_APPLICABLE_RESOLUTION 204 撮影開始要求時の解像度の不適切な指定
UVC_RESULT_NOT_APPLICABLE_FORMAT 205 撮影開始要求時の画像圧縮形式の不適切な指定
UVC_RESULT_NOT_APPLICABLE_FPS 206 撮影開始要求時のフレームレートの不適切な指定
UVC_RESULT_ERROR 255 その他エラー

※"uvcAPI.h"に定義

USB接続の状態変化時のイベントが発行されます。
そのイベントの1つ目の引数(rcplib_TASK_GetArg(0))から、イベント発行理由を取得できます。


[状態監視の登録の方法]
#incliude "uvcAPI.h"
   ~
//////////////////////////////////////////////////////////////////
// internal define //
//////////////////////////////////////////////////////////////////
// 内部非同期イベント
#define UNSYNC_EVENT_INIT 0
#define UNSYNC_EVENT_USB_BUS 1
   ~
// mainループ処理
void main_loop() {
   ~
    // USBバスの状態変化時のイベント処理 (リスナー登録完了後、イベント通知が入るようになる)
    else if(event_id == UNSYNC_EVENT_USB_BUS) {
        event_usb_video_status_change(rcplib_TASK_GetArg(0));
    }
   ~
}
   ~
func any_func() {

    // USBバスの状態監視リスナー登録APIを登録のために実行
    UVC_RegistUsbVideoStatusListener(UNSYNC_EVENT_USB_BUS, UVC_LISTENER_REGIST_ADD);

    // 以降、指定したevent_id(UNSYNC_EVENT_USB_BUS)に対してUSBバス関連のイベントが通知されるようになる

    return(0);
}
// USBバスの状態変化時のイベント処理
func event_usb_video_status_change(uint8 result) {

    // ★USBバスの状態変化時時の処理をここに記載すること★
    // resultからはイベントの詳細情報が得られる

    return(0);
}

[状態監視の解除の方法]
#incliude "uvcAPI.h"
   ~
func any_func() {

    // USBバスの状態監視リスナー登録APIを登録解除のために実行
    UVC_RegistUsbVideoStatusListener(event_id, UVC_LISTENER_REGIST_REMOVE);

    // 以降、USBバス関連のイベントが通知されなくなる

    return(0);
}

接点入出力とシリアルIO

概要

本製品には16pinコネクタの外部インターフェースがあり、ここに接点、RS-232C、RS-485、CANの機能を搭載しています。ここでは16pinコネクタの外部インターフェースをレシピ言語から制御する方法を紹介します。

16pin外部インターフェースの特徴

16pinコネクタの外部インターフェースの特徴を以下に示します。

シリアルIOと接点のコネクタ出力は以下の通り

コネクタ出力

接点

接点機能の特徴

接点機能は入出力可能な端子が2本、入力専用の端子が1本あり、外部機器の制御や状態検出をすることが可能なオープンコレクタ動作のインターフェースとなります。

入力:

出力:

内部等価回路:
接点等価回路


接点制御API

接点制御するAPIは以下があります。

API 内容
1 rcplib_GPIO_Read() 端子の入力状態を取得する
2 rcplib_GPIO_Write() 端子の出力状態を設定する
3 GPI_SetNotifyPort() 端子の状態変化を検知して、指定したイベントに通知を出す
4 GPI_SetCheckGpiInterval() 端子の状態を監視するポーリング間隔を設定する

rcplib_GPIO_Read()

端子の入力状態を取得するAPIです。

rcplib_GPIO_Read(uint32 port, uint16 pin) 
    port : ポート番号
    pin : ピン番号

    10pin, 11pin, 13pinのport,pinは、#defineで定義されており、
       10pin : EXT_USER_GPI1  →  (GPIOC,GPIO_PIN_1) : port,pin 
       11pin : EXT_USER_GPI2  →  (GPIOC,GPIO_PIN_9) : port,pin 
       13pin : EXT_USER_GPI3  →  (GPIOH,GPIO_PIN_7) : port,pin 
    で定義がされています。
    このため、
        rcplib_GPIO_Read(EXT_USER_GPI1);
    という形で利用することが出来ます。

rcplib_GPIO_Write()

端子の出力状態を設定するAPIです。

rcplib_GPIO_Write(uint32 port, uint16 pin, uint8 data) 
    port : ポート番号
    pin  : ピン番号
    data : 設定値
        EXT_USER_GPO_SET_LOW : CLOSE制御
        EXT_USER_GPO_SET_OD  : OPEN制御

    10pin, 11pinのport,pinは、#defineで定義されており、
       10pin : EXT_USER_GPI1  →  (GPIOC,GPIO_PIN_1) : port,pin 
       11pin : EXT_USER_GPI2  →  (GPIOC,GPIO_PIN_9) : port,pin 
    で定義がされています。
    このため、
        rcplib_GPIO_Write(EXT_USER_GPI1, EXT_USER_GPO_SET_OD);
    という形で利用することが出来ます。

[接点入力のコード例]
#include "main.h"
   ~
func any_func() {
    //入力結果の変数を準備
    int8 c1; int8 c2; int8 c3;

    c1 = rcplib_GPIO_Read(EXT_USER_GPI1);  //10pin
    c2 = rcplib_GPIO_Read(EXT_USER_GPI2);  //11pin
    c3 = rcplib_GPIO_Read(EXT_USER_GPI3);  //13pin

    //端子がOpenなら1, Closeなら0が取得出来ます。

    return(0);
}
[接点出力のコード例]
#include "main.h"
   ~
func any_func() {

    rcplib_GPIO_Write(EXT_USER_GPI1, EXT_USER_GPO_SET_OD);  //open
    rcplib_GPIO_Read(EXT_USER_GPI2, EXT_USER_GPO_SET_LOW);  //close

    return(0);
}

GPI_SetNotifyPort()

端子の状態変化を検知して、指定したイベントに通知を出すAPIです。

GPI_SetNotifyPort(uint8 mode, uint8 port, uint8 state, uint8 notify_event_id) 
    mode : 有効/無効
        NOTIFY_GPI_ENABLE  : 有効
        NOTIFY_GPI_DISABLE : 無効
    port  : 監視するポート
        NOTIFY_GPI1 : GPI1を監視  //10pin
        NOTIFY_GPI2 : GPI2を監視  //11pin
        NOTIFY_GPI3 : GPI3を監視  //13pin
    state : 検知する状態変化
        NOTIFY_GPI_L : HIGH->Lowへの変化を検知
        NOTIFY_GPI_H : LOW->HIGHへの変化を検知
    notify_event_id : 通知するイベントID <1〜254>

GPI_SetCheckGpiInterval()

端子の状態を監視するポーリング間隔を設定するAPIです。
本関数を用いてポーリング間隔を設定しない場合、標準で1000[msec]間隔でポーリングが動作します。

GPI_SetCheckGpiInterval(uint32 interval) 
    interval : ポーリング間隔 <1~4294967295[msec]>

[特定の端子の状態変化(High->LowとLow->Highの両方の変化)を一定間隔で監視するコード例]
#include "main.h"
#include "logAPI.h"
#include "gpiAPI.h"
   ~
#define NOTIFY_GPI1_L 1
#define NOTIFY_GPI1_H 1
#define LOW 0
#define HIGH 1
#define POLLING_INTERVAL 3000
   ~
void main_loop() {
   ~
   else if(event_id == NOTIFY_GPI1_L) {
        GPI1(LOW);
   }
   else if(event_id == NOTIFY_GPI1_H) {
        GPI1(HIGH);
   }
}
func any_func() {

    //GPI1のHigh->Lowの状態変化を検知した場合、NOTIFY_GPI1_Lのイベントを通知
    GPI_SetNotifyPort(NOTIFY_GPI_ENABLE, GPI1, NOTIFY_GPI_L, NOTIFY_GPI1_L);
    //GPI1のLow->Highの状態変化を検知した場合、NOTIFY_GPI1_Hのイベントを通知
    GPI_SetNotifyPort(NOTIFY_GPI_ENABLE, GPI1, NOTIFY_GPI_H, NOTIFY_GPI1_H);
    // 3000msec間隔でポーリングが動作するよう設定
    GPI_SetCheckGpiInterval(POLLING_INTERVAL);
    return(0);
}
func GPI1 (uint8 state) {
    if (state == LOW) {
        rcplib_LOG_Print("GPI1 detect LOW!");
    } else if (state == HIGH) {
        rcplib_LOG_Print("GPI1 detect HIGH!");
    }
}

RS-232C

RS-232C機能の特徴

パソコンと周辺機器の接続や工場での製造装置との接続等で多く使われているシリアルインターフェースで、送信信号(TX)と受信信号(RX)での調歩同期式で通信を行います。本機器のインターフェースではこのRS-232Cの信号と直接接続することが出来ます。


システム概要

シリアルインターフェースの制御はシリアルタスクにて統括して処理を行います。シリアルタスクはAPIを準備しており、このAPIを制御することで円滑なシリアル送受信を実現しています。APIとシリアルタスク制御のイメージ図を以下に示します。

シリアル処理


RS-232C制御API

RS-232Cを制御するには以下のAPIがあります。
APIはシリアル(RS-232CとRS-485)制御として共通です。

API 内容
1 SIO_Regist() データ受信時のイベント番号を登録
2 SIO_SetUartMode() UART通信設定
3 SIO_SetSeparateModeSize() データを受信する単位のサイズを登録
4 SIO_SetSeparateModeCode() データを受信する単位の区切り文字を登録
5 SIO_SendDataSerial() RS-485/RS-485[2WIRE]/RS-232Cデータの送信

SIO_Regist()

シリアル機能の開始とデータ受信時のイベント番号を登録するAPIです。
本APIで登録(SIO_BIND)をすることで、シリアル機能の電源がONになります。
このため、シリアル機能を利用する際には必ず最初に本APIにて登録が必要です。

SIO_Regist(uint8 device_id, uint8 sw, uint8 event_id)
   device_id : デバイス識別ID
        0:SIO_DEVICE_RS485
        1:SIO_DEVICE_RS232
        2:SIO_DEVICE_CAN
        5:SIO_DEVICE_RS485_2WIRE
   sw : 登録/登録解除の指定
        0:SIO_RELEASE  : (登録解除)
        1:SIO_BIND     : (登録)
   event_id : イベントID (1-256)
        データを受信した時のイベント番号を指定

デバイス識別IDはそのタスクで利用したいシリアルインターフェースを指定します。
シリアルにデータを送信するだけの場合でも本APIをコールする必要があります。この場合の受信イベントIDはそのタスクで定義していない番号を指定することが可能です。


SIO_SetUartMode()

UART通信設定をするAPIです。

SIO_SetUartMode(uint8 device_id, uint32 baudrate, uint8 parity) 
   device_id : デバイス識別ID
        0:SIO_DEVICE_RS485
        1:SIO_DEVICE_RS232
        2:SIO_DEVICE_CAN
        5:SIO_DEVICE_RS485_2WIRE
   baudrate : ボーレート設定
        9600, 115200等
   parity : パリティ設定 
        0:SIO_UART_PARITY_NONE
        1:SIO_UART_PARITY_EVEN
        2:SIO_UART_PARITY_ODD

ビット幅(8bit),ストップビット(1bit),フロー制御(none)は固定となっています。
これらに変更が必要な場合は、APIでのI/Fが準備されていないため、SDK内のsapp_serial.krsのdevice_power_on()関数のイニシャル条件の変更をお願いします。


SIO_SetSeparateModeSize()

シリアルデータを受信するサイズを登録するAPIです。

SIO_SetSeparateModeSize(uint8 device_id, uint8 size) 
   device_id : デバイス識別ID
        0:SIO_DEVICE_RS485
        1:SIO_DEVICE_RS232
        2:SIO_DEVICE_CAN
        5:SIO_DEVICE_RS485_2WIRE
   size : 受信データサイズ[Byte]
        指定サイズ受信後にイベント発生する

このAPIで指定したサイズ単位で受信イベントが発生します。
デフォルトはASCIIデータ受信の区切り文字(CR)になっていますが、このAPIでサイズを指定すると、サイズ単位での受信イベントが優先されます。
ASCIIデータ受信で区切り文字単位の受信に戻す場合は、受信データサイズを「0」で設定してください。


SIO_SetSeparateModeCode()

シリアルデータを受信した時の区切り文字を登録するAPIです。

SIO_SetSeparateModeCode(uint8 device_id, uint8 code) 
   device_id : デバイス識別ID
        0:SIO_DEVICE_RS485
        1:SIO_DEVICE_RS232
        2:SIO_DEVICE_CAN
        5:SIO_DEVICE_RS485_2WIRE
   code : 区切り文字コード
        指定の区切り文字を受信後にイベント発生する

このAPIで指定した区切り文字の単位で受信イベントが発生します。
SIO_SetSeparateModeSize()でサイズを指定しているとサイズ単位が優先されます。


SIO_SendDataSerial()

RS-485/RS-485[2WIRE]/RS-232Cデータを送信するAPIです。

SIO_SendDataSerial(uint32 device_id, uint8 data[], uint16 data_size) 
   device_id : デバイス識別ID
        0:SIO_DEVICE_RS485
        1:SIO_DEVICE_RS232
        2:SIO_DEVICE_CAN
        5:SIO_DEVICE_RS485_2WIRE
   data[] : 送信データ (byte配列)
   data_size : 送信データサイズ

[RS-232Cの送信と受信のコード例]

RS-232Cの利用開始の登録と、送信/受信のAPIは以下のように利用します。

#include "serialAPI.h"
   ~
// 内部非同期イベント
#define EVENT_INIT 0
#define EVENT_RCV_SERIAL 1
   ~
// mainループ処理
void main_loop() {
   ~
    // 受信イベント
    else if(event_id == EVENT_RCV_SERIAL) {
        event_rcv(rcplib_TASK_GetArg(0));
    }  
   ~
}

func any_func() {
    //シリアル開始登録、受信イベント登録
    SIO_Regist(SIO_DEVICE_RS232, SIO_BIND, EVENT_RCV_SERIAL);
    //通信設定9600pbs, パリティnone設定
    SIO_SetUartMode(SIO_DEVICE_RS232, 9600, SIO_UART_PARITY_EVEN);
    //データ受信サイズを10byteごとに設定
    SIO_SetSeparateModeSize(SIO_DEVICE_RS232, 10);
    return(0);
}
// シリアル送信処理
func any_func_snd() {
    uint8 tx_data[2];
    tx_data[0] = 0x01;
    tx_data[1] = 0x02;
    // RS-232に2byte送信
    SIO_SendDataSerial(SIO_DEVICE_RS232, tx_data, 2);
    return(0);
}
// シリアル受信のイベント
func event_rcv(uint8 buf_id) {
    uint8 rx_data[100]; 
    uint16 size;
    //シリアルデータ取出し
    size = rcplib_FIFOBUF_GetData(buf_id, 100, rx_data);
    //rx_data[]に受信データが入ります。
    //100と受信サイズを大きく入れてもそれ以下の
    //実際の受信サイズ分が取得出来ます。
    return(0);
}

RS-485

RS-485機能の特徴

PLC等の制御機器等で多く使われているシリアル通信で、同一バス上に複数の機器を接続できるインターフェースです。本機器のインターフェースでは4線式の全二重通信と2線式の半二重通信の両方に対応していますので、多くのRS-485機器に対応することが出来ます。


システム概要

シリアルインターフェース制御はRS-232C、RS-485共に同じAPIを使用して処理を行います。APIとシリアルタスク制御の流れはRS-232Cと同じになりますので、前記RS-232Cのシステム概要イメージ図を参照してください。


RS-485制御API

RS-485を制御するには以下のAPIがあります。
APIはシリアル(RS-232CとRS-485)制御として共通です。

API 内容
1 SIO_Regist() データ受信時のイベント番号を登録
2 SIO_SetUartMode() UART通信設定
3 SIO_SetSeparateModeSize() データを受信する単位のサイズを登録
4 SIO_SetSeparateModeCode() データを受信する単位の区切り文字を登録
5 SIO_SendDataSerial() RS-485/RS-485[2WIRE]/RS-232Cデータの送信

APIはRS-232C制御と共通なため、詳細は前記RS-232Cの項を参照お願いします。


[4線式と2線式の設定]

APIで指定する第1引数はどの物理インターフェースを利用するかを指定しまが、RS-485は物理インターフェースの接続に合わせてここで4線式と2線式の選択をします。

物理インターフェース指定 内容
SIO_DEVICE_RS485 RS-485の4線式を指定します。
SIO_DEVICE_RS485_2WIRE RS-485の2線式を指定します。

SIO_DEVICE_RS485_2WIREを指定する事で、2線式の時のエコーキャンセルの制御をハードウェアで行います。

4線2線


[RS-485の送信と受信のコード例]

RS-485の利用開始の登録と、送信/受信のAPIは以下のように利用します。

#include "serialAPI.h"
   ~
// 内部非同期イベント
#define EVENT_INIT 0
#define EVENT_RCV_SERIAL 1
   ~
// mainループ処理
void main_loop() {
   ~
    // 受信イベント
    else if(event_id == EVENT_RCV_SERIAL) {
        event_rcv(rcplib_TASK_GetArg(0));
    }  
   ~
}

func any_func() {
    //シリアル開始登録、受信イベント登録
    SIO_Regist(SIO_DEVICE_RS485, SIO_BIND, EVENT_RCV_SERIAL);
    //通信設定9600pbs, パリティnone設定
    SIO_SetUartMode(SIO_DEVICE_RS485, 9600, SIO_UART_PARITY_EVEN);
    //データ受信サイズを10byteごとに設定
    SIO_SetSeparateModeSize(SIO_DEVICE_RS485, 10);
    return(0);
}
// シリアル送信処理
func any_func_snd() {
    uint8 tx_data[2];
    tx_data[0] = 0x01;
    tx_data[1] = 0x02;
    // RS-232に2byte送信
    SIO_SendDataSerial(SIO_DEVICE_RS485, tx_data, 2);
    return(0);
}
// シリアル受信のイベント
func event_rcv(uint8 buf_id) {
    uint8 rx_data[100]; 
    uint16 size;
    //シリアルデータ取出し
    size = rcplib_FIFOBUF_GetData(buf_id, 100, rx_data);
    //rx_data[]に受信データが入ります。
    //100と受信サイズを大きく入れてもそれ以下の
    //実際の受信サイズ分が取得出来ます。
    return(0);
}

CAN

CAN機能の特徴

Under Construction.


Modbus(RS-232C/RS-485)

Modbus機能の特徴

ModobusはClient/Serverシステムとなり、Clientから命令を発行しServerがそれに答える動きを取ります。KC4-C-100A/KC4-C-101A(以後本機器)のModbus構成では本機器がClientになり、接続されるModbus機器がServerとなります。

Modbusプロトコル定義では物理レイヤの規定は無いため、さまざまな物理インターフェースの上に乗せることが出来ます。本機器ではRS-232C, RS-485(2線/4線)のシリアルインターフェースがあるため、この物理インターフェース上でModbus通信を行う事が出来ます。ModbusにはASCIIモードとRTUモードの2種類ありますが、本機器ではModbus RTUモードのみに対応しています。

Modbus対応機器としては2線式のRS-485が多いことから、このサンプルコードでは、基本的に2線式のRS-485のModbus RTUを前提とした説明をしますが、APIに設定するインターフェースを変えることで、4線式のRS-485やRS-232C上でのModbus通信も可能となります。

概要


Modbusの対応コマンド

Modbusプロトコルの仕様上は、多くのファンクションコードが定義されておりますが、本機器では以下のファンクションコードに対応しています。

[対応ファンクションコード]
コード ファンクション名 内容
01 Read Coil Status Discrete出力状態を読出し
02 Read Input Status Discrete入力状態を読出し
03 Read Holding Register 保持レジスタの内容を読出し
04 Read Input Register 入力レジスタの内容を読出し
05 Force Single Coil Discrete出力のON/OFF変更
06 Write Single Register 保持レジスタの書込み
08 Diagnostics 診断ファンクション
16 Write Multiple Registers 複数の保持レジスタの書込み

Modbus APIの概要

以下にModbus APIとその周辺処理の概要図を示します。

概要

Modbus APIの実体はsub application(以下sapp_serial)に存在し、そのAPIをuser application(以下uapp)からコールする作りとなります。Modbusのコマンド送信や受信内容の解析はsapp_serialにて行いModbusのデータ部のみuappに通知されます。APIはタスク間通信で行われる為、パラメータ以外の実データ(送信データや受信データの中身)はFIFOバッファを通して受け渡しが行われます。基本的には受信データが多いですが、Write Multiple Registers (Function Code 16)は、書き込む為の複数データをModbus機器に送信する必要が有ることから、uappからFIFOバッファに送信データを書込んだ後にAPIコールを行って、sapp_serialで該当データを取り込む流れとなります。

概要図の中では、sapp_serialの動きやuart Driversの動きも書かれていますが、uappからModbusを操作するには左側の赤枠のみ意識することでModbus機器を制御することが出来ます。


Modbus APIの使い方

本機器の物理インターフェース上(RS-485,RS-232C)でModbus通信を行う為に必要なAPIは以下の通りとなります。

[必要なシリアルインターフェースAPI]
API 内容
1 SIO_Regist() シリアル電源制御/受信イベントの登録
2 SIO_SetUartMode() シリアル通信設定

※シリアルインターフェスAPIの詳細は、API仕様書またはシリアルインターフェースのサンプルコード説明を参照ください。

ModbusインターフェースAPI

FC API 内容
-- SIO_ModbusInterfaceSettings() Echo制御, I/Fバッファ登録
01 SIO_ModbusReadCoilStatus() Discrete出力状態を読出し
02 SIO_ModbusReadInputStatus() Discrete入力状態を読出し
03 SIO_ModbusReadHoldingRegister() 保持レジスタの内容を読出し
04 SIO_ModbusReadInputRegister() 入力レジスタの内容を読出し
05 SIO_ModbusForceSingleCoil() Discrete出力のON/OFF変更
06 SIO_ModbusWriteSingleRegister() 保持レジスタの書込み
08 SIO_ModbusDiagnostics() 診断ファンクション
16 SIO_ModbusWriteMultipleRegisters() 複数の保持レジスタの書込み
[Modbus API制御の流れ]

本機器のModbus APIはModbus clientとして動作します。このためAPIは該当のコマンドを発行してその受信を得るというシンプルな制御ですが、ハードウェアに合わせる為の設定や、受信したデータの受け渡し方等の注意点がありますので、利用頻度が高いファンクションコード03番(ReadHoldingRegister)を発行するサンプルコードをベースに要点を絞って説明します。


[Modbus制御のソースコード抜粋]
//(--include部)-----------------------
#ifdef C_SYNTAX
#include "basicInformation.h"
#endif // C_SYNTAX
#include "taskid.h"
#include "eventLib.h"
#include "svitem.h"
#include "logAPI.h"
#include "kcSystemAPI.h"
#include "serialAPI.h"
#include "bufferLib.h"
#include "olcAPI.h"

//(--define部)------------------------
// Event番号定義
#define UNSYNC_EVENT_INIT 0
#define UNSYNC_EVENT_RS485_RCV 1
//Modbus server情報
#define SERVER_ID 1
#define HREG_ADDR 30
//FIFOバッファ設定
#define MD_BUFFSIZE 256
#define MD_QUEUESIZE 1

//(--option部 )---------
options {
    StackSize = 1500;
    EventQueueSize = 20;
    BufferFIFOSize = 25600; 
}

//(--event loop部)--------------------
#ifdef C_SYNTAX
void main_loop(){
    WAIT_REQUEST_EVENT_QUEUE
#endif // C_SYNTAX
    rcplib_TASK_InitArg();

    uint16 event_id;
    event_id = rcplib_SV_Read(ENGINE_SVITEM_ID_TASKEVENTITEM_EVENT_ID);

// 初期化(API)
if(event_id == UNSYNC_EVENT_INIT) {
    event_init();
}
// データ受信(RS-485)
else if(event_id == UNSYNC_EVENT_RS485_RCV) {
}
#ifdef C_SYNTAX
}
#endif // C_SYNTAX

//(--実行コード部)---------------------
// 初期化
func event_init() {

    //FIFO Buffer作成
    rcplib_FIFOBUF_Create(FIFOBUF_ID_RECIPE_0, MD_BUFFSIZE, MD_QUEUESIZE, FIFOBUF_OVERWRITE_ENABLE);

    //Serial Setting(RS-485)
    SIO_Regist(SIO_DEVICE_RS485_2WIRE, SIO_BIND, UNSYNC_EVENT_RS485_RCV);
    SIO_SetUartMode(SIO_DEVICE_RS485_2WIRE, 9600, SIO_UART_PARITY_EVEN);

    //Modbusインターフェースの設定
    SIO_ModbusInterfaceSettings(SIO_MODBUS_RS485_HW_ECHO_CANCEL,FIFOBUF_ID_RECIPE_0);

    //保持レジスタの読み込み (Function code 03)
    SIO_ModbusReadHoldingRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,2);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[4];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,4,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

    return(0);
}

※上記サンプルコードは実行後すぐにModbus通信を行い、結果を画面に表示します。このため一部起動時の画面に数字が上書かかれます。


[実行コード部 FIFOバッファの作成]
    //I/F Buffer作成
    rcplib_FIFOBUF_Create(FIFOBUF_ID_RECIPE_0, MD_BUFFSIZE, MD_QUEUESIZE, FIFOBUF_OVERWRITE_ENABLE);

ここからが実際のコード実行部となります。
Modbus APIはデータのやり取りをFIFOバッファを通じて行うため、最初に利用するFIFOバッファを作成する必要があり、rcplib_FIFOBUF_Create()関数にてバッファのエリアを確保できます。第一引数のFIFOBUF_ID_RECIPE_0がバッファの番号となり、この番号でAPI間のデータの受け渡しを行います。引数の最後のFIFOBUF_OVERWRITE_ENABLEは、Modbus APIはコマンド/レスポンスで1つのバッファを使いまわしますので、最後に設定したものと取り出しのズレが生じないようバッファの上書きを許可の設定しておきます。


[シリアルの設定]
    //Serial Setting(RS-485)
    SIO_Regist(SIO_DEVICE_RS485_2WIRE, SIO_BIND, UNSYNC_EVENT_RS485_RCV);
    SIO_SetUartMode(SIO_DEVICE_RS485_2WIRE, 9600, SIO_UART_PARITY_EVEN);

この例ではRS-485上でModbusプロトコルの送受を行うため、RS-485のシリアル設定を行います。最初にSIO_Regist()にてRS-485のブロックの電源制御及びレスポンス受信時のコールバック登録をおこない、続いてSIO_SetUartMode()にて信号のボーレートとパリティービットを指定します。
ここで指定している第1引数はどの物理インターフェースを利用するかを指定します。指定できるデバイスは以下の通りです。

物理インターフェース指定 内容
SIO_DEVICE_RS232 RS-232Cを指定します。
SIO_DEVICE_RS485 RS-485の4線式を指定します。
SIO_DEVICE_RS485_2WIRE RS-485の2線式を指定します。

SIO_DEVICE_RS485とSIO_DEVICE_RS485_2WIREの指定の違いはハードウェアのエコーバックのキャンセル機能を制御するかどうかとなります。SIO_DEVICE_RS485_2WIREを指定することで、2線式の時のエコーキャンセルの制御をハードウェアで行います。

4線2線


[Modbusインターフェースの設定]
    //Modbusインターフェースの設定
    SIO_ModbusInterfaceSettings(SIO_MODBUS_RS485_HW_ECHO_CANCEL,FIFOBUF_ID_RECIPE_0);

Mobusインターフェースの設定は、エコーキャンセルをハードウェアで行っているかソフトウェアで行うかの指定と、APIとやり取りをするバッファ番号の登録を行います。

前記にて物理インターフェースに「SIO_DEVICE_RS485_2WIRE」を指定することでハードウェアによるエコーキャンセルを行うとありますが、ハードウェアによるエコーキャンセルは送信中の受信バッファをOFFする機能となり、タイミングによっては受信信号のビット化けが起こる可能性は少なからずあります。仮にそれが起こる可能性を排除したい場合、ハードウェアの機能を使わずソフトウェアでエコーキャンセルを行う事が出来ます。この場合の物理インターフェース指定と、エコーキャンセル指定の組み合わせは以下の通りとなります。

物理インターフェース指定 エコーキャンセル指定
1 SIO_DEVICE_RS485 SIO_MODBUS_RS485_SW_ECHO_CANCEL
2 SIO_DEVICE_RS485_2WIRE SIO_MODBUS_RS485_HW_ECHO_CANCEL

通常は2の設定で問題ありません。

続くAPIとやり取りをするためのFIFOバッファの番号は、前記でCreateしたFIFOバッファの番号を指定する必要があります。


[保持レジスタの読出し]
    //保持レジスタの読み込み (Function code 03)
    SIO_ModbusReadHoldingRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,2);

SIO_ModbusReadHoldingRegister()は、Modbus Functionコード03を発行するAPIとなり、サーバー機器の保持レジスタの値を読み出します。第2引数と第3引数で該当のアドレスと読出しサイズを指定します。この例では30番地から2レジスタ分の読出しを行います。保持レジスタの1レジスタは16bitとなるので、4byteのデータが取得出来ることになります。
この例では通信エラー等の障害があった場合のケアをしていませんので、実用的なコードを組む場合には、関数の戻りを参照してエラー処理を加える必要があります。


[読出し値の取り出し]
    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[4];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,4,rx_buf[]);

前記でFIFOバッファをCreateしたとおり、APIとのデータのやり取りはこのFIFOバッファを通じて行われます。命令を発行した段階では受信データはこのFIFOバッファに置かれている状況の為、プログラム中の変数に取り出すにはrcplib_FIFOBUF_GetData()関数を用います。この例では「FIDOBUF_ID_RECIPE_0」番のFIFOバッファから、4byteのデータを取り出し、rx_buf配列に格納しています。指定してるサイズの4byteは格納されたデータのサイズが分かっている場合は正確に指定することが理想ですが、100byte等大きめに指定した場合においても、GetDataのAPIがFIFOバッファで管理されているサイズ分を取り出して、関数の返り値(この例ではrx_size)にそのサイズが格納されますので、この返り値を使用するロジックにすることも可能です。

この命令により受信したデータがrx_buf[]に格納されますが、FIFOバッファはデータをbyte単位で扱う為に注意点が一つあります。Modbus上の保持レジスタは16bit(2byte)のデータの扱いですが、読み出したデータは1byte単位で管理がされています。このためrx_bufには以下のようにデータが格納されています。

    rx_buf[0] = 受信データ(上位) 30番地
    rx_buf[1] = 受信データ(下位) 30番地
    rx_buf[2] = 受信データ(上位) 31番地
    rx_buf[3] = 受信データ(下位) 31番地

16bit(2byte)単位のデータがビッグエンディアンで格納されているため、プログラム中でuint16(2byte)として扱うためには、
 data = (rx_buf[0] * 256) + rx_buf[1]);
のように整形し直す必要があります。


SIO_ModbusReadCoilStatus() -- 01

Modbusに接続されるserver機器のDiscrete Output状態を読み出します。(ここでのCoilとはserver機器に接続されるモータやランプなどの負荷のかかる個々の機能に対する制御状態(ON/OFF)を表します。)

[SIO_ModbusReadCoilStatus の書式]
SIO_ModbusReadCoilStatus(uint8 device_id, uint8 server, uint16 addr, uint16 bitqty);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号 (0:ブロードキャストは非対応)
    addr :  読出し番地 (0x0000 - 0xFFFF)
    bitqty :  読出しビット数 (1 to 2000 (0x7D0))

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIでは読み出したデータがFIFOバッファにて通知されることからAPI発行後に、
 rcplib_FIFOBUF_GetData()にてデータの取得をしてください。
 FIFOバッファからの取り出し方法は下記のサンプルコードを参照ください。

ReadCoilStatusは読出しの番地と該当の「ビット数」を指定します。これにより読み出されるコイルの状態のON/OFFは0/1で通知されます。
読み出されるコイルは1bit単位ですが、データの読出しは1byte単位となるので、byteの中の対象のbitが1となって通知され、8bitに足りない部分は0で返されます。

[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket01

サーバー機器のコイル情報が以下のようになっていた場合の例:

server Device:   Addr 0-9 : Address mapped as Coil
    Addr 0: ON
    Addr 1: ON
    Addr 2: OFF
    ~Addr 9 までOFF

client Device:
   SIO_ModbusReadCoilStatus(SIO_DEVICE_RS485_2WIRE,1,0,9);
   にて読み出されるデータは
      0x03, 0x00
   となります。
[コード例]
    #define SERVER_ID 1
    #define CSTS_ADDR 0
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //コイルの読み込み (Function code 01)
    SIO_ModbusReadCoilStatus(SIO_DEVICE_RS485_2WIRE,SERVER_ID,CSTS_ADDR,9);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[2];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,2,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

SIO_ModbusReadInputStatus() -- 02

Modbusに接続されるserver機器のDiscrete Inputの状態を読み出します。Modbus機器に接続される外部機器からのInput状態(ON/OFF)の読出しに使われます。

[SIO_ModbusReadInputStatus の書式]
SIO_ModbusReadInputStatus(uint8 device_id, uint8 server, uint16 addr, uint16 bitqty);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号 (0:ブロードキャストは非対応)
    addr :  読出し番地 (0x0000 - 0xFFFF)
    bitqty :  読出しビット数 (1 to 2000 (0x7D0))

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIでは読み出したデータがFIFOバッファにて通知されることからAPI発行後に、
 rcplib_FIFOBUF_GetData()にてデータの取得をしてください。
 FIFOバッファからの取り出し方法は下記のサンプルコードを参照ください。

ReadInputStatusには読出しの番地と該当の「ビット数」を指定します。これにより読み出されるサーバー機器の外部機器からのインプットの状態のON/OFFは0/1で通知されます。
読み出されるインプット情報は1bit単位ですが、データの読出しは1byte単位となるので、byteの中の対象のbitが1となって通知され、8bitに足りない部分は0で返されます。

[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket02

サーバー機器のインプット情報が以下のようになっていた場合の例:

server Device:   Addr 20-29 : Address mapped as Discrete Input
    Addr 20: ON
    Addr 21: ON
    Addr 22: OFF
    ~Addr 29 までOFF

client Device:
   SIO_ModbusReadInputStatus(SIO_DEVICE_RS485_2WIRE,1,20,9);
   にて読み出されるデータは
      0x03, 0x00
   となります。
[コード例]
    #define SERVER_ID 1
    #define ISTS_ADDR 20
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //インプット情報の読み込み (Function code 02)
    SIO_ModbusReadInputStatus(SIO_DEVICE_RS485_2WIRE,SERVER_ID,ISTS_ADDR,9);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[2];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,2,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

SIO_ModbusReadHoldingRegister() -- 03

Modbusに接続されるserver機器の保持レジスタを読み出します。保持レジスタはserver機器内のMemory空間のイメージでModbusを通じて読み書きが出来るレジスタになります。このためModbus機器へのパラメータの書込みやデータの受け渡しに使われます。Modbus規格上はRead/Write出来るアドレス空間ですが、server機器によってRead Onlyなどのアドレスなど独自のメモリ空間にしている場合がありますので、server機器の仕様に準じてください。

[SIO_ModbusReadHoldingRegister の書式]
SIO_ModbusReadHoldingRegister(uint8 device_id, uint8 server, uint16 addr, uint16 regqty);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号
    addr :  読出し番地 (0x0000 - 0xFFFF)
    regqty :  読出しレジスタ数 (1 to 125 (0x7D))

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIでは読み出したデータがFIFOバッファにて通知されることからAPI発行後に、
 rcplib_FIFOBUF_GetData()にてデータの取得をしてください。
 FIFOバッファからの取り出し方法は下記のサンプルコードを参照ください。

ReadHoldingRegisterには読出しの開始番地と該当の「レジスタ数」を指定します。このレジスタは16bitとなるため、1つのレジスタを読み出すと2byteのデータが返ってくることになります。

[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket03

サーバー機器の保持レジスタ情報が以下のようになっていた場合の例:

server Device:   Addr 30-39 : Address mapped as Holding Registers
    Addr 30: 0x1234
    Addr 31: 0x5678
    Addr 32: 0x0000
    ・・・

client Device:
   SIO_ModbusReadHoldingRegister(SIO_DEVICE_RS485_2WIRE,1,30,3);
   にて読み出されるデータは
      0x12, 0x34, 0x56, 0x78, 0x00, 0x00
   となります。

Modbus規格として1つの保持レジスタは16bitですが、API間のデータバッファが1byte単位の管理となるため、読出しのデータは「上位byte」「下位バイト」と分かれて通知されます。このため、プログラム上で2byteに整形するためには「上位byte *256 + 下位バイト」の計算を施して16bitのデータとしてください。(下記のサンプルコードを参考にしてください。)

[コード例]
    #define SERVER_ID 1
    #define HREG_ADDR 30
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //保持レジスタの読み込み (Function code 03)
    SIO_ModbusReadHoldingRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,9);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[6];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,6,rx_buf[]);

    //hreg0-2に読み出した保持レジスタの値を格納
    uint16 hreg0;
    uint16 hreg1;
    uint16 hreg2;
    hreg0 = rx_buf[0] * 256 + rx_buf[1];
    hreg1 = rx_buf[2] * 256 + rx_buf[3];
    hreg2 = rx_buf[4] * 256 + rx_buf[5];

SIO_ModbusReadInputRegister() -- 04

Modbusに接続されるserver機器の入力レジスタを読み出します。入力レジスタは保持レジスタと違い読出し専用のレジスタになります。このためserver機器に接続されている機器からのデータやセンサーのデータなどが格納されるなどの利用がされています。入力レジスタも保持レジスタと同様に16bitのレジスタとなります。

[SIO_ModbusReadInputRegister の書式]
SIO_ModbusReadInputRegister(uint8 device_id, uint8 server, uint16 addr, uint16 regqty);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号
    addr :  読出し番地 (0x0000 - 0xFFFF)
    regqty :  読出しレジスタ数 (1 to 125 (0x7D))

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIでは読み出したデータがFIFOバッファにて通知されることからAPI発行後に、
 rcplib_FIFOBUF_GetData()にてデータの取得をしてください。
 FIFOバッファからの取り出し方法は下記のサンプルコードを参照ください。

ReadInputRegisterには読出しの開始番地と該当の「レジスタ数」を指定します。このInputRegisterは16bitとなるため、1つのレジスタを読み出すと2byteのデータが返って来ます。

[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket04

サーバー機器の入力レジスタ情報が以下のようになっていた場合の例:

server Device:   Addr 40-49 : Address mapped as Input Registers
    Addr 40: 0x1234
    Addr 41: 0x5678
    Addr 42: 0x0000
    ・・・

client Device:
   SIO_ModbusReadInputRegister(SIO_DEVICE_RS485_2WIRE,1,40,3);
   にて読み出されるデータは
      0x12, 0x34, 0x56, 0x78, 0x00, 0x00
   となります。

Modbus規格として1つの入力レジスタは16bitですが、API間のデータバッファが1byte単位の管理となるため、読出しのデータは「上位byte」「下位バイト」と分かれて通知されます。このため、プログラム上で2byteに整形するためには「上位byte *256 + 下位バイト」の計算を施して16bitのデータとしてください。(下記のサンプルコードを参考にしてください。)

[コード例]
    #define SERVER_ID 1
    #define IREG_ADDR 40
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //入力レジスタの読み込み (Function code 04)
    SIO_ModbusReadInputRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,IREG_ADDR,3);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[6];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,6,rx_buf[]);

    //hreg0-2に読み出した保持レジスタの値を格納
    uint16 ireg0;
    uint16 ireg1;
    uint16 ireg2;
    ireg0 = rx_buf[0] * 256 + rx_buf[1];
    ireg1 = rx_buf[2] * 256 + rx_buf[3];
    ireg2 = rx_buf[4] * 256 + rx_buf[5];

SIO_ModbusForceSingleCoil() -- 05

Modbusに接続されるserver機器のコイル情報を書き換えます。コイル情報の書換は1bit単位となります。ForceSingleCoilのAPIは書き換えるコイルの開始番地とコイルのON/OFFを指定して使用します。

[SIO_ModbusForceSingleCoil の書式]
SIO_ModbusForceSingleCoil(uint8 device_id, uint8 server, uint16 addr, uint8 coil);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号
    addr :  書換番地 (0x0000 - 0xFFFF)
    coil :  書換コイル (1 or 0)

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIで取得できるデータは無いため、rcplib_FIFOBUF_GetData()の発行は禁止です。
[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket05

[コード例]
    #define SERVER_ID 1
    #define COIL_ADDR 20
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //1bitのコイルを書換る (Function code 05)
    SIO_ModbusForceSingleCoil(SIO_DEVICE_RS485_2WIRE,SERVER_ID,COIL_ADDR,1);

    //20番地のコイルが書き換わっていることを確認
    //コイルの読み込み (Function code 01)
    SIO_ModbusReadCoilStatus(SIO_DEVICE_RS485_2WIRE,SERVER_ID,COIL_ADDR,9);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[2];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,2,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

SIO_ModbusWriteSingleRegister() -- 06

Modbusに接続されるserver機器の保持レジスタの情報を書き換えます。WriteSingleRegisterでは保持レジスタの書換は1レジスタ(16bit)単位となります。

[SIO_ModbusWriteSingleRegister の書式]
SIO_ModbusWriteSingleRegister(uint8 device_id, uint8 server, uint16 addr, uint16 regval);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号
    addr :  書換番地 (0x0000 - 0xFFFF)
    regval :  書換レジスタ値

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIで取得できるデータは無いため、rcplib_FIFOBUF_GetData()の発行は禁止です。
[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket06

[コード例]
    #define SERVER_ID 1
    #define HREG_ADDR 30
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //1つのレジスタを書き換える (Function code 06)
    SIO_ModbusWriteSingleRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,0x1234);

    //30番地のレジスタ書き換わっていることを確認
    //レジスタの読み込み (Function code 03)
    SIO_ModbusReadHoldingRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,1);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[2];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,2,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

SIO_ModbusDiagnostics() -- 08

Modbusに接続されるserver機器の動作確認や診断用に使われるAPIです。
診断用のサブコードを指定してその返り値を取得するオペレーションで該当server機器の診断を実施します。

[SIO_ModbusDiagnostics の書式]
SIO_ModbusDiagnostics(uint8 device_id, uint8 server, uint16 subfunc, uint16 data);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号
    subfunc :  診断用サブファンクションコード (0x0000 - 0xFFFF)
    data :  診断用データ(0x0000 - 0xFFFF)

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIでは読み出したデータがFIFOバッファにて通知されることからAPI発行後に、
 rcplib_FIFOBUF_GetData()にてデータの取得をしてください。
 FIFOバッファからの取り出し方法は下記のサンプルコードを参照ください。
[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket08

[コード例]
    #define SERVER_ID 1
    #define SFUNC 0
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    //診断用コマンドを発行する (Function code 08)
    SIO_ModbusDiagnostics(SIO_DEVICE_RS485_2WIRE,SERVER_ID,SFUNC,0x1234);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[2];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,2,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

SIO_ModbusWriteMultipleRegisters() -- 16

Modbusに接続されるserver機器の複数の保持レジスタの情報を書き換えます。WriteMultipleRegisterでは保持レジスタの書換は1レジスタ(16bit)単位となり、連続した123個のレジスタの書換が出来ます。

[SIO_ModbusWriteMultipleRegisters の書式]
SIO_ModbusWriteMultipleRegisters(uint8 device_id, uint8 server, uint16 addr, uint16 regqty);
    device_id : シリアルデバイスの種類
             SIO_DEVICE_RS485_2WIRE : RS-485(2線)を指定
             SIO_DEVICE_RS485 : RS-485(4線)を指定
             SIO_DEVICE_RS232 : RS-232Cを指定
    server : サーバー機器の番号
    addr :  書換開始アドレス (0x0000 - 0xFFFF)
    regqty :  書換レジスタ数(0 - 123 (0x7B))

送信データ:
 書換えるレジスタのデータは、上記APIをコール前にFIFOバッファに
 rcplib_FIFOBUF_SetData()にてセットしておきます。
 FIFOへのセットの仕方はサンプルコードを参考にしてください。

返り値:
   エラーがない場合は 0:SIO_MODBUS_NO_ERRORが返りますが、
   それ以外の返り値はエラーとなります。エラーの内容はエラーコードの項を参照ください。

取得データ:
 このAPIで取得できるデータは無いため、rcplib_FIFOBUF_GetData()の発行は禁止です。
[APIとModbus patketの関係]

APIとModbus Packetとの関係は以下の模式図となります。
Paket16

[コード例]
    #define SERVER_ID 1
    #define HREG_ADDR 30
    //~SIO_ModbusInterfaceSettings()までの設定完了済み

    uint16 size;     size = 4;
    uint8 buff[4];
    buff[0] = 0x12;
    buff[1] = 0x34;
    buff[2] = 0x56;
    buff[3] = 0x78;
    //FIFOバッファに送信内容をセット
    rcplib_FIFOBUF_SetData(FIFOBUF_ID_RECIPE_0, size, buff[]

    //複数のレジスタを書き換える (Function code 16)
    SIO_ModbusWriteMultipleRegisters(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,2);

    //30番地のレジスタ書き換わっていることを確認
    //レジスタの読み込み (Function code 03)
    SIO_ModbusReadHoldingRegister(SIO_DEVICE_RS485_2WIRE,SERVER_ID,HREG_ADDR,2);

    //読出し値の取り出し
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[4];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,4,rx_buf[]);

    //読出し値をOLEDに配列内容表示
    OLC_DisplayArray(OLC_CHAR_FORCE,0,0,rx_size,rx_buf[]);

APIのエラーコード

各APIコール時のエラーコードは共通なためここでまとめます。
エラーコードの通知は2種類あり、一つはAPIの返り値、もう一つはサーバ機器からの通知となります。

[API返り値のエラーコード]
APIの返り値:
   0x00:SIO_MODBUS_NO_ERROR         // エラーなし
   0x01:SIO_MODBUS_ERROR_RCV_ECHO   // エコーバック内容違い
   0x02:SIO_MODBUS_ERROR_RCV_HEAD   // 応答メッセージヘッダ違い
   0x04:SIO_MODBUS_ERROR_RCV_CRC    // 応答CRC違い
   0x08:SIO_MODBUS_ERROR_SND_CMD    // コマンド送信エラー
   0x10:SIO_MODBUS_ERROR_RCV_BUFF_NOTSET   // バッファ未設定
   0x20:SIO_MODBUS_ERROR_RCV_BUFF_SETDATA   // バッファへデータ設定エラー
   0x40:SIO_MODBUS_ERROR_RCV_RESP    // コエラーレスポンス受信
   ※エラー番号はbit orされるので複数重なることもあります。

エラーコードの0x01-0x04までは送信信号のエラーとなるため、信号品質が問題である可能性があります。0x08はAPI内でコマンドを送信する際のエラーのため、他のタスクなどで対象のシリアルを制御して競合している可能性があります。0x10-0x20はタスク間でデータをやり取りするFIFOバッファのエラーとなることから、CreateしたバッファのID等の指定間違いがないかを確認することで解決する可能性があります。0x40はサーバ側のModbus機器から送信されてくるエラーコードとなるため、その内容はデータとしてFIFOバッファに格納されるため、rcplib_FIFOBUF_GetData()にて読み出してください。

APIの返り値が「0x10:SIO_MODBUS_ERROR_SND_CMD」の場合

  if (ret == SIO_MODBUS_ERROR_SND_CMD) {
    //エラーコードの読出し値
    uint8 rx_size;       //受信サイズ
    uint8 rx_buf[1];     //受信バッファ
    rx_size = rcplib_FIFOBUF_GetData(FIFOBUF_ID_RECIPE_0,1,rx_buf[]); 

    //rx_buf[0] にエラーコードが入ります。
  }

サーバからのエラーコード:
    01 : サポートしていないファンクションコード
    02 : 指定したアドレスが不正
    03 : 指定した読出し数/書込み数が不正
    04 : サーバ機器内のエラー
[Error PacketとFIFO buferの関係]

Error PacketとFIFO bufferとの関係は以下の模式図となります。
PaketError