今作っているマイコンのプログラムの一部は、
外部ツールで生成した設定情報をPythonのスクリプトでCのヘッダファイルに変換して、
それを反映した上でビルドするという工程を踏む部分がある。
当初あまり意識してなかったのだが、この設定をあれこれ変えて実験することがあり、
こうすると設定ファイルの反映漏れが気になってしまう。
というわけで生成されるバイナリファイルの末尾に設定ファイルの生成日時などの情報を付加することにした。
設定ファイル自体の生成日時がPythonでの変換後には
#define XXX_GENERATE "02-04-2025_10:20:30"
のように格納されているわけである。
とりあえずの暫定対応としてはこの日時を起動時にUARTから出力するようにしたが、
将来的にはUARTは使えなくなるわけで、別の方法で確認できた方がよい。
で、バイナリファイルを見て一目瞭然というのがよいだろうと、
末尾にプログラム自体のバージョン情報などとともに格納することにした。
ところでプログラムデータはIntel Hex形式で出力されるのだが、
上記のデータを単純に埋め込むとこんな感じになる。(チェックサムは適当にXXと書く)
:10FFE00030322D30342D323032355F31303A3230XX
:10FFF0003A333000000000000000000000000000XX
パッと見て生成日時がわからんわけである。
当然、このデータをASCIIに変換すればよいが、けっこう面倒である。
ASCIIコードで’0’~’9’は0x30~0x39で表現されるので、
0x3□ というのを拾っていけば読めると言えば読めるのはそうだけど。
確かに順番に拾っていくと “02?04?2023?10?20?30” のように並んでいるのはわかるが。
そんなわけでプリプロセッサの処理でこれを二進化十進法に変換できないかと試していたが、
そこで発覚したのはプリプロセッサは文字列リテラルの処理は苦手ということである。
すなわち上記のようにすでに “02-04-2025_10:20:30” と並べられてしまったデータを、
1文字単位に分解してあれこれするということはできないということである。
定数データとしてバイナリに埋め込むにはコンパイラが定数として認識できなければならない。
そうなると頼れるのはプリプロセッサのみだが、どうにも難しい。
逆にプリプロセッサで文字列リテラルを作るのはなんとでもなりますが。
#define XX_GEN_HOUR 10 #define XX_GEN_MIN 20 #define XX_GEN_SEC 30 #define STR_INNER(X) #X #define STR(X) STR_INNER(X) #define XX_GEN_TIME STR(XX_GEN_HOUR) ":" STR(XX_GEN_MIN) ":" STR(XX_GEN_SEC)
これもこれでめんどくさいのでは? と言われればそうだけど、
マクロで文字列化演算子(#)を使って、”10″ “:” “20” “:” “30” のように文字列リテラルを並べれば、
これらは勝手にくっつけて解釈されるので “10:20:30” と書くのと同じ意味になる。
こんな風になっていればバラバラに数値データとして使うこともできるし、文字列としても使える。
こんなところにこだわっても仕方ないわけで、結局は単純に文字列を埋め込みむことに。
Intel Hex形式での出力ファイルはパッと見て読めないが、
0x3□ の□の部分だけを読んでいけばいいのでなんとでもなると。
設定ファイルの生成日時を確認するのにそれぐらいの手間は勘弁してくれと。
出力ファイルの末尾のわかりやすい場所に固めたのだし。
ところでIntel Hex形式で出力するのはこの次のフローでの指定形式だからなんだけど、
“:10FFE000…” という部分にはデータアドレスの下位16bit分しか入ってないんだね。
それより上位のアドレスはそれ以前のレコードで “:020000042000…” って形で設定されている。
別にこのファイルを使って自分であれこれするわけではないのでよいのだが、
モトローラ形式のSレコードだと、”S3152000FFE0…” のように
32bitのアドレス全部が格納されているので前に何があるかと調べなくても解釈が容易である。
この後の処理を自分で作るなら断然モトローラ形式だなと思った。
いずれにせよ古典的な出力形式に過ぎる気はするけど。