平成27年10月18日
汎用の高速シリアル通信機能(12) 組み込みでC++に期待すること
ハードウェアの話からソフトウェアの話に移行していますが、どうしても平行して進めていく必要があるので、少し読み難いのですが已む無しとします。
前に少しだけ書いてあるのですが、従来Cで書いていたのをC++に移行したいと考えるきっかけは、一つのCPU内に複数のスレ−ブ動作を記述したいことでした。当然Cでも書けるのですが、少しだけ面倒な部分が出てきます。
例えば、入力ポートの値を更新する処理を考えると、これらは各スレーブで動作内容が異なるので、
- update_inport(slave_no)のように処理関数の引数にどのスレーブを動作させるかを引数で渡し、関数内部で処理を分ける
- update_inport_slave1()のように処理関数の名前にどのスレーブを動作させるかを組み込む
- 各スレーブ毎に関数のテーブルを用意して、これを呼び出す(これでもスレーブの指定は必要)
などの処理が必要です。
本来、別CPUに実装したときの関数名や引数そのままでは動作せず機械的な書き直しをするか、プログラムの可読性が下がる関数テーブルを使用することになります。これはあまり進んでやりたくない。
一方、C++ではクラスや名前空間といった機能を使うことで、例えばslave1::update_inport(), slave2::update_inport()といった書き方が出来ます。この記述方法はスレーブ動作を個別のCPUに分割しても基本的には変更の必要がありません。クラスや名前空間を使うことによるカプセル化の影響で僅かに処理が重くなるかもしれないですが、その程度のことは問題ではありません。
私の希望は、出来るだけ自然な名前で関数や変数の名前付けを行い、かつ似たような処理でも名前の衝突が起きないようにしたいのです。これはカプセル化の要求と基本的に一致します。
同じ要求は、今までもありました。例えば、今作っているRS-485の通信プログラムでも、今の仕様まで一気に作ってきた訳ではないのです。
- 最初は1バイト送受信する毎に割り込み処理を行う基本的な送受信動作
- 次はFIFOを使った複数バイトを一度の割り込み処理で処理するブロック転送動作
- そして今はDMAとタイマを併用した可変長通信のブロック転送動作
と徐々にプログラム機能を追加して書いてきています。
これらの処理に関連して、例えば割り込みフラグの設定処理では各段階での内部処理が全く同じには出来ないので同じ関数名には出来ず、SCIFIFO_setTXIF(value),
SCIDMA_setTXIF(value)のように、各段階ごとに共通のヘッダを追加した関数名を使用しています。
しかし、この記述方法は通信方式をFIFO方式からDMA方式に変更することを考えると、プログラムの変更箇所が非常に多くなります。ここでもクラスを使用しておけば、クラスの宣言部分を書き換えるだけで多くは変更をしないで済ますことが出来ます。
class Cscififo com_port; //従来記述を削除して
class Cscidma com_port; //新しいクラスに置き換えるだけで
........
com_port::setTXIF(1); //多くの関数名を書き換える必要が無い
C++言語は非常に多機能の上、この数年でも機能仕様自体が大きく変化しているので、全ての機能を使うのは非常に大変です。C++らしくないとしても、その機能の一部分だけでも有用に活用する方針でプログラムを書いていきます。