2017年10月20日金曜日

妄想:サバゲでピンチ!


テトリス


 最低限、テトリスっぽい遊びをすることはできるようになりました。
 描画のAPIはCで作っていますが、ゲーム本体はLuaで作っています。コード的には、ほとんどニコ動のコピーですけれども。

 【プログラミング】テトリスを1時間強で作ってみた【実況解説】 - ニコニコ動画

 とりあえず遊ぶことはできるようになったので、次は何をやるかなーと思ってyoutubeのゲームボーイ動画を見たんですが、やっぱあれ解像度高いなぁ。素人の片手間で市販ゲームの再現ができるとは思ってないですけども。

 パネルは1枚位買い足してもいいかなと思ってますが、それでも64x64pxにしかならないので、ファミコンはおろか、ゲームボーイにも遠くおよびません。

 とりあえず遊びからは離れて、もっと実用的な方向に行くべきかな。
 CANバスが使えるようになれば遠距離から表示データを送ることができるので、少し離れたところに置いた風速計の情報を表示したりとか、そういうことができるようになります。映画に出てくるような、いかにもな発射ボタンを作って、モデルロケットのカウントダウンを表示することもできます。でもどっちも僕は使わないので、動機が薄い。


 電光掲示板は、かなり飽きてきました。
 何か次のネタを探さないと。

 超音波風速計の続きは、校正環境が無いし。
 人工衛星の追尾は、安物光学系だと厳しくなってきたし、寒い季節は嫌だし。
 そういえば、超音波風速計は前の冬にやってたのか。断熱が薄い家だから、冬はΔtが稼げるという。。

2017年10月19日木曜日

メモ:ぬるぽ

 我が国も購入を決定したF-35、開発時の名をJoint Strike Fighterと呼ぶが、この戦闘機のプログラムにはC++が使用されている。その前に開発されたF-22では信頼性を重視して米国防総省が開発したAdaという言語を使っている。
 C言語を使おうとしたことが有る人ならわかると思うが、C言語には「未定義」「実装依存」が多い。またプログラミングの勉強の最初のほうで使うであろうscanfは、少し調べると「危険だから使うな」という話が溢れてくる。

 話は変わるが、アリアン5というロケットの初号機は、ソフトウェアのバグによって打ち上げに失敗している。この時使用された言語はAdaで、浮動小数点数の計算に問題が有った。このロケットは無人であり、比較的高い高度で爆発したために人的被害はなかった。しかしロケット1機の損失コストは膨大なものになる。
 他にも、パトリオットミサイルは内部の時間処理に浮動小数点数を使用しており、時計が約0.3秒ずれたことにより迎撃に失敗、多数の死傷者・負傷者を出す結果となってしまった。

 航空宇宙・防衛産業では少しのプログラムミスが大きな結果となってしまう。プログラムミスが戦闘機で発生した場合、対価は国民の命で支払うことになる。
 そこでF-35ではどうしたか。
 JSF++というC++のコーディングルールを作った。


 と、シリアスムードで書いてみたけど、「軍用規格ってどんなもんぞ」って軽く読んでみただけ。

 JSF C++ コーディング ルール - MATLAB & Simulink - MathWorks 日本

 基本的にはstdio.hやtime.h等は使うな、#if #endif #defineはヘッダの多重インクルード対策以外に使うな、読み間違いしやすい名前は避けろ、とかそういう感じ。あんまり厳しくない。


 1つ面白いのは、「ポインタはnullとの比較、nullの代入はしません。nullの代わりに0を使え」みたいなのが書いてある。
 「組み込み現場のCプログラミング標準コーディングガイドライン」という本によると、「ポインタにはnullを使い、0は使うな」というふうに書いてある。

 NULLマクロを使え、という主張では、Cでは0には様々な意味があり、整数の0、浮動小数点数の0(0.0)、真偽値の0(FALSE)、ポインタの0(NULL)等がある。それらの型が混在している場所では hoge = 0 と書いてあった際に、hogeの型を覚えていない限りは何を目的とした代入かがわからない。
 一方、hoge = FALSEと書いてあればフラグだとわかるし、hoge=NULLと書いてあればポインタだと理解できる。ということで、NULLの使用が推奨されているっぽい。

 一方、JSF++の方では、そもそも#defineマクロの使用が禁止されているので、NULLマクロが使えないという点がある。ただそれは const void * NULL = (void* )0; とか書いてヘッダで共有すればいい話であって、大きな理由ではない気がする。
 JSF++ではコード内で数値をハードコードすることを禁止しているので、int hoge = 0;というのはルール違反となる。そのため、hoge = 0というのは例外なくポインタに対する代入である、ということになるため、「紛らわしいからやめろ」ということにはならない。

 というのが、ちょっと考えて浮かんだ理由なんだけど、本来はどうなんだろうか。元文見ろという話だけど、英文140pの文章はきつい。だれか翻訳してくれ。

 他にもC++になってNULLの意味が変わったとか、いろいろありそう。


 企業で大規模な開発とかするならJSF++その他のルールに対応したテストツールを使うんだろうけど、個人でフリーに使えるJSF++テストツールって有るんだろうか。
 とりあえず「こういうのはやんないほうが良いね~」とか「こうすると良いね~」ってのを頭の片隅にでも入れておけば、何も考えないでやるよりは役に立つんじゃないかな、と思いつつ。

2017年10月18日水曜日

メモ:Lua

 Luaでテトリス作ってる。電光掲示板の低レイヤAPIが必用なのでPCだと動かないけど、文法チェックはPCのLuaインタラプタでもできる。シリアルポートで転送するとハンドシェイク作ってないので遅いし、転送後だとエラーメッセージがわかりづらいので、PCで文法チェックできるのは便利。

 Cだと当たり前な文法ができないのがつらい。特にインクリメントができないのが厳しい。

 変数のswapとかは楽。a, b = b, aでswapできる。あとはx, y = -y, xで回転できたりするのが便利。

 Cだとa = +1みたいな文法は問題ない。でもLuaでは不可。Cだとa = +-+-+1という書き方はエラー無しで通ってしまう(+--+のような、マイナス記号の連続はエラー)。
 ちなみにa = +-+-+1はa=1となります。+記号は連結に使われて、「符号を反転しない」という動作。マイナス記号は符号を反転させるので、反転が3回で正数になる。

 forループはCと違いちょっと独特。for i = 1, 5 do end だと for (i = 1; i <= 5; i += 1) { } みたいな動作。for i = 1, 5, 2 do end だと for (i = 1; i <= 5; i += 2) { } みたいな動作。for i = 5, 1 do end では1回も回らないが、for i = 5, 1, -1 do endで増加方向を指定すれば for (i = 5; i >= 1; i -= 1) { } のような動作となる。


 Luaは結構面白い。特にコルーチンは便利。もっといろんな便利な使い方がありそう。
 ヒープ領域が85Kほどあるので、メモリには余裕ありそう。どれくらい使ってるか見積もりができないので、実はもう余裕ないのかもしれないけど。
 テトリスはニコニコに有る1時間プログラミングのアルゴリズムをそのままコピーしてる。コピーしてるだけなのに動画よりはるかに時間かかってるわけだが。。

 ロータリーエンコーダはあんまり性能が出ないので使わない方向。たまに他のキーに誤認するので、使うならそのあたりの対策をしてから。

2017年10月17日火曜日

100Wの電源を買ったと言ったな。あれは(以下略

 70Wって書いてやんの。

 RWS100B-5という電源を買いました。ピーク100W、定格70Wの5V電源です。
 ガワの固定ビスが1本なので、触るとカチャカチャうるさいです。あと端子台がかなりヤワイ印象。もっとも、どちらも製品に組み込んでしまえば触らない場所なので、本来の用途なら気にならない部分でしょう。黄緑のパワーLEDがついてますが、これは好みの分かれるところかな。
 端子台のビスはM3.5です。Y端子や丸端子が使えます。ビスを完全に抜いても脱落しない構造ですが、端子台を上にした向きだと丸端子を入れるにはピンセットが必須です。丸端子は「使えないことはない」程度ですね。
 メーカー不詳詳細不詳の古い12V4Aの電源も持ってますが、それと比べて体積で1/2.3くらいです(突起物除く)。容量1.5倍で1/2.3倍なので、7分の2くらいでしょうか。技術は進歩してるな。


 電源とパネルの間にINA226モジュールを入れたので、電圧と電流をオンボードで計測可能になりました。

 全面白の最大輝度で24Wくらい食ってます。今までは最大輝度で全点灯は避けてましたが、5V4Aの電源を使っていたので、かなりギリギリの運用でした。
 新しい電源は5V14A(ピーク20A)なので、かなり余裕があります。とはいえ、4Aでもかなり鳴きがうるさいです。TDKラムダなのでそんなに変な電源じゃないと思うんですが、開発陣の平均年齢が高いのかなぁ。電光掲示板はかなり変な消費電力パターンなので、それが原因かもしれません。

 容量的にはかなり余力が有るはずですが、短時間でも暖かくなるのがわかるくらいの発熱があります。推奨設置向きで上面に発熱体が配置されてます。それ以外の設置向きでも、発熱体が下面になる向きはありません。
 素人考えだと、発熱体が下にあれば全体にエアフローが発生しますが、エアフローの利点以上に周囲への熱移動のデメリットが大きいんでしょう。そりゃ発熱しない部品を冷やす必要はないし、ましてや温める利点は無いですからね。
 電源が発熱すると言っても、それ以上にパネルが発熱するので、全体で見ればあんまり気にならない範囲です。むしろ鳴きのほうが気になる。

 最大輝度では、ノイズは100mVくらいかな。Vppはもうちょっと大きいけど。FFTで見てみると、130kHzあたりにピークがあり、高調波も1個あります。他にも低周波にいくつかのピークが出てますが、面倒なので割愛。


 一応全点灯ならパネル3枚分、輝度を制限すれば4枚くらいは表示できます。もちろん大幅に輝度を下げればいくらでも枚数は増やせますが。ちなみに最低輝度だと全点灯で0.2Aなので、70枚分くらいです。


 とりあえず前回のエントリで書いた、外部のスイッチやロータリーエンコーダも動作確認ができました。とりあえずテトリスとか作りたいな。パネル1枚買い足して正方形にして、オセロとか。

 I2Cの動作確認ができたので、動作確認をしていないのはCANドライバとDACのみとなります。CANは使う予定ないので後回し。DACもデータを作るのが面倒だからなぁ。とりあえずテトリスとか作ってみて、効果音が欲しくなったらその時に考えるということで。

2017年10月16日月曜日

1ピンでスイッチ

 せっかく電光掲示板があるんだから、何かやりたい。でも動画はかなり高負荷。じゃぁどうしよう、ということで、テトリスでも作ろうか、という流れ。
 でも諸々の予約を考えると、スイッチ入力に使えるピンは1本しかない。スイッチ1個でどうやってテトリスをやれと。
 幸いにして、アナログ入力ピンが開いてるので、そこにスイッチを付けることにした。

 回路はこういうイメージ。


 今回はスイッチを計8個使い、ゲームボーイとかファミリーコンピュータっぽい配置になってる。タクトスイッチだけど。
 ついでに、ロータリーエンコーダもあれば面白いかな、と思って追加してみた。
 この回路では、基本的に同時押しは許可されない。右側のスイッチ群では、同時押しをした場合、根本に近い方が優先される。スイッチAとスイッチBを押したらスイッチCとして認識される、ということはない。
 一方、ロータリーエンコーダは同時に2本のスイッチを監視する必要があるので、ちょっと回路が違う。

 とりあえず、こういう回路でマイコンのADCを使って電圧を見てみると、こんな感じ。

 ADCは10kspsで、FallingEdgeから50サンプル分を出力している。
 タクトスイッチは予想より遥かに綺麗な波形。今のところチャタリングも全く無い。経年劣化とか怖いけど、そんなに長く使う気はないし、イザとなればコントローラ部分だけ作り直せるし。
 一方、ロータリーエンコーダの波形はかなり汚い。一応、AとBとABで電圧が違うのはわかるので、A相/B相の位相は把握できる。でもこれならFallから5サンプル後くらいの電圧を見て、A相かB相かを判定したほうが楽そうだなぁ。


 とりあえず、タクトスイッチの入力は問題なさそうなので、最悪でもファミコンやゲームボーイの操作系は作ることができる。
 ま、ゲームボーイと比べても、横幅で2.5分の1、縦幅で4.5分の1しかないのだけど。パネルを買い足せばゲームボーイの5分の1くらいの面積か。フルカラー8bit256色くらい表現できるのはこっちのほうが圧倒的に優位。動作確認はしていないが、2chの12bitDACもあるので、はいれぞおーでぃおも出力できる。画面解像度に目をつぶれば、ゲームボーイより高性能。

 とりあえず、もうちょっとエンコーダの動作確認をしてみる予定。

2017年10月14日土曜日

メモ:STM32F4のメモリ配置

 STBee F4miniに使われてるSTM32F405RGTには、メモリが192kB搭載されている。
 0x10000000からの64kB、0x20000000から112kB、0x2001C000からの16kBの3種類。
 112kと16kは連続していて、128kBとして使える。この2箇所は同時にアクセスすることができるので、例えば112kをメインのメモリとして使い、16kはUSBやEthernetのバッファとして使う、というような用途らしい。
 64kはARMコアとは接続されているが、DMAバスとは接続されていないので、64k領域にある配列をDMA転送しようとするとハングアップするらしい。

 今までは128kをメインのメモリとして使い、64kをスタックとして使っていた。これはねむいさんのリンカそのもの。しかし、FreeRTOSを使っているので、スタックはほぼ全く使っていない。
 今回、試しにFreeRTOSのスタックを64k領域に配置し、128kをそれ以外に使うようにしてみた。FreeRTOSは今のところ16kほど使ってるので、まだ48kほど開いてる。DMA転送で使う場合はstaticで宣言して128k領域から確保すればいい。またmallocも128k領域から取ってくるようになる。

 配列のアドレスを指定するのは、リンカスクリプトとかでうまくできるのだろうが、方法がわからなかったので、 static uint8_t *ucHeap = (uint8_t *)0x10000000; のように、直接指定することにした。これはFreeRTOS/portable/MemMang/heap_4.cの100行目あたりに書いてある。配列の宣言をポインタに置き換えたもの。リンカスクリプトには64k領域を書いていないので、コンパイラがこの領域を使うことはできない。configTOTAL_HEAP_SIZEは64KiBにしておけばok。


 ということで、微妙に使い勝手の悪い64k領域をそれなりに活用しつつ、128k領域を目一杯使えるようになった。
 mallocで確保できる領域も増えたので、動的にメモリを使う機能も使いやすくなった。


 Luaを走らせたいんだけど、ちょっと苦戦中。

追記
 もうちょっとスッキリした方法(たぶん)。
 FreeRTOSConfig.hに #define configAPPLICATION_ALLOCATED_HEAP 1 を追加する。
 freertos_heap.cという、FreeRTOS.hをインクルードし、uint8_t ucHeap[configTOTAL_HEAP_SIZE]; と配列だけを宣言したソースを作る。
 リンカスクリプトのセクションに
.freertos_heap_sec :
{
*freertos_heap.o(.bss .bss.*)
} >RAM3
 を追加する。
 これで、FreeRTOSのヒープ領域をRAM3に確保できる。

 main.cの最初のほうでスタックのアドレスを変数に保存して、後で表示してみると、0x1000000の領域に確保されている。FreeRTOSのスレッド内でスタックのアドレスを見ても、0x10000000の領域に確保されている。
 ということで、64kの領域にFreeRTOSのヒープを確保しつつ、残りの領域はOS外のスタックを確保し、残りの128kをmallocに使う、という感じになった。今のところ、FreeRTOSでは16kくらいしか使ってないらしいが、とりあえず32kを確保している。

 今までのheap_n.cを書き換える方法だと、Cubeで出力するたびに戻されるので、面倒だと思う(未確認)。今回の方法だと、一応Cubeの制御外のはず(未確認)。