こんばんは。
いきなり、前回の記事の補足をします。
ESP-WROOM-32 ( ESP32 )のGPIO から方形波(パルス波)を出して、市販の電波時計を合わせてみましたが、ちょっとした情報が入ってきました。
実は、こちらの記事のコメント欄で Kat-Kai さんから有益な情報をいただき、ESP32 の CPUクロック数からμ秒オーダーのパルス長時間調整の方法を教えていただきました。
以下のブログでも紹介されていますので、合わせてご参照ください。
格段に安定した出力ができて、お勧めですヨ!
さて、本題に戻りまして、前回の記事では電波時計の疑似標準電波 ( JJY )は方形波(パルス波)だったため、高調波ノイズを放出していました。
そのため、受信できる電波時計と受信できないものがあったり、時計を置く場所が微妙に難しかったりと、受信感度はあまり良くありませんでした。
そこで、今回は 40kHz のできるだけ純粋なサイン波(正弦波)をESP-WROOM-32 ( ESP32 )を使って出力してみたいと思います。
ただし、アナログデバイセズの AD9833 というプログラマブル波形発生器を搭載したモジュールを使用します。
要するに、Wave Generator ですね。
これ、使って見るとなかなか優れものです。
ESP-WROOM-32 ( ESP32 )で SPI通信で制御するのですが、ピッタリ 40kHz の驚くほどキレイなサイン波(正弦波)が出力できますし、周波数も Arduino IDE プログラミングで意外と簡単に変えられるんです。
こんなことだったら初めから使っておけば良かったと後悔しました。
これを使えば、無線電波の搬送波を作ることもできますし、オーディオ測定用信号発生器も作れそうですね。
シンセサイザーも作れそうです。
では、これの使い方を説明します。
準備するもの
AD9833 プログラマブル信号発生器モジュール
アナログデバイセズ社のプログラマブル波形発生器AD9833 を使ったモジュールです。
私は以下の物を使いました。
これは中国の販売店で、到着するまでに15日くらいかかりました。
格安なので仕方ないところです。
日本の販売店ではなかなかこのモジュールを見つけることが出来ませんでした。
秋月電子通商さんなどでも探したのですが、なかなか安価なサイン波発生モジュールは見当たりませんでした。
実際に購入した写真はこんな感じです。
ピンヘッダ(2.54mmピッチ)は付属していました。
25MHz 外部発信器がありますね。
裏側はこんな感じです。
上の部分の空白基板は任意にカットできるようですが、これはワザとそうしているのか、あるいはコスト削減のためなのかは分かりません。
ESP-WROOM-32 ( ESP32 )開発ボード
スイッチサイエンスさんの ESPr Developer 32 は保護機能が充実していてお勧めです。
ピンヘッダは別売りです。
これの使い方は以下のページを参照してください。
ESPr Developer 32 ( スイッチサイエンス製 ) を使ってみました
その他、Amazon.co.jpさんでは ESP32-DevKitC があります。
ブレッドボード
サンハヤトさんの SAD-101 がお勧めです。
その他、超小型ブレッドボードもお勧めです。
SAD-101 と合体できます。
積層セラミックコンデンサ
●0.1μF ×1個
● 2200pF ×1個
AD9833 モジュールにもバイパスコンデンサが組み込まれていますが、新たにコンデンサを追加したことによって、私の場合は特性が良くなりました。
100MHz 級のオシロスコープ
40 kHz サイン波が純粋かどうかを測定するには、100MHz sampling / sec 程度のオシロスコープが必要です。
私はこれを使っています。
これは簡易 FFT測定もできるので便利です。
プローブは別売りです。
ジャンパーワイヤー、パソコン、USBケーブル等
Arduino core for ESP32 の設定
事前に Arduino IDE の設定を済ませておきます。
Arduino IDE は 1.8.3 以上を使用してください。
Arduino core for ESP32 は最新版をインストールしてください。
インストール方法は以下のページを参照してください。
Arduino core for the ESP32 のインストール方法
接続する
では、AD9833 モジュールにピンヘッダをハンダ付けして、ブレッドボード上で下図を参照して接続してみてください。
( ESP32-DevKitC で説明してます )
バイパスコンデンサは任意で接続してみてください。
オシロスコープのノイズが少なくなれば良いです。
因みに、Arduino – IDE の SPIライブラリの HSPI 接続を使います。
HSPI については以下の記事を参照してください。
ESP32 の SPI_MODE が修正。HSPI , VSPI , 複数SPIデバイス制御 , SPI高速化などについて
写真ではこんな感じになります。
スケッチの入力
まず、アナログデバイセズ社のサイトにある、AD9833 のデータシートを参照してみてください。
以下のリンクにあります。
日本語なので有り難いです。
http://www.analog.com/media/jp/technical-documentation/data-sheets/AD9833_JP.pdf
これを読んでみて、その通りにプログラムを作ってもなぜかうまくいきません。
まず、疑問は図3のSPI通信のタイミングチャートです。
FSYNC が SPI通信の CS ( Chip Select )信号に当たります。
SDATA は MOSI ( Master Output Slave Input )信号です。
この図をパッと見る限り、SPI_MODE1 に見えますね。
(SPI_MODE については以下の記事を参照してください)
ESP8266 ( ESP-WROOM-02 ) SPI 通信高速化、その2 ( 複数デバイス、Mode、Watchdog Timer 問題 )
実はよく見ると、セットアップタイムとして、SCLKの HIGH が最低5ns 経過させることが必要で、次にLOW にした時点で SDATA のビットを読み込むというようになっています。
つまり、SCLK の最初は HIGH のままで良いということになるので、SPI_MODE2 で良いということになります。
これには随分悩まされました。
みなさん、気を付けてください。
そしてもう一つ。
次の計算式が意味不明でした。
この通りにプログラムを組んでも動かず、悩みに悩みました。
そしたら、edy さんや、JF1VRR さんの以下の記事に解決方法が掲載されていました。
JF1VRR さんの記事
PSoC3 AD9833 DDSによる波形発生
edy さんの記事
AD9833 DDSモジュールを試す(1)
なんか、データシートの計算式と違うような気もするし、私の読み間違いかもしれません。
データシートだけ読んでも、なかなか正しい計算式を導き出せませんでした。
でも、JF1VRRさんやedyさんのソースコードを拝見して、それでちゃんと動いたので今回はヨシとします。
JF1VRR さん、edy さん、ありがとうございました。
m(_ _)m
ということで、私なりに解釈し易いようにアレンジした以下のスケッチを入力してみてください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include <SPI.h> const uint8_t SCLK_OLED = 14; //SCLK const uint8_t MOSI_OLED = 13; //MOSI (Master Output Slave Input) const uint8_t Fsync_PIN = 17; //CS (Chip Select) const int SINE = 0b0010000000000000; //0x2000 const int TRIANGLE = 0b0010000000000010; //0x2002 const int SQUARE = 0b0010000000100000; //0x2020 const int SQUARE_half = 0b0010000000101000; const float refFreq = 25000000.0; SPIClass spi(HSPI); void setup() { Serial.begin(115200); spi.begin(SCLK_OLED, 12, MOSI_OLED, Fsync_PIN); spi.setFrequency(40000000); spi.setDataMode(SPI_MODE2); pinMode(Fsync_PIN, OUTPUT); digitalWrite(Fsync_PIN, HIGH); Control_Resister_Write(0b0000000100000000); //Reset delay(50); } void loop() { AD9833_SetFrequency(10000, SINE); delay(3000); AD9833_SetFrequency(40000, SINE); delay(3000); AD9833_SetFrequency(20000, TRIANGLE); delay(3000); AD9833_SetFrequency(40000, TRIANGLE); delay(3000); AD9833_SetFrequency(20000, SQUARE); delay(3000); AD9833_SetFrequency(20000, SQUARE_half); delay(3000); AD9833_SetFrequency(50000, SQUARE); delay(3000); AD9833_SetFrequency(50000, SQUARE_half); delay(3000); } void AD9833_SetFrequency(uint32_t frequency, int Waveform) { uint32_t FreqWord = (frequency * pow(2, 28)) / refFreq; uint16_t MSB = (uint16_t)((FreqWord & 0xFFFC000) >> 14); uint16_t LSB = (uint16_t)(FreqWord & 0x3FFF); LSB |= 0b0100000000000000; //=0x4000 MSB |= 0b0100000000000000; //=0x4000 Control_Resister_Write(0b0010000000000000); //制御ワード書き込み Control_Resister_Write(LSB); Control_Resister_Write(MSB); Control_Resister_Write(0b1100000000000000); //位相シフトはゼロ Control_Resister_Write(Waveform); } void Control_Resister_Write(uint16_t b){ digitalWrite(Fsync_PIN, LOW); spi.transfer16(b); digitalWrite(Fsync_PIN, HIGH); }
【解説】
●1行目:
Arduino core for ESP32 標準の SPI ライブラリのインクルードです。
●3-5行:
HSPIインターフェース用に GPIO を設定します。
●7-10行:
ここで、サイン波、三角波、方形波を出力するためのレジスタ値を定義します。
16bit で初期化します。
16進数の方が文字数を減らすことが出来るのですが、データシートと照合しやすいように2進数表記しています。
●12行:
外部クロックの周波数を設定します。
ここでは発信器が 25MHz なので、その通りにします。
●14行:
Arduino core for ESP32 の HSPI接続を宣言して、クラス名を spi とします。
HSPI については以下の記事を参照してください。
ESP32 の SPI_MODE が修正。HSPI , VSPI , 複数SPIデバイス制御 , SPI高速化などについて
●19-21行:
SPIライブラリの初期化です。
GPIO #12 は実際使いませんが、HSPI接続のMISOなので、とりあえず12番としました。
AD9833 のSPI信号最高周波数は 40MHz までのようなので、一応最高周波数としました。
SPI MODE は先にも述べた通り、SPI_MODE2 とします。
●23-24行:
SPIライブラリを使う場合、ハードウェア CS ピンを使うと高速で処理してくれるのですが、それを使ってしまうと、他のSPI機器のハードウェアピンが使えなくなってしまうので、ここでは digitalWrite を使います。
●26行:
ここで、AD9833 をリセットします。
●31-46行:
メインループで波形や周波数を3秒毎に変えています。
●49-70行:
ここで周波数や位相、波形レジスタを書き込んで、OUT 端子から出力させています。
50行の計算式はデータシートを見ただけではなかなかこの計算式を導きだせません。
でも、これでちゃんと動作します。
52-56行はデータシートに記載されている通りの計算で、28ビットを14ビットづつ分割しています。
位相シフトは今回不要なのでゼロとしています。
63行目で波形レジスタをセットすると、サイン波、三角波、パルス波と選択できます。
●66-70行:
SPIライブラリの標準の16bitレジスタ出力方法です。
コンパイル書き込み実行
では、Arduino IDEでコンパイル書き込みをして、実行させてみてください。
AD9833 モジュールの OUT端子にオシロスコープのプローブを接続して波形を見ると、3秒毎にこんな感じに表示されると思います。
図のように、サイン波と三角波のピーク電圧値は0.65V 程度ですが、パルス波になると 3.3V 付近まで振れてくれます。
パルス波はハーフサイズのレジスタ値があるので、いろいろと使い道があるかもしれません。
サイン波はデジタルにしては意外とキレイな正弦波が出ていますね。
これは電波の搬送波として十分使えますよ。
まとめ
どうでしょうか?
これをトランジスタや FET で増幅すれば、電波の搬送波として十分使えます。
もう実験済みですが、電波時計の JJY 電波を出すことにも成功し、ガッツリと電波時計が合わせられるようになりました。
これは次回に紹介したいと思います。
これはオーディオ測定信号としても使えるし、シンセサイザーの VCO としても使えそうですね。
SPI通信で周波数をスイープさせるということもできます。
いろいろと使い道が考えられて、幅が広がりますね。
以上、今回はここまでです。
ではまた・・・。
Amazon.co.jp 当ブログのおすすめ
コメント
基盤の余分な部分について SMDを機械実装する際に必要な原点と言うか基準点が書かれています。小さな基盤の場合 その中に書けないので あの様な形に成ります。
匿名さん
記事をご覧いただき、ありがとうございます。
今は基板製作を全くやっていいないので、そういう事情とは知りませんでした。
確かに、小さい基板だと、基準点が必要そうですね。
コメントありがとうございました。
m(_ _)m
Hey! Love the whole article! You have done amazing job writing it !
Anyway I wonder why You are connecting pin 17 for CS instead of pin 15, which is normal HSPI CS ?
If I used GPIO # 15 of HSPI CS, I could not add another HSPI device, so I did not use it this time.
Thanks for seeing this blog post!
Thank You very much about the response!
I am still new to the SPI master-slave ideology. 🙂
So – You were using #15 for another device ?
I use GPIO #15 to use hardware CS when I want to do high-speed processing with VSPI.
Thank You very much for your response and clarification fellow ! 🙂