« ESP32 WROOM/WROVER GPIO16,17使用可否実機検証 | トップページ | ESP32開発ボード用にブレッドボードをカット »

2024年4月12日 (金)

★★★タイニーBASIC GPIO+AnalogIO拡張 PWM高速化・ADC高精度化

E S P 3 2
豊四季タイニーBASIC WiFi Telnet版
GPIO・PWM・ADC・DAC
操作 拡張関数
プチ改良:
PWM高速化・高精度ADC

先日拡張が成功したGPIO+AnalogIO版ですが、以下の点で気になっていました。
・TinyBasicは扱う数値の最大値が32767のため、単純にはPWM出力周波数を32,768Hz以上に出来ない
・ESP32で用意されているキャリブレーションされた、精度の高いanalogReadMilliVolts()関数を取り込んでいなかった
GPIO16,17はWROOMでは使えるものの、非対応としていた。

今回、時間があったので、以下の通りプチ改良してみました。
・PWM出力最大周波数をESP32限界の40MHzまで出せるようにした。
 参考 PWM制御とは?Arduino(ESP32)コマンドで使い方を詳しく紹介 | ロジカラブログ (logikara.blog)
 TinyBasicでは40MHzは直接指定できないので、数値FRQと係数FMLTの2つの引数を記述し、
 その積を出力周波数としuint32_t型に変換して、ledcSetup()関数へ渡すようにした。
・上記に合わせて、分解能RESをESP32の最大値20bitsまでにし、
 最大40MHz Duty50%まで出せ、分解能も最大限細かく設定できるようにした。
・analogReadMilliVolts()に相当する、高精度ADC関数を用意した。
 参考 ESP32のADCでキャリブレーションされた精度の良い電圧値を取得する方法 | kohacraftのblog
・GPIO・PWM操作の対象GPIOに、WROOMで利用可能な、GPIO16,17を加えた

一応、PWMの指定可能範囲を拡大したので、いろいろと設定値を変えて、動作確認をしました。
その中では、設定変更が効かないケースもあり、その場合シリアルインターフェイスでエラーメッセージと共に、ヒントが送信されてくるので、デバッグに役立つ事も分かりました。これに関しての関連記事等は今現在見つけきれていませんが。
analogReadとanalogReadMilliVoltsの変換結果を比較では、ちょっと予想外の結果も判明しました。
arduino ESP32のライブラリのバージョンで、使用可能なAPI関数名や書式が変わってしまう事にも気づいたので、補足しました。

豊四季TinyBasicの文法は、豊四季タイニーBASICの文法 | 電脳伝説 (wordpress.com) を参照ください。
また、SAVE [BOOT]、LOAD が使えます。 

-------------------------------------------
本記事の目次です。
・改訂した拡張関数の仕様とGPIO選択表
・主に工夫した点
・デモ結果
 ・PWM出力の最高周波数40MHzの例
 ・PWMでの76Hz出力の例
 ・TinyBasicで扱える最小値1HzのPWM出力例
 ・不適切なPWM設定はシリアルからのメッセージでデバッグ
 ・AD変換のデモと結果のグラフ
 ・AD変換のオフセットの考察と追加実験
・ソースコードの公開と注意・使用方法
・Arduino IDE ESP32 APIライブラリの補足
・雑感

以下詳細です。

 


改訂した、拡張関数の仕様と、GPIO選択表は、以下です。

Ttbfunc
 尚、ライブラリのバージョンは Arduino ESP32 version 2.0.15 (現時点のLatest版)を使っています。

Gpiopintable2

今回、主に工夫した点は以下です。

・PWM出力の周波数を指定するledcSetup()への周波数の引数はuint32_t型で与える必要があり、
 FRQとFMLTの積を取りstatic_castでuint32_t型に変換しました。
 ledcSetup(chn, static_cast<uint32_t>(frq * fmlt), res);
 TinyBasicでは数値や変数・配列は全てshort int型なので、static_castで十分なはず。

・キーワードテーブルとenumではANALOG_READ_MILLIVOLTSをANALOG_READよりも先に記述した。
 最初は逆に記述してしまい、ANALOG_READ_MILLIVOLTSを実行したいのに、内部処理的にANALOG_READが実行されてしまい、
 残りの文字列"_MILLIVOLTS"でpin番号を得ようとしエラー、この原因究明に数時間悩んでしまいました。
 先頭からの文字列が同じキーワードは、長い方を先に記述する必要がある点、「タイニーBASICをCで書く」に注意書きありました。
 Photo_20240414085901

・ADCの減衰量設定は、前回確認できたので、以下の1行で済ませました。
 analogSetPinAttenuation(pin, static_cast<adc_attenuation_t>(attn));

 



以下、デモ結果です。


■ PWM出力の最高周波数40MHzの例です。
40MHzだと、GPIO出力の遷移時間・出力インピーダンス、オシロスコープ・プローブの帯域、測定系の配線状態等、種々の影響を受け綺麗な方形波になりませんが、こんなもんでしょう。

100 REM GPIO32 PWM simple test
110 P=32; REM GPIO 32
120 C=0 ; REM PWM channel = 0
130 R=1 ; REM resolution
200 F=4000
210 M=10000
220 D=1
300 LEDC_SETUP C,F,M,R
310 LEDC_ATTACH_PIN P,C
320 LEDC_WRITE C,D

Img_0238

 

■ PWMでの76Hz出力の例です。
RESが20bitsに対し、Dutyが32767なので、オシロ波形のデューティは3%(32767/(2^20=1048567))になっています。
RESを荒くすれば、76Hz出力でもデューティをもっと大きく出来ます。

100 REM GPIO32 PWM simple test
110 P=32; REM GPIO 32
120 C=0 ; REM PWM channel = 0
130 R=20 ; REM resolution
200 F=76
210 M=1
220 D=32767
300 LEDC_SETUP C,F,M,R
310 LEDC_ATTACH_PIN P,C
320 LEDC_WRITE C,D

Img_0239

 

■TinyBasicで扱える最小値1Hzでの出力例です。
PWM 1Hz デューティ50%の波形例です。

100 REM GPIO32 PWM simple test
110 P=32; REM GPIO 32
120 C=0; REM PWM channel = 0
130 R=12
200 F=1
210 M=1
220 D=2048
300 LEDC_SETUP C,F,M,R
310 LEDC_ATTACH_PIN P,C
320 LEDC_WRITE C,D

Img_0241

 

■ 不適切なPWM設定はシリアルからのメッセージでデバッグ
PWM回路に対し不適切な設定を行った場合は、シリアルインターフェイスでメッセージが送信されますので、ヒントになります。
上記に対し、130 R=6に変えてrunしてみると、以下のメッセージが返ってきました。

E (1069533) ledc: requested frequency and duty resolution can not be achieved, try reducing freq_hz or duty_resolution. div_param=320000000

この例では、RES=6bitsのため、Duty指定範囲は1~63ですが、2048を指定したままなので、設定変更されず、周波数かデューティを下げろと言ってきています。

100 REM GPIO32 PWM simple test
110 P=32; REM GPIO 32
120 C=0; REM PWM channel = 0
130 R=6
200 F=1
210 M=1
220 D=2048
300 LEDC_SETUP C,F,M,R
310 LEDC_ATTACH_PIN P,C
320 LEDC_WRITE C,D
シリアルインターフェイスに出力されるエラーメッセージ例Photo_20240413145701

WiFiでのTinyBasicとの接続だけでなく、PCのUSBでSerial (上記では TeraTerm シリアル COM6 115200bps 8bits パリティ無し)に接続しておけば、簡易的ですがデバッグに役立ちそうです。

2024-4-21追記
ESP-IDFのAPIの説明に、以下の記述がありました。注意Arduino ESP32 APIの説明では記述は見当たりません。
Photo_20240421223501
LEDC APIはハードウェアの対応範囲を超えた周波数とデューティ分解能をセットしようとすると、シリアルモニターにエラーをレポートするように設計されているとの事です。
親切なライブラリと言う事か? ユーザーでの設定トラブルが多く、問い合わせやフォーラムでの質問が多いと言う事かな?

2024-4-22追記
Arduino ESP32 APIのライブラリにも、メッセージを出すと思われる記述がありました。以下自分のPC環境での例です。
C:\Users\username\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.15\cores\esp32\esp32-hal-ledc.c 
を見ると、ledcSetup()関数では、LEDCチャンネルかビット幅が最大値を超えた際にメッセージを出し、実行無しでリターンですね。

uint32_t ledcSetup(uint8_t chan, uint32_t freq, uint8_t bit_num)
{
   if(chan >= LEDC_CHANNELS || bit_num > LEDC_MAX_BIT_WIDTH){
       log_e("No more LEDC channels available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
       return 0;
   }

単純な引数の数値範囲チェックだけは、本簡易拡張でもしていますけどね。

 


 

■AD変換のデモと結果のグラフです。
analogReadMilliVoltsの方は、縦軸をmVとして電圧値が読み取れますし、特に11dbで直線性が改善しているのが分かります。
ただ、全ての減衰量でも低電圧側オフセットが100mV程度ある様です。

100 REM ------ DAC OUTPUT and ADC INPUT Test ------
110 P=25; PIN_MODE P,192; A=36; PIN_MODE A,192
120 ANALOG_SET_PIN_ATTENUATION A,3
130 GOSUB 300
140 ANALOG_SET_PIN_ATTENUATION A,2
150 GOSUB 300
160 ANALOG_SET_PIN_ATTENUATION A,1
170 GOSUB 300
180 ANALOG_SET_PIN_ATTENUATION A,0
190 GOSUB 300
200 STOP
300 REM ----------- DAC & ADC Subroutine
310 FOR D=0 TO 255 STEP 1
320 DAC_WRITE P,D
330 DELAY_MS 10
340 PRINT #5,ANALOG_READ A,",",#5,ANALOG_READ_MILLIVOLTS A
350 NEXT D
360 PRINT;PRINT
400 FOR D=255 TO 0 STEP -1
410 DAC_WRITE P,D
420 DELAY_MS 10
430 PRINT #5,ANALOG_READ A,",",#5,ANALOG_READ_MILLIVOLTS A
440 NEXT D
450 PRINT;PRINT
460 RETURN
Analodread

Photo_20240415140001


・AD変換結果のオフセットの考察と追加実験
analogReadMilliVoltsの下限側100mV程度のオフセットが気になります。
たぶんDAC出力がオフセットを持っていると考えられますが、測定での確認まではしていません。
データシート類には情報が見当たりませんが、以下のフォーラム記事を見つけました。

Digital To Analogue Converter Characteristics - ESP32 Forum

記事を読むと、上限は電源電圧より少し(120mV?)程度下がり、下限はGND(0V)より少し(50mV?)程度上がっている模様。

--------------------------------------------------

ならば、単純に、AD入力にGND(0V)を繋いで、AD変換結果がどうなるか調べれば済むので、やってみました。
アッテネーションを変えながら、10ms毎に2回づつ、analogReadとanalogReadMilliVoltsを実行させた結果です。

100 REM ------ DAC OUTPUT and ADC INPUT Test ------
110 P=25; PIN_MODE P,192; A=36; PIN_MODE A,192

120 ANALOG_SET_PIN_ATTENUATION A,3
130 GOSUB 300
140 ANALOG_SET_PIN_ATTENUATION A,2

150 GOSUB 300
160 ANALOG_SET_PIN_ATTENUATION A,1
170 GOSUB 300
180 ANALOG_SET_PIN_ATTENUATION A,0
190 GOSUB 300
200 STOP
300 REM ----------- DAC & ADC Subroutine
330 DELAY_MS 10
340 PRINT #5,ANALOG_READ A,",",#5,ANALOG_READ_MILLIVOLTS A
360 PRINT
420 DELAY_MS 10
430 PRINT #5,ANALOG_READ A,",",#5,ANALOG_READ_MILLIVOLTS A
450 PRINT
460 RETURN

 

GPIO36(ADC入力)をGNDに接続した場合
減衰量に応じて0ではない、特定の値が得られています。

減衰量 analogRead analogReadMilliVolts
-------+--------------+----------------------
 11db           0                   142
 11db           0                   142
   6db           0                   107
   6db           0                   107
 2.5db          0                     78
 2.5db          0                     78
   0db           0                     75
   0db           0                     75

参考で、GPIO36(ADC入力)に何も繋がない場合です。

ノイズできちんと暴れています。

減衰量 analogRead analogReadMilliVolts
-------+--------------+----------------------
 11db         453                  142
 11db       1488                 1100
   6db       1297                 1368
   6db             0                  252
 2.5db            0                    78
 2.5db            0                    78
   0db        4095                    75
   0db        4095                 1112

何と、予想に反し

analogReadの方は全て0が返ってきたものの、
analogReadMilliVolts側は、キャリブレーション(補正)で付与されたと思われる値が返ってきました

しかも[mV]単位で電圧値を返すはずなのに、減衰量設定によって電圧値が変わっています
減衰量11dbだと142mV、6dbで107mV、2.5dbで78mV、0dbで75mVのオフセットがある様です。

どう解釈するのかは良く分かりませんが、
analogReadMilliVoltsでは、AD変換結果に一定のオフセットが付与されているのが判明しました。

まあ、esp32_datasheet_en.pdf (espressif.com)にある下の表では、実効的な測定範囲が明記されていて、GND(0V)入力はこの範囲未満となり、そもそも精度保証範囲外、気にしてはいけないって事なのでしょう。



ESP32の電気的特性に関する情報はかなり乏しいので、これ以上の調査は厳しそうな予感。

まあ、ESP32では、ADC・DACに精度を求めるべきではなく、心配なら実験で性能をある程度把握し、使えそうな範囲内で使う。
そんな扱いが必要そうです。

 


・ソースコードの公開と注意・使用方法
今回作成の
basic.cpp はここで公開しますが、前回同様参考で、拡張の仕方としての事例紹介だけになります。
如何なる誤動作・破損・損害の責任は当方は負いません
ESP32で豊四季TinyBasicを走らせたり、独自の拡張に取り組んでみたい方の、
情報源の一つとして少しでも役に立てば幸いです。

利用条件に関しては、以下、豊四季タイニーBASICの作者鈴木氏に従うものとします。
Photo_20240422093701

Arduino IDEでは、 変更していない ttbasic_ESP32_WiFiTelnet.ino と合わせてのコンパイルとESP32への書き込みが必要です。
また、WiFiのssidとpwはご自身のWiFi環境にあわせての変更が必要です。

 


・Arduino IDE ESP32 APIライブラリの補足

尚、Arduino ESP32 の最新APIを参照すると、ledcAttachや、ledcAttachChannelがありますが、実際には使えませんでした。
調べると、ESP-IDF 5.1に対応したArduino ESP32 v3.0.0の説明で、v3.0.0自体はまだ、Pre-Release段階の様です。
私のArduino IDEでは、v2.0.15なので、ledcSetupやledcAttachPinはまだ、使えました。

もし、Arduino ESP32 v3.0.0 がLatestリリースになり、Arduino IDEで自動アップデートがかかってしまうと、コンパイルでエラーが多発する可能性が高く、その場合は、2.0.15に戻せばOKのはずと思います。

別記事で、もう少し詳しく書きました。

GitHubのArduino ESP32 ライブラリの最新情報(以下のURL、赤丸部分)を今後もウォッチしておく必要がありそうです。

GitHub - espressif/arduino-esp32: Arduino core for the ESP32
Photo_20240416195601

 


・まとめと雑感
今回、PWMの高速化、高精度ADC関数の、TinyBasicへの取り込みが実施できました。
拡張には慣れてきましたが、未知の対処方法を考えないといけないケースも出てきました。
例えばstatic_castが必要だったり、short int型2つの積をuint32_t型に型変換してからライブラリ関数の引数として渡すとかです。
TinyBasicの数値範囲を超える必要性が出てきて、どうしようかと少し考え、スマートではないにしても対応できました。
それはそれで良い勉強になりました。

一応、デモ等でも動作確認・設定値の有効範囲の確認など実施したつもりですが、誤りが含まれている可能性があります。
特に、PWMの設定では、不適切な設定の場合シリアルインターフェイスでエラーメッセージが送信されてくるので、いろいろと気付けたし、デバッグに役立つ事も知りました。

Arduno ESP32のAPIをどんどん取り込むのも一つの方向性かも知れませんが、TinyBasicで扱える範囲の簡単で低速で済むアプリ開発を考え、そこそこ頻繁に使いそうなAPIに絞り、便利そうなら拡張する程度が、本来のTinyBasicの守備範囲として適切なんだろうとの思いもあります。
今後ESP32をもっと使ってみて、どうするかは未知数ですけどね。


-------
本ブログは、過還暦脳活を第一の目的としております。なので、ここに記載のTinyBasic改良の実用性やコード記述のスマートさ等は、少し置いといて、「脳活題材でTinyBasicの拡張にチャレンジしているやつもいるんだなー」程度の目で見て頂けると助かります。
しかし、TinyBasicの拡張はなかなか面白く、楽しみながらやっています。

 


続報あればまた。

 

« ESP32 WROOM/WROVER GPIO16,17使用可否実機検証 | トップページ | ESP32開発ボード用にブレッドボードをカット »

電子工作:ESP32 TinyBasic」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

« ESP32 WROOM/WROVER GPIO16,17使用可否実機検証 | トップページ | ESP32開発ボード用にブレッドボードをカット »