2018年9月28日金曜日

mbed OS 5のThread

mbed OS 5のThreadについて以下の記事を参考にしました。

Docs › Reference › RTOS › RTOS overview
https://os.mbed.com/docs/v5.9/reference/rtos.html

Docs › Reference › RTOS › Thread
https://os.mbed.com/docs/v5.9/reference/thread.html

Mbed OS 5のRTOSはMutexやSemaphoなど小難しいものがありますが、Thread間でメモリなどのリソースを競合せずに共有するための仕組みで、使わなくて済むならそれに越したことはありません。

というわけで、まずはThreadだけ試してみました。

Docs › Reference › RTOS › Thread」のExampleをNucleo F446RE用に多少書き換えました。

ブレッドボード配線図

Thread example


#include "mbed.h"
 
DigitalOut led1(D2);
DigitalOut led2(D3);
Thread thread;
 
void led2_thread() {
    while (true) {
        led2 = !led2;
        wait(1);
    }
}
 
int main() {
    thread.start(led2_thread);
    
    while (true) {
        led1 = !led1;
        wait(0.5);
    }
}

led1の点滅はメイン・ループで行われ、led2の点滅は生成したスレッドで行われます。おおざっぱにいうと、メインループとは独立して、生成したスレッドが勝手にled2を点滅させているイメージです。

Thread example with callbacks

引数を1つ取るコールバック関数を使った例です。引数によってThreadの開始の仕方(State)を変えられます。この例ではどのLEDを点滅させるかを、Threadの開始時に指定しています。


#include "mbed.h"

Thread thread;
DigitalOut led1(D2);
DigitalOut led2(D3);

volatile bool running = true;

// Blink function toggles the led in a long running loop
void blink(DigitalOut *led) {
    while (running) {
        *led = !*led;
        wait(1);
    }
}

// Spawns a thread to run blink for 5 seconds
int main() {
    thread.start(callback(blink, &led1));
    wait(10);
    running = false;
    thread.join();
 
    thread.start(callback(blink, &led2));
    wait(10);
    running = false;
    thread.join();
 
}

LED1が10秒間(5回)点滅したあと、LED2が10秒間(5回)点滅し、プログラムは終了します。thred.join()はLEDを点滅させるスレッドが終了するのを待ちます。

OLED表示とADC読み取りをThread化


実験中のNucleo 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_Test04

ADC読み取りとOLED表示をThread化し、Thread::set_priority()でADC読み取りの優先順位を高、OLED表示を低に設定しました。

優先順位は以下の通りです。

0 波形生成  Ticker
1 ADC読み取り Thread(priority:高)
2 メインループ Thread(priority:普通) LED点灯
3 OLED表示 Thread(priority:低)

ADC読み取りとOLED表示のタイミング

メインループでのポーリング

ch1:D3(OLED表示) ch2:D4(ADC読み取り)

それぞれの処理の最初と最後でピンの出力をH/Lしています。OLED表示(赤色)に0.133秒かかってその後にADC読み取り(黄色)が一瞬行われています。

この場合ADC読み取りはOLED表示の完了を待つため、サンプリング周期が長くなり、POTを回すと出音が段階的な変化になりました。口でいうと「ポ、ポ、ポ、ポ・・・」という感じ。

Thread化

ch1:D3(OLED表示) ch2:D4(ADC読み取り)

Thread化した場合、OLED表示(赤色)がHになっている間にもADC読み取り(黄色)が行われています。こちらはPOTを回すとなめらかに変化しました。口でいうと「プーーーーーーン」という感じ。

波形生成Tickerのタイミング


メインループでのポーリング

ch1:D2(波形生成) ch2:D4(ADC読み取り)

Thread化

ch1:D2(波形生成) ch2:D4(ADC読み取り)

Thread化した場合、ADC読み取り(黄色)の間隔が詰まっています。どちらの場合も波形生成のTicker割り込み(赤色)が優先されていて、50kHzのサンプリングレートがキープできています。

DAC出力波形


メインループでのポーリング

ch1:D2(波形生成) ch2:A2(出力波形)

Thread化

ch1:D2(波形生成) ch2:A2(出力波形)

ch1のデジタル波形がオシロ内で干渉してサイン波の表示が汚くなっていますが、1kHzのサイン波が出力できています。

メモ:


Tickerの様に使えるRtosTimerは非推奨になり、EventQueueが推奨されています。←機能が多いのでちょっとめんどくさそう。

https://os.mbed.com/docs/v5.9/reference/rtostimer.html

2018年9月25日火曜日

Nucleo DCO 作戦検討 その4

操作パネルを仮組みして実験中ですが、構想していた機能は実現できそうです。


操作パネルは以下のような配置を考えています。

パネルデザイン(案)

ブロック図(案)

テストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test19

POTの値をADCで読み取ると値がふらつきます。移動平均を取っていますが、それでもビシッとは決まらないので、ADC読み取りをOFFにするモードを作りました。

音源として使うときはADC読み取りをONにした方が微妙に揺れていい感じかも?

出力波形


OSC1のみ、ADC / Display オフにして約1kHzのサイン波を、クリップしないレベルにして出力して測定しました。

電源は単3×6です。

電源電圧: 7.52V
電源電流: 230mA (23mV@0.1Ω)

出力波形

FFT

サンプリングレートが62.5kHzなので62.5kHz付近にエイリアス大きく出ています。

13kHz、25kHz、38kHz、50kHz、75kHz、87kHz付近にもエイリアスが出ています。

WaveSpectra

TASCAM US-144mkII Mic/Lineモード


2次、3次の高調波歪以外にも、1.5kHzや500Hzなど0.5次の歪も出ているようです。12bit DACなので仕方ないでしょうか。

全体としては歪は約-70dB以下に収まっています。

ToDo:


適当なダンボール箱に入れて専有面積を小さくする。

サンプリングレート62.5kHzとしてLPFを設計して出力テスト。

TLF01、Dual OTA VCAと接続して音出し。

MIDI対応→CVコンバーターを通してADCで拾う?

u8g2-mbed + SSD1306の通信速度を挙げられるか検討。

ファームウェアでCallback、Threadを使って見る。

暗闇でも操作しやすいようにする?


2018年9月23日日曜日

mbed OS 5でCallbackを使う。

mbed OS 5ではコールバック関数にFunctionPointer(参考「mbedのコールバック FunctionPointerを使うテスト」)ではなくCallbackクラスが推奨されています。以下の記事を参考にして少し調べました。

Callbackについて
https://os.mbed.com/docs/v5.8/reference/platform.html#callbacks

状態(State)の重要性
https://os.mbed.com/docs/v5.8/reference/platform.html#the-importance-of-state

APIドキュメント
https://os.mbed.com/docs/v5.8/reference/callback.html

「[em] bed ded」さんの「mbed OS5 の Callback

Tickerのコールバック


NucleoF446REでTickerのサンプルプログラムを動作させました。

参考「https://os.mbed.com/docs/latest/reference/ticker.html

ブレッドボード配線図

drivers/Ticker.hでコールバック関数の登録は以下のように定義されています。

void attach (Callback< void()> func, float t)

サンプルプログラム

#include "mbed.h"
 
Ticker flipper;

DigitalOut led1(D2);
DigitalOut led2(D3);
 
void flip() {
    led2 = !led2;
}
 
int main() {
    led2 = 1;
    flipper.attach(&flip, 2.0);
 
    while(1) {
        led1 = !led1;
        wait(0.2);
    }
}

普通の関数の場合、見た目はCallbackクラスを使わないmbed 2のときと変わりありません。

クラスの中でコールバック関数を登録する場合は以下のように定義されています。

template<typename T , typename M >
void attach (T *obj, M method, float t)

サンプルプログラム

#include "mbed.h"
 
// LEDチカチカクラス
class Flipper {
public:
    Flipper(PinName pin) : _pin(pin) {
        _pin = 0;
    }
    void flip() {
        _pin = !_pin;
    }
private:
    DigitalOut _pin;
};
 
DigitalOut led1(D2);
Flipper f(D3);
Ticker t;
 
int main() {
    // mbed 2のときは
    // t.attach(&f, &Flipper::flip, 1.0);
    t.attach(callback(&f, &Flipper::flip), 1.0); 
 
    while(1) {
        led1 = !led1;
        wait(0.1);
    }
}

RotaryEncoder LibraryをCallbackに対応させる


以上を踏まえて、Callbackを使うように警告が出ていたRotaryEncoderクラスを修正しました。

RotaryEncoder Library
https://os.mbed.com/users/ryood/code/RotaryEncoder/#cfc8f362bce6

テストプログラム
https://os.mbed.com/users/ryood/code/RotaryEncoder_Test/

RotaryEncoderクラスで内部的に使っている、Ticker::attach_us()の書き方が

ticker.attach_us(this, &RotaryEncoder::func_ticker, t);

のとき、以下のような警告が出ていましたが、


Warning: Function "mbed::Ticker::attach_us(T *, M, us_timestamp_t) [with T=RotaryEncoder, M=void (RotaryEncoder::*)()]" (declared at line 144 of "/extras/mbed-os.lib/drivers/Ticker.h") was declared "deprecated" in "RotaryEncoder/RotaryEncoder.h", Line: 98, Col: 17

Callbackを使い、

ticker.attach_us(callback(this, &RotaryEncoder::func_ticker), t);

とすればワーニングは出なくなりました。

C++のテンプレートは難解でよくわかっていませんが、かいつまんで言えば、普通のコールバック関数の登録は特に変わらず、クラス内でコールバック関数を使う場合は、

attach(オブジェクトへのポインタ, オブジェクトのメンバ関数へのポインタ, 引数)

と書いていていたところを、callback()関数を使って

attach(callback(オブジェクトへのポインタ, オブジェクトのメンバ関数へのポインタ), 引数)

と書けば良いようです。

callback()関数はplatform/Callback.hで定義されています。

/** Create a callback class with type infered from the arguments
 *
 *  @param func     Static function to attach
 *  @return         Callback with infered type
 */
template <typename R>
Callback<R()> callback(R(*func)() = 0)
{
    return Callback<R()>(func);
}

/** Create a callback class with type infered from the arguments
 *
 *  @param obj      Optional pointer to object to bind to function
 *  @param method   Member function to attach
 *  @return         Callback with infered type
 */
template<typename T, typename U, typename R>
Callback<R()> callback(U *obj, R(T::*method)())
{
    return Callback<R()>(obj, method);
}


/** Create a callback class with type infered from the arguments
 *
 *  @param func     Static function to attach
 *  @param arg      Pointer argument to function
 *  @return         Callback with infered type
 */
template <typename T, typename U, typename R>
Callback<R()> callback(R(*func)(T *), U *arg)
{
    return Callback<R()>(func, arg);
}

など。

2018年9月21日金曜日

Nucleo(mbed)でADC_VREFを使って電圧を測定する。

STM32のmbedでは「ADC_VREF」という内部レファレンス電圧のピン名がPinNames.hで定義されています。

NucleoF446REのPinNames.h

参考「STM32_ADC_InternalChannels

この値をAnalogInで読み取れば、比較的正確な電圧が得られます。

この内部レファレンス電圧がはたして何ボルトなのかは、DATASHEETに載っていました。

Nucleo F446REの場合は、「STM32F446xC/E」のTable 16にある「V12」の値のようです。


NucleoF446REは180MHz駆動なのでtyp. 1.32Vで1.26V ~ 1.40Vだと思います。

ブレッドボード配線図


電源は可変電圧電源を使用して1.25V ~ 12Vを与え、33kΩと68kΩで分圧しました。分圧すれば、Nucleo F446REのADCの入力電圧におさまり、0.4V ~ 3.9V程度になります。

<追記:2018.09.23>

Nucleo F446REは3.3V駆動ですが、5Vトレラントになっていれば上記の電圧範囲の入力可です。5Vトレラントになっているピンは、ボードによって異なるので注意が必要です。

Nucleo F446REの場合はほとんどのピン(AnalogOutに使えるピン以外)は5Vトレラントになっていますが、Nucleo F303K8の場合は5Vトレラントで使えるピンが少なく、AnalogInに使えるピンは5Vトレラントになっていません。

参考「Nucleo F446REとNucleo F303K8の一部ピンは5Vトレラントではない模様

</追記>

ちなみに電圧の調整を可変電圧電源ではなく可変抵抗で行うと、分圧用の抵抗と干渉して33:68の分圧になりません。

テストプログラム

/*
 * ADC_VREF Test01
 *
 * 2018.09.18
 *
 */

#include "mbed.h"

#define INTERNAL_VOLTAGE  (1.23f)
#define VDIV_R1           (68.0f)   // 分圧用抵抗値1
#define VDIV_R2           (33.0f)   // 分圧用抵抗値2
#define VOLTAGE_DIVIDE    ( (VDIV_R1 + VDIV_R2) / VDIV_R2 )

Serial pc(USBTX, USBRX);

AnalogIn Ain0(A0);
AnalogIn VrefInt(ADC_VREF);

DigitalOut CheckPin1(D4);

int main()
{
 pc.baud(115200);
 pc.printf("\r\nADC_VREF Test01\r\n");
 pc.printf("%s %s\r\n", __DATE__, __TIME__);
 pc.printf("System Clock: %lu Hz\r\n\r\n", SystemCoreClock);
 
 float vref = VrefInt.read();
 float vrefFactor = INTERNAL_VOLTAGE / vref;
 printf("INTERNAL_VOLTAGE: %f\r\n", INTERNAL_VOLTAGE);
 printf("VD_R1           : %f\r\n", VDIV_R1);
 printf("VD_R2           : %f\r\n", VDIV_R2);
 printf("VOLTAGE_DIVIDE  : %f\r\n", VOLTAGE_DIVIDE);
 printf("vref            : %f\r\n", vref);
 printf("vrefFactor      : %f\r\n", vrefFactor);

 printf("\r\nvref,\tvrefFactor,\tread,\tafter div,\tbefore div\r\n");
        for (;;) {
  CheckPin1.write(1);
  vref = VrefInt.read();
  vrefFactor = INTERNAL_VOLTAGE / vref;
  float v = Ain0.read();
  CheckPin1.write(0);
  printf("%5.3f,\t%5.3f,\t%5.3f,\t%5.3f V,\t%6.3f V\r\n", 
   vref, vrefFactor, v, v * vrefFactor, v * vrefFactor * VOLTAGE_DIVIDE);
  wait(1.0);
 }
}

テスタで入力した電圧とUARTで表示させた値を比較すると「#define INTERNAL_VOLTAGE  (1.23f)」で誤差が少なくなりました。DATASHEETの値より低いので何か間違っているかもしれません。

以前VDDを基準電圧として電圧を測定しましたが、内蔵レファレンス電圧を使うと、電源電圧が変動しても影響を受けにくいというメリットがあります。

2018年9月18日火曜日

Nucleo DCO 作戦検討 その3

ふたたび作戦を検討しました。


ブロック図(案)


テストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test16

ロータリーエンコーダー


波形切り替えと周波数レンジ切り替えは、操作性がよくなるのでプッシュスイッチからロータリーエンコーダーに変更しました。(現状はOSC 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に下げて計測。

補間処理を入れる前は割り込み時の処理時間は2マイクロ秒程度でしたが(参考「Nucleo DCO 内蔵ADCで入力系のテスト」)5マイクロ秒にまで増えてしまいまいした。結構な量の浮動小数点数演算をしていますが、F767ZIのFPUの能力のおかげでなんとかなっています。

ここまでFPUが強力だと、DDSではなく毎回波形を計算しても間に合うんじゃないかとも。

サンプリングレート62.5kHz(サンプリング周期:16マイクロ秒)までは動作します。

前回やった、浮動小数点数演算の速度を比較の結果などから、もう少しプログラムを改良できるかもしれません。

メモ:


u8g2-mbedのI2Cのクロックが50us程度になっている(I2CのStandard Modeは100kHz)。→手を加えてHW I2CやSPIで接続できるようにする?

OLED表示を別スレッドにして、プライオリティを下げればADC読み取りが優先されてOLED表示をオフにしなくても済む?

Callbackクラスを使うようにする?

2018年9月16日日曜日

Nucleo F767ZIでmbed 2とmbed OS 5の浮動小数点数演算の速度を比較

Nucleo F767ZIでオンラインコンパイラのmbed 2と、mbed-cliのmbed OS 5の浮動小数点数演算の処理時間を測定しました。

テストプログラム
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/FloatingPoint_Test01

測定結果

青とオレンジがfloat型の演算結果で、グレーと黄色がdouble型の演算結果です。

add, sub, mul, divはそれぞれ加算、減算、乗算、除算で圧倒的にfloat型が高速です。

ところが、なぜだがか三角関数や指数関数のsin, cos, exp, logは、float型とdouble型の差があまりありません。

DDSのチューニングワードを計算する
// calc tuning word
    t.reset();
    t.start();
    for (int i = 0; i < LOOP_N; i++) {
        buffer[i] = pow(2.0, 32) * (double)i / 100000.0;
    }
    t.stop();
    elapse = t.read_us();
    pc.printf("tword\t%d\t%f\r\n", elapse, (float)elapse / LOOP_N);
という演算では、むしろmbed OS 5のdouble型(グラフではtwordのグレイ)が最速となっています。「pow(2.0, 32)」という項は定数なので、この場合最適化されているのかもしれません。

Nucleo F767ZIをmbedで使った場合に限りますが、浮動小数点数演算の四則演算はfloat型でやったほうが速く、三角関数や指数関数(特にDDSがらみの演算)はdouble型でやってもそれほど遅くないと言えると思います。

測定データ

op OS 5 float Mbed 2 float OS 5 double Mbed 2 double
add 0.03708 0.02549 2.23522 1.64085
sub 0.07417 0.04866 2.29228 1.684
mul 0.11125 0.07416 2.36179 1.7396
div 0.21784 0.16682 2.54717 1.90871
sin 0.55664 0.37082 0.88683 0.93309
cos 0.59404 0.35549 0.95088 1.11853
exp 0.76539 0.53734 1.16889 1.04258
log 0.82766 0.48837 1.2759 1.36975
sqrt 1.31143 0.22935 3.23866 0.37065
pow32 2.28002 1.59155 0.6069 3.92082
tword 2.1812 1.60074 0.21782 3.48061

<追記:2018.09.16>

mbed-cliのOS 5とオンラインコンパイラのmbed 2で分けてグラフ化しました。

Mbed OS 5

Mbed 2

</追記>

2018年9月14日金曜日

Nucleo DCO 作戦検討 その2



パラメーター

2  6  956.50    0.000   30039   0.200:  4  3  119.26    -0.005  28422   0.200:  0  4  236.41    -0.023  47531   0.200:  0.257   1
2  6  953.49    0.000   30087   0.200:  4  3  118.65    -0.009  28342   0.200:  0  4  234.68    -0.031  47403   0.200:  0.257   1
2  6  955.64    0.000   30007   0.200:  4  3  119.09    -0.006  28310   0.200:  0  4  235.73    -0.027  47403   0.267:  0.257   1
2  6  954.14    0.000   30151   0.200:  4  3  118.79    -0.008  28486   0.200:  0  4  235.71    -0.024  47483   0.200:  0.257   1
2  6  956.07    0.000   30039   0.200:  4  3  119.17    -0.006  28438   0.200:  0  4  238.76    -0.002  47323   0.200:  0.257   1
2  6  955.64    0.000   30071   0.200:  4  3  118.97    -0.008  28406   0.200:  0  4  235.61    -0.028  47339   0.200:  0.258   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の表示項目


I2C接続の128x32のSSD1306 OLEDで実験していますが、表示域が少なくて一覧性が確保できません。

AliExpressで発注した128x64のOLEDは関空水没のせいか届きません。同じ日に発注したものは届いたのですが、タイミングが悪かったのか。

ここで一句

「中華製、忘れた頃に、届いてる」

ToDo:


POTの読み取り値がふらつくので、補間してみる。

波形切り替え、周波数レンジ切り替えは、プッシュスイッチでも役目は果たせますが、ロータリーエンコーダならもっと操作性が良くなるのではないかと妄想。


2018年9月11日火曜日

Nucleo DCO 作戦検討

ファームウェアのめどが立ったので作戦を検討しました。


テストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test10

ブロック図(案)

ピン接続表(案)

POT入力


3オシレーターですが、それぞれのオシレーターで周波数を設定するのではなく、OSC1で基本の周波数を1つ設定(ブロック図のMaster Freq POT)し他のオシレーターはデチューン(ブロック図のDTN POT)として設定しようと思います。

3オシレーターの波形をプログラムでMIXしてオーバーフロー/アンダーフローしないように境界を設けていて、オーバーフローした場合、かなりきつい歪が発生するのでマスターレベル(図のMaster Level POT)で内部的なレベルを調整できるようにします。

当初はPOTで位相を調整するように考えていましたが、音出ししてみるとあまり効果がないので廃止しました。デチューンの仕方によってうなりが発生します。

3OSCともノコギリ波でおおよそ1オクターブずつずらした合成波形

これを7オシレーターぐらいに増やしてちょっとずつデチューンすればSuper SAWになります。

プッシュスイッチ入力


プッシュスイッチの読み取りはmbedのInterruptIn(割り込み)で処理しました。

NucleoでInterruptInを使う場合はピン名のアルファベットの部分がかぶっても大丈夫ですが、数字の部分がかぶると使えません。例えば、PA_0とPA_1の組み合わせはOKで、PA_0とPB_0の組み合わせはNGです。

そのためプッシュスイッチにつなぐZIOヘッダの「PE_10」を飛ばしています。

処理が煩雑なのでInterruptInのオブジェクトは配列にしたかったのですが、引数付きのCallbackの仕方がよくわからず、かっこ悪いですが1つずつ定義しました。

こうしたいが、
InterruptIn Button[] = {
InterruptIn(PB_11, PullUp),
InterruptIn(PB_10, PullUp),
<後略>
こうした
InterruptIn Button0(PB_11, PullUp);
InterruptIn Button1(PB_10, PullUp);
<後略>
以前、FunctionPointer.hで定義されている、int型の引数を1つとるコールバック関数型の「event_callback_t」を使いましたが、Mbed OS5では非推奨となっています。

参考「mbedのコールバック FunctionPointerを使うテスト

Mbed OS 5の「platform/FunctionPointer.h」には

MBED_DEPRECATED_SINCE("mbed-os-5.1",
                          "FunctionPointer has been replaced by Callback<void()>")

という記述がありCallbackクラスをつかうことが推奨されています。いずれ使ってみようと思います。

参考「https://os.mbed.com/docs/v5.8/reference/callback.html

チャタリング対策


チャタリングが発生したのでタイマー割り込みのTickerクラスで対策しました。ピン割り込みがかかったとき、タイマー割り込みを有効にしています。

※1回のみの割り込みなのでTimeoutクラスにすれば良かった?

処理時間の計測

テストプログラム(mbed-cli)

Nucleo F446RE
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/InterruptIn_Test02

Nucleo F767ZI
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/InterruptIn_Test03

Board function ピン割り込み(us) チャタリング対策(us)
F446RE attach() 10.9 3.1
F767ZI attach() 5.5 2.1
F767ZI attach_us() 4.9 2.1

※attach()の引数はflot型、attach_us()の引数は整数型

ピン割り込みの処理時間はF767ZIのattach_us()で4.9usと、波形生成の割り込み周期の10usと比べて十分短いとは言えませんが、Tickerの方が優先順位が高いようです。

Nucleoの割り込みベクタ表は「RM0410 Reference manual」の「10  Nested vectored interrupt controller (NVIC)」に載っています。


赤枠で囲みましたが、TickerがSysTick、InterruptInがEXTxを使っているかどうかはわかりません。mbed OS 5もまだ良く分かっていないのに、1896ページもあるReference Manualを読み解くのはとてもじゃないけど無理な話です。

割り込みの優先順位は、NVIC_SetPriority()で設定できるようです。

参考「割込み 256

出力波形


Aruduino LFOと同じような方法で出力波形を増やしました。メモリが潤沢なので12bit / 64k要素のWave Tableを使用しました。

random()関数の処理時間を計測すると0.113 usで、ノイズも無事出力できました。

random()関数の処理時間を計測するプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/random_Test01

ToDo


周波数設定(Range切り替え含む)

電源電圧監視

OLED表示項目の整理

PA_7(ADC)の読み取り値がおかしい?

2018年9月6日木曜日

Nucleo DCO 内蔵ADCで入力系のテスト

POTx16 PizzaBoxのMCP3008(SPI ADC)でPOTの値を読み取れないかと試してみましたが、通信速度が遅いせいかメインループがうまく回らなくなりました。

うまく動かないテストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test06

MCP3008によるPOT値の読み取り


単体のテストプログラムを書いてMCP3008から読み取る時間を計測しました。

オンラインからライブラリ(mcp3008)を追加

> mbed add https://os.mbed.com/users/ryood/code/mcp3008/

MCP3008のテストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/MCP3008_Test01



ch1:D4 ch2:D5

ch1は16ch分、ch2は1ch分でH/Lしています。1ch分で48.3usかかっていて100kHzのサンプリング周期の10usより長くなっています。

はっきりとは言えませんが、Nucleo_DCO_Test06がうまく動作しないのは、MCP3008とのSPI通信中に何かが競合して処理が戻ってこない可能性が高いためだと思います。

内蔵ADCによるPOT値の読み取り


NucleoF767ZIは12bit ADCが24chあり、ZIOヘッダにも18ch分引き出されています。そのうち2chはDACと共用されています。

内蔵ADCのテストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/InternalADC_Test01


ch1:D4

内蔵ADCの読み取りは7.88usです。これもかなり遅いですが100kHzのサンプリング周期の10usよりは短いです。内蔵ADCの読み取り中に割り込み(Ticker)が優先されればOKです。

内蔵ADCでNucleo DCOのPOT入力を読み取り


前回の3オシレーターMIXするテストプログラムに結合しました。


u8g2-mbed + DDS(3OSC) + 内蔵ADCの結合テストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test07 (69e60db6bbf20065a08d25b653dfb63fc732724e)

処理時間の計測


メインループのタイミング

周波数は7.752Hzになっています。OLEDに表示させているFPSも7.8fps程度になっています。

波形出力の割り込み処理のタイミング



サンプリングレート100kHz程度で(ジッターあり)、処理時間は2usです。まだ余裕がありそうですが、サンプリングレートを倍の200kHzにするとメインループに処理が戻らず、割り込みも処理落ちしてしまいました。


波形出力


POTで周波数がだいたい200Hz、100Hz、50Hzになるようにして波形出力しました。

UART_TRACEを有効にして読み取ったパラメーターを表示


OSC1:drate      OSC1:phase      OSC1:PulseWidth OSC1:amplitude  OSC2:drate      OSC2:phase      OSC2:PulseWidth OSC2:amplitude  OSC3:drate      OSC3:phase      OSC3:PulseWidth OSC3:amplitude
201.648364      0.012698        0.012943        0.159463:       98.107455       0.012698        0.011233        0.155800:       50.439564       0.012454        0.010745        0.157265:
201.453004      0.012943        0.012943        0.160684:       97.912095       0.009524        0.010745        0.157265:       50.439564       0.013919        0.012698        0.159707:
201.501844      0.012210        0.008547        0.160928:       97.912095       0.012210        0.010745        0.156532:       50.390724       0.012698        0.012210        0.156288:
201.697204      0.012943        0.012454        0.161416:       97.912095       0.010745        0.010989        0.156777:       50.341884       0.012210        0.012454        0.156777:
201.892564      0.011722        0.012454        0.159707:       97.814415       0.008059        0.010989        0.155556:       50.293044       0.011477        0.011966        0.156288:

読み取り値はfloat型で、結構ふらついています。

出力波形はUART_TRACE、CHECK_PINを無効にして計測しました。

3オシレーター合成(OSC1 + OSC2 + OSC3)

他のチャンネルのLevel POTを最小にして1chだけのオシレーターの波形出力を計測しました。

OSC1のみ

OSC2のみ

OSC3のみ

特にOSC3の波形が歪んでいますが、POTの読み取り値がふらついているためだと思います。ADC読み取りを抑止して、プログラム上でパラメーターを決めて(OSC1、OSC2のamplitude=0.0、OSC3のamplitude=0.3にした)出力すると以下のように比較的きれいです。


メモ:


波形を増やす。波形切り替え。

Range切り替え。

ADCの読み取りは内蔵でもかなり時間がかかる。

Freq POTを回すと周波数の変化がトビトビ。Phase POT、Level POTを回すとブチブチ。→補完する?

2018年9月1日土曜日

Nucleo DCO 3オシレーターをMIXするテスト

3波形を合成(加算)して出力するテストをしました。

テストプログラム(mbed-cli)
https://github.com/ryood/Nucleo_DCO/tree/master/mbed/Nucleo_DCO_Test05

パラメータ OSC1 OSC2 OSC3
波形 サイン波 サイン波 サイン波
周波数 1kHz 2kHz 1kHz
振幅 0.5 0.5 0.0
位相 0.0 0.0 / 0.5 0.0

OSC2は、位相が0の場合と0.5(π rad)の場合の2通りです。

OSC3は、加算していますが振幅0なので、OSC1とOSC2の2波形の合成波形が出力されます。

OSC1 + OSC2(位相:0.0)

ch1:Dac1(PA_4)

OSC1 + OSC2(位相:0.5)

ch1:Dac1(PA_4)

同様の波形合成をシミュレーションしました。

シミュレーション回路図

過渡解析

緑の線が位相0°、青の線が位相180°(π rad)です。

波形出力の割り込み処理時間

ch2:CheckPin1(D4)

割り込み処理に要した時間(PW)は1.7マイクロ秒です。ジッターがあるため、線が多重になっています。


OLEDに表示させている書き換えのフレームレートは9.4fps程度です。

レスポンスがどうなるかわかりませんが、メインループ内で入力処理を行う余裕はありそうです。