2018年7月28日土曜日

メモ:arm-none-eabi-gccのセクション

 arm-none-eabi-gccで変数を指定したセクションに配置する - Catalystize!

 CubeMXが吐くリンカスクリプトでCCMRAMに割り当てるには __attribute__((section(".ccmram"))) と書けばいいらしい。

 例えばFreeRTOSのヒープ領域をCCMRAMにする場合は

#if (configAPPLICATION_ALLOCATED_HEAP == 1)
uint8_t ucHeap[configTOTAL_HEAP_SIZE] __attribute__((section(".ccmram")));
#endif

 というふうになる。

***

 STM32F3のCCMは4kしかない。FreeRTOSのヒープを置くにはちょっと狭い。小さなタスク3個程度+小さなqueue2個くらいなら入るけど、それより大きくなると心もとない。
 Cubeはいちおうヒープ領域が足りない場合は警告を出すけど、Queueのサイズはコードを解析して構造体の大きさを調べないとわからないので、Queueの分は反映されていないはず。

 CCMはDMA領域に使えないので、大きなバッファを使うDMA転送には活用できない。

 なんともままならない。


 RAMの使用量の見方がよくわかってない。
 dataが5104、bssが3860、らしい。和がトータルの使用量かな? とすると8964byteが使用されていて、残りがスタックとヒープに使われる、という感じ?
 今回のマイコンだとRAMが12Kあるので、まだカリカリする必要はなさそう。
 今回はmalloc/newでヒープ領域を使うようなコードは無いし、もうちょっと多めに確保しても大丈夫そうだ。といってもFreeRTOSのヒープはキロバイト単位で持っていくので、あっという間に底をつきそうだが。あんまりキチキチまで使うとスタックが足りなくなるし。

***

追記
 CubeのTaskやQueueをStaticにすれば、管理構造体やデータ領域をグローバル変数で宣言できる。こうするとFreeRTOSのヒープ領域を使わずに済む。
 基本的にタスクやキューを生成したり削除したりといったことは行わないので、staticで問題ない。はず。
 むしろ、FreeRTOSのConfigでMemory AllocationをStaticにしてしまって、ヒープ領域の確保自体も行わない、というのもアリかも。

 staticの場合はfreertos.cに各構造体やバッファが宣言されるが、これはCubeの自動生成エリアなので、書き換えてもCubeで書き出すたびに戻されてしまう。
 この宣言の部分でセクションの指定を書けば、あるタスクのスタックはCCMへ、CCMに入り切らないものはRAMへ、というふうにできるのだが。


 あと、staticだとQueueの構造体の大きさが0だとコケるっぽい。
 先にキューだけ作って、とりあえずコンパイル通るように空の構造体を作っておいたら、動作しなかった。コレがわかるまであちこちいじくり回してしまった。適当な変数を1個入れておけば動くので、仮に構造体を作るときも適当な変数を作っておくように。。

小ネタ

 最近はSTM32でもC++を使うようにしているが、今回、std=c++17を指定したらコンパイラに怒られた。
 C++17からregisterが削除された。マイコンの低レベルライブラリはCで書いてあることもあり、registerが使われているところがある。とりあえず今回はstd=c++14にして逃げた。extern "C" { } で囲ってもダメみたい。
 あるヘッダをインクルードすると、それが別のヘッダをインクルードし、5回位別のをインクルードして、その先にregisterを使ってるインライン関数がある。ソースに書いて有れば問題ないんだけど、ヘッダファイルに書かれると困る。

***

 クラスのアクセサってどうするのが良いんだろう? 一般的なのはprivate変数の名前にGet接頭辞をつけたりSet接頭辞をつけたり、というパターン。またprivate変数はスネークケースで、Getterはキャメルケースで、SetterはSet接頭辞+キャメルケースで、というパターンも有るらしい。
 ドキュメントなんぞ書いてないけど、もしドキュメントを書く場合、名前順で並んだほうが見やすい。気がする(エディタによっては関数の一覧を表示する機能もあるが、これも名前順かな?)。そういう場合、Set/Get接頭辞ではGetBar(), GetBaz(), GetFoo(),SetBar(), SetFoo() という並びになり、SetBaz()が有るかを確認するにはSまでスクロールする必要がある。
 BarGet(), BarSet(), BazGet(), FooGet(), FooSet() と並んでいれば、BazSet()が無いのがすぐに分かる。

 普段ゆるふわ遊びプログラミングしかしない人間なので、命名規則とか、このあたり自分でも統一できてない。

***

 今使ってるオシロは2011年春に買ったPDS5022Sというヤツ。STN液晶になったモデルだが、その後にTFT液晶のモデルが発売されてたはず。
 だいぶ前からコントラスト調整のボリュームの調子が悪くなってきた。接触不良っぽい。分解記事を見るとカッチリとシールドがしてあるようで、開けるのが大変そう。コントラスト調整ができないわけではないので、だましだまし使ってる。

 今回、比較的小規模なマイコンの開発をやっていて、UARTやprintfといった機能を入れるのが大変(面倒)、という状態。ということで、DACをデバッグに使っている。これが意外と便利で、内部のアナログデータをオシロでグラフにして表示したり、棒グラフみたいに表示できる。
 persist機能を使えば過去5秒分の波形を重ねて表示したり、ということもできる。動きが早い波形はこれで表示するとかなり便利。ただ、5022は処理能力があまり高くなく、persistを使うとたまに波形を取り逃がしてる気がする。オシロが取り逃がしてるのか、マイコンがバグってるのか、いまいちよくわからない。
 そろそろ新しいオシロ欲しいなーと思うのだけど、致命的に問題が有るわけじゃないのでなかなか入れ替えられない。

***

 STM32F3は何日か前に初めて触ったが、ペリフェラルの使い方はF1やF4とだいたい同じだし、Cubeで初期化コードが生成されるし、使い慣れたマイコンという感じがする。ペリフェラルはだいたいHAL_hoge_Startで開始するので、初めて触るペリフェラル(e.g.オペアンプ)もサックリ1発動作した。

***

 STM32F303K8TにはRAMが16K、Flashが64Kしかない(しか、というか、も、というか、人それぞれだろうが)。ちなみにSTBee MiniのSTM32F103CBTは128K/20Kなので、それよりだいぶ小さい。

 相変わらずFreeRTOSを使っているが、データ処理のタスクが大きいだけで、他の3,4個のタスクは大部分の期間をdelayで過ごし、また数行の処理しかしていない。さすがにそのためだけにタスクを作るのもなぁ、と思ってコルーチンを使いたいなぁと思ったり。
 でも調べてみるとそう簡単な話でもないようだ。

 Tasks and Co-routines

 "タスクとコルーチンは異なるAPI関数を使用するため、キュー(またはセマフォ)を使用してタスクからのデータを渡すことはできません"
 "コルーチンは非常に小型のデバイスで使用するために実装されましたが、現時点ではほとんど使用されていません。そのため、コードからコルーチンを削除する予定はありませんが、それをさらに発展させる計画はありません"


 C言語によるco-routinesの実装も有るが、使うのがちょっと面倒。な、気がする。
 簡単な実装だとスタックが使えないので、全部staticで宣言するとか。まぁ、大したことではないんだが。

***

 最近はかなり暑くて、室温が35℃くらいある。
 北海道の古い住宅なのでエアコンなんて無い。ケトルを使ったら扇風機の回転数が下がるような家なので、エアコン増設も嫌だなぁ。
 何年か前は、夏は明るくなるまで作業して、暑くなり始める時間に寝て、暑い時間はぐだーっとして、夜の涼しくなってから作業を初めて、みたいな生活パターンだった。
 今年、特に夏季は月2回以上朝早くから出かける用事が有って、それに生活を合わせる必要が有って、結構しっかりと位相ロックができてる。が、暑い時期に作業しなきゃいけないのが大変。

2018年7月25日水曜日

STM32F3

 最近暑いですね。暑いと言っても30℃前後ですが、ずーっと北海道に住んでる身としてはつらい。部屋で座ってるだけでも汗が出てきます。

 今回、オペアンプが1回路欲しい基板があったので、試しにSTM32F3を買ってみました。秋月で扱ってる唯一のF3チップである、STM32F303K8Tです。1個400円くらいです。変換基盤も合わせると500円位しますが。
 32ピンQFPで、0.8mmピッチなのではんだ付けもしやすいです。

 このチップは72MHzまで(HSIは64MHzまで)ですが、Cortex-M4コアを持ち、FPUも内蔵しているようです。オペアンプやコンパレータ、タッチセンサのようなアナログ回路もいろいろ載せています。



 QFPのはんだ付けは何年ぶりかなーという感じです。久しぶりすぎてコツとか忘れてます。
 0.5mmピッチだとガーッとハンダ盛って吸い取り線で抜いたりしてましたが、0.8mmピッチならコテを滑らすだけで綺麗に抜けていきますね。楽でいい。

 電源周りはデータシートに書いてあるとおりに作りました。
 VDDの0.1uFとVDDAの0.01uF、リセットの遅延の0.1uFを変換基盤の上に実装、VDDの4.7uFとVDDAの1uFはユニ基板側に実装しています。VDDAとVDDの間には470Ωのチップインダクタを入れてみました。
 また、BOOT0とその横にあるPB7を短絡し、47kでプルダウンし、タクトスイッチでVDDに引っ張り、BOOT0とUSER SWを兼用しています。

 32pinパッケージではBOOT1ピンがありません。大型のパッケージではBOOT0がHの場合に、BOOT1がLならシステムメモリから、BOOT1がHならRAMから起動する、といった感じになっていたと思いますが、それができません。BOOT0がHの場合は毎回ブートローダが起動するようです。
 そもそもマイコンの電源が切れたらRAMに入ってるプログラムは消えるので、毎回何らかの手段でRAMに転送する必要があります。ブートローダでRAMに転送するなら、ブートローダからRAMに飛ぶコマンドを叩けばいいし、Flashからのプログラムで起動するならそこからRAMに飛べばいい、ということなのでしょう。

 テスト基板にはFT232の6pinコネクタがあり、電源はここから5Vを得て3.3Vに変換しています。STM32F303K8にはUSB Phyが無いので、USBブートローダを使用できません。ということで、以前F1用に作ったUART経由のブートローダでプログラムを書き込んでいます。
 F1の場合はイレースを0x43コマンドで行いますが、F3にはこれがないので、0x44コマンドを追加しました。

 とりあえず、CubeMXでクロックの設定(HSIの8MHzから64MHzを作る)と、ユーザースイッチやユーザーLEDの設定を行い、RTOSは使用せず、メインループの中でLEDをトグルし、ユーザースイッチの状態に応じてforループの回数を変えてディレイする、という処理にしてみました。
 何の問題もなく、気味が悪いくらいあっさりと動作しました。


 ということで、これからFreeRTOSを走らせて、それからオペアンプやらの動作確認に移ろうと思います。

2018年7月20日金曜日

SM-2 Block IIIA

 以前、こんなエントリを書きました。
 2015年7月に発生したSM-2爆発のはなし

 ふとその後が気になってググってみたところ、こんな記事を見つけました。
 VIDEO: Missile Explodes Over German Frigate During Training Exercise -- USNI News

 2018年6月21日、ドイツ海軍のフリゲート(ザクセン級ネームシップ・ザクセン/Sachsen)が、15年7月の事故と同じく、SM-2 Block IIIAを発射した際、爆発した、とのことです。
 この際、2人のドイツ人水兵が軽症を負ったようです。

 "explosion after just departing the vertical launch cell"とのことで、爆発した際、セルからは出ていたようです。


 いろいろ書きたいことも有るけど、暑くてへばってるので今日はここまで。。

2018年7月5日木曜日

超音波風速計:温度と位相

 発泡スチロールの箱に保冷剤を入れて温度特性を見てみました。




 温度センサはADT7420とDS18B20の冗長系で、ADTがメインですが、なぜかDS側の値が出ています(ノイズのLSBから推測)。冗長系が役に立ったとはいえ、DSは計測誤差もそれなりにあるので、データとしてはあまり正確ではありません。

 上の図は横軸が時間で、下の図は横軸が気温です。どちらも縦軸は位相ですが、上の図は右軸が気温です。

 保冷剤を買ってきて数時間冷凍庫に突っ込んで寝る前に発泡スチロールの箱に入れたので、保冷剤が冷却されきっていません。そのため気温も11.5℃程度までしか下がっていません。保冷剤のパッケージには-15℃まで下がると書いてあったので、ちゃんと冷やせばそれくらいはいくはずです。ただ"48時間冷凍した場合"とか書いてあって、かなりしっかりと冷やす必要があるようです。
 また、温度の降下がかなり早すぎます。もっとゆっくり下がってくれたほうが良いんだけど。。。

 横軸に気温を取ったグラフは、右端から左側に動き、左下で反時計回りに折り返して右上に動いています。
 最初の水平に左に動いてる区間(気温が下がっているのに位相が変わらない)が謎です。温度計よりも超音波のほうがはるかに応答性が高いので、先に温度センサが強烈に反応し、超音波に変化が出ないのが不思議です。温度センサに遅れて超音波部が冷えるような配置なので、その影響かもしれません。
 またヒステリシス特性も不思議で、温度センサのほうが遅れが大きいはずなので、本来は時計回りに回復していくはずですが、実際は反時計回りに回復しています。

 発泡スチロールの大きさの関係で、超音波部を床においていますが、その際に箱で反射した超音波の影響と思われるノイズが有ったので、超音波部をタオルでくるんで簡単な吸音材としています。そのため、タオルの外にある温度センサのほうが先に温度変化が始まり、タオルの中の超音波部が温度変化に遅れているのかもしれません。
 そう考えると「温度が下がっているのに位相が変わらない」や「位相変化が温度計に遅れている」というのも納得できます。
 が、タオル1枚程度でここまで遅れるかなぁ、というのも微妙なところです。温度遅れが発生してるのは温度変化の大きい降下部分のみで、上昇部分ではあまり影響はないのかもしれません。と考えてグラフを見てみると、確かに上昇部では直線性が良いように見えます。

 やっぱり恒温槽欲しい~~。容量が大きくて、かつ壁面に吸音材を貼ってあるような。
 業務用の動作温度範囲が広いマイクとかの動作試験をやるような環境かな?


 x気温/y位相ではなく、x音速/y位相のグラフにすると、xとyが1次関数になるはずです(気温と音速の関係は2次関数です(たぶん))。
 ということで、音速と位相を直線の最小二乗法に入れればa,bがいい感じに得られるはずです。aが素子間隔に影響を受け、bが素子の極性と処理部の遅延に影響を受けるはずです。それらをうまく処理すれば位相から音速や風速、気温を推定できるようになるはずです。
 ただ、aを大きくするにはΔtを大きくする必要があり、温度センサ由来のヒスを消すには同じ傾きで温度を上昇/降下させ、それを複数回繰り返した結果が必要になります。
 箱と保冷剤では降下と上昇が非対称で、しかも1回しか計測できないので、そのあたりで誤差が出そうです。
 他の方法としては、ヒーターを使って保冷剤と似た形の(上下が逆な)温度変化を作る、といった方法もあると思います。もっとも、65℃まで上げる必要があるので、ちょっと嫌な感じです。電解コンデンサのような、高温/低温に弱い部品は使っていないし、アナログ能動素子も無いので、ある程度の高温/低温に晒したところで特に大きな問題はないはずなんですが、気分の問題です。

2018年7月3日火曜日

ADT7420とDS18B20


 ざっくりと熱結合させて計測してみた。
 最初に急激に温度が上がってるのは手で触れてたから。その後は似たような傾きで温度が下がっている。傾きがほぼ同じなので、ある程度熱結合できている、と言えるはず。

 ADT7420はI2C、DS18B20は1wireだけど、ADTは手で触れてたときにsignビットが反転してる。1wireは後半時々変な値になる。
 I2Cも1wireもバスに異常があればそもそも通信が不可能なので、接続は問題ないはず。1wireはCRCチェックがあるので、偶然なシングルビットエラーが起きてもCRCチェックでエラーとなり弾かれるはず。

 DS18B20が異常値となったときの数値は85℃だった。これはDS18B20の温度レジスタのリセット値の値なので、どうやら温度変換が正常に行えていないっぽい。
 DS18B20はステータスレジスタが無いっぽいので、85℃が本当にその温度なのか、変換が行えていなくてその温度になっているのかの切り分けができない(変換終了ビットが無い)。


 温度の差を見てみると、DSのほうが0.25℃ほど高く出てる。データシートによるとDS18B20の標準エラーカーブは25℃付近でmean errorが-0.2℃、σ3が+0.05℃ -0.45℃、といったあたり。
 ADT7420は25℃付近(3V時)で±0.05℃程度、最大精度範囲が±0.25℃、という感じ。
 おそらくADTのほうが信頼度が高いはず。またADTのほうが分解能が高い(3bit分)。
 ただADT7420はパッケージが扱いづらく、ストリナの基板はピラピラして面倒。
 変換時間は双方1Hzなので同じ。ADTは高速変換(4Hz?)モードもあるけど、精度が怪しいらしい(未確認)。

 ADT7420はI2CなのでMCUに2ピン必要。
 DS18B20は基本的に1ピンでいいけど、UARTを使った1wireドライバを作ろうとすると2ピンが必要になる。
 STM32の場合、I2Cは比較的低コストで使えるはず。1wireの場合はQueueやRTOSで1bitずつ制御するので若干多くリソースが必要。

 基本的に温度の計測にはDS18B20を使って、精度が必要な部分だけADT7420を使う、というふうにするべきなんだろうな。

追記:2018/07/12
 DS18B20をパラサイトパワーで使用し、ストロングプルアップが足りない場合、温度が高く出る傾向がある。気がする。極端に足りない時は実際の温度より20℃とか100℃とか、極端に離れた値になる。上のグラフではそこまで極端ではないが、もしかしたらストロングプルアップが不足しているために温度が高めに出ている、という可能性がある。

2018年7月1日日曜日

超音波風速計

 久しぶりに超音波風速計で試行錯誤。
 モノは去年の2月頃に作ったフレームで、受信基板はSTBee F4mini用に新造した(たぶんF4mini用の基板は作ってなかったはず)。

 センサ間距離は15cmで、受信はオペアンプを挟まず、直接マイコンのADCに接続している。受信レベルは240くらいなので、-24dBくらいの電圧レベル。

 送信はGPIO2本で6.6Vppになる。片方は保護抵抗100Ω、もう片方はAC結合0.1uFを挟んでいる。
 受信は片方をGND固定、もう片方を0.1uFでAC結合した後、GNDと3.3Vの間を4.7kΩで分圧しオフセットしている。
 受信素子の回路はもうちょっとやりようがあるはず。

 送信は80kHzでGPIOをトグルし、結果的に40kHzの矩形波が出力される。位相がずれなきゃ良いので、矩形波でも問題ないはず。STMはDACが2本しか無いから、送信素子と受信素子を1対1にして正弦波を6経路用意しようとするとアナログスイッチとかオペアンプとか複雑になってしまう。
 // オペアンプでアイソレーションしてからインピーダンス高めにして送信しないchをマイコンのGPIOに吸わせて、送信するchはマイコンのGPIOをHiZにすれば、必要なchだけ出せるかな? でも1chあたりオペアンプが3回路必要になるからかなり大規模になってしまう。

 受信は1.4Mspsでサンプリングしている。サンプリングは3cycleなので入力インピーダンスはかなり低いはず。

 今の所、マイコンと送受信素子以外には、抵抗が18個、抵抗が12個だけで、回路としてはとても小規模。

 CubeMXになってからTIMやADCやDMAやらの連携がとても楽になった。チップの選択から位相を計測するまで、ガッツリやれば丸一日で作れるくらい。


 計測結果はこの様になる。ディレイを挟まずに100回サンプリングした。7.2秒くらいかかるので、1回あたり72msec、1軸あたり12msec程度かかっている計算。
 FIRが191タップあるし、ADC結果が2048サンプルあるし、ゼロクロスの検出も最適化してないので、結構遅い。FIRはもうちょっと弱くても良いかもしれない。位相の検出も、2048サンプルも必要ないかもしれない。ゼロクロスの検出は明らかにもっと早くできる。ということで、最適化すればもうちょっとどうにかなるだろう。

 このグラフでは3方向から息を吹いている。上の3本線は弱く動いているが、これが正しい反応。
 オレンジと赤はかなり暴れている。内部処理(位相の平滑化)の問題な気がする。位相が0を超えて動くと各々を平滑する際に中途半端な結果になってしまう。位相の平滑ってどうやるんだろうか。sin/cosで2次元空間にしてから平滑して1次元に戻す、って感じなんだろうか? 浮動小数点使うとsin/cosの計算コストが高いので適当なところで整数にしてやる必要がありそう。そもそもfloatの是非云々

 今回も、超音波風速計は位相しか計測できない。とりあえず、当面の目標は何波目かを手動で設定して、そこからクローズドループで波数と位相をトラッキングして気温と風速を計測する、という感じか。そこまでできるようになれば、外部の温度センサとかを使って起動時の波数を推測したりできるようにすれば、自動で計測できるようになる。


 試しにフレームをちょっと押してみると、それなりに位相の変化が見られる。息を吹くと位相が0.1程度変化するが、これは距離にすると0.8mmの変化に相当する。ということは、0.5mm程度歪むと風速の変化として見えるかもしれない。市販の超音波風速計はかなりヤワそうな形だけど、実際の歪みは0.1mm未満程度なんだろう。どんな素材なんだろうか。まぁ、普通は風があたったくらいじゃそんなに歪むもんじゃないだろうが。鳥が乗ったりすると歪むかも。超音波風速計はヘリコプターの乱流を測ったりにも使われてるから、ヘリコプターのダウンウォッシュを至近距離で浴びても歪まない程度の強度はあるんだろうが。