はじめに
ステッピングモーターをリモートで動作させる際のマスタ側のクエリのメッセージ構成が、
- スレーブアドレス(8ビット)
- ファンクションコード(8ビット)
- データ(N * 8ビット)
- エラーチェック(16ビット)
となっていた。
このエラーチェックにCRC-16方式が採用されていたため、その実装を行なった。
詳細
実際に使用したものは、オリエンタルモーターのAZD-KDドライバ。
モーター構成:オリエンタルモーター
これをリモートでコントロールするために、RS-485通信によるModbus RTU制御を行なった。
このときのクエリのメッセージを作成する際に、エラーチェックのメッセージを作成する必要があった。
環境
Python 3.7.1
アルゴリズム
CRC-16の計算方法を以下に示す。
- 初期値をFFFFhとし、FFFFhと最初のアドレス(8ビット)の排他的論理和(XOR)を計算
- 1.の結果を1bit右シフト。これを桁あふれが1になるまで繰り返す
- 2.の結果とA001hのXORを計算
- シフトが8回になるまで2.と3.を繰り返す
- 4.の結果と次のアドレス(8ビット)のXORを計算。すべてのバイトに対して2.から4.を繰り返す。最後の計算結果がCRC-16の計算結果となる。
排他的論理和(XOR)
入力A | 入力B | 出力 |
---|---|---|
偽 | 偽 | 偽 |
偽 | 真 | 真 |
真 | 偽 | 真 |
真 | 真 | 偽 |
異なる入力だと真、同じ入力なら偽。 |
複数桁は桁ごとに各々で計算する。
0101 0101 0101
^) 0010 0101 1010
------------------
0111 0000 1111
実装
Pythonで実装。CRC-16の計算のみ示す。
フローチャート
data_byteを更新に対してYES/NOなのは目をつむってください(更新できる→YES、更新できない→NO)。
クエリ
メッセージは、リモートI/Oへのアクセスのため以下のようになった。
構成 | 値 | 内容 |
---|---|---|
スレーブアドレス | 01h | スレーブアドレス1(設定) |
ファンクションコード | 03h | 保持レジスタからの読み出し |
データ(レジスタアドレス(上位)) | 00h | 読み出す起点となるレジスタアドレス(上位) |
データ(レジスタアドレス(下位)) | 7Fh | 読み出す起点となるレジスタアドレス(下位) |
データ(レジスタ数(上位) ) | 00h | 起点のレジスタアドレスから読み出すレジスタの数(上位) |
データ(レジスタ数(下位) ) | 01h | 起点のレジスタアドレスから読み出すレジスタの数(下位) |
エラーチェック(下位) | - | ここを求める。 |
エラーチェック(上位) | - | ここを求める。 |
コード
# -*- coding: utf-8 -*-
# エラーチェック以外のクエリ
command = b"\x01\x03\x00\x7f\x00\x01"
# 最初のCRCレジスタ値をFFFFhに設定
crc_register = 0xFFFF
for data_byte in command:
# CRCレジスタとデータバイトのXOR
tmp = crc_register ^ data_byte
# シフト回数を記憶
shift_num = 0
# シフトが 8回になるまで繰り返す
while(shift_num < 8):
if(tmp&1 == 1): # 桁あふれが1なら
tmp = tmp >> 1
shift_num += 1
tmp = 0xA001 ^ tmp
else:
tmp = tmp >> 1
shift_num += 1
# 計算結果をcrc_registerにセット
crc_register = tmp
# 計算結果をbytes型へ変換
crc = crc_register.to_bytes(2, 'big')
# 結果を表示
print(crc)
実際は、右シフトする前にtmp
の1桁めの値を確認するために、tmp%2
tmp&1
で0か1かを確認した後に右シフト処理を行なっています。
もっと良いやり方があればご教授願います。
結果
実行
$ python crc16.py
結果
$ b'\xd2\xb5'
これから、以下のような結果を得ることができました(順番に注意:下位→上位の順)。
構成 | 値 | 内容 |
---|---|---|
エラーチェック(下位) | B5h | CRC-16の計算結果(下位) |
エラーチェック(上位) | D2h | CRC-16の計算結果(上位) |
よってb'\x01\x03\x00\x7f\x00\x01\xb5\xd2'
というクエリを送信し、スレーブからのレスポンスを読むことでステッピングモーターのリモートI/Oを読むことができました。