5のThreadについて以下の記事を参考にしました。5のRTOSはMutexやSemaphoなど小難しいものがありますが、Thread間でメモリなどのリソースを競合せずに共有するための仕組みで、使わなくて済むならそれに越したことはありません。いうわけで、まずはThreadだけ試してみました。F446RE用に多少書き換えました。}}led1の点滅はメイン・ループで行われ、led2の点滅は生成したスレッドで行われます。おおざっぱにいうと、メインループとは独立して、生成したスレッドが勝手にled2を点滅させているイメージです。callbacks引数を1つ取るコールバック関数を使った例です。引数によってThreadの開始の仕方(State)を変えられます。この例ではどのLEDを点滅させるかを、Threadの開始時に指定しています。}LED1が10秒間(5回)点滅したあと、LED2が10秒間(5回)点滅し、プログラムは終了します。thred.join()はLEDを点滅させるスレッドが終了するのを待ちます。DCOでやっているOLED表示とADC読み取りをThread化し、DAC出力はTickerのままでテストしました。DAC出力は波形生成を行っていてタイミングにシビアで、RTOSを使った場合ミリ秒単位(最大1kHz)でしか制御できないため、マイクロ秒単位で制御できるTickerを使用しています。ブレッドボード配線図メインループでのポーリング(mbed-cli)https://github.com/ryood/Mbed_OS_5_Thread/tree/master/mbed/Thread_Test03メインループ内でADC読み取りとOLED表示を逐次処理して、TickerでDACからサイン波を出力しています。ADC読み取りとOLED表示をThread化(mbed-cli)https://github.com/ryood/Mbed_OS_5_Thread/tree/master/mbed/Thread_Test04ADC読み取りとOLED表示をThread化し、Thread::set_priority()でADC読み取りの優先順位を高、OLED表示を低に設定しました。優先順位は以下の通りです。ch2:D4(ADC読み取り)それぞれの処理の最初と最後でピンの出力をH/Lしています。OLED表示(赤色)に0.133秒かかってその後にADC読み取り(黄色)が一瞬行われています。この場合ADC読み取りはOLED表示の完了を待つため、サンプリング周期が長くなり、POTを回すと出音が段階的な変化になりました。口でいうと「ポ、ポ、ポ、ポ・・・」という感じ。ch2:D4(ADC読み取り)Thread化した場合、OLED表示(赤色)がHになっている間にもADC読み取り(黄色)が行われています。こちらはPOTを回すとなめらかに変化しました。口でいうと「プーーーーーーン」という感じ。ch2:D4(ADC読み取り)Thread化した場合、ADC読み取り(黄色)の間隔が詰まっています。どちらの場合も波形生成のTicker割り込み(赤色)が優先されていて、50kHzのサンプリングレートがキープできています。ch2:A2(出力波形)ch1のデジタル波形がオシロ内で干渉してサイン波の表示が汚くなっていますが、1kHzのサイン波が出力できています。メモ:Tickerの様に使えるRtosTimerは非推奨になり、EventQueueが推奨されています。←機能が多いのでちょっとめんどくさそう。その4操作パネルを仮組みして実験中ですが、構想していた機能は実現できそうです。操作パネルは以下のような配置を考えています。パネルデザイン(案)ブロック図(案)テストプログラム(mbed-cli)https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test19POTの値をADCで読み取ると値がふらつきます。移動平均を取っていますが、それでもビシッとは決まらないので、ADC読み取りをOFFにするモードを作りました。オフにして約1kHzのサイン波を、クリップしないレベルにして出力して測定しました。(23mV@0.1Ω)出力波形FFTサンプリングレートが62.5kHzなので62.5kHz付近にエイリアス大きく出ています。13kHz、25kHz、38kHz、50kHz、75kHz、87kHz付近にもエイリアスが出ています。Mic/Lineモード2次、3次の高調波歪以外にも、1.5kHzや500Hzなど0.5次の歪も出ているようです。DACなので仕方ないでしょうか。全体としては歪は約-70dB以下に収まっています。ToDo:適当なダンボール箱に入れて専有面積を小さくする。サンプリングレート62.5kHzとしてLPFを設計して出力テスト。VCAと接続して音出し。SSD1306の通信速度を挙げられるか検討。ファームウェアでCallback、Threadを使って見る。5でCallbackを使う。FunctionPointerを使うテスト」)ではなくCallbackクラスが推奨されています。以下の記事を参考にして少し調べました。Callback」TickerのコールバックNucleoF446REでTickerのサンプルプログラムを動作させました。参考「https://os.mbed.com/docs/latest/reference/ticker.html」ブレッドボード配線図drivers/Ticker.hでコールバック関数の登録は以下のように定義されています。2のときと変わりありません。クラスの中でコールバック関数を登録する場合は以下のように定義されています。LibraryをCallbackに対応させる以上を踏まえて、Callbackを使うように警告が出ていたRotaryEncoderクラスを修正しました。t);とすればワーニングは出なくなりました。引数)と書けば良いようです。callback()関数はplatform/Callback.hで定義されています。52018年9月21日金曜日Nucleo(mbed)でADC_VREFを使って電圧を測定する。STM32のmbedでは「ADC_VREF」という内部レファレンス電圧のピン名がPinNames.hで定義されています。NucleoF446REのPinNames.h参考「STM32_ADC_InternalChannels」この値をAnalogInで読み取れば、比較的正確な電圧が得られます。この内部レファレンス電圧がはたして何ボルトなのかは、DATASHEETに載っていました。16にある「V12」の値のようです。1.40Vだと思います。12Vを与え、33kΩと68kΩで分圧しました。3.9V程度になります。F446REは3.3V駆動ですが、5Vトレラントになっていれば上記の電圧範囲の入力可です。5Vトレラントになっているピンは、ボードによって異なるので注意が必要です。F303K8の場合は5Vトレラントで使えるピンが少なく、AnalogInに使えるピンは5Vトレラントになっていません。F303K8の一部ピンは5Vトレラントではない模様」</追記>ちなみに電圧の調整を可変電圧電源ではなく可変抵抗で行うと、分圧用の抵抗と干渉して33:68の分圧になりません。(1.23f)」で誤差が少なくなりました。DATASHEETの値より低いので何か間違っているかもしれません。以前VDDを基準電圧として電圧を測定しましたが、内蔵レファレンス電圧を使うと、電源電圧が変動しても影響を受けにくいというメリットがあります。その3ふたたび作戦を検討しました。波形切り替えと周波数レンジ切り替えは、操作性がよくなるのでプッシュスイッチからロータリーエンコーダーに変更しました。1の2個だけ)OLED表示とADC読み取りOLEDはSSD1306(I2C)の128x64のものが届いたので交換しました。OLED表示とADC読み取りはメイン・ループ内で行っています。OLEDの表示は非常に時間がかかり、ADCの読み取りのレスポンスが悪くなるので、OLEDの表示をオフにするスイッチを設けました。OLEDの表示中は4.6fps程度でPOTを回すと出音がブチブチですが、OFFにするとなめらかに変化します。ADCの読み取りは移動平均で補間しました。補間するタイミングは波形生成割り込み中に行いました。結果、周波数からDDSのチューニングワードを毎回計算することになり、割り込みごとの処理時間が大幅に増えました。ch2:CheckPin1(D4)※サンプリングレートが100kHzだと処理が間に合わないので、50kHzに下げて計測。内蔵ADCで入力系のテスト」)5マイクロ秒にまで増えてしまいまいした。結構な量の浮動小数点数演算をしていますが、F767ZIのFPUの能力のおかげでなんとかなっています。ここまでFPUが強力だと、DDSではなく毎回波形を計算しても間に合うんじゃないかとも。サンプリングレート62.5kHz(サンプリング周期:16マイクロ秒)までは動作します。速度を比較の結果などから、もう少しプログラムを改良できるかもしれません。Modeは100kHz)。5の浮動小数点数演算の処理時間を測定しました。青とオレンジがfloat型の演算結果で、グレーと黄色がdouble型の演算結果です。圧倒的にfloat型が高速です。logは、float型とdouble型の差があまりありません。5のdouble型(グラフではtwordのグレイ)が最速となっています。32)」という項は定数なので、この場合最適化されているのかもしれません。四則演算はfloat型でやったほうが速く、三角関数や指数関数(特にDDSがらみの演算)はdouble型でやってもそれほど遅くないと言えると思います。2で分けてグラフ化しました。1ハードウェアでやったこと外部ADCではなくて、NucleoF767ZIの内蔵ADCを使うためにPOTパネルを作って操作しやすくしました。ソフトウェアでやったことファームウェア(mbed-cli)https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test12周波数レンジプッシュスイッチで、オシレーターごとに1オクターブ音程をずらせるようにしました。デチューンOSC2とOSC3は、MasterとなるOSC1に対して±1オクターブ可変するようにしました。±1オクターブ可変するために、疑似指数カーブで対応しています。マスター・レベル波形がクリップした場合も、デジタルならではの過激な歪が出るので、歪ませる/歪ませないを選択できるようにマスター・レベルを調節できるようにしました。割り込みのチャタリング対策をTickerからTimeoutに変更mbedではTimeoutクラスは内部でTickerクラスのオブジェクトを持っているようなので、Timeoutにした方がいいのか悪いのかわかりません。OLEDで実験していますが、表示域が少なくて一覧性が確保できません。AliExpressで発注した128x64のOLEDは関空水没のせいか届きません。同じ日に発注したものは届いたのですが、タイミングが悪かったのか。「中華製、忘れた頃に、届いてる」ToDo:POTの読み取り値がふらつくので、補間してみる。波形切り替え、周波数レンジ切り替えは、プッシュスイッチでも役目は果たせますが、ロータリーエンコーダならもっと操作性が良くなるのではないかと妄想。作戦検討ファームウェアのめどが立ったので作戦を検討しました。POT)として設定しようと思います。POT)で内部的なレベルを調整できるようにします。当初はPOTで位相を調整するように考えていましたが、音出ししてみるとあまり効果がないので廃止しました。デチューンの仕方によってうなりが発生します。プッシュスイッチ入力プッシュスイッチの読み取りはmbedのInterruptIn(割り込み)で処理しました。NucleoでInterruptInを使う場合はピン名のアルファベットの部分がかぶっても大丈夫ですが、数字の部分がかぶると使えません。PA_0とPA_1の組み合わせはOKで、PA_0とPB_0の組み合わせはNGです。そのためプッシュスイッチにつなぐZIOヘッダの「PE_10」を飛ばしています。処理が煩雑なのでInterruptInのオブジェクトは配列にしたかったのですが、引数付きのCallbackの仕方がよくわからず、かっこ悪いですが1つずつ定義しました。OS5では非推奨となっています。Callback<void()>")という記述がありCallbackクラスをつかうことが推奨されています。使ってみようと思います。参考「https://os.mbed.com/docs/v5.8/reference/callback.html」チャタリング対策チャタリングが発生したのでタイマー割り込みのTickerクラスで対策しました。割り込みがかかったとき、タイマー割り込みを有効にしています。2.1※attach()の引数はflot型、attach_us()の引数は整数型ピン割り込みの処理時間はF767ZIのattach_us()で4.9usと、波形生成の割り込み周期の10usと比べて十分短いとは言えませんが、Tickerの方が優先順位が高いようです。(NVIC)」に載っています。赤枠で囲みましたが、TickerがSysTick、InterruptInがEXTxを使っているかどうかはわかりません。Manualを読み解くのはとてもじゃないけど無理な話です。割り込みの優先順位は、NVIC_SetPriority()で設定できるようです。LFOと同じような方法で出力波形を増やしました。Tableを使用しました。usで、ノイズも無事出力できました。ADC)でPOTの値を読み取れないかと試してみましたが、通信速度が遅いせいかメインループがうまく回らなくなりました。うまく動かないテストプログラム(mbed-cli)https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test06MCP3008によるPOT値の読み取り単体のテストプログラムを書いてMCP3008から読み取る時間を計測しました。ch2:D5ch1は16ch分、ch2は1ch分でH/Lしています。1ch分で48.3usかかっていて100kHzのサンプリング周期の10usより長くなっています。言えませんが、Nucleo_DCO_Test06がうまく動作しないのは、MCP3008とのSPI通信中に何かが競合して処理が戻ってこない可能性が高いためだと思います。ADCが24chあり、ZIOヘッダにも18ch分引き出されています。そのうち2chはDACと共用されています。内蔵ADCのテストプログラム(mbed-cli)https://github.com/ryood/Nucleo_DCO/tree/master/mbed/InternalADC_Test01ch1:D4内蔵ADCの読み取りは7.88usです。これもかなり遅いですが100kHzのサンプリング周期の10usよりは短いです。内蔵ADCの読み取り中に割り込み(Ticker)が優先されればOKです。DCOのPOT入力を読み取り前回の3オシレーターMIXするテストプログラムに結合しました。(69e60db6bbf20065a08d25b653dfb63fc732724e)処理時間の計測メインループのタイミング周波数は7.752Hzになっています。OLEDに表示させているFPSも7.8fps程度になっています。波形出力の割り込み処理のタイミングサンプリングレート100kHz程度で(ジッターあり)、処理時間は2usです。余裕がありそうですが、サンプリングレートを倍の200kHzにするとメインループに処理が戻らず、割り込みも処理落ちしてしまいました。波形出力POTで周波数がだいたい200Hz、100Hz、50Hzになるようにして波形出力しました。0.156288:読み取り値はfloat型で、結構ふらついています。出力波形はUART_TRACE、CHECK_PINを無効にして計測しました。POTを最小にして1chだけのオシレーターの波形出力を計測しました。OSC1のみOSC2のみOSC3のみ特にOSC3の波形が歪んでいますが、POTの読み取り値がふらついているためだと思います。ADC読み取りを抑止して、プログラム上でパラメーターを決めて(OSC1、OSC2のamplitude=0.0、OSC3のamplitude=0.3にした)出力すると以下のように比較的きれいです。