チュートリアルのサンプルコード
以前、公式チュートリアルのDACの解説のサンプルコードを試してみた。そのときのサンプルコードはノコギリ波のデータを配列で用意しておき、それをanalogWrite()でDACに出力するというものだった。
ところが、その解説が変更されていて、今はanalogWaveクラスを使ったものになっている。以前のサンプルコードは消えている。ちょっともったいない気がするので、手元に保存してあったものを載せておく(問題があったら教えてください)。
ファイルは二つ。最初がWaveform.hで、もう一つがWaveform.c。
#ifndef _Waveforms_h_
#define _Waveforms_h_
#define maxSamplesNum 120
static int waveformsTable[maxSamplesNum] = {
0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0x110, 0x132, 0x154,
0x176, 0x198, 0x1ba, 0x1dc, 0x1fe, 0x220, 0x242, 0x264, 0x286, 0x2a8,
0x2ca, 0x2ec, 0x30e, 0x330, 0x352, 0x374, 0x396, 0x3b8, 0x3da, 0x3fc,
0x41e, 0x440, 0x462, 0x484, 0x4a6, 0x4c8, 0x4ea, 0x50c, 0x52e, 0x550,
0x572, 0x594, 0x5b6, 0x5d8, 0x5fa, 0x61c, 0x63e, 0x660, 0x682, 0x6a4,
0x6c6, 0x6e8, 0x70a, 0x72c, 0x74e, 0x770, 0x792, 0x7b4, 0x7d6, 0x7f8,
0x81a, 0x83c, 0x85e, 0x880, 0x8a2, 0x8c4, 0x8e6, 0x908, 0x92a, 0x94c,
0x96e, 0x990, 0x9b2, 0x9d4, 0x9f6, 0xa18, 0xa3a, 0xa5c, 0xa7e, 0xaa0,
0xac2, 0xae4, 0xb06, 0xb28, 0xb4a, 0xb6c, 0xb8e, 0xbb0, 0xbd2, 0xbf4,
0xc16, 0xc38, 0xc5a, 0xc7c, 0xc9e, 0xcc0, 0xce2, 0xd04, 0xd26, 0xd48,
0xd6a, 0xd8c, 0xdae, 0xdd0, 0xdf2, 0xe14, 0xe36, 0xe58, 0xe7a, 0xe9c,
0xebe, 0xee0, 0xf02, 0xf24, 0xf46, 0xf68, 0xf8a, 0xfac, 0xfce, 0xff0
};
#endif
/*
Simple Sawtooth Waveform generator with Arduino UNO R4 Minima
*/
#define oneHzSample 10000 / maxSamplesNum // sample for the 1Hz signal expressed in microseconds
int i = 0;
int sample;
void setup() {
analogWriteResolution(12); // set the analog output resolution to 12 bit (4096 levels)
analogReadResolution(12); // set the analog input resolution to 12 bit (4096 levels)
}
void loop() {
sample = map(analogRead(A5), 0, 4095, 0, 1000);
analogWrite(DAC, waveformsTable[i]); // write the selected waveform on DAC0
i++;
if (i == maxSamplesNum) // Reset the counter to repeat the wave
i = 0;
delayMicroseconds(sample); // Hold the sample value for the sample time
}
今のサンプルコードはとってもシンプル。比較のために、これも掲載(ファイルは一つだけ)。
#include "analogWave.h"
analogWave wave(DAC);
int freq = 10; // in hertz, change accordingly
void setup() {
Serial.begin(115200);
pinMode(A5, INPUT);
wave.sine(freq);
}
void loop() {
freq = map(analogRead(A5), 0, 1024, 0, 10000);
Serial.println("Frequency is now " + String(freq) + " hz");
wave.freq(freq);
delay(1000);
}
wave.sine()の引数で周波数を指定するだけで正弦波を出せるようだ。周波数の変更はwave.freq()。実にシンプル。A5ピンに可変抵抗をつないで周波数を変えられるようになっている。なお、ArduinoIDEのスケッチ例にもこのコードはあるが、バグっている。可変抵抗をつなぐピンがA0になってしまっている。A0はDACの出力ピンなので他のアナログピンに変えなければならない(DAC出力はA0ピンに固定されている)。ついでにいうと、「freq = map(analogRead(A5), 0, 1024, 0, 10000)」も1024じゃなくて1023だと思うが。
話がそれたので戻す。
analogWave.hを探してみてみたところ、sineのほかにsquareやsawもある。amplitudeで大きさも変えられるようだ。
public:
/* constructor 1 with only DAC pin (to be used when predefined waveform are used) */
analogWave(pin_size_t pinNumber);
/* constructor 2 with buffer to be used (sample in buffer are output ciclycally */
analogWave(pin_size_t pinNumber, uint16_t *_buffer, uint32_t _size, uint32_t _offset);
/* begin the output of the waweform the frequency is the one the complete
set on sample are output (all samples will be output in 1 period */
bool begin(float freq_hz);
/* updated the frequency used */
bool freq(float freq_hz);
/* make the current index in the buffer to "jump" of the specified quantity */
void offset(uint32_t o);
/* start the generation of sample */
void start();
/* stop the genration of sample */
void stop();
/* every sample is multiplied for this factor must be a value between 0 and 1 */
void amplitude(float amplitude);
/* get the number of sample used */
uint32_t num_of_sample();
void _pre_sync();
void sync(analogWave &w);
void sine(float freq_hz);
void square(float freq_hz);
void saw(float freq_hz);
};
動作(出力波形)
では、実際にanalogWaveクラスを使ったコード(サンプルコードそのもの)を動かして波形を見てみる。
1kHz
最初は1kHz。可変抵抗を回して1000Hzになるように(シリアルコンソールに表示される)。しかし、これが難しい。細かな変化をさせにくい。なので、約1000。
DAC出力そのままでLPFは入れていないので階段がそのまま見える。階段状の一つ(カーソル間)が25kHzということは、サンプリングが25kHzということかな?
CDもDACにLPFを付けないとこういう事になっているんだろう。
周波数を変化させる
あのコードだと、出力周波数は0Hz~10kHzの範囲。
しかし、実際に設定できた最低周波数は120Hzくらい。シリアルに表示された値もそれくらいだったので、ADコンバータからの出力(をmapしたもの)がそうなのだろう。ここではその追求はしないでおく。
サンプリング周波数は3kHzくらいになっている。サンプリング周波数を変えることで出力周波数を勝ているようだ。
続いて最高周波数。約10kHz。オシロでの表示では15.1kHzとなっているが、これは波形が揺らいでおり、たまたまその周波数と判定したため。250kHzサンプリングで、その階段が一周期中に24個ほどあるので10kHzくらい(15kHzではないことがわかる)。
周波数変化は離散的
可変抵抗を回していて気づいたのが、周波数が飛び飛びに変化すること。シリアルコンソールには連続的に数字が並んでいくが、波形が変化するのは飛び飛び。
500Hz付近
502Hz(カーソル間で判断)の次は507Hz。
1kHz付近
シリアルコンソールでは約1000Hzの表示だったが、オシロ上は1.01kHz。可変抵抗を回してもしばらく変化せず、次の周波数は1.04kHz、その次は1.06kHzだった。
2kHz付近
シリアルコンソール上の周波数表示、すなわち、freqの設定値は2000なのだけど、観測される周波数は2.19kHz。設定値と実際の出力周波数の乖離が大きくなってきた。
周波数の飛びも大きくなり、次は2.31kHz、その次も見たつもりだったけど波形を保存し忘れたようだ。
5kHz付近
設定値との乖離はますます広がり、5000の設定で5.23kHz。
周波数の飛びも益々大きく、次は5.95kHz、その次は6.99kHz。ここまで粗いと使い道が限られそう。
まとめなど
analogWaveクラスを使うとDACを使ったアナログ波形を容易に出力できる。しかしながら、周波数の変化量が連続的ではなく離散的なことと、設定値(周波数)と出力周波数が一致しないのが問題。この問題は周波数が高いほど顕著。
この問題がArduino UNO R4 Minima(のチップ)によるものなのか、analogWaveクラスによるもの(つまり、ソフト的なもの)なのかは未調査。
なお、測定の際には、可変抵抗での設定をしやすくするために設定範囲を都度変えた。例えば、5kHz付近のときは次のように。
freq = map(analogRead(A5), 0, 1023, 4900, 6500);
最初は4900~5500にマップしたのだけど、これだと一段階しか変化してくれなかったので上限を6500にした。6500までしか設定できないのに、出力されたのは6.99kHzだったのは上の説明のとおり。
コメント