5 オリジナルのスケッチが通らない
5.1 コンパイルが通らない(トラブル I)
// Interrupt service routine for the 'frequency' rotary encoder.
ISR(PCINT2_vect) {
ISR(PCINT2_vect) { ---> void rotary_encoder(){
とすると、コンパイルが通るようになりました。想像なのですが、ESP32は全GPIOを割り込みに使えるので、より厳密にしているのかと思っています。
5.2 OLEDを使うと波形が出ない(トラブル II)
void WriteRegister(int dat) {
// Display and AD9833 use different SPI MODES so it has to be set for the AD9833 here.
SPI.setDataMode(SPI_MODE2);
<省略>
SPI.transfer(highByte(dat)); // Each AD9833 register is 32 bits wide and each 16
SPI.transfer(lowByte(dat)); // bits has to be transferred as 2 x 8-bit bytes.
<省略>
修正後
void WriteRegister(uint16_t dat) {
// Display and AD9833 use different SPI MODES so it has to be set for the AD9833 here.
// hspi.setDataMode(SPI_MODE2);
<省略>
hspi.transfer16(dat); // bits has to be transferred as 16 bit bytes. 2 x 8-bit bytes are not well.
<省略>
5.3 周波数をスイープさせる方法
void AD9833setFrequency(long frequency, int Waveform) { // レジスタへ書き込む内容(波形定義)や順番を設定
void WriteRegister(int dat) { // レジスタへの書き込み処理
// Display and AD9833 use different SPI MODES so it has to be set for the AD9833 here.
周波数スイープのスケッチ
void AutoSweep() {
<省略>
float startHz = 1000, stopHz = 5000, incHz = 1, sweepTimeSec = 5.0;
// Calculate the delay between each increment.
uint16_t numMsecPerStep = (sweepTimeSec * 1000.0 ) / ((uint16_t)((stopHz - startHz) / incHz) + 1); // 小数なら、小数点以下は切り捨てられる
if ( numMsecPerStep == 0 ) numMsecPerStep = 1; // 1以下の小数なら、小数点以下は切り捨てられ、numMsecPerStep = 0 ---> 1 ms
// Apply a signal to the output. If phaseReg is not supplied, then
// a phase of 0.0 is applied to the same register as freqReg
AD9833setFrequency(startHz-incHz, waveType); //意味が分からんが、D9833-Library-ArduinoのIncrementFrequencyTest関数に倣った
for ( float j = startHz ; j <= stopHz; j += incHz ) {
if (!sweepState){break;} /* stateがfalse(sweepさせたい割り込みが消滅)ならばfor文を抜ける。(Sweepを抜ける)これがなければ、stopHzまでfor を繰り返したのち、Sweepを抜ける */
AD9833setFrequency(j,waveType);
delay(numMsecPerStep);
if((millis() - dsTime) > 500) { // 500ミリ秒以下は無視する 0.5sec毎に周波数を表示する
dispfrequency(j); // Remember new frequency to avoid unwanted display
dsTime = millis();
}
}
}
5.4 波形に定期的にノイズが乗る(トラブル III)
if (!sweepState){WriteRegister(0x2100);} // Write control register, both register are allowed to write and reset
// Sweep モードのときこれがあると、波形の始まりにレジスタ書き込み信号らしきものが載る
Analog DevicesのAD9833 - FAQには、周波数を切り換える方法について以下のQ&Aがあります。
これが本来の方法でしょうが、AD9833_test_suite.inoがこの方法を取っていないと思います。この先、波形がもしおかしくなった時にはこの方法をやってみようと思います。
DDSの周波数切り替え
Q: 周波数を変更しようと、16ビット/ワードで周波数レジスタ内容を書き換えると、出力が不連続になる。連続的に周波数を変更することはできないか。
A: AD9833はデータレジスタの書き込みが16ビット毎となっておりますので、出力に選択している周波数ジスタの値を書き換えると、最初に16ビット書き込んでから次の16ビットデータが書き込まれるまでに意図しない周波数が出力されます。 AD9833は周波数レジスタが2組ありますので、周波数を変更する場合には、出力に選択していない方の周波数レジスタを更新しFSELで出力周波数レジスタを切り替えてください。
5.5 スイープ割り込みの解除(通常モードへの復帰)ができない(トラブル IV)
「割り込み」は、H/W的に割り込みルーチンが自動的に呼び出されます。 そして、割り込み処理が終了すると、前まで実行していたプロセスに戻ってきますので、forが無限ループだとすると、forから抜ける事はせず無限ループを続行する事になります。 スイッチを押すことによってforを途中で抜けたい場合は、「スイッチの状態I/O」を直接監視するか、または スイッチが押された割り込みで「スイッチONフラグ」を立て、forでこのフラグを監視する必要があると思います。
スイープの割り込みを定義するISR
// sweep 押下の割り込み できるだけ簡素にする
void IRAM_ATTR select() {
sweepState = !sweepState; //stateの値が変わる
digitalWrite(sweepPin, sweepState); //stateの真偽によりLEDをオンオフ
}
// freaquency sweep の割り込みが起きた時の処理
if(sweepState ){ // 割り込みフラグがtrueなら、AutoSweep()関数を実行
if((millis() - msTime) > 100) { // 100ミリ秒以下の割り込みは無視する
sweepStateOld = sweepState; // 割り込みフラグ
AutoSweep();
msTime = millis();
}
} else if (sweepStateOld == true) { // 割り込みフラグがtrueではなく、前のフラグがtrueなら、freaquency Stepモードへ復帰
AD9833reset();
AD9833setFrequency(temp, waveType); // must have been turned so update AD9833 and display.
mylcd.Fill_Screen(BLACK);
waveOld = 1;
updateDisplay(freq);
sweepStateOld = false; // 割り込み 復帰フラグ
}
5.6 割り込みとシリアル通信は相性がよくないのか?(トラブル V)
・シリアル通信により受信したデータは、失われる可能性があります@レファレンスマニアル・割り込み関数の中でSerial.printを書いていると、割り込みを使うと時々リセットがかかる。Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1).
後からESP32のGPIOの割り振りを整理していて気づいたのですが、シリアル通信UARTと割り込みが競合している割り振りになっているのが理由かもしれません。4章で載せた一覧でGPIO#1と#3がUART(HardwareSerial)となっています。スケッチでUARTを設定している訳ではないのですが、何らかの縛りがあるのでしょうか?
5.6 EPS32で割り込みを使うときの注意点
- ISR関数は引数を伴わないvoid型でなければならない
- ISRの関数にはIRAM_ATTR属性を必ずつける(フラッシュではなく内部RAMメモリに配置する)
- IRAM_ATTR属性をつけたISR関数は setup より前に書かないとコンパイルエラーになる
- ISRの関数からアクセスする変数にはvolatile修飾子を必ずつける
- 必要最低限の処理だけを行う
- 高度な処理は排他制御を使って保存し、別タスクで実行する
- 呼び出す関数群にFromISRがついている関数があったらそちらを使う
void setup()
{
pinMode(GPIO_pin, INPUT);
attachInterrupt(GPIO_pin, Ext_INT1_ISR, RISING);
}
void IRAM_ATTR Ext_INT1_ISR()
{
// Do Something ...
}