レシピ言語ではUSBカメラを制御するAPIを準備しています。本サンプルコードではこのUSBカメラのAPIについて説明します。
USBカメラ機能の概要を以下に示します。
※ UVC対応のUSBカメラでもカメラ機器によって動作しない場合があります。
※ 動作確認済みのUSBカメラは「ハードウェアリファレンスの動作確認済みUSBカメラ」を参照ください。
※USB Type-AはUSBカメラ専用となり、他のUSB機器を動かすことは出来ません。
USBカメラのAPIとシステム動作のイメージ図を以下に示します。
USBカメラ機能はアプリケーションにて画像データを直接扱わない作りとなっており、カメラタスク側のカメラバッファに保存されている画像データをモデムタスクが参照して、HTTPボディーとして直接クラウドに送信する作りとなっています。
受信した動画はJPEG画像として内蔵メモリのカメラバッファに保存します。
カメラバッファに保存できる枚数は解像度により異なります。
解像度 | 保存可能枚数 |
---|---|
QVGA | 190枚 |
VGA | 46枚 |
HD | 14枚 |
FlulHD | 6枚 |
カメラバッファは最大数を超えた場合には、古い画像から上書きします。
USBカメラはサンプリングレート(撮影したJPEG画像をカメラバッファに記録する周期)を設定せずに撮影開始した場合、カメラバッファへの記録する周期はカメラ撮影のフレームレートに従います。サンプリングレートを事前に設定して撮影開始した場合、指定された周期でカメラバッファに画像を記録します。
細かい周期で記録する必要がない場合や、極力長期間の画像を記録したい場合はサンプリングレートを設定することを推奨します。
カメラバッファに記録される画像は、記録上限に達すると古い画像を消去して上書きするリングバッファ方式です。
カメラバッファに記録する速度よりLTE送信速度の方が遅い場合は全ての画像データを送信することができません。
例えばドライブレコーダーのように一定期間内の画像データのまとまりを欠落なくLTE送信したい場合は、USBカメラの撮影を停止してから、後にLTE送信することで実現できます。
画像データをLTE送信する際には、カメラバッファ内の「最新の画像」「最古の画像」「n番目に古い画像」のようにどの画像データを送信するか任意に指定することができます。
画像送信に成功すると、その画像より古い画像データは全て削除されます。もしカメラバッファ内の全てのデータを送信したい場合は古い画像から順番に送信するようにしてください。
全ての画像データを送信したい場合は「最古の画像」を指定して送信を複数回実行することで実現可能です。
USBカメラ機能は以下の制御が可能です。
・撮影開始要求(解像度、フレームレートを実行時に指定する)
・撮影停止要求
・サンプリングレート(撮影したJPEG画像をカメラバッファに記録する周期)の変更要求
・USB接続状態の監視用リスナー登録要求
・USB接続状態の監視用リスナー登録解除要求
※USBカメラの設定変更要求や撮影開始/停止要求は単一タスクからのみ利用可能
※サンプリングレートは撮影するフレームレートとは別に最大60秒周期まで指定可能 (例えば、30fpsでUSBカメラ撮影を行いながら、カメラバッファへの記録は0.5秒に1枚という指定も可能)
API | 説明 | |
---|---|---|
1 | UVC_VideoStreamCtrl() | 撮影開始や停止、解像度やフレームレートの設定をする |
2 | UVC_BufCtrlSmpRate() | カメラバッファに記録する周期を設定するためのAPI |
3 | UVC_RegistUsbVideoStatusListener() | USB接続状態の監視リスナー登録要求 |
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やシステムリセットの発生にて、カメラバッファの画像データは全て削除されます。
カメラバッファに記録する周期を設定を行う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);
}
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との接続状態の監視をするには、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);
}
本製品には16pinコネクタの外部インターフェースがあり、ここに接点、RS-232C、RS-485、CANの機能を搭載しています。ここでは16pinコネクタの外部インターフェースをレシピ言語から制御する方法を紹介します。
16pinコネクタの外部インターフェースの特徴を以下に示します。
シリアルIOと接点のコネクタ出力は以下の通り
接点機能は入出力可能な端子が2本、入力専用の端子が1本あり、外部機器の制御や状態検出をすることが可能なオープンコレクタ動作のインターフェースとなります。
入力:
出力:
内部等価回路:
接点制御するAPIは以下があります。
API | 内容 | |
---|---|---|
1 | rcplib_GPIO_Read() | 端子の入力状態を取得する |
2 | rcplib_GPIO_Write() | 端子の出力状態を設定する |
3 | GPI_SetNotifyPort() | 端子の状態変化を検知して、指定したイベントに通知を出す |
4 | GPI_SetCheckGpiInterval() | 端子の状態を監視するポーリング間隔を設定する |
端子の入力状態を取得する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);
という形で利用することが出来ます。
端子の出力状態を設定する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);
}
端子の状態変化を検知して、指定したイベントに通知を出す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>
端子の状態を監視するポーリング間隔を設定するAPIです。
本関数を用いてポーリング間隔を設定しない場合、標準で1000[msec]間隔でポーリングが動作します。
GPI_SetCheckGpiInterval(uint32 interval)
interval : ポーリング間隔 <1~4294967295[msec]>
#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!");
}
}
パソコンと周辺機器の接続や工場での製造装置との接続等で多く使われているシリアルインターフェースで、送信信号(TX)と受信信号(RX)での調歩同期式で通信を行います。本機器のインターフェースではこのRS-232Cの信号と直接接続することが出来ます。
シリアルインターフェースの制御はシリアルタスクにて統括して処理を行います。シリアルタスクはAPIを準備しており、このAPIを制御することで円滑なシリアル送受信を実現しています。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データの送信 |
シリアル機能の開始とデータ受信時のイベント番号を登録する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はそのタスクで定義していない番号を指定することが可能です。
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()関数のイニシャル条件の変更をお願いします。
シリアルデータを受信するサイズを登録する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」で設定してください。
シリアルデータを受信した時の区切り文字を登録する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()でサイズを指定しているとサイズ単位が優先されます。
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の利用開始の登録と、送信/受信の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);
}
PLC等の制御機器等で多く使われているシリアル通信で、同一バス上に複数の機器を接続できるインターフェースです。本機器のインターフェースでは4線式の全二重通信と2線式の半二重通信の両方に対応していますので、多くのRS-485機器に対応することが出来ます。
シリアルインターフェース制御はRS-232C、RS-485共に同じAPIを使用して処理を行います。APIとシリアルタスク制御の流れはRS-232Cと同じになりますので、前記RS-232Cのシステム概要イメージ図を参照してください。
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の項を参照お願いします。
APIで指定する第1引数はどの物理インターフェースを利用するかを指定しまが、RS-485は物理インターフェースの接続に合わせてここで4線式と2線式の選択をします。
物理インターフェース指定 | 内容 |
---|---|
SIO_DEVICE_RS485 | RS-485の4線式を指定します。 |
SIO_DEVICE_RS485_2WIRE | RS-485の2線式を指定します。 |
SIO_DEVICE_RS485_2WIREを指定する事で、2線式の時のエコーキャンセルの制御をハードウェアで行います。
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);
}
Under Construction.
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プロトコルの仕様上は、多くのファンクションコードが定義されておりますが、本機器では以下のファンクションコードに対応しています。
コード | ファンクション名 | 内容 |
---|---|---|
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の実体は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機器を制御することが出来ます。
本機器の物理インターフェース上(RS-485,RS-232C)でModbus通信を行う為に必要なAPIは以下の通りとなります。
API | 内容 | |
---|---|---|
1 | SIO_Regist() | シリアル電源制御/受信イベントの登録 |
2 | SIO_SetUartMode() | シリアル通信設定 |
※シリアルインターフェスAPIの詳細は、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 clientとして動作します。このためAPIは該当のコマンドを発行してその受信を得るというシンプルな制御ですが、ハードウェアに合わせる為の設定や、受信したデータの受け渡し方等の注意点がありますので、利用頻度が高いファンクションコード03番(ReadHoldingRegister)を発行するサンプルコードをベースに要点を絞って説明します。
//(--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通信を行い、結果を画面に表示します。このため一部起動時の画面に数字が上書かかれます。
//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線式の時のエコーキャンセルの制御をハードウェアで行います。
//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]);
のように整形し直す必要があります。
Modbusに接続されるserver機器のDiscrete Output状態を読み出します。(ここでのCoilとはserver機器に接続されるモータやランプなどの負荷のかかる個々の機能に対する制御状態(ON/OFF)を表します。)
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 Packetとの関係は以下の模式図となります。
サーバー機器のコイル情報が以下のようになっていた場合の例:
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[]);
Modbusに接続されるserver機器のDiscrete Inputの状態を読み出します。Modbus機器に接続される外部機器からのInput状態(ON/OFF)の読出しに使われます。
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 Packetとの関係は以下の模式図となります。
サーバー機器のインプット情報が以下のようになっていた場合の例:
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[]);
Modbusに接続されるserver機器の保持レジスタを読み出します。保持レジスタはserver機器内のMemory空間のイメージでModbusを通じて読み書きが出来るレジスタになります。このためModbus機器へのパラメータの書込みやデータの受け渡しに使われます。Modbus規格上はRead/Write出来るアドレス空間ですが、server機器によってRead Onlyなどのアドレスなど独自のメモリ空間にしている場合がありますので、server機器の仕様に準じてください。
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 Packetとの関係は以下の模式図となります。
サーバー機器の保持レジスタ情報が以下のようになっていた場合の例:
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];
Modbusに接続されるserver機器の入力レジスタを読み出します。入力レジスタは保持レジスタと違い読出し専用のレジスタになります。このためserver機器に接続されている機器からのデータやセンサーのデータなどが格納されるなどの利用がされています。入力レジスタも保持レジスタと同様に16bitのレジスタとなります。
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 Packetとの関係は以下の模式図となります。
サーバー機器の入力レジスタ情報が以下のようになっていた場合の例:
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];
Modbusに接続されるserver機器のコイル情報を書き換えます。コイル情報の書換は1bit単位となります。ForceSingleCoilのAPIは書き換えるコイルの開始番地とコイルのON/OFFを指定して使用します。
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 Packetとの関係は以下の模式図となります。
#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[]);
Modbusに接続されるserver機器の保持レジスタの情報を書き換えます。WriteSingleRegisterでは保持レジスタの書換は1レジスタ(16bit)単位となります。
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 Packetとの関係は以下の模式図となります。
#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[]);
Modbusに接続されるserver機器の動作確認や診断用に使われるAPIです。
診断用のサブコードを指定してその返り値を取得するオペレーションで該当server機器の診断を実施します。
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 Packetとの関係は以下の模式図となります。
#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[]);
Modbusに接続されるserver機器の複数の保持レジスタの情報を書き換えます。WriteMultipleRegisterでは保持レジスタの書換は1レジスタ(16bit)単位となり、連続した123個のレジスタの書換が出来ます。
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 Packetとの関係は以下の模式図となります。
#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コール時のエラーコードは共通なためここでまとめます。
エラーコードの通知は2種類あり、一つは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 bufferとの関係は以下の模式図となります。