ジャンク箱にあった D/A コンバータ「8BIT 12CH D-A CONVERTER WITH BUFFER AMPLIFIERS」M62352AGP を使って、Arduino から正弦波を出力してみようと考えています。
前回「D/A コンバータを使って Arduino から正弦波を出力する (2)」では、shiftOut() を使ってデータを転送していたのですが、転送に約 0.23ms かかっています。loop() を一回処理する時間の 59% です。そのために出力できる正弦波の周波数は 100Hz ぐらいが限界かなと。
そこで、データ転送に SPI 通信を使ってみることにしました。
SPI 通信とは
はい、ググってください m(__)m
ただし、Arduino 日本語リファレンスの SPI の内容は古い仕様なので、原文を参照しましょう。
解説してくださっているサイトでは、SPISettings クラスのインスタンスとして設定する方法を解説しているサイトを探してください。ちなみに俺は「ArduinoでSPI通信を行う方法」を参考にさせていただきました。とてもわかりやすかったです。ほぼ丸写しです (^_^;) ありがとうございました。
Arduino と D/A コンバータの回路図
回路に変更はありません。
shiftOut() を使うときはピンを自由に設定できますが、SPI 通信の場合はピンは固定されています。
まぁ、そんなことを見越して最初から D10 ~ D13 番ピンを使っていたわけなんですケド。
SPI 通信を使ったスケッチ
前回の shiftOut() を SPI.transfer() に置き換えたスケッチです。
- // Sine wave oscillator 2020.12.19 by meyon230
- #include <SPI.h>
- void setup() {
- SPI.begin();
- SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
- }
- void loop() {
- int f = 100;
- unsigned long t = micros();
- byte AO = 0x08;
- float y = (sin(TWO_PI*f*t*0.000001)+1.0)*0.5;
- byte DI = y * 255;
- digitalWrite(SS, LOW);
- SPI.transfer(AO);
- SPI.transfer(DI);
- digitalWrite(SS, HIGH);
- }
3 行目でライブラリ SPI.h をインクルードします。
6 行目は SPI の初期化です。Arduino NANO をマスタとして使用しますので、MOSI は D11 番ピン、SCK D13 、SS D10 として出力に設定されます。同時に、MOSI 、SCK を LOW に、SS は HIGH に初期化されます。
7 行目で、最大速度を 8MHz (分周比 1/2) 、データ送出を MSB ファーストに、データモードをモード 0 に設定します。モード 0 は、クロック SCK のアイドリング状態が LOW 、ビット転送はクロックパルスの立ち上がりで行なうというモードです。
18 ~ 21 行目でデータの転送を行ないます。SPI.transfer() も shiftOut() と同様に 1 バイトを送出しますので、最初にアナログ出力チャンネルのアドレスデータ 8 ビットを送り、次にアナログデータ 8 ビットを送って、シフトレジスタで頭の 4 ビットを溢れさせます。
出力波形
まず、ラッチパルスの状況を見てみましょう。
shiftOut() を使ったとき loop() を一回りするのに 0.39ms かかっていましたが、今回の SPI 通信では 0.19ms と約半分になりました。データの転送をしている LOW の時間は 0.23ms から 0.01ms へと、大幅に短縮されています。
100Hz の出力波形です。
shiftOut() を使ったときの 50Hz 出力波形と同じ感じで 100Hz を出力しています。処理時間が半分になったので、出力周波数を倍にできたってことでしょう。
周波数を 200Hz にしたときの出力波形です。
こちらも shiftOut() を使ったときの 100Hz 出力と同じ感じです。ギザギザが大きくなっていますが、とりあえず使い物にはなるんじゃないかと。
Arduino で正弦波を出力してみたけど
いやぁ、正弦波とゆーよりも、D/A コンバータを使うほうに主眼がいっちゃったなぁって感じです。shiftOut() や SPI 通信を使ってシリアルデータをシフトレジスタに送るってね。
で、正弦波ですが、sin() の計算をテーブルを使うとか別のアルゴリズムを使うとかも考えてみたんですけど、たとえ 0.19ms が半分になったところで出力できるのは倍の 400Hz ってことです。音階も出せない。この方法ではその程度が限界なんだろうなぁって。
たとえば MIDI ライブラリとか使えば音階は出せるようだし、tone() 使ってローパスフィルターで正弦波にしちゃうという力技もできるかもしれない。だからまぁ、そんなことです (^_^;)
あ、そうだ。この出力を 4-20mA 電流信号にして伝送してみようと思ってたんだった。あとでやってみよう。