2017年10月31日火曜日

妄想:異世界現代兵器チートっぽいやつ

 ぼくの好みの作品があんまり無い。だれかこういう作品を知ってる人がいて教えてくれたらいいなーって一縷の望みをかけて。


2017年10月26日木曜日

CとC++

 とりあえずJSF++は全部機械翻訳したので(データは某所Gitにおいてあります)、じゃぁ次は使う番だ、ということで、試しにマイコンでC++のコードを走らせてみた。

 いままではCしか使っていないので、C++は使ったことがなかった。
 makefileをちょっと書き換えて、CPPもビルドできるようにした。gcc c++のオプションはgccのそれをそのまま使ったので、c++では使えないぞ、という警告がいくつか出てる。

 ヘッダファイルとか作るのが面倒だったので、externとかべた書きしてる。明らかにルール違反。

 とりあえず、C++といえばクラスだろ、ということで、試しに書いてみた。

#include 

extern "C" void test_func(void);

class Hoge_class
{
public:
 void set_value(int value)
 {
  val = value;
 }
 void print_value(void)
 {
  printf("value:%d\n", val);
 }
protected:
private:
 int val;
};

void test_func(void)
{
 printf("test_func\n");

 Hoge_class cls1, cls2;

 cls1.set_value(123);
 cls2.set_value(456);

 cls1.print_value();
 cls2.print_value();
}


extern void test_func(void);
test_func();

 結果としては、ちゃんと予想通りに表示される。

 ということで、簡単ながらマイコンでもC++が動くのは確認できた。

 もっとも、STM32Cubeが吐くコードはCコードだから、これを全部C++に移植するのは無理ゲー。おそらくF-35だって全部が全部C++じゃないだろうから、このあたりは妥協することにする。

***

 JSF++の4.2.1 "Should, Will, and Shall Rules"がいまいちよくわからなかったので、英語が得意そうな知り合いに聞いてきた。会うの何年ぶりだろうか。積もる話もいろいろ。頭の中も似たような感じなので、ちょっと話題を出せばあっという間に膨らんで、いろいろ話せて楽しかった。ウチの周りじゃこういう話できる人は皆無なので、話して楽しい人と話す機会ってのはかなり貴重。
 閑話休題。

 JSF++ではルールに関してShould, Will, Shallの3段階があるけど、曰く、Shouldは提案、Willはもっと強い感じ、Shallは強制的、という感じらしい。
 もっとも、JSF++ではShould, Will, Shallにかかわらず、ルールを無視するには手順を踏む必要がある。
 「「shall we dance?」というのは、聞いている感じだけど、shallでほとんど強制」みたいな例えが普通に出てくる。知識ある人ってすげーな、と。


 大昔にmbedを使ってた頃はC++を少し触った気がするけど、もうすっかり忘れてる。C++でパフォーマンスがどれくらい変化するのかはわからないけど、キチキチまで最適化する必用があるようなコードが必要になる機会はめったにないので、とりあえずC++で遊んで見る予定。

2017年10月23日月曜日

SourceTreeでアカウントを追加したい

 SourceTreeでGitのアカウントを追加したいんだけど、方法がわからない!と結構時間食われたのでメモ。

 調べると、SourceTreeをインストールした際にアカウントを追加できるらしい、ということはわかった。が、後から追加する方法は出てこない。
 後から追加できないことはないだろ、と思っていろいろ探したけど、一番有り得そうな"ツール"→"オプション"→"認証"でもアカウントの追加はできない。
 どうしたものか。

 灯台下暗しでした。

 

新しいタブを開いて"Remote"を選べば、"アカウントを追加"というボタンが出てくる。普段ローカルでしかGitを使ってないので全然知らなかった。こんなに簡単に追加できたのか。。。

 そもそもなんでアカウントを追加しなきゃいけなくなったのかというと、"remote: Empty password"というエラーでプッシュができなくなったため。
 アカウントを消したり追加したりしてもダメで、結局Gitのバージョンを上げたら治りました。古いバージョン使ってるとダメだね。

 Gitは"ツール"→"オプション"の"Git"タブにある"内蔵Gitを更新"をクリックすればアップデートできます。

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の制御外のはず(未確認)。

2017年10月12日木曜日

電光掲示板



 だいぶ良くなった。写真で撮ると走査線が入ってしまうが、肉眼だとあんまり気にならない。
 肉眼で見る時は、十分に離れた位置から見る必要がある。十分に離れれば空間的なLPFとなるので、ピクセルドットが目立たない。LEDは点灯密度が狭いので、何らかのLPFを通さないと変な感じ。




 同じ写真だけど、下はガウシアンフィルタを掛けてある。LPFを通したほうが見やすい。気がする。

 いままで、コントラストを稼ごうと1bit暗いピクセルは10倍くらい暗くしてたけど、考えればそれだと低輝度bitは捨ててるのと同じで、それじゃコントラストが稼げるわけがない。ということで、とりあえず1bit減る毎に半分の点灯時間にしてみた。だいぶいい感じ。
 輝度は点灯時間で制御しているが、点灯時間はBLANK(OE)端子をLowに引っ張っている時間なので、これのパルスはタイマで作っている。分解能1usecだが、一番暗い設定では最低輝度が1usec、最高輝度が16usec、みたいな設定。一番明るいピクセルでも16msecの内で32usecしか点灯してないのに、結構明るい。
 一番明るい設定だと、たぶん800usecくらい点灯させられるので、単純計算で25倍くらい明るくできる。
 ただ、今使ってる電源が5V4A20Wなので、輝度を上げると電源の悲鳴が聞こえてくる。とりあえず5V20A100Wというバケモノみたいな電源を買ったので、それの到着待ち。100WもあればこのLEDパネル3枚くらいは接続できる。意外と少ない。ストリナのINA226ボードが有るので、マイコンからI2Cを生やせば25Aまで計測できる。それができれば消費電力の評価ができる。マイクロ秒なんてパルスを計測できるかはさておき。
 こういうモノならシャント抵抗+オシロみたいな組み合わせのほうが良いかもなぁ。シャント抵抗+LPFでマイコンのADCに突っ込むとか。

 あと、いままではパレットは256個だったが、1024個に増やしてみた。画像はPNG8bitなので256色だが、2枚目の写真は1024色で生成している。64x32pxだと2048色まで表示できるが、メモリが足りなかった。データ変換の高速化のためにパレットをビット深度分持ってるので、かなりデカい。このあたりは表現能力とRAMのトレードオフなので、いい感じの点を探す必要がある。

 無圧縮8bppPNGを10fpsで読み込めるので、アニメのOPを表示してみた。結構ちゃんと見える。コレ以上の高速化はちょっと厳しそう。RasPiでやってる人がいるけど、さすがRasPiの処理能力。向こうは900MHzだから、ウチの5.3倍か。うーん。解像度が5分の1くらいだから、こっちでも30fps出せても良いはずなんだが。。
 マイコンのアイドル率が結構高い代わりにSDカードのバス使用率がかなり高い。ちょっとした圧縮データにして、バス使用率を下げつつCPU使用率を上げればもうちょっと性能出るかも。
 あとはデータの読み込みがブロックリードでものすごい効率が悪いので、シーケンシャルリードで8KiBくらい一気に読んでしまうとか。あぁ、ここでもRAMとの戦いが。。。


 せっかく電光掲示板がある程度使えるようになったんだから、何か表示したいなぁ。でも時計くらいしか思いつかない。いちおうCANバスとか有るから、屋外に引き出した環境センサの情報を表示したりとかはできると思うんだけども。あんまり家の周りの情報は興味ないからなぁ。

メモ:天馬いれと庫

 天馬いれと庫というシリーズ、何種類か有る。僕が使ってるのはCDボックス、DVDボックス、文庫ボックス、コミックボックスの4種類。

 寸法はamazonにもあるけど、とりあえずCADで。


 基本的に寸法違いはスタックできない。ただし、幅広の箱の上に幅狭の箱を載せるのは、多少不安定になるのを妥協すれば可能。
 コミックとDVDの違いは、DVDが幅で5mm、高さで10mm大きいだけなので、DVDの箱にコミックを入れることは可能。スタックすることを考えると、DVDにコミックを入れたほうが良いかも。寸法的にはDVDの箱にCDを入れることも可能だが、転倒防止の仕切り板が高さ方向に余裕が無いので、CDはCDで入れたほうが良い。それにCDとDVDは高さ以外は同じなので、スタック可能。
 CDやDVDは、特殊なケースに入っているものでなければ大抵は入る。DVDにはDVDサイズのPCソフトも入れることができる。


 部屋に本が平積みされてるので、今ある本棚より一回り大きいモノを作りたい。ついでにいれと庫も置きたいので寸法を調べた次第。

2017年10月10日火曜日

HUB75パネル

ドイツ娘あらためティアさん。

 amazonで64x32pxのパネルが安く売ってたので、試しに買ってみました。
 かなりコントラスト悪いですねぇ。もっとも、これは制御の問題だと思うので、もうちょっと改善すると思います。

 画像データは無圧縮PNGで持たせてみました。意外に読み込みに時間がかかります。とはいえ、カードアクセスはもうちょっと最適化できそうです。画像読み込みもかなり冗長なファイルアクセスなので、そこも最適化できそう。あとDMAを使えばさらに早くなるかもしれませんが、ちょっとDMAに痛い思い出があるので。。うまく動くといいなぁ。。

 ところで、このパネル、64x32ピクセルで計2048ドット有るわけですが、パネルは3400円で売ってて、LED1素子あたり1円くらいの計算です。
 秋月だとフルカラーチップLEDが5個200円くらいですから、中国製って凄まじいな。

 さすがに64x32pxだと解像度が足りない感じです。あと3枚位買い足して128x64pxとかにすればもうちょっと表現度も上がりそうだけど、ネタでそこまでやるにはなぁ。
 以前使ったやつとか借り物とくらべ、今回のパネルはドット密度が4倍ほどあります。消費電力は2倍ですが、それでも密度が高い分、放熱が悪くて発熱も大きいです。


 いろいろ最適化したいところはありますが、一番懸案のHUB75出力処理は、アセンブリコードを見る限り、これ以上の最適化は難しそう。
 現在約64fpsくらい出てますが、1フレームでHUB75のデータ変換が64回必用です。ということで、1秒間に約4096回のデータ変換が行われています。1回のデータ変換が約33usecなので、1秒の内140msec程度はデータ変換に費やされている計算になります。
 フレームバッファでCPUの使用率が14%と考えるとかなり余裕がありますが、自前でデータを作ったりする必要が有ることを考えると、もうちょっとなんとかしたいところです。解像度を増やした時に破綻してしまいます。複数パネル使うことを考えると、もう余裕がありません。
 とはいえ、処理内容を考えると、33usecの内、大半はFreeRTOSのタスクスイッチのような気もします。


 今回試作した制御ボードは、2x HUB75、I2C、CAN、2x DAC、microSDソケット、2x UART(1本はデバッグ用)という構成です。HUB75は、1chを4bit、もう1chを3bitとしていますが、ジャンパを1本飛ばせば4bit2本にもできます。ただ、32x32パネルでも3bitだったりするので、とりあえず3bitと4bitの構成にしています。4bitパネルを3bitバスに接続すると、パネルの半分が表示できませんし、3bitパネルを4bitバスに接続すると、アドレスの上位1bit分は地絡するのでよくありません。

 色深度3bitで256色のパレットですが、CANで遠くにおいたセンサのデータを表示したり、SDカードから画像を読み込んで表示したり、I2Cで近くのセンサを読んだり、UART経由でPCからコマンドを送ったり、といったことができるようになる予定です。
 今のところ使ってるのはHUB75を1ch分とSDソケットだけなので、まだまだいろいろな機能が作れるわけですが、どうなることやら。


非圧縮PNG

 PNG推しな僕ですが、マイコンからするとPNGはかなりつらいフォーマットです。
 一番大変なのは、大量のメモリとそれなりの計算リソースが必用な点でしょう。
 例えばPNGを読み込む場合は、Deflateを展開するのに約32kByteのバッファが必要になります(計算に使うメモリがさらに必用です)。また、PNGは隣のピクセルの輝度を参照する場合があるので、少なくとも上1行部のピクセルデータも必用です。ピクセルのバッファは、通常は横解像度の4倍程度で足ります。マイコンで使う画像ならせいぜい横数百ピクセルとして、約1KiBです。STM32F4ならこの程度は問題ないですが、計算リソースが必用なことに変わりはありません。

 ところで、PNGには他のピクセルを参照しないフィルタリングモードがあります。また、Deflateも無圧縮データを入れるモードがあります。ではこれらを組み合わせればどうなるかというと、非圧縮画像の出来上がりです。もちろん、PNGデータの仕様内ですから、通常のビューアで表示可能ですし、圧縮データではないので、任意のピクセルを直接取り出すことができます。
 シークする際は、ピクセル位置のちょっと面倒な計算以外に、Deflateの仕様による詰め物が有るので、その計算が面倒ですが、ピクセル間の計算や圧縮データの展開に比べれば微々たるものでしょう。


 今回、Deflateにかなりつまずきました。
 PNGはビッグエンディアンですが、Deflateはリトルエンディアンです。コレに気がつくのにかなり時間がかかりました。また、Deflateはビットストリームを下位ビットファーストで詰め込んでいくので、例えば「最初の1bitが1の8bitデータ」は0x01が正解となります。
 Deflateに無圧縮データを入れる場合、4バイトのヘッダを入れます。内訳は16bitの長さデータと、それをビット反転した16bit分です。つまりデータ長は65535バイトが最大になります。これを超える長さの場合は、都度4バイトのデータ長情報をもたせる必要があります。
 さらにDeflate(を格納したzlib)を格納するPNGチャンクは32bitのデータ長を持たせ、これに収まらない場合はチャンクを分ける必要があります。しかし、32bitでは4GB程度を詰め込むことができ、1ピクセル4バイトとしても3万px四方くらいになります。この面積をマイコンで処理することはありえないでしょうし、PCでデータを処理するにしても、そんなに大きな画像はめったにないので、今回は32bit制限は気にしないことにしています。

 ということで、マイコンでも扱いやすいPNGデータを作ることができるようになりました。あとはHUB75を制御するマイコンで、非圧縮PNGに対応したローダーを作れば画像を表示できるようになります。画像が表示できるようになれば、APNGに対応させてアニメーションが表示できるようになります。拡張チャンクにPCMデータを入れれば、音声も再生できるかもしれません。

 とりあえず簡単にDecompPNGをロードするコードを書いてみましたが、結構面倒ですね。例えばBitmapならファイルヘッダにイメージへのオフセットが書いてあるので、データの先頭54バイトを読んで、いくつかの情報をチェックし、あとは画像データを直接取り出すことができます。
 あと将来的にAPNGに対応したいので、決め打ちで画像を読み込むことができません。そのあたりもどうにかしないと。
 ガーッと全部データをメモリに読み込んで置ける、PCのプログラムは楽だなぁ、と改めて実感。


2017年10月9日月曜日

セメダイン SX720W

 SX720Wという接着剤を買ってみた。

 データはここ。
 スーパーXシリーズ:データ|工業用接着剤|セメダイン
 難燃型のタブにある。
 読み方、というか、パラメータの意味ががいまいちよくわからない。

 指触乾燥時間が23℃で9分、つまり、指で触っても表面が変化しなくなるまでの時間?が23℃で9分。これは表面だけの時間なので、内部まで硬化するにはさらに時間がかかる。とはいえ塗布厚にもよるだろうから、1液型だと完全硬化の時間はデータシートに出てこないっぽい。
 せん断とか剥離のパラメータもある。試験方法はパナソニックのページがわかりやすい。
 接着剤評価試験方法のご紹介|信頼性|パナソニックプロダクト解析センター情報館 | Panasonic

 破断時伸びは、破断する時にどれくらい伸びるか、というパラメータで、100%なら2倍に伸びた時に破断、50%なら1.5倍に伸びた時に破断、ということらしい。
 1倍(伸びずに破断)で100%、2倍に伸びて破断なら200%、とかのほうがわかりやすい気がする。


 接着剤なのでもちろん部品の接着にも使えるけど、今回は充填剤みたいな感じで買ってみた。空中配線を固定したりとか、そういう感じ。
 試しに壊れても苦しくない基板のジュンフロン線にちょっと塗ってみた。直径6mmくらいでも結構しっかり固定できる。剥がすのはかなり大変なので、基本的に不可逆。
 塗る時は爪楊枝みたいなので塗りたいところに置いていく感じ。塗ったときにはツノが出るが、水平においておけば硬化前にはなめらかになる。

 ユニ基板に塗布した様子。だいたい24時間くらい経過してる。左側は試しにピンセットで削ってみたところ。基板から剥離するより先に樹脂が割れてくる感じ。触るとシリコンっぽい感じ。感じってシリコン樹脂そのものなんだけども。



 200g入りで結構デカイので、躊躇なく怪しいところは全部固定したりするのに良さそう。とはいえ前述したように、剥がすのはかなり大変なので、修正する可能性があるところには使わないほうが無難。修正する可能性のある基板って、修正が終わった頃には用済みになってしまうというジレンマがあるのだけど、まぁソレはソレ。
 ハンダで接続したコネクタの断線防止とかにも良いかも。ただ硬化するのに多少の時間がかかるので、熱収縮チューブのほうがお手軽。でも綺麗な見た目は樹脂のほうが作りやすいかも。

 結構量が多いので、もうちょっと少なくて安く売ってくれれば買いやすいと思うけど、まぁ業務用向けだから仕方ないね。
 ぜひ一家に1本SX720Wを!


2017年10月3日火曜日

HUB75メモ

 HUB75の制御はRGB各2本の計6本と、アドレス(3bitあるいは4bit)、それからCLKとLAT、BLANKの計12本あるいは13本を使用する。

 CLKは立ち上がりでRGBをキャプチャする。CLKがHの状態でRGBを動かしても反映されない。
 LATはHの時にキャプチャされ、Lの時は動かない。実質立ち下がりエッジでキャプチャ。ただしLATがHの時はシフトレジスタからLEDドライバへ常に反映されるので、基本的にLATを動かす時はBLANKをHにして非表示にしておく必要がある。
 LATがLならLEDは変化しないので、BLANK↑, LAT↑,ADR set,LAT↓,BLANK↓の順で制御すれば、最短時間で処理できる。LATトグルの間にADRを動かすのは、LATのパルス幅を確保するため。NOPでも良いけど、ADRを動かしたほうが低コスト。

 サクッと試したところでは、ピクセルクロック20MHzまでは問題なく動作する。これ以上の速度だとSTBee F4miniのGPIOからは出せない。FMCとか使えばできるかもしれないが、SF4mにはFMCがないので試せない。もちろん最大速度は配線の引き回しや、フラットケーブルの長さにも依存する。クロストークとかタイミングとかはATA(IDE)の歴史に近い。

 STBee F4miniを使った場合、9KiPix3bit程度までは問題なく表示できることを確認している。うまく処理すれば32KiPix3bitくらいは表示できそうな気もする。ただそこまで来るとかなりのデータ転送速度が必用な気がする。Cortex-Mでやることじゃない。

/*
 そういえば、ドットマトリクスLEDパネルは6年くらい前にも作ったんだった。ソレは単色で40x8pxくらいだった気がするけど。それはTLC5940を使って、コイツもBlankとかLATで苦労した気がする。デイジーチェーンも。なんか似たようなことやってるなぁ。フルカラーになったし解像度も30倍近くになってるんだけど。
*/

2017年10月1日日曜日

C#でWavの無音削除

 手伝ったイベントの運営で使ってた特小を録音してたんだけど、結構面白かったのでデータとして残したい。でも6時間分は長すぎる。ということで無音部分を削除したい。と思ったけど、ちょっと調べた限りだとSoundEngine Freeでは処理できないらしい。いちいち新しいソフトの使い方を調べるのも面倒。
 ということでC#でサクッと作ってみた。サクッとといっても、この程度のコードで1時間もかかってるのだが。

using (FileStream fsr = new FileStream(path, FileMode.Open, FileAccess.Read))
using (FileStream fsw = new FileStream(path + " sd.wav", FileMode.Create, FileAccess.Write))
{
 byte[] buff = new byte[36];

 fsr.Read(buff, 0, 36);
 fsw.Write(buff, 0, 36);

 if (Encoding.ASCII.GetString(buff, 0, 4) != "RIFF" ||
  Encoding.ASCII.GetString(buff, 8, 4) != "WAVE" ||
  Encoding.ASCII.GetString(buff, 12, 4) != "fmt ")
 {
  throw (new Exception());
 }

 if (
  BitConverter.ToUInt16(buff, 20) != 1 || // fmt id
  BitConverter.ToUInt16(buff, 22) != 1 || // ch
  BitConverter.ToUInt16(buff, 34) != 16 || // bit depth
  false)
 {
  throw (new Exception());
 }

 UInt32 SamplingRate = BitConverter.ToUInt32(buff, 24);

 while (true)
 {
  fsr.Read(buff, 0, 4);
  if (Encoding.ASCII.GetString(buff, 0, 4) == "data")
  {
   break;
  }

  fsr.Read(buff, 4, 4);
  int size = BitConverter.ToInt32(buff, 4);

  byte[] tmp = new byte[size];
  fsr.Read(tmp, 0, size);
 }

 fsr.Read(buff, 0, 4);
 UInt32 len = BitConverter.ToUInt32(buff, 0);

 fsw.Write(Encoding.ASCII.GetBytes("data"), 0, 4);
 long sizePos = fsw.Position;
 fsw.Write(BitConverter.GetBytes((UInt32)0), 0, 4);

 double thrStart = -30; // 録音開始レベル(dB)
 double thrEnd = -30; // 録音停止レベル(dB)
 double delay = 3; // 録音停止レベルを下回ってから、実際に録音を停止するまでの時間(秒)

 UInt32 delayCount = (UInt32)(delay * SamplingRate * 2 * 1);
 bool record = false;

 UInt32 count = 0;
 UInt32 end = 0;

 double fs = Math.Pow(2, 15);
 int thrStartInt = (int)(Math.Pow(10, thrStart / 20) * fs);
 int thrEndInt = (int)(Math.Pow(10, thrEnd / 20) * fs);

 // len = Math.Min(len, (UInt32)(1 * 60 * 60 * SamplingRate * 2 * 1)); // とりあえず1時間分だけ処理

 for (UInt32 i = 0; i < len; i += 2)
 {
  fsr.Read(buff, 0, 2);

  Int16 val = BitConverter.ToInt16(buff, 0);

  if (val < 0)
  {
   val *= -1;
  }

  if (record)
  {
   if (val > thrEndInt)
   {
    end = i + delayCount;
   }
   if (i == end)
   {
    record = false;
   }
  }
  else
  {
   if (val > thrStartInt)
   {
    record = true;
   }
  }

  if (record)
  {
   fsw.Write(buff, 0, 2);
   count++;
  }
 }

 fsw.Position = sizePos;
 fsw.Write(BitConverter.GetBytes((UInt32)(count * 2)), 0, 4);
}

 string pathにファイルパスを設定しておけば、thrStart[dB]を超えたタイミングで書き出しを開始し、thrEnd[dB]を下回ったタイミングから一定時間経過すると書き出しを停止する。これをずーっと繰り返す、という処理。
 入力されるファイル形式は16bit1chのみに対応。

 意外と時間がかかる。整数データと侮ってると、秒88.2kByteのデータ量に飲まれる。
 最初、forの中でvalをdBに変換し、thrStart/thrEndと比較していた。先にIntに変換して、forの中で浮動小数点演算を行わないようにすると、4倍近く高速化した。
 6時間分のWavを処理するのも、ちょっと待ってれば終わる程度。

 6時間分のWavは、delay=3で1時間分くらいになった。BGM代わりに聞けるくらいの量かな。

 某声優の声が空電ノイズ混じりに聞こえてくるのに興奮したりとか、やっぱ僕はこういうのが好きなんだなぁ。グフフ。

 録音は僕が背負ってたバックパックに入れたIC-R6にICレコーダーを接続して行ったので、僕から離れた位置で送信された声は聞き取りづらい。けどまぁ面白い。
 ちゃんと記録として残すなら、利得の高いアンテナを使ったりとかする必要があるんだろうけど、まぁ遊び半分だからね。
 サバゲ中の無線とか取れば面白そうだけど、僕が参加するサバゲだとTRは全然使わないからなぁ。
 スケルチが閉じて十分に音量が下がれば良いので、エアバンドとかにも使える。けどウチの地域はエアバンドほとんど入らないから使えない。
 もちろんアマチュア無線にも使えるけど、このあたりは違法無線しか無いので聞いても面白みも何もない。