2017年6月29日木曜日

SPI Digital PotのAD8403の処理速度を調べてみた。

KIK01で使うかもしれないので、Analog DevicesのSPI接続のデジタル・ポテンショメータのAD8403の処理速度をArduinoとNucleoで調べてみた。

Arduino Uno


配線は「Arduinoでデジタル・ポテンショメータメーター(AD8403)を使ってみた」のものと同じ。

スケッチ
<AD8403_Control.ino>

// inslude the SPI library:
#include <SPI.h>

// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode(slaveSelectPin, OUTPUT);
  // initialize SPI:
  SPI.begin();
}

void loop() {
  // go through the four channels of the digital pot:
  for (int channel = 0; channel < 4; channel++) {
    // change the resistance on this channel from min to max:
    digitalPotWrite(channel, 255);
    digitalPotWrite(channel, 0);
  }
}

void digitalPotWrite(int address, int value) {
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin, LOW);
  //  send in the address and value via SPI:
  SPI.transfer(address);
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin, HIGH);
}


AD8403への出力はノーウェイトで0と255を切り替えている。slaveSelectPin(CS)がアクティブ(L)の時間を見ればAD8403に値を送るのにかかった時間がわかる。

CS

ch1:CS ch2:SCK

CSがアクティブな時間は9.680us。SPIクロック(SCK)は3.538MHzとなっているが、拡大してオシロのカーソルで測ると約4MHzだった。

2回目のSPI.transfer()の後、CSをHiにするまでに結構時間がかかっているようだ。

W1

ch1:CS ch2:W1

W1はAD8403のchannel 1のWiperの出力電圧で、プログラムでここの電圧値を制御している。CSがL->H、つまりSPI通信が終了したタイミングでほぼ遅延なく送った設定値が反映されている。

Nucleo F446RE


配線はArduinoと同じ。(NucleoのArduino Headerで配線した)

プログラム

/*
 * Digital Potentiometer AD8304のテスト
 *
 * 2017.06.28
 *
 */

#include "mbed.h"

#define SPI_CLOCK   (16000000)

SPI SpiM(SPI_MOSI, SPI_MISO, SPI_SCK);  // mosi, miso, sclk
DigitalOut Cs(D10, 1);

// address: 0..4
// value:   0..255
void outDigiPot(uint8_t address, uint8_t value)
{
    Cs = 0;
    SpiM.write(address);
    SpiM.write(value);
    Cs = 1;
}

int main()
{
    SpiM.format(8, 0);
    SpiM.frequency(SPI_CLOCK);

    for (;;) {
        for (int channel = 0; channel < 4; channel++) {
            outDigiPot(channel, 255);
            wait_us(1);
            outDigiPot(channel, 0);
            wait_us(1);
        }
    }
}

メインループ内でwait_us(1)を入れているが、これは処理が速すぎてオシロで信号が見辛かったため。

AD8403の最大クロックは10MHzなので、SPI_CLOCKを1MHz、6MHz、12MHzにして測定してみた。

1MHz CS

ch1:CS ch2:SCK

CSがアクティブな時間は26.240us。SPIクロックは597.0kHz

1MHz W1

ch1:CS ch2:W1

Arduinoと同じくCSがインアクティブになってからほぼ遅延なく設定した値が出力されている。

6MHz CS

CS:7.820us。SCKは拡大して測ると5.627MHz。

12MHz CS

CS:6.220us。SCKは拡大して測ると11.25MHz。(AD8403の仕様外)

Nucleo F303K8


F303K8はSPIクロックを1MHz、8MHz、16MHzにして測定。


1MHz CS

CS:34.6us。SCKは拡大して測ると約1MHz。

8MHz CS

CS:20.8us。SCKは拡大して測ると約8MHz。

16MHz CS

CS:20.84us。SCKは拡大して測ると約16MHz。(AD8403)

比較表


Device SCK(設定値) SCK(実測値) CS
Arduino 4MHz 4MHz 9.680us
F446RE 1MHz 597.0kHz 26.24us
F446RE 6MHz 5.627MHz 7.820us
F446RE 12MHz 11.25MHz 6.22us
F303K8 1MHz 1MHz 34.6us
F303K8 8MHz 8MHz 20.8us
F303K8 16MHz 16MHz 20.84us

比較してみると、Arduino Unoがかなり健闘している。SPIクロックが同程度ならNucleoF446REと同じぐらいかもしれない。NucleoF303K8はSPI通信前後のオーバーヘッドが大きく、SPIクロックを上げてもArduino Unoよりも悪い結果となってしまった。

F446もF303もコアは同じCortex-M4だが処理能力はかなり差がある。

Interface2016年12月号に「定番ARMマイコンSTM32便利帳2016」 という付録がついているので見てみると、F3はADC、DACなどのアナログ機能やPWMなどのタイマ機能が充実しているそうだ。



使いこなすにはもう少し慣れる必要がありそう。

2017年6月27日火曜日

細いテスタ棒(プローブ)を作ってみた

ブレッドボードやピンソケットに挿すのに共立で売られているQIピンを使ってブレッドボードやピンソケットにさして端子としてつかっている。

最近は同じタイプの圧着ピンがaitendoや秋月でも販売されています。

秋月でテストピンジャックというのが売られていて、普通のテスタ棒に直接挿すことができる。


これを組み合わせれば、何かとべんりなテスタ棒(プローブ?)になるんじゃないかと思って試してみた。


テストピンジャックの穴と、QIピンのひらいてるところの幅が同じぐらいなので、ラジオペンチで合わさるように圧着した。


圧着して固めたあとにハンダ付け。


熱収縮チューブでいらないところを絶縁。



テスタ棒にぶっ刺します。


これでブレッドボードにテスタ棒を直接刺すことができるようになります。


ピンソケットにもさせます。

足のピッチの狭い表面実装タイプのICでもショートしないようににテスタ棒をあてられるかも?

2017年6月25日日曜日

POTx16 PizzaBox MDFで木枠を作った。

ダイソーで売っている6mm厚のMDFで木枠を作ってみた。


いきなり切り出しで失敗




MDFはやわらかいのでカッターで軽く溝を掘ってノコギリで切断したが、ずれてしまって切断面がガッタガタ。


使ったノコギリは細工用というやつで、薄刃で細かい作業に便利だが、刃渡りが12cmしかなくて切り出しはかなり疲れた。翌日あちこちが筋肉痛になってしまった(@@;

板材があまっているので新たに切り出そうかと思ったが、ミスったところは組み上げたときに外からあまりわからない感じで使うことにした。←疲れてやる気がでない(^q^;

接着



木工用ボンドで接着して、木ねじで固定。ついでにパンツのゴム(ダイソー)で(かなり弱いが)圧をかけた。


しばらく放置して補強用に隅に三角材(これもダイソーで売ってた)を接着してさらに丸一日放置。

組み立て



底板はクリップボードを切り出したプラ板。新しいのがもう売ってなかったので使いまわしです。

見栄えを良くするために別に側板を作って、木ネジで固定。


6mm厚のMDFに2.1mmの木ネジを使ってみたが木端(?)だと板がわれてしまいました。

もう少し厚ければ大丈夫なんだろうか。この辺は経験を積むしかなさそう。


見栄えを良くするために、前面に天板と同じプラ板を切り出して両面テープで貼り付けてみた。最初は考えていなかったので、側板とずれていてイマイチ。

とはいえ、ダンボール枠より外観、強度は良くなったと思う。

Nucleo用テストプログラム
https://developer.mbed.org/users/ryood/code/Nucleo_MCP3008_Ticker_Test/ Revision3

メモ:


ベースマシンの筐体用の木枠も作ってみたい。

加工しやすいMDFでも手作業でノコギリで精度良く切断するのは難しい&かなりの重労働。

もう少し切り出しやすいサイズのノコギリを買ってみる?いっそのこと電動ノコとか。

ホムセンの切断サービスを利用する?

平ネジ用のザグリの入れ方ももう少し考える。太めのドリルでザグってるが、収まりが悪い。太さを2~3段階に分けて円錐状になるようにしてみる?

2017年6月23日金曜日

Nucleo F303K8(mbed)の内蔵DACとSPI DACのMCP4922の速度を比較する

サイズの小さいNucleo F303K8でも内蔵DACとMCP4922の処理時間を比較してみた。

ブレッドボード配線図


プログラムはNucleoF446REの場合と同じです。

SPIクロックに16MHzを指定した場合



ch1:D2 ch2:SPI_SCK

MCP4922に出力完了するまで、21.04usかかっている。


ch1:SPI_MOSI ch2:SPI_SCK

SPIクロックに16MHz指定した場合、SCKの周波数は表示では15.98MHzとなっているが測定誤差で16MHzだと思う。

SPIクロックの指定を変えて測定


SPIクロック設定値 SPIクロック実測値 D2(MCP4922の処理時間) D3(内蔵DACの処理時間)
1MHz 1.0MHz 34.8us 2.8us
2MHz 2.0MHz 27.3us 2.8us
4MHz 4.0MHz 23.5us 2.8us
8MHz 8.0MHz 21.04us 2.8us
16MHz 16.0MHz 21.04us 2.8us
32MHz 32.0MHz 19.8us 2.8us
64MHz 32.0MHz 19.8us 2.8us

32MHzまでは設定値通りのSPIクロックが出力される。やはりNucleo F303は64MHz駆動で、SPIクロックはマスタークロックの1/2まで設定できるんだと思う。

MCP4922の仕様ではSPIクロックは20MHzまでだが、32MHzでも動作した。

NucleoF446REと比べると、SPIクロックを上げてもSPI通信の処理時間はあまり短縮されない。

2017年6月22日木曜日

Nucleo F446RE(mbed)の内蔵DACとSPI DACのMCP4922の速度を比較する

結論から言うと、内蔵DACの方が圧倒的に速かった。

mbedでプログラムを書いて、SPI DACのMCP4922とNucleo F446RE内蔵DACの処理時間を比較してみた。

どちらも12bit。

/*
 * 内蔵DACとMCP4922に出力するテスト
 *
 * 2017.06.21
 *
 */

#include "mbed.h"
#include "rtos.h"

#define SPI_CLOCK   (24000000)

SPI SpiM(SPI_MOSI, SPI_MISO, SPI_SCK);  // mosi, miso, sclk
DigitalOut Cs(D10, 1);

AnalogOut InternalDac(PA_4);

DigitalOut CheckPinD2(D2);
DigitalOut CheckPinD3(D3);

// v: 0..0x0fff
void OutMCP4922(uint16_t v)
{
    //v &= 0x0fff;

    // Channel A
    Cs = 0;
    SpiM.write(v >> 8 | 0x30);    // 0x30: DAC_A(0) | Vref Unbuffered(0) | Vout 1x(1) | !SHDN(1)
    SpiM.write(v & 0xff);
    Cs = 1;
}


// v: 0..0xffff
void OutInternalDac(uint16_t v)
{
    InternalDac.write_u16(v);
}

int main()
{
    SpiM.format(8, 0);
    SpiM.frequency(SPI_CLOCK);

    uint16_t count = 0;
    for (;;) {
        uint16_t v12 = count >> 4;
        uint16_t v16 = count;
        
        CheckPinD2 = 1;
        OutMCP4922(v12);
        CheckPinD2 = 0;

        CheckPinD3 = 1;
        OutInternalDac(v16);
        CheckPinD3 = 0;
        
        count += 0x100;

        Thread::wait(1);
    }
}

Nucleoをmbedで使って、SPIの周波数設定をするとちょうどの周波数にはならない。おそらくマスタークロックの分周比なのだと思う。

こういうハードウェア寄りの使い勝手はmbedの弱点ではあります。

SPI::frequency()でSPIクロックの周波数を変えて測定してみた。

ブレッドボード配線図


MCP4922にはLDAC pinがあって、これをH→LするとA ChannelとB Channelが同期して
同時に電圧が出力されるが、LDACをL(GND)に落としておくと、CSのアクティブ/インアクティブで制御できるようだ。配線や処理時間のコストがシビアな場合は役に立つと思う。(A Channelだけしか試してないです)

1MHz

ch1:MCP4922 ch2:内蔵DAC

MCP4922から出力すると、27.3usかかる。一方内蔵DACは700ns(0.7us)で済む。


ch1:MCP4922 ch2:SPI_SCK

1MHzを設定した場合はMCP4922との通信に使っているSPIのクロック周波数は643kHz

24MHz

ch1:MCP4922 ch2:内蔵DAC

SPIのクロック周波数を上げるとMCP4922でも6.8us(≒147kHz)で済む。


ch1:MCP4922 ch2:SPI_SCK

SPIのクロック周波数は22.44MHz。クロックが高周波数になるとダンピング抵抗を入れていても波形が怪しくなる。

MCP4922の最大周波数は20MHzなので仕様外だが、VOUTAから波形は出力される。

Zoom Out

ch1:MCP4922 ch2:SPI_SCK

高クロックになると、8bitずつのSPI通信の間隔が開くようだ。

<追記:2017.06.25>
間隔が開くと言うより、SPI::write()のオーバーヘッドが2~3us程度ある?
</追記>

設定値と出力周波数


設定値 SPIクロック 処理時間
1MHz 643.8kHz 27.3us
2MHz 1.154MHz 16.76us
4MHz 2.810MHz 11.32us
6MHz 5.762MHz 8.76us
12MHz 11.29MHz 7.36us
24MHz 22.44MHz 6.74us

2017年6月21日水曜日

キックマシン KIK01 POTをMCP3008で読み取り

Nucleo F446RE(mbed)でA/DコンバーターのMCP3008を使ってPOTの値を読み取るようにしてみた。


出力は内蔵12bitDACから、A/DコンバーターとのSPI通信はSCK、MOSI、CSに33Ωのダンピング抵抗を入れた。MISOは1kΩのRでプルダウンした。

オーディオ出力波形は前回と変わらない。(参考:「キックマシン KIK01 Nucleo(mbed)でプロトタイピング」)

mbed repository:
https://developer.mbed.org/users/ryood/code/KIK01_Proto02/ Revision:13

SPI通信のようす


MOSI

ch1:MOSI ch2:SCK

MISO

ch1:MISO ch2:SCK

CS1(D11)

ch1:CS1 ch2:SCK

CS2(D10)

ch1:CS2 ch2:SCK

SPIクロック(SCK)が419.7kHzとなっているが、MCP3008クラスがSPIオブジェクトをコピーして持つ仕様になっているようで、MCP3008クラスのコンストラクタを呼び出す前にSPI.frequecy()を呼び出さないとクロックの設定が反映されないようだ。なので、デフォルトの周波数になっている。

SPIオブジェクトの周波数をローカルで設定をしないとダメなので、しばし保留。参照かポインタでSPIオブジェクトを持つように変更する予定です。

処理時間の計測


DigitalOutをH/Lさせて、

D2: サンプリング周期ごとの割り込み中の処理時間(エンベロープ生成+波形出力)
D3: エンベロープ生成時間
D4: MCP3008/16chの読み取り時間

がわかるようにしてみた。

D2: サンプリング周期ごとの割り込み中の処理時間

ch1:D2 ch2:SPI_SCK

50kHzの割り込み中の処理時間は5us。プログラムで48kHzに設定しているのでちょっと速い。

割り込み処理中のch1がHiになっている間も、ch2のA/Dコンバーター読み取り用のSPIの SCK(クロック)が出力されているのでSPI通信は非同期で行われているようだ。

普通、SPI用ハードウェアはレジスタに値を設定してやれば、CPUと独立して勝手に通信を行ってくれる。mbedでもこの仕組が効いているんだと思う。ありがたいことです(^q^/

D3: エンベロープ生成時間

ch1:D3 ch2:SPI_SCK

エンベロープ生成には3usかかっている。

D2:D3

ch1:D2 ch2:D3

わかりやすいようにD2とD3を同時に測定。48kHzのサンプリングレートだと周期の1/4を処理に使っているので、まだ余裕がありそう。

D4: MCP3008/16chの読み取り時間

c1:D4 ch2:SPI_SCK

MCP3008の16ch分の読み取りに0.732msかかっていて、その間SCKが出力されている。A/Dコンバーターの読み取りはメイン・ループ内で行っているのでエンベロープ生成、波形出力の割り込みが優先される。(←はず)

2017年6月19日月曜日

POTx16 PizzaBox ダンボールver.でけた


枠は、ダンボールの作ったもの。すでに崩壊寸前で見た目も残念な感じなので近いうちに木材で枠を作り直すつもりです。

Moog
や、MPC

っぽくしてみたいところですが、枠の塗装はのちのち。

A/Dコンバーター基板 回路図

A/Dコンバーター基板 基板図

A/Dコンバーター基板 部品面

A/Dコンバーター基板 ハンダ面

MCP3008が2個の16チャンネルのADC基板。12bit A/DコンバーターのMCP3208もピンコンパチなので差し替えられる。ADCにつなぐマスター側のプログラムの変更で対応できると思う。

また、PINOUT_0、PINOUT_1のピンヘッダで、可変抵抗で分圧した電圧を直接取れる。

POTパネル 表面

POTパネル 裏面

パネルはダイソーで売っていたクリップボードを切り出して使った。最近プラ製ではなく紙製のクリップボードしか売ってなくて困っている。ホムセンのアクリ板は結構お高いので気軽に試しにくいです(^q^;

配線は去年の正月買った共立の福袋に入っていたBELDENの8503というケーブルを使ってみた。このケーブルは線材が固めでパネル取付型のPOTなど空中配線するときには使いやすいと思う。線材にクセをつけられるので手で押さえていなくてもはんだ付けしやすい。

どっちにしろ、はんだ付け箇所が多いのでめんどくさいです。量産する場合はプリント基板に基板取り付け型の可変抵抗でしょうか。

POTは40円、つまみは30円のものを秋月で仕入れたので、パネルだけだと材料費は1200円ぐらいです。