2017年11月21日火曜日

武装メイド(妄想ダンプファイル)

 色々トラブルが有りつつも、新しいHDDを買ったので、データ移動待ち。

2017年11月16日木曜日

うにおん

 要素の大きさが固定のキューで様々な情報をやり取りする際は、union(共用体)が役に立つ。
 1つの情報は構造体によりデータを持つが、複数の情報を排他で保持したい場合、それらの構造体をさらに構造体に入れるのは、メモリ効率の点で無駄が多い。

 構造体を使う場合、1回の操作に必要な情報をひとまとめにして渡すことができる、という利点がある。例えばキューが整数型1個(と内容を示す列挙型)しか扱えない場合、1回の設定で複数の変数を設定する必要がある場合、何回もキューに入れる操作が必要になる。

 共用体が使えるなら、複数の構造体を選んでキューに入れられるので、キュー1本で様々なデータを受け渡しすることができる。
 共用体を使わなくても、例えばキューの要素サイズを64バイトの固定長にする、といった方法で複数の構造体を入れることができる。しかし、後から要素数の大きい構造体を使おうと思った際に、保守が困難となる。共用体を使えば、コンパイラが自動的に要素サイズを設定してくれる。


 ということで、共用体を使いたいんだけど、コーディングルールで共用体が使えない。
 どうやってデータを渡そうかなーといろいろ考え中。

***

 最近、軽くスランプ気味。
 すっごい簡単な試作基板(コネクタ何個か乗ってて、ジュンフロン線数本という程度)を作っただけでも気力がゴッソリ削り取られた。コーディングもなかなか進まない。
 まぁ、いつもこんな感じな気もするけど。
 なんかぱーっと気分転換したい。

 そういえば、今季もアニメ1本も見てない気がする。いくつか録画はしてるけど、見る気が出ない。

2017年11月14日火曜日

ルームランナー

 某所でルームランナーに乗ってエアガンを撃ってきた。
 イメージはこんな感じ。


 この人すごい上手。

 でも意外となんとかなる。M4で、4km/hくらいなら5m先の直径20cmくらいにほとんど命中する。12km/hでも50cmくらいにほとんど命中する。リロードも特に問題なく可能だった。
 もしも相手が自分を撃ってでも確保したい、あるいは射殺したい、と思っているようであれば、走って逃げても絶対に逃げ切れない。
 サバゲだとどうだろう。こっちが走っていて、相手を撃つ状態。相手がカバー無しで見えてるなら当たるだろうけど、隠れてたらたぶん無理。逆に、相手はむき出しのこっちを撃てる。とはいえエアガンだと弾速が遅いので、リード射撃が必要。相手が距離や速度を見誤れば、撃たれずに移動できるかも。それにこっちは相手を壁の向こうに押し込めるし。とはいえ、それは1対1の状況で、相手が複数人だと誰かに撃たれる。

 ある人曰く、「相手を見てから撃つまでに、どんなに訓練しても3秒未満にはできない」らしいので、逃げるなら10m毎に遮蔽物があるルートを探すしか無いのかな。この遮蔽物は、弾が抜けない強度が必要。エアソフトならベニヤ板で十分だけど、実弾だと鉄筋コンクリート位ないと厳しい(ブロック塀や自動車くらいなら実弾は抜ける)。
 この3秒というの、たぶん正規軍のような統制が取れた組織での話なので、「動くものはすべて撃つ」というテロリストとかトリガーの軽いハンターみたいな相手だと、たぶんもっと早い。ローレディから挙銃して正面の的に撃つのもかなり早いから、3秒ってのはかなり条件が厳しそう。

 ランダムなタイミングで起き上がって、5発くらい撃ったら倒れる的、というのがあれば、ルームランナーの周りに10個くらい置いておけば、かなり楽しそう。5x5mくらいの部屋でも、かなり楽しめそうだ。うちにもルームランナーほしい。

2017年11月13日月曜日

暗黙の型変換

 なぞのきょどうに遭遇して結構ハマった。

#include 
#include 

int main(void)
{
    {
        int32_t v1 = -104;
        uint32_t v2 = 32768;
        int32_t v3 = 450869;
        int32_t v4 = v1 * v2 * 32 / v3;
        printf("1:%d\n", v4);
    }
    {
        int32_t v1 = -104;
        int32_t v2 = 32768;
        int32_t v3 = 450869;
        int32_t v4 = v1 * v2 * 32 / v3;
        printf("2:%d\n", v4);
    }

    return(0);
}


 というコード。最初のブロックは当初のコード、次のブロックは修正後のコード。値は実際に使われた値を書いている。
 どちらも、結果は-241を予想している。
 -104 * 32768 * 32 / 450869 = 約-241.870で、切り捨てて-241となる。関数電卓とかで計算しても、このような結果になる。
 しかし、実際には1では9284という結果になる。
 arm-none-eabi-gcc 4.9.3でも、WSLのgcc 4.8.4でも同じ結果。

 これは、符号ありと符号なしで演算を行う場合、符号なしに変換されて計算されるためらしい(よくわかってない)。
 -104はu32で0xFFFFFF98となる。これに32768を掛けると0x7FFFFFCC0000となるが、32bitに切り落とされるので0xFFCC0000となる。32を掛けて同0xF9800000、450869で割って9284となる。

 型変換が行われるのは、違う形で演算を行うためであり、同じ型同士であれば変換は行われない。ということで、v2をs32にすれば正しい結果となる。


 JSF++で、「符号付きおよび符号なしの値は、算術演算または比較演算で混在してはなりません(理論的根拠: 符号付き値と符号なし値を混在させると、演算が多数の算術変換および積分型昇格のルールに従うため、エラーが発生しやすくなります)」(AV Rule 162 shall not)というのがある。
 今回、身をもって実感した。
 ついこの間、JSF++を翻訳してたので、そう言えば混在しちゃいけないルールが有ったな、と思い出したけど、これを読んでなかったらもっと深みにハマってた気がする。


 このv2は、元のコードでは、ハードウェアの符号なし32bitレジスタから読み取った値なので、そのままu32に突っ込んでいた。実際に取りうる範囲は0x00000000-0x0000FFFFなので、s32でも足りたのだが。

2017年11月11日土曜日

メモ:浮動小数点数

 浮動小数点数は誤差があるから使ってはいけないのだという。例として、「0.1ですら性格に表現できない」とか言われている。実際、JSF++でも浮動小数点数の演算は一部の例外を除き、明確に禁止されている。

 そもそもなぜ浮動小数点数は誤差を持つかというと、大抵の実数は2進数の有限長では表現できないためで、整数値の範囲であれば問題はない。例えば整数同士の加減乗は問題ない。除算の場合は、整数でも1.0/3.0=3.33...のように実数となり、また有限長の2進数では表現できない。

 じゃぁdoubleでも実数しか使わないなら問題ないね、ということになるが、それならint使ったほうが良いだろ、となってしまう。

 参考までに、浮動小数点数の表現範囲は、単精度が23bit、倍精度が52bit、4倍精度が112bitとなる(さらに符号ビットや指数がある)。
 23bitではfloor(log10(2^23))で 10進6桁分の精度がある。52bitでは15桁、112bitでは33桁となる(4倍精度はまだ一般的ではない感じだが)。
 ちなみに8bitから128bitまでだと、8bitで2桁、16bitで4桁、32bitで9桁、64bitで18桁、128bitで38桁を表現できる(符号ありで1bit少ない幅の場合)。
 浮動小数点でも整数でも、演算途中を含め、数値がこの桁数の範囲に収まるのであれば、誤差は発生しない。
 例えば1/128は0.0078125だが、これは有効数字5桁なので、単精度浮動小数点数型でも誤差は発生しない。しかし1/2048は0.00048828125で8桁が必用なので、単精度浮動小数点数型では表現できない。倍精度浮動小数点数型は15桁まで表現できるので、8桁なら問題なく表現できる。
 単精度浮動小数点数型は約6桁しか表現できないから、例えば12345678という数値も正しく表現できない。

 浮動小数点にかかわらず、プログラミングで変数を使う場合には、必要な精度を見極める必要がある。無闇矢鱈と大きな変数を使えば、丸め誤差は発生しないが、それではメモリを余計に使用してしまう。


 例えば、GPSの本によると、単精度では足りず、倍精度を使う必要があると書いてある。
 GPS衛星は約20,000kmの高度にいるから、単精度では100メートル単位でしか表現できない。これでは数百mの計算誤差を生じてしまう。倍精度ならマイクロメートルの単位まで表現することができる。
 実際にはGPSの位置情報は数メートルの誤差があるが、精密測量機器ではミリメートルの精度で計測できるし、汎用型でも誤差数百メートルは許容できないだろうから、倍精度が必用、というわけ。


 整数型で計算を行う場合、加減乗は範囲だけ気にすればいいので気が楽だが、除算はかなり面倒になる。例えば10/6は1.666を丸めた2を期待するが、整数型では切り捨てのために1となる。このあたりの誤差をどう処理するか、といった、本来の処理からは外れた部分を考える必要があるのが大変。
 STM32F4にはFPUが乗ってるんだし、多少誤差が有ったって問題ないんだから、浮動小数点を使ってもいいでしょ、とは思うのだが、一旦整数型縛りを始めるとなかなか。

2017年11月9日木曜日

メモ

 FreeRTOSのコルーチン。日本語ドキュメントだと「FreeRTOSではコルーチンを使用できます」くらいしか書いて無くて、どうやって使うのかが謎い。公式のページが一番わかりやすい。
 Example code creating scheduling and using RTOS co-routines
 Quick Co-routine Exampleの通り、簡単にわかる。

 FreeRTOSのコルーチンはRTOSによるスタックの割当は行われず、FreeRTOS外のスタック領域を使うらしい。またQueueとかも使えない気がする(詳細未確認)。デバッグメッセージをprintfからQueue経由でUARTに出そうとしたらハングアップした。

 基本的に、OSで管理するほどではないけど、ISRとかに書くには大きい、位の低レイヤ処理をターゲットにしてそう。サンプルだとLチカとかやってる。とはいえ、Lチカするにしたってその指令を出すのはタスクなわけで、じゃぁその間の橋渡しはどうやるんだ、という問題が有る。グローバル変数とか?

 今回はRTCの校正をコルーチンで行おう、と思っていた。処理自体はISRでもできないことはないくらいに小さいコードだけど、printfデバッグとかしたいので。ということで、printfが使えない?コルーチンも不可ということに。

***

 STM32F4RTCは、SSRの分解能を優先すると、キャリブレーションが面倒な感じになるっぽい。F4RTCはDIV_AとDIV_Sの2本のプリスケーラの積で1Hzを作るが、DIV_Aを小さくしてDIV_Sを大きくするとSSRの分解能が向上する。ただしDIV_Aが3未満の場合は正の方向にキャリブレーションできないらしい。
 対策として、わざとDIV_Sを外した値にして、キャリブレーションで負のレンジの真ん中に設定しておく、という感じにするらしい。キャリブレーションは9bitのレジスタと1bitの符号で10bit分の分解能があるが、DIV_S<3の場合は9bitの分解能しか得られない。
 ま、今試してるボードでは5bit+Signで足りてるので、9bitでも十分なのだけど。

***

 かなり面倒なことになってきてる。ステッピングモータで遊びたいだけだったのに、どうしてこうなった。

2017年11月8日水曜日

F4RTCメモ

 微妙に痒いところに手が届かない。
 例えばRTCの動作中は位相をモニタしたい。ということで1Hz出力を有効にする。すると、外部イベントによるタイムスタンプの記録ができなくなる。

 とりあえず、32.768kHzの水晶がそれなりに誤差が大きいらしい。といっても21ppmくらいなので、時計としては十分に高精度。だけどオシロで見るとどんどん位相が動いていく(GPS基準)。

 周期はCALR(キャリブレーションレジスタ)で調整する。CALIBRという似たようなレジスタが有るけど、こっちは互換性のために残している?らしい。約1ppm単位で調整ができる。調整幅が約488ppmを超えると、調整幅が倍になって分解能が4分の1になる。それでも調整しきれない場合は、さらに調整幅が倍になって分解能が4分の1になる。最大2000ppmくらいの範囲で8ppm単位、くらいかな。

 一方、位相の調整は、SHIFTR(シフトレジスタ)で調整する。デフォルトでは1/256秒(約4msec)の分解能で移動できる。
 リファレンスマニュアルによると、最大約30usecの分解能が得られるらしい。ただし「消費電力が増加する場合がある」と書いてある。ま、モーター制御に使ったりするなら誤差のレベルであろう。

 とりあえず、大まかな位相差はシフトで取り除き、それより細かい分解能ではキャリブレーションで移動すると良いかもしれない。
 ただし、キャリブレーション値を変化させたままでコアが停止した場合、そのままずーっと移動し続けてしまう。電源供給断で割り込みがかかるようにして、その後数十msec分の電力をキャパシタに入れておき、ISRの中でキャリブレーション値を戻す、みたいな工夫が必用そう。
 あるいは、シフトレジスタの分解能を限界まで上げてしまうか。これで30usecの精度で位相を合せられる。ただ、原因がわからないが、ジッターが100usecくらいある。なので30usecで合せてもあんまり意味がない。


 ま、とりあえず、今の用途なら50msec程度で十分なので、まずは1PPSから自動で位相を合せられるような処理を作らなければ。

 でも、これでもEXTIとISRを使う方法とか、タイムスタンプトリガを使う方法とか、いろいろやり方がある。
 精度を求めるならタイムスタンプだけど、EXTIだとデバッグが楽。そのあたりは追々。


 デスクトップ機のVS Codeが不調で、ドキュメントのフォーマットができない。症状としては、右クリックメニューが変更・切り取りコピー・貼り付け・コマンドの4個以外表示されない。PCを再起動したり、VS Codeを入れ直したりしてもダメだった。ショートカットキーも反応しないので、右クリックメニュー周りのバグではなく、コンポーネントの読み込みがされてないらしい。しかしショートカットキーリストにはフォーマットとかのキーコンフィグが出てる。

 とりあえずしょうがないので、ノートにリモートデスクトップ接続して、ノートからデスクトップ機のコードを置いてあるフォルダをVS Codeで開いて、ビルドと書き込みはデスクトップ機で、みたいなわけのわからないことになってる(ノートは解像度が低いので開発には向かない/ノートから4k液晶は30pしか出力できない/机の上にそもそも置くスペースがない/etc)。