ESP32でJTAGデバッグ

ESP32にFT232Hを接続してJTAGデバッグしてみる

概要

ESP32のデフォルトの開発環境だと、シリアル通信でFlash書き込んで、
せいぜいprintfデバッグするしかないが、
JTAG接続でオンチップデバッグ機能を使えば、もっと使いやすくなるはず。

ESP32のボードはESP32-DevKitC-VE (ESP32-WROVER-E搭載) を使用。

JTAGコントローラはFTDIのFT232HLを使用した 秋月電子のAE-FT232HLを使用する(安価なので)。

[!NOTE] サンハヤトのMM-FT232H が拡張コネクタが付いてて使いやすそうなんだけど、お値段かなりお高め…orz…

IDEは以前はEclipse一択だったけど、最近はVisual Studio Code に拡張機能PlatformIOを使うのが 流行ってるみたいなので、こっちを選択。
もう Visual Studio Code 最強だな…

まずはチュートリアルに沿って試してみる

PlatformIOの公式チュートリアルに沿ってツールのインストール~JTAGを使用しないプログラムの書き込み、実行を試す。

まずはツールとボードの動作確認ということで、まだJTAGモジュールは接続しない。

ツールのインストール

これは上記ページには書いてないので、こっちを参照(Visual Studio Codeインストール済みなら参照するまでもないけど。)
薫染庵 途上日誌 PlatformIO IDE for VSCode でESP32のプログラム開発
「3 ESP32プロジェクト作成」の手前まで(以降の説明はarduinoプロジェクトなので)。

[!NOTE] PlatformIOのインストールには結構時間がかかる(数分くらい)。 右下に出るinstalling~のウィンドウが見難いので「ハングアップした~」と焦って強制終了しないように注意。

プロジェクトの作成

公式チュートリアルのSetting Up the Project に従ってプロジェクトを作成。

[!NOTE] 初回のみ、プロジェクトを作成すると、自動でESP-IDFがインストールされる。
環境にもよるけど、15分とかのオーダで覚悟してちょ。

[!NOTE] ESP-IDFのインストールにはpythonが必要だが、platformioが自前で持っているのでインストールしなくても大丈夫っぽい。
gitはインストールしとかないとダメなのかな?なくても大丈夫な気もするけどわからん。

iniファイルの修正

platformio.inimonitor_speed = 115200の1行を追加
最終的なの内容は以下の通り。

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = espidf
monitor_speed = 115200

ソースコードの追加

公式チュートリアルのAdding Code to the Generated Project にあるソースをsrc\main.cにコピペする

[!NOTE] CMakeLists.txtがどーたらこーたらとwarningが書いてあるけど、無視して良い。
バージョン変わってちょっと書き方変わったらしい。

そのままでも無問題だけど、以下のパッチをあてておくと LEDがチカチカして かつ コンソールにカウント値が表示されるので、 プログラムが動いてることが一目瞭然。
(もちろん、IO26端子にLEDを接続しておかないと見えないよ。端子変えるならGPIO_NUM_26の部分(2か所)を適当に変更してちょ。)

あと、tcpip_adapter_init()が非推奨だとワーニングがでるので、esp_netif_init()に変更してある。
参考:https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/tcpip_adapter_migration.html

--- main.c.org  2021-12-26 11:13:05.474815000 +0900
+++ main.c      2021-12-26 11:12:42.842345600 +0900
@@ -18,6 +18,9 @@
 #include "lwip/err.h"
 #include "lwip/sys.h"

+#include       <stdio.h>
+#include "driver/gpio.h"
+
 #define EXAMPLE_ESP_WIFI_SSID      "mywifissid"
 #define EXAMPLE_ESP_WIFI_PASS      "mywifipass"
 #define EXAMPLE_MAX_STA_CONN       (3)
@@ -40,7 +43,7 @@

 void wifi_init_softap()
 {
-    tcpip_adapter_init();
+    esp_netif_init();
     ESP_ERROR_CHECK(esp_event_loop_create_default());

     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
@@ -81,4 +84,14 @@

     ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
     wifi_init_softap();
+
+    gpio_set_direction(GPIO_NUM_26, GPIO_MODE_OUTPUT);
+    int level = 0;
+    int count = 0;
+    while (true) {
+        gpio_set_level(GPIO_NUM_26, level);
+        level = !level;
+        printf("count=%d\n", count++);
+        vTaskDelay(300 / portTICK_PERIOD_MS);
+    }
 }

ビルド

ビルド実行方法色々書いてあるけど、お好きな方法でどうぞ。
初回はライブラリもビルドするので時間がかかる。
(platform.ini修正時も?)

ターミナルにSUCCESSと出てるのを確認して次へ。

ターミナルの内容確認して不要になったら何かキーを押すと閉じられる。

ターゲットプログラムのダウンロード

チュートリアルページにはuploadって書いてあるけど、普通はdownloadだと思うんだけど…
これもお好きな方法でどうぞ。

実行状況の確認

シリアルモニタでコンソール入出力を確認できる。
これも起動方法はお好きな方法でどうぞ。

うまく動いてたら、スマホやタブレットでWi-Fiスキャンすると「mywifissid」というSSIDが見つかるはず。
接続してもつながらないけど、なんか接続要求を受けたのがコンソールに表示されるみたい。

JTAGデバッガを使用したデバッグ

PlatformIOとESP32の動作が確認できたので、次はJTAGデバッガ。
念のため、cleanしてbuildファイルを一度消しておくのが良いかもしれない。

でも、公式チュートリアルのDebugging the Firmware 以降は OLIMEXのARM-USB-OCD-H を使用することが前提なので、今回は参照しない。

代わりにLong-ship ESP32をPlatformIO上でJTAG(FT232H)デバッグするを参照。
(でも、微妙に異なるのでちょっと補足書いとく)
事前準備(自動書き込み)までは上記で済んでいるのでスキップ。

ドライバの更新

ドライバ更新 にあるように、ドライバの更新をする。

ドライバ更新の詳しい手順はESP32で遊ぶ 開発環境を作る (4)の「FT232HL のドライバをインストール」あたりを参照すると分かりやすい。

最初に PCにFT232Hを接続しておくことを忘れないように。
ツールは、Zadigのページ のダウンロードからダウンロード。2021/12/23現在の最新は2.7。
メニューのoptions→ListAllDevices をやるのを忘れずに(忘れると表示されなくて「あれ?」となります(^^ゞ )
対象のデバイスを見つけやすいように、不要なUSBデバイス(特に他のFTDIデバイス)は取り外しておいた方がいいかも。

これは最初に1回やればOK

ESP32とFT232Hの結線

結線の記載とは ボードが異なるので、ボードのコネクタ名を含めて結線情報を掲載しておく。
以下の端子を結線する。

FT232H ESP32
AD0(TxD)J2-7 IO13J1-15
AD1(RxD)J2-8 IO12J1-13
AD2(RTS#)J2-9 IO15J2-17
AD3(CTS#)J2-10 GPIO14J3-12
AD5J2-12 ENJ1-2
GNDJ1-1 GNDJ3-1

[!NOTE] 結線には
AC1-EN結線と書いてあるけど、
設定値から判断してAD5-EN結線と思われる。

[!NOTE] ESP32で遊ぶ 開発環境を作る (4)には
ENの結線書いてないけど、結線なくても動くらしい。
RESET信号がJTAG側から入るのかな?

[!NOTE] espressifのページによると、sRSTはオプションで繋いでも対応してるコンフィギュレーションが少ないと書いてある。
でも、CH_PDってどこやねん?
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/jtag-debugging/index.html#jtag-debugging-selecting-jtag-adapter

iniファイルの修正

platformio.inidebug_tool = minimoduleの1行を追加
最終的なの内容は以下の通り。
これはプロジェクト毎に必要。

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = espidf
monitor_speed = 115200
debug_tool = minimodule

minimoduleの設定変更

minimoduleの設定変更 のように以下のパッチでファイル修正
(コメントは変えなくても良いけど、あとで参照して分からなくなるので変更しておく)

[!WARNING] この段階では%HOMEPATH%.platformio\packages\tool-openocd-esp32\ディレクトリがまだない(ダウウンロードされていない)ので、
一度、Visual Studio Codeのデバッグサイドバーを開き、デバッグターゲットに「PIO Debug」を選択し、実行ボタン(三角アイコン)をクリック
コンパイルが行われたあと、デバッガプログラムをダウンロードし、起動される。
デバッガの起動でエラー(no device found)になるので、一旦キャンセルし、以下の修正を行う。

修正するファイル:
%HOMEPATH%.platformio\packages\tool-openocd-esp32\share\openocd\scripts\interface\ftdi\minimodule.cfg

変更内容は、ディスクリプタとPID(FT2232H→FT232H)。
変更後、Visual Studio Codeの再起動必要。
これは最初に1回やればOK

--- minimodule.cfg.org  2021-07-21 22:38:02.000000000 +0900
+++ minimodule.cfg      2021-12-26 13:09:51.740579600 +0900
@@ -1,16 +1,16 @@
 #
-# FTDI MiniModule
+# Akizuki AE-FT232HL(FTDI FT232HL)
 #
-# http://www.ftdichip.com/Support/Documents/DataSheets/Modules/DS_FT2232H_Mini_Module.pdf
+# https://akizukidenshi.com/catalog/g/gK-06503/
 #

 interface ftdi
-ftdi_device_desc "FT2232H MiniModule"
-ftdi_vid_pid 0x0403 0x6010
+ftdi_device_desc "Single RS232-HS"
+ftdi_vid_pid 0x0403 0x6014

 # Every pin set as high impedance except TCK, TDI, TDO and TMS
 ftdi_layout_init 0x0008 0x000b

-# nSRST defined on pin CN2-13 of the MiniModule (pin ADBUS5 [AD5] on the FT2232H chip)
+# nSRST defined on pin J2-12 of AE-FT232HL(pin ADBUS5 [AD5] on the FT232HL chip)
 # This choice is arbitrary. Use other GPIO pin if desired.
 ftdi_layout_signal nSRST -data 0x0020 -oe 0x0020

[!NOTE] 好きなツール名を付けれれば良いんだけど、できないみたいなので一番近いものを修正して使用するということらしい。

[!NOTE] ftdi_layout_signalの設定値を変えればESP32のEN端子に接続するFT232Hの端子を変更できるはずだけど試してない。
たぶん、値の意味はbit15から順に、 GPIOH7GPIOH6、・・・、GPIOH0GPIOL3、・・・、GPIOL0TMS/CSTDO/DITDI/DOTCK/SKに割り当てられていて、それらの端子はAC7AC0AD7AD0にあたるものと思われる。
https://ftdichip.com/wp-content/uploads/2020/08/DS_FT232H.pdf の「3.2 FT232H Pin Description」の表の MPSSEの桁を参照。
よって、ftdi_layout_signalの設定値0x80はAD5端子にあたると推測できる。
でも、つながなくても動いてるなぁ… 🤔

デバッグ

Visual Studio Codeのデバッグサイドバーを開き、デバッグターゲットに「PIO Debug」を選択し、実行ボタン(三角アイコン)をクリックすると ターゲットの依存関係にしたがってbuildを行い、ダウンロード、実行(app_main()の先頭で一旦停止)を行う。
なお、デバッグターゲットに「PIO Debug (skip Pre-Debug)」だと、buildは行わず ダウンロード~のみ行う。 「PIO Debug (without uploading)」ダウンロードも行わず、実行のみ。
通常は「PIO Debug」を選んでおけば問題ない。

変数の確認やブレークポイントの設定、実行制御(go, step over, step in,…)などは他の環境と大差ない。
レジスタダンプも動いてるっぽい。
コールスタックも動いてるっぽい。
メモリと逆アセンブルは動かし方分からんかった…

[!NOTE] コンソール入出力もVSCodeで上記のシリアルモニタ起動で送受信を行うことができるが、
外部ツール(TeraTermなど)でモニタすることが可能。
Flashの書き換えがJTAG経由なので、シリアルポートを他のツールがつかんだままでも大丈夫みたい。

[!WARNING] プログラムを実行すると、コンソールにBrownout detector was triggeredと表示されてリセットを繰り返すことがある。
これは、電圧低下を検出したことによるfail safeのリセット動作らしい。
Wi-Fiを有効化したとき、モジュールに流れる電流が10mA程度→100~200mA程度(実測値)に上がる。
モジュールを接続しているUSB Hubにたくさんのデバイスを接続していると、電流を確保できなくなって 電圧が低下することがある模様。
この場合、USB Hubに接続されている他のデバイスを取り外すか、セルフパワードHubに交換すると正常に動作するようになる。
あと、使用するUSBケーブルのインピーダンスが大きいと電流増加で電圧が低下するので、大電流対応のUSBケーブルを使用しましょう。