本製品には16pinコネクタの外部インターフェースがあり、ここに接点、RS-232C、RS-485、CANの機能を搭載しています。ここでは16pinコネクタの外部インターフェースをレシピ言語から制御する方法を紹介します。
16pinコネクタの外部インターフェースの特徴を以下に示します。
シリアルIOと接点のコネクタ出力は以下の通り
接点機能は入出力可能な端子が2本、入力専用の端子が1本あり、外部機器の制御や状態検出をすることが可能なオープンコレクタ動作のインターフェースとなります。
入力:
出力:
内部等価回路:
接点制御するAPIは以下があります。
API | 内容 | |
---|---|---|
1 | rcplib_GPIO_Read() | 端子の入力状態を取得する |
2 | rcplib_GPIO_Write() | 端子の出力状態を設定する |
端子の入力状態を取得する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);
という形で利用することが出来ます。
#incliude "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);
}
#incliude "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);
}
パソコンと周辺機器の接続や工場での製造装置との接続等で多く使われているシリアルインターフェースで、送信信号(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は以下のように利用します。
#incliude "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は以下のように利用します。
#incliude "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との関係は以下の模式図となります。
この接点入力のサンプルコードでは付属の16pinコネクタケーブルを使用して、接点での入力を検出してその状態をディスプレイに表示します。
サンプルコードを実行後に接点をGND(12pin)に接続すると、表示画面の数値が0→1になることを確認してください。
このRS-232Cのサンプルコードでは付属の16pinコネクタケーブルを使用して、RS-232CのTXDとRXDを接続してループバックでのデータの送受信を確認します。
このRS-485のサンプルコードでは付属の16pinコネクタケーブルを使用して、RS-485通信をループバックで行い、その送信と受信をデバッグ出力します。
このサンプルコードでは、RS-485-USB変換器などを用いてPC側に接続してASCIIコードの文字を送受信します。ボタンを押すと本機器側から「ABCDEFGH」の文字列と終端コードCRLFを送信します。本機器側はASCIIコードの終端コードLFを待つ作りとなりますので、PC側のターミナルから例えば「abcdefgh」+ CRLFを送ると、機器側のログに受信した旨を表示します。
このRS-485のサンプルコードでは付属の16pinコネクタケーブルでModbus対応機器に接続をし、HoldingRegisterの0000番地の読出しをしてOLEDとデバッグ出力します。
各サンプルコードについて動作確認を行っておりますが、全ての環境において動作を保証するものではありません。正しく動作することを確認の上でご利用ください。
対応機種:KC4-C-100A/KC4-C-101A