記事執筆の皆様、お疲れ様です。
組込み関連で思いついたものを投稿します。
振る舞い
UART通信(通信速度は9600bpsとしましょう)にて、<CR><LF>
を終端とする30文字ほどの文字列(カンマで区切られた数値が続く)を受けた時に応答するマイコンがあるとします。
そのようなマイコンに以下の手順で文字列を送るとしましょう。
- ターミナルソフト(例:TeraTerm)を立ち上げる
- 送信設定にて
<CR><LF>
を終端文字列として送るようにしておく
- 送信設定にて
- コマンドをぽちぽち入力していく
- Enter (
<CR><LF>
送信)
処理結果として以下の2種類があるかもしれません。
- ケースA: 応答を返す
- 文字列読取り成功している
- ケースB: 応答を返さない
- 文字列読取り失敗している
一方で、上記手順2において「30文字を一気に送信」すると、ケースA・ケースBともに「文字列読取り成功」になるとします。
上記2つのケースは何が異なるのでしょうか?
マイコン側のプログラム
マイコン側では「30文字」ほどの文字列を受信することになります。
マイコンで2KBのメモリしか残っていない場合、メモリ使用は極力減らしたいものです。(残りのメモリでデータ処理などするかもしれない。将来の機能追加があるかもしれない。デバッグ用に残しておきたい、などの理由です)
さて、マイコンで30文字の文字列を受信する場合、その30文字はどこにどう確保するのか、という疑問が出てきます。
関数の中の変数にする案
void processCommand()
{
char rcvCmd[40]; // 30文字以上受けるため
/* 受信処理 */
/* 受信データに基づく処理 */
}
この場合、メモリ領域としては「スタック領域」を消費します。
参考 https://www.uquest.co.jp/embedded/learning/lecture16.html
processCommand()
が、あるタスクから使用されている場合、タスク用に割り当てたメモリ領域を消費します。
例として120バイトのタスク用メモリ割り当ての時に30バイトを使うとなると、タスクの他の処理でメモリ使用がしんどくなりますね。
そして、タスク内のメモリ使用を失敗すると、途中で動作しなくなるというような状況に遭遇するかもしれません。
staticをつける案
「関数の中の変数にする案」ではなく、staticをつけるとどうなるでしょうか。
static char rcvCmd[40]; // 30文字以上受けるため
この場合、rcvCmdは「静的領域」に確保されます。
参考 http://oresi.hatenablog.com/entry/2014/04/24/133315
「静的領域」に確保した場合、「タスク用メモリを使用しなくなる」という利点があります。
タスク用メモリ割り当て時は関数を抜けた時点でメモリが解放されるという利点がありますが、静的領域の場合はメモリ解放がありません。
静的領域をたくさん使うと、それはそれでメモリ使用方法はよくないかもしれません。
解放されないメモリをメモリプール (もしくはメモリパラシュート?)として確保して、複数から使用する、という方法もありますが、設計時には検討事項が増えていきます。
メモリを30文字分確保しない案
上の2つの場合とは違う方法として、メモリを30文字分確保しない場合があります。
3.14,1.59,2.65,3.58,9.793,2.38,4.626,2.718,6.022,...,1.023<CR><LF>
(...は省略記号)
というような文字列が到達する時、全部の文字列を受けてから処理するのではなく、カンマで区切られた数値ごとに処理をする、という方法です。
この場合、項目ごとの文字数は4文字(例: 3.14
)程度になるため、「関数の中の変数にする案」と同じで4+α程度のchar型配列用メモリを確保すれば良くなります。4+α程度なら、タスク割り当てのメモリの消費としては良いかもしれません。
一方で、このような実装の場合、終端までの文字列読込みを如何に実装するかという検討事項が出てきます。
その方法としては以下などがあります。
- 方法1
- 指定の時間内に連続して送信された文字列だけを受信する (例: 10msec以内の連続性)
- 途中で文字列が途切れた時に処理を抜けることになる
- 指定の時間内に連続して送信された文字列だけを受信する (例: 10msec以内の連続性)
- 方法2
- 終端文字列が送られるまで受信待ちする
- 方法3
- 前回受信した位置を保持しておき、続きの文字列を受信した時に続きの処理を行う
方法1は実装が簡単です。タイムアウト処理で抜けるだけですね。
方法2は「ポーリング」という状態になります。マイコンでの処理としては無駄が多いかと思います。
方法3は、前回受信した位置情報の保持、その初期化処理など複雑になります。
さて、冒頭の話に戻りましょう。
コマンドをぽちぽち入力していく
コマンドをぽちぽち入力した場合、それぞれの文字の入力間隔は何msecくらいでしょうか?
おそらく10msecよりも速くはないでしょう。
(2週間前に見た映画「タイピスト!(2012)POPULAIRE」(監督: Rgis Roinsard) では、当時の世界記録保持者でも1分間に500文字程度。1文字が120msec程度ですね)
上の方法1での実装の場合には、「送信の連続性」が担保されずに「ケースB: 応答を返さない」ということになります。
一方で、上記手順2において「30文字を一気に送信」すると、ケースA・ケースBともに「文字列読取り成功」になるとします。
こちらの場合は、文字間の送信間隔は10msec以内になるため、方法1でも問題がなく、ケースA・ケースBともに「文字列読取り成功」ということになります。
おわりに
ある通信仕様の装置があるとします。
その取説にコマンドが掲載されていて、手入力でコマンドを送信しても応答がない場合、今回の記事の「方法1」のような実装かもしれません。
そういう場合は、テキストファイルにコマンドを用意して「一気に送信する」という方法が有効かもしれません。
一方で、組込みプログラミング作成者の立場では、(10msec以内で)コマンドが連続して送られない場合も考慮する必要がある時は設計段階でそれを意識して設計し、コーディングしましょう。
なお、この記事で書いた方法以外でメモリ使用を少なく、送信の連続性も必要ない実装方法がありましたら、ご教授お願いいたします。