Hobby Lab 趣味のモノ作り実験のサイトです。
部品 プロセッサー他 CH32V DS18B20_ModBus
1 はじめに
1.1 装置開発経緯と概容
1.1.1 制御・設定内容
1.1.2 情報の取得
1.1.3 電源を入れた場合の処理
1.1.4 主な処理
1.1.5 処理速度を早くする処理
1.1.6 エラー処理
1.2 回路
1.2.1 開発時の回路
1.2.2 開発後の確認用回路
1.3 通信速度
1.4 動作確認
2 MODBUS仕様
2.1 マスター側
2.1.1 フレーム構造
2.1.2 DeviceAddress
2.1.3 Function
2.1.4 DataAddress
2.1.5 Register
2.1.6 CRC
2.2 スレーブ側
2.2.1 フレーム構造
2.2.2 DeviceAddress
2.2.3 Function
2.2.4 DataAddress & Register
3 プログラムの設定と動作確認
3.1 割り込み順位
3.2 エラー処理
3.2.1 マスターメッセージ長の確認処理
3.2.2 マスターメッセージの確認処理
3.3 動作確認
3.3.1 通常動作
3.3.2 DeviceAddress変更
3.3.3 USART1速度変更
3.3.4 CRC処理方法の比較
3.3.5 コピー方法の比較
3.3.6 メインプログラム
4 プログラム

Sf:Pt プロトコル関係
SfPt:MODBUS

Pr:Prプロセッサ関係
PrP:プロセッサ
動作比較
 STM32F動作比較
 CH32V203&STM32F 動作比較
 arduino動作比較
raspberrypi関係
 RaspberryPiハード
CH32V関係
 -CH32V開始
 -203K8T6(32Pin)開始
 -203C8T6(48P)開始
 -003J4M6(8Pin)開始
 -003F4P6(20Pin)開始
 -Moun River StudioⅡ
 プログラミング!
  203_GPIO関係
  203_TIME関係
  203_TIME Encoder
  203_I2C関係
  203_1-Wire関係
  003_DS18B20テスター
  USART(UART)関係
  DS18B20をModBus制御
 -マニュアル
 203データシート
 203取説
  MBA メモリとバス方式
  PWR 電力制御
  RCC リセット・拡張・クロック
  BKP バックアップレジスタ
  CRC 巡回冗長検査
  RTC リアルタイムクロック
  GPIO GPIOと代替機能
  DMA ダイレクトメモリアクセス制御
  ADTM 高度な制御タイマー
  GPTM 汎用タイマー
  BCTM 基本タイマー
  USART 同期非同期通信
arduino関係
 ESP12関係
 (a)ESP-8266D1mini注意
PrP:その他  RS485ドライバー
 CP2102 BRIDGE
 WCH-LinkEエミュレーター
Pr:Wire 電線関係
Pr:Resistance 抵抗
Pr:Capacitor コンデンサ
Pr:Coil コイル
Pr:PassiveElmt 受動素子
Pr:Diode ダイオード関係
Pr:OPAMP オペアンプ関係
PrO:送受信機
Si4735
 Si4735について
 Si4735ラジオを作って見よう1
PrO:オペアンプ  LM324
 LM358
Pr:Tr トランジスタ関係
2SC1815
 リレードライバー設計
 アンプ設計
 発振器
TLP152
 TLP152テスト
TLP2361
 TLP2361テスト
TLP5754
 TLP5754テスト
Pr:Source 電源関係  ツェナーダイオード
 TL431
 LM317
PrS:Downモジュール
 EGS002_IR2110S
 SKU011012
 ACDC02
 XH_M299
 LM2596
 Mini360_MP23070N
 DROK
 WH140
PrS:UPモジュール
 MT3608
PrS:充電モジュール
 TP4056
Pr:Sensor_AD_時計等
PrS:電圧、電流
ADS1115 16bit4CH I2C A/D
 Hardware
 RaspberryPi_コマンド接続
 RaspberryPi_Python
 Arduino
 CH23V203 MounRiverStudioⅡ
INA226 I2C 直流電圧電流
 Hardware
 Arduino
 RaspberryPi_Python
WCS 電流ホール素子
 Hardware
PrS:温度、気圧、湿度、照度
BNE280 I2C 気圧,湿度,気温
 Hardware
 Arduino
 RaspberryPi_Python
BH1750 I2C 照度
 Hardware
 Arduino
 RaspberryPi_Python
DS18B20 1-Wire 温度計
 Hardware
 Arduino
 RaspberryPi_Python
PrS:時間、日時
DS3231 I2C 時計
 Hardware
 Arduino
 RaspberryPi_Python
PrS:表示器
MAR3953 320X480 3.95"
 概要と線や点を描く
 フォントを描く
SSD1306 I2C 0.96"OLED
 Hardware
 Arduino
 RaspberryPi_Python
Pr:Old Processor他

1 はじめに

1.1 装置開発経緯と概容

太陽光発電装置のモニター(一部未実装)を RaspberryPi と言う MPU を搭載したシングルボードコンピュータ(SBC)で作成している。
このモニターに、外温・室温・インバータ・バッテリー×2台で5個の温度測定を追加したい。

温度検出器にはデジタルで温度値を出力するもの、アナログで出力し別置きでデジタルに変換する装置を設置するものがあるが、 今回は安価なものとして DS18B20(以下スレーブと呼ぶ)と言う名前の検出素子を使用する。
これはデジタルの温度値を出力し 1-Wire と言う信号伝送方法の温度検出器を使用する。

今回必要なスレーブは5個だが、温度情報を受け取る場合は下記プログラムが必要となる。
初期:スレーブのIDを取得
①温度変換指示を出す。
②温度変換するまで待つ
③温度を読み出すスレーブのIDを送信し、
 次に温度変換後の値を読み出し
 (今回は)5回繰り返し 終われば ①へ

ところでMODBUSで使用している通信デバイスにはUSARTなどがあるが、これにはハード的なドライバーにより処理する機能があるため、Raspberry Pi はそのドライバーに Byte信号だけを受け渡しすれば良いが、 上記 初期や①、③で使用する 1-Wire と言う通信方法では ハード的ドライバーがなく通信信号をソフトで数十(us) 単位で制御する必要がある。
Raspberry Pi の標準 OS は特定の処理をミリ秒・マイクロ秒単位の精度で必ず実行する(リアルタイム性)機能は持たないので、 処理負荷によって応答時間が変動する非リアルタイム(汎用)OSなため スレーブとの通信でタイミング信号(μs単位)がずれて通信エラーになる可能性がある。

そこでRaspberry Pi に直接組み込むことをやめ、安価な MCU で温度検出部を集約しそこで①~③のプログラムに加え、 RaspberryPi に温度情報を渡すプログラムを動作させることにした。

MCU と RaspberryPiの情報を渡す方法は、現状でRaspberryPi から電力関係の情報を MODBUS プロトコルで収集しているので、 今回作成するMCUも MODBUS を使用して情報を伝送することにした。

MCUはできるだけ安価な003系を使用する。
想定するスレーブの最大数は8個とした。
※003系は割り込みが2系×2しか無いので、どうなるか・・

1.1.1 制御・設定内容

CH32V003J4m6による試作機
温度分解能は12bit固定とする。
(1) DeviceAddress:
 0x00(初期)〜0xF7(247)まで設定可能
(2) 通信速度:
0x009600(標準)
0x0119200
0x0238400
0x0357600
0x04119200
0x05230400
0x06460800
0x07576000
(3) 変換待ち時間:
0x00分解能変換時間 12bitの場合は 800(ms)
0x01〜0x411〜65秒で設定
(4) 温度変換指令:
0x00off(標準)
0x01自動繰り返し
0x10手動温度変換指示
スレーブ登録が完了し、自動に設定した場合は次を繰り返す。
・全スレーブに対し温度変換指示。
・設定した変換待ち時間を待つ。
・全スレーブのScratchpadを読み出し温度値16(bit)を記録する。
・手動変換指示の場合は温度変換結果をアンサーする。
(5) 接続しているスレーブを登録する。
接続されているスレーブを検索し関係する情報を記録する。
最大スレーブ数は8個とする。
・検出されたスレーブの数を記録する。
・RomCodeを記録する。
・Scratchpadを読み出し、設定値(分解能)を記録する
なお標準の分解能(12bit)と違う場合は、1回のみScratchpadとEEPROMに記録指示を出す。
指定不能な場合はそのままの分解能とする。

1.1.2 情報の取得

(1) 記憶アドレスを指定してスレーブのRomCodeを読み出し
(2) 記録アドレスを指定してスレーブの設定(分解能)を読み出し
(3) 記録アドレスを指定してスレーブの温度(16bit)を読み出し

1.1.3 電源を入れた場合の処理

以下の初期処理を行う
(1) GPIO初期化
(2) TIM2で1000(ms)タイマー動作
(3) UART1初期化/割り込み初期化
(4) SW の割り込みを有効化
(5) スレーブ登録処理
(6) 分解能適正確認 12bitが悪ければ、11,10,9bitの動作確認
(7) 温度変換待ち時間計算

1.1.4 主な処理

プログラムは大きく別けて1つの割り込み方式の処理と1つのポーリング方式の処理を行っている。
(1)割り込み処理部
MODBUSの信号をUSART1で受信している。
この受信処理を割り込み処理としている。
スレーブ側で受信するbyte数は8Byteなので、その数でポーリング処理部にMODBUS信号を受信した事を示すフラグ変数(FgIntMBA)をセットします。
マスターから送る信号は他のスレーブとの通信も有ることから通信長は 8Byteとは限らないので、この信号をキャンセルするためにTIME1を使用している。
詳細は 3.2.1 マスターメッセージ長の確認処理 を参照

(2)ポーリング処理部 メイン関数部の繰り返し部の説明をする。
(1)温度変換が自動設定でないなら(9)へ
(2)温度検出素子が登録されていないなら(9)へ
(3)全温度検出素子に温度変換指示
(4)変換待時間になったか:なったら(8)へ
(5)フラグ変数(FgIntMBA)がセットさてないなら(4)へ
(6)MODBUSの信号解析(ModBusAnalyze)へ
(7)フラグ変数をリセットする (8)全温度検出器の温度データを読み込んで(1)へ

(9)フラグ変数(FgIntMBA)がセットさてないなら(1)へ
(10)MODBUSの信号解析(ModBusAnalyze)へ
(11)フラグ変数をリセットして(1)へ

MODBUSの信号解析はFunctionにより、DataAddresデータ、Registerデータ を解析してその処理を行うが、DeviceAddressやCRCが合ってない場合は、エラーメッセージをマスターへ返す。

1.1.5 処理速度を早くする処理

(1)USART送信にDMA転送処理
(2)CRC生成処理 444(us)→118(us)
(3)メモリー転送 43.5(us)→28(us)
(4)温度だけの読み込み処理 177(ms)→120(ms)
(5)USART受信を割り込み処理 1Byte= (us) 最後のByte処理= (us)

1.1.6 エラー処理

マスターがスレーブに対しエラーコードを指示した場合は、スレーブからマスターに対し下記応答をする。
0:Device Address = スレーブ側のアドレス
1:Function = マスターのコード + 0x80
2:Data数 = 2
3:Hi Register = ModBusErrCode0
4:Lo Register = ModBusErrCode1

なお温度検出器のエラーコードは ホールディングレジスタ の 0x0007〜0x000E に格納されている。
格納しているエラーコードを読み出した場合は、そのレジスターは初期化(0x00)される。
(1) スレーブが無い
(2) ファミリーコード0x28のスレーブが無い
(3) CRCエラー
(4) 分解能設定変更ができない
(5) EEPROMに設定変更の記録ができない

1.2 回路

1-Wireの温度計を最大8個接続し、そのデータをMODBUSにより転送する。
スイッチを動作させることにより、DeviceAddressと通信速度をリセットできるようにする。
リセットでは
・DeviceAddressは 0x00
・通信速度は 9600(bps)

1.2.1 開発時の回路

開発時はH32V003F4P6を使用して開発する。
理由は 『003J4M6_開始』ページ の4.3.2 USARTを使用する場合を参照下さい。


1.2.2 開発後の確認用回路

開発時はH32V003J4m6を使用して確認する。


1.3 通信速度

最低通信速度を9600(bps)から上限を576(kbps)とした。
これは単純に速度を8種類として
0:9800 1:19200 2:38400 3:57600 4:119200 5:230400 6:460800 7:576000
としただけで、大した根拠はない。

576(kbps)で動作できることを確認した。

右図のオシロ黄色線はマスター(PC)の送信信号だが、速度が早いた方形波が三角波になっており USB-RS23変換器の性能が危ない事がわかる。
青線はCH32V003の送信出力でまだ大丈夫そうだ。

1.4 動作確認


スレーブ(DS18B20)が3個接続しているが、矢印のモノは分解能 12bit しか反応が無い偽物ですが、 今回作成したDS18B20-MODBUSプログラムは、各bitで全てが動作し、大きい分解能のbitで測定を実施するようになっており 下記温度は12bitの分解能の値になっている。
Pythonでテスト用のMODBUSプログラムでスレーブの温度情報を読み取ったデータを下に示す。 アドレス8にスレーブを検出した台数が入っている。





2 MODBUSの仕様

2.1 マスター側

マスターとはCH32V003に指令を出す側を言います。
プログラムの開発中はWCH-LinkEを接続するPC側です。

2.1.1 フレーム構造

8Byte固定です。
NoBytrs01234567
項目Device
Address
FunctionDataAddresRegisterCRC
TopLoTopLoTopLo

2.1.2 DeviceAddress

スレーブ側のアドレスを入力する。
最初は0x00になっているが、MODBUSを通して 0x01〜0xF7(10進数 247) まで設定が出来る。

2.1.3 Function

今回作成するスレーブ Dataには
・読み書きできる2Byteデータ
・読込専用の2Byteデータ
の2種類がある。
このデータをどうするかこのFunctionで指示する。
Function Code指示・レジスタ概  容
0x03Read Holding Registersホールディングレジスタ(16ビットの値)の読み出し。
0x06Write Holding Registerホールディングレジスタ(16ビットの値)の書き込み。
0x04Read Input Registersインプットレジスタ(16ビットの値、読み出し専用)の読み出し。
0xFF----------ーーーーーーーーーースレーブからマスターへエラーメッセージ

2.1.4 DataAddres

レジスタ名開始アドレス終了アドレス概  容
Holding Registers0x00000x003Fアドレスなど設定・エラー・ROM*1・閾値*1・分解能*1
Input Registers0x00000x0010測定した温度、エラーコード
*1 現在は変更出来ない。 データアドレス詳細はスレーブの項を参照下さい。

2.1.5 Register

ここはファンクションにより違う内容になります。
Function Codeレジスターの内容
0x03読み出すDevice Addressの数
n = (Hi Register +Lo Register)*n
0x04
0x06書き込む レジスタの値
ただしスレーブ側の応答の Data数 = N は Registerの数なので、上記レジスターの内容に出てくる nと違い、
n = N*2 となるので注意すること。

2.1.6 CRC

CRCについては『Software Protocol MODBUS 全般』のCRC参照下さい

2.2 スレーブ側

2.2.1フレーム構造

No Byte0123~(N*2)+14~(N*2)+2N*2+3N*2+4
項目Device
Address
FunctionData数 = N Hi
Register
Lo
Register
Hi CRCLo CRC
スレーブの送信バッファーは 最大 133Byte必要 理由は以下による。
スレーブからマスターへ送るデータは
1Byte(Device Address) +
1Byte(Function) +
1Byte(Data数) +
128Byt(最大レジスター数 64×2) +
2Byte(CRC) = 133Byte
バッファーの数を確保できない場合は、プログラムで制限するとともに取り扱い説明書にData数の最大値を表記しておく必要がある。
なお
Data数 n = 単位は1Byteです。
ModBusのレジスターのデータは通常2Byteなので通常最低値は n = 2 となります。

2.2.2 DeviceAddress

ブロードキャストアドレスは0x00です。
 ブロードキャストアドレスでは全スレーブで
  保持レジスターの書き込みに使用される。
  マスターへ返答はしません。
設定できるアドレスの範囲は0x01〜0xF7です。
初期設定は0x01です。

2.2.3 Function

Function Code内  容
0x03Read Holding Registers :ホールディングレジスタ(16ビットの値、読み書き可能)の読み出し。
0x06Write Holding Register :ホールディングレジスタへの書き込み。
0x04Read Input Registers :インプットレジスタ(16ビットの値、読み出し専用)の読み出し。

2.2.4 DataAddress & Register

(1) ホールディングレジスタ
Data
Address
項目内容
StartEndHi RegisterLo Register
0x0000----DeviceAddress-ーーー初期値は0x00 設定範囲は0x01〜0xF7
0x0001----通信スピード----初期値は0:9600bps ※1参照
0x0002----変換待ち時間----初期値は0で分解能の応じた時間 1~255:単位は1(sec) 
0x0003----温度指令----初期値は0x00:off 0x01:自動(0x0002で設定した時間で計測開始) 0x10で変換指示
0x0004----スレーブの登録ーーーー初期値0:完了 1:指示
0x0005----温度素子全体Hi温度閾値Lo温度閾値
0x0006----温度素子全体0x00変換分解bitなど
0x0007----スレーブ0 ErrCode 01※2ErrCode 02※3
0x0008----スレーブ1 ErrCode 01※2ErrCode 02※3
0x0009----スレーブ2 ErrCode 01※2ErrCode 02※3
0x000A----スレーブ3 ErrCode 01※2ErrCode 02※3
0x000B----スレーブ4 ErrCode 01※2ErrCode 02※3
0x000C----スレーブ5 ErrCode 01※2ErrCode 02※3
0x000D----スレーブ6 ErrCode 01※2ErrCode 02※3
0x000E----スレーブ7 ErrCode 01※2ErrCode 02※3
0x000F----MODBUSErrCode 01※4ErrCode 02※5
0x00100x0013スレーブ064bit RomCode 上位〜下位
0x00140x0017スレーブ164bit RomCode 上位〜下位
0x00180x001Bスレーブ264bit RomCode 上位〜下位
0x001C0x001Fスレーブ364bit RomCode 上位〜下位
0x00200x0014スレーブ464bit RomCode 上位〜下位
0x00240x0017スレーブ564bit RomCode 上位〜下位
0x00280x001Bスレーブ664bit RomCode 上位〜下位
0x002C0x001Fスレーブ764bit RomCode 上位〜下位
0x0030----スレーブ0 Hi温度閾値Lo温度閾値
0x0031----スレーブ1 Hi温度閾値Lo温度閾値
0x0032----スレーブ2 Hi温度閾値Lo温度閾値
0x0033----スレーブ3 Hi温度閾値Lo温度閾値
0x0034----スレーブ4 Hi温度閾値Lo温度閾値
0x0035----スレーブ5 Hi温度閾値Lo温度閾値
0x0036----スレーブ6 Hi温度閾値Lo温度閾値
0x0037----スレーブ7 Hi温度閾値Lo温度閾値
0x0038----スレーブ0 ----分解能bit
0x0039----スレーブ1 ----分解能bit
0x003A----スレーブ2 ----分解能bit
0x003B----スレーブ3 ----分解能bit
0x003C----スレーブ4 ----分解能bit
0x003D----スレーブ5 ----分解能bit
0x003E----スレーブ6 ----分解能bit
0x003F----スレーブ7 ----分解能bit
今回は 0x0005,0x0006について変更出来ない。
※1 通信スピード枠の値と通信スピード(bps)
dec(bps)8byte(us)PassTime(us)
0x009600833310000
0x011920041675000
0x023840020832500
0x035760013891670
0x04115200694830
0x05230400347420
0x06460800174210
0x07576000139170
PssTimeはマスターからのメッセージが8Byteより短い場合に受信カウンターをリセットする時間を言う。


(2) インプットレジスタ
Register項目備考
0x0000温度計接続数HiByteは用途無しLoByteに温度計接続数
0x0001温度計エラー※1
0x0002温度 温度0※1値は符号付きで1/16にすること。
0x0003温度 温度1※1
0x0004温度 温度2※1
0x0005温度 温度3※1
0x0006温度 温度4※1
0x0007温度 温度5※1
0x0008温度 温度6※1
0x0009温度 温度7※1




3 プログラムの設定と確認

3.1 割り込み順位

ディバイスPreemptionSub備考
USART1 Rx01
USART1 Tx DMA11
TIM100
TIM210

3.2 エラー処理

3.2.1 マスターメッセージ長の確認処理

マスターメッセージ長の確認が必要な理由
このアプリて使用するマスターのメッセージ長は8Byte。
プログラムではメッセージを1Byte受信するたびに配列に入れる。
8Byte受信すると配列のデータを解析してコードが正しければ応答メッセージを返すようにしている。
しかしマスターのメッセージが8Byteでない場合に次に送られてくるメッセージの受信に対して不具合が出てきます。
例えば本装置以外へのマスターからのメッセージで6Byteしか来ない場合、次のマスターからのメッセージの先頭2Byteを加えて、 一つのメッセージとして処理することになるため以降、常に間違ったメッセージになってしまう。
これを防止するため
8Byteより短いデータであることを検出し、受信データの配列を最初に戻すようにTIM1を使用する。
このTIM1の時間は8Byteより多少長い時間を設定する。
USART1の速度を変更する時にTIM1の時間も変更する。
もう少し具体的に
メッセージの1byte目でTIM1の割り込み処理を使用する。
メッセージの8byte目でTIM1の割り込み処理を停止、受信データを解析して処理、受信データの配列を最初に戻す。
メッセージが8Byte以下の場合はTIM1の割り込み処理により、受信データの配列を最初に戻す。

TIM1:タイマー値確認

通信速度(bps)設定値(us)測定結果(us)
960091709160
1920045804580
3840022902290
5760015301530
115200760760
230400380380
460800190190
576000150150
921600100100
上記に多少関係することでマスターから次のデータが送らてくるまでの時間をGoogleのAIで確認した。
MODBUSのメッセージで次のメッセージを送るまでの間隔(時間)について規定はありますか
Modbusプロトコルでは、次のメッセージを送るまでの間隔(時間)について、使用する
伝送モード(主にRTUモードとASCIIモード)によって具体的な規定があります。 
RTUモードの場合
RTUモードでは、メッセージの開始と終了を識別するために無通信時間が重要です。 
    ・メッセージ間の間隔: 1つのメッセージの終了から次のメッセージの開始までは、
    最低3.5文字時間(3.5キャラクタータイム)以上の無通信時間が必要です。
    ・メッセージ内のバイト間隔: 1つのメッセージ内の各バイト間隔は、
    最大1.5文字時間以内である必要があります。これを超えると、
    受信側デバイスはメッセージが不完全であると判断し、破棄します。
    ・通信速度と時間: この「文字時間」はボーレート(通信速度)によって変化します。
    例えば、9600bpsでは約4.0ms、19200bpsでは約2.0msが3.5文字時間に相当します。
    ただし、19200bpsを超えるボーレートでは、3.5ms(または1.75ms)といった固定値が
    使用されることもあります。 

ASCIIモードの場合
ASCIIモードでは、メッセージの開始と終了が特定の文字(開始はコロン『:』、
終了はCR/LF)で区切られるため、メッセージ内のバイト間隔の規定はRTUモードほど厳密ではありません。 

    メッセージ内のバイト間隔: メッセージ内の文字間隔は最大1秒まで許容されます。 

補足事項
    ・マスタとスレーブ: 通常、マスタ側は次の要求メッセージを送信する前に、
    スレーブからの応答を待つか、タイムアウトするまで待機します。
    スレーブは応答メッセージの送信後、次のメッセージを受信する前に3.5文字時間以上の
    無通信時間を確保する必要があります。
    ・機器固有の仕様: 多くの機器では標準仕様に準拠していますが、一部の機器(スレーブ)によっては、
    応答遅延時間の設定(0~1000msなど)ができる場合や、特定の推奨値が設定されている場合もあります。 

したがって、Modbusのメッセージ間隔は、使用するモードとボーレートに依存し、
特にRTUモードでは厳密なタイミング規定が存在します。機器の仕様書を確認することをお勧めします

3.2.2 マスターメッセージの確認処理

表面上には出てこないが
プログラムを作成していく中で動作テストをするが、MODBUSコードが合っていあに場合にUSART1にエラーコードを送信するようにしている。
そこで使用しているエラーコードを記載する。(通常使用時はコメントアウトしている。)
変数名 ErrCode
CodeNo使用関数
bitHexModBusAnalyzeModBus0*USART1_SET
0b000000010x01--Register 0---
0b000000100x02--Register > Max---
0b000001000x04--DataAddres > Max---
0b000010000x08--DataAddres+Register > Max---
0b000100000x10DeviceAddress---
0b001000000x20CRC---
0b010000000x40FunctionNo---
0b100000000x80通信速度No

テスト例(TIM1の設定も含む確認する)

マスターからのメッセージ: 0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x53 0x41
下図のオシロ波形説明
黄色線:マスターの要求メッセージ 速度は115200(bps)
水色線:スレーブで処理したエラーコード CRC異常(テスト用エラー表示、通常はコメントアウトしている)
赤色線:USARTのRx割り込み処理を示す。
青色線:TIM1の動作を示す。
1.マスターの要求メッセージ8Btye(0x02,0x03,0x00,0x00,0x00,0x00,0x00)を受信。
2.TIM1をリセットして
3.メッセージ最後の2ByteはCRC値になるが、CRCを計算すると0x45 0xF9になり、違うためエラー出力
続いて
4.TIM1を動作させ残り6Byteを受信開始(図の[AX]部)
5.メッセージが8ByteにならないのでTIM1の設定値 760(us)経過(図の[BX]部)で受信データの配列を最初に戻す。
[AX]〜[BX]の時間は Cursors の ΔX: 760(us)
以上により次のマスター要求データを異常なく受信できるようになる。


続いて正常なマスターからのメッセージを送信: 0x2 0x3 0x0 0x0 0x0 0x1 0x84 0x39
スレーブ側では正常に応答メッセージを送信(自分のDeviceAddressを返答)


3.3 動作確認

3.3.1 通常動作

UART1速度115200(bps)
マスターからのメッセージ : 0x02 0x03 0x00 0x00 0x00 0x02 0xC4 0x38
下図のオシロ波形説明
黄色線:マスターの要求メッセージ
水色線:スレーブの返答メッセージ
赤色線:USARTのRx割り込み処理を示す。
青色線:TIM1の動作を示す。

応答メッセージは DeviceAddress = 2 通信速度 = 4 = 115200(bps)を表している。

3.3.2 DeviceAddress変更

DeviceAddressを2➡1へ変更 UART1速度115200(bps)
マスターからのメッセージ : 0x02 0x06 0x00 0x00 0x00 0x01 0x48 0x39
下図のオシロ波形説明
黄色線:マスターの要求メッセージ
水色線:スレーブの返答メッセージ
赤色線:USARTのRx割り込み処理を示す。
青色線:TIM1の動作を示す。

DeviceAddressを1以外はスレーブから返答がなかった


3.3.3 USART1速度変更

速度を230400(bps)➡115200(bps)へ変更する。
マスターからのメッセージ : 0x2 0x6 0x0 0x1 0x0 0x4 0xd9 0xfa
下図のオシロ波形説明
黄色線:マスターの要求メッセージ 速度は230400(bps)
水色線:スレーブの返答メッセージ 速度は115200(bps)
赤色線:速度変更の動作を示す。
青色線:TIM1の動作を示す。


3.3.4 CRC処理方法の比較

(1) CRCの計算は2箇所あり

 マスターからの要求フォーマットのCRC計算
 スレーブからの応答フォーマットのCRC計算

(2) ModBusのCRC計算方法として今回は2種類を比較する。

 ①純計算方式
 ②配列を組合せた計算方式

(3) CRCの対象Byte数

 マスターからの要求フォーマットは6Byte固定
 スレーブの応答ファーマットは 最大131Byte 〜 最小5Byte
処理時間の測定は、マスターからの8Byte目の受信〜スレーブの応答データをTx用のDMAへ送るまでの時間を測定する。
計算方法対象Byte実行時間(us)
6+131444
6+546
6+131118
6+519

(4) 結果

 配列を組合せた計算方式の方が4倍早い事が判った。
 今回のこの方式を採用する。

今回の時間データから
A + B × Byte数 の式で 固定時間(A)とByte変動時間(B) を出したのが下表。
計算方式A(us)B(us)
11.253.16
10.350.79
①/②1.0874


3.3.5 コピー方法の比較

6+131118
6+519
6+131111
6+518

3.3.6 メインプログラム

プログラム概容

初期
・各部の初期化
・スレーブ登録
・スレーブがあれば温度測定 停止→自動

①自動になっているか no → ⑩ → ①へ
 ↓
②スレーブ台数が有る場合は no → ⑩ → ①へ
 ↓
③温度変換指令
 ↓
④-1 ⑩
④-2 待時間が超えたか no→④-1へ
 ↓
⑤各スレーブの温度を読込、配列へ記録
 ↓
①へ

⑩ModBusの信号が来たか(ディバイス番号が合っているかまでを割り込み処理で判定している。) no→ 戻る
 ↓
 ⑳
 ↓
戻る

⑳ ファンクション他解析、実行
 ↓
戻る

メインプログラムの動作例

(1)ModBusで変換指示 0x(00,06,00,03,00,10,79,d7)
(2)全スレーブに温度変換指示を出す
(3)変換待ち時間待機
(4)各スレーブの温度を取得してModBusで使用している配列にデータを格納する。
(5)ModBusで変換したデータを受信 0x(00,04,12,01,37,01,3a,01,33,01,3f,01,3f,01,3b,01,4a,01,3c,00,08,fa,92)
時間をオシロで観測 黄線 CH32V003のRX、水線 TX、赤線 1-Wire動作 青線 1-Wire
(1)〜(5)の全体波形

(4) 処理時間は121(ms)  (5) 24(ms)

(1) 8.22(ms)  (2)3.2(ms)

信号内容について (1) 黄線したのHex値の通り
(1)~(2)初め 8.25(ms)
(2) 0011001100100010→11001100(0xCC)01000100(0x44)





4 プログラム

プログラムはMounRiver Studio Ⅱで作成しました。
コメントや無駄なプログラムは消したつもりです。
プログラムとしては 1000行程度になりました。
無駄なものや改善点がございましたら、お教え下さい。
プログラムの説明用ページは後日になります。




































更新日 2026/03/19 14:38  管理者 平林 剛Hirabayashi Takeshi