diff --git a/extras/board_spec/test/board_spec.test.js b/extras/board_spec/test/board_spec.test.js index 3d6f687b..c20ca1fd 100644 --- a/extras/board_spec/test/board_spec.test.js +++ b/extras/board_spec/test/board_spec.test.js @@ -661,7 +661,7 @@ test("explicit fallback takes precedence over detector candidates", async (t) => const selection = /bool board_detected = \(board != board_t::board_unknown\);([\s\S]*?)_board = board;/.exec(source)?.[1]; assert.ok(selection); const explicit = selection.indexOf("board = cfg.fallback_board;"); - const candidate = selection.indexOf("board = Display.getBoardCandidate();"); + const candidate = selection.indexOf("board = Display.getCandidateBoard();"); const build = selection.indexOf("board = _default_fallback_board();"); assert.ok(explicit >= 0 && explicit < candidate && candidate < build); assert.match(selection, /if \(!board_detected\)/); diff --git a/extras/board_spec/test/detect_attempt.test.js b/extras/board_spec/test/detect_attempt.test.js index a84f20fe..f8c85752 100644 --- a/extras/board_spec/test/detect_attempt.test.js +++ b/extras/board_spec/test/detect_attempt.test.js @@ -16,7 +16,7 @@ test("production attempt yields before construction and preserves adoption and r const compat = body(main, "board_t M5GFX::autodetect("); const projection = main.slice(main.indexOf(" const auto board = outcome.setup_succeeded"), main.indexOf("#if defined ( ARDUINO_M5STACK_ATOM )", main.indexOf(" const auto board = outcome.setup_succeeded"))); const header = await fs.readFile(path.join(src, "M5GFX.h"), "utf8"); - const candidateGetter = body(header, "board_t getBoardCandidate("); + const candidateGetter = body(header, "board_t getCandidateBoard("); await compileRun(` #include "board_detect/detect_session.hpp" #include @@ -70,7 +70,7 @@ struct holder_t {int* get() {return nullptr;}}; struct M5GFX { board_t _board=board_t::board_unknown,_board_candidate=board_t::board_unknown; board_t getBoard() const {return _board;} - board_t getBoardCandidate() const ${candidateGetter} + board_t getCandidateBoard() const ${candidateGetter} void project(const board_detect::detect_outcome_t& outcome) { ${projection} } struct {board_t fallback_board=board_t::board_unknown;} _detect_config; holder_t _panel_last; @@ -97,7 +97,7 @@ int main() { const bool success=failure==0 && !yields; M5GFX standalone;standalone.project(out); assert(standalone.getBoard()==(success?board_t::member:board_t::board_unknown)); - assert(standalone.getBoardCandidate()==(provisional&&!success?board_t::member:board_t::board_unknown)); + assert(standalone.getCandidateBoard()==(provisional&&!success?board_t::member:board_t::board_unknown)); assert(out.setup_succeeded==success); assert(out.verdict==(provisional ? verdict_t::candidate : verdict_t::confirmed)); assert(out.candidate_kind==(provisional ? candidate_kind_t::provisional : candidate_kind_t::none)); @@ -133,7 +133,7 @@ int main() { assert(yielded.attempts==5&&!yielded.setup_succeeded&&yielded.result.candidate==&preferred_desc.def); assert(yielded.candidate_kind==candidate_kind_t::provisional&&yielded.verdict==verdict_t::candidate); M5GFX projected;projected.project(yielded); - assert(projected.getBoard()==board_t::board_unknown&&projected.getBoardCandidate()==static_cast(2)); + assert(projected.getBoard()==board_t::board_unknown&&projected.getCandidateBoard()==static_cast(2)); assert(construct_count==0&&adopt_count==0&&rollback_count==5&&commit_count==0); assert(!should_persist_detection(yielded,0)); refined_member=nullptr; diff --git a/extras/board_spec/test/fixed_board.test.js b/extras/board_spec/test/fixed_board.test.js index 58c5e01e..2896275a 100644 --- a/extras/board_spec/test/fixed_board.test.js +++ b/extras/board_spec/test/fixed_board.test.js @@ -256,7 +256,7 @@ enum class board_t{board_unknown=0,fixed=1,fallback=2,candidate=3,default_board= int warnings;void Log(int,const char*,unsigned,unsigned){++warnings;} struct display_t { board_t fixed,adopted,candidate; - board_t getFixedBoard(){return fixed;}board_t getBoard(){return adopted;}board_t getBoardCandidate(){return candidate;} + board_t getFixedBoard(){return fixed;}board_t getBoard(){return adopted;}board_t getCandidateBoard(){return candidate;} }; struct unified_t { display_t Display;struct {board_t fallback_board;} cfg; diff --git a/src/M5GFX.cpp b/src/M5GFX.cpp index 55d2c2bd..586a88b1 100644 --- a/src/M5GFX.cpp +++ b/src/M5GFX.cpp @@ -129,7 +129,7 @@ namespace m5gfx #if defined ( ESP_PLATFORM ) - // 判別 transaction の候補機種 I2C プローブはソフトウェアポートを使う。 + // Candidate I2C probes during detection use a software port. static constexpr int_fast16_t probe_i2c_port = -1; static constexpr std::uint32_t m5pm1_i2c_freq = 100000; diff --git a/src/M5GFX.h b/src/M5GFX.h index 0b2b6b7f..88034369 100644 --- a/src/M5GFX.h +++ b/src/M5GFX.h @@ -59,9 +59,19 @@ namespace m5gfx { using namespace lgfx; + /// Board detection settings. Pass them to M5GFX::setDetectConfig() before the + /// first init(); once a board is set up, init() returns early and does not use them. struct detect_config_t { + /// Used when detection identifies the family but not the member. If this board + /// is a possible member it is set up; otherwise detection fails and the member + /// is reported by getCandidateBoard(). Not used when the board is confirmed or + /// nothing is detected. board_t fallback_board = board_t::board_unknown; + /// Skips detection and sets up this board. init() fails if the board is not + /// supported on this chip (getFixedBoard() stays unknown) or its display setup + /// fails (getFixedBoard() still reports it). Never saved to NVS. + /// On SDL it takes precedence over M5GFX_BOARD. board_t fixed_board = board_t::board_unknown; }; //---------------------------------------------------------------------------- @@ -220,9 +230,15 @@ namespace m5gfx using LGFXBase::drawBitmap; static M5GFX* getInstance(void) { return _instance; } - board_t getBoardCandidate(void) const { return _board_candidate; } + /// Weak guess from the last init() when no board was set up; board_unknown otherwise. + /// The guessed board is not set up. + board_t getCandidateBoard(void) const { return _board_candidate; } + /// Board accepted from detect_config_t::fixed_board by the last init(), even if + /// its display setup failed; board_unknown otherwise. board_t getFixedBoard(void) const { return _fixed_board; } + /// Stores the settings for the next detection. See detect_config_t. void setDetectConfig(const detect_config_t& config); + /// Returns the stored settings, as requested. const detect_config_t& getDetectConfig(void) const { return _detect_config; } void progressBar(int x, int y, int w, int h, uint8_t val); diff --git a/src/lgfx/v1/misc/enum.hpp b/src/lgfx/v1/misc/enum.hpp index 39c8e773..9a6ad28a 100644 --- a/src/lgfx/v1/misc/enum.hpp +++ b/src/lgfx/v1/misc/enum.hpp @@ -480,10 +480,11 @@ namespace lgfx enum error_t { unknown_err - , invalid_arg // 引数が範囲外等でinvalidな場合のエラー; - , connection_lost // 通信が切断されたり正しく行えない場合のエラー; - , mode_mismatch // I2C通信が書込みモード時に読込みを指示するなど不一致な操作を行った場合のエラー; - , periph_device_err // ペリフェラルが動作していない等のエラー; + , invalid_arg // an argument is out of range or otherwise invalid + , connection_lost // communication was lost or could not be completed (e.g. no ACK) + , mode_mismatch // the call does not match the transaction state: reading while writing (or the reverse), + // a nested beginTransaction, or a call from a task that does not own the I2C transaction + , periph_device_err // the peripheral is not working }; //---------------------------------------------------------------------------- diff --git a/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp b/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp index 73134cdd..3ccbc551 100644 --- a/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp +++ b/src/lgfx/v1/platforms/esp32/Bus_SPI.hpp @@ -25,8 +25,8 @@ Original Source: #elif __has_include() #include #endif -// SPI の DMA が AXI 経由のチップ (ESP32-P4) はディスクリプタが 16 バイト (8 バイト境界)。判定は IDF の spi_dma_desc_t と同じ。 -// 境界は MALLOC_CAP_DMA の確保で満たされる (IDF がキャッシュ行に揃える) +// On chips whose SPI DMA goes through AXI (ESP32-P4) a descriptor is 16 bytes (8-byte aligned), as IDF's spi_dma_desc_t. +// MALLOC_CAP_DMA allocations satisfy the alignment (IDF aligns them to the cache line). #if defined ( SOC_GDMA_BUS_AXI ) && defined ( SOC_GDMA_TRIG_PERIPH_SPI2_BUS ) && ( SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI ) #define LGFX_SPI_DMA_DESC_ALIGN8 #endif @@ -59,21 +59,21 @@ Original Source: #endif -// Segmented-Configure-Transfer (SCT) を持つチップか。 -// SPI の 1 トランザクションは 18 bit 長 (32 KB) が上限で、超える転送はセグメントごとに CPU が完了を待って -// 再起動する必要がある。SCT を持つチップでは CONF バッファを DMA チェーンに挟むことでハードが自力で次の -// セグメントへ進めるので、CPU は開始時の 1 回だけで済む。 -// 対象は SCT と GDMA の両方を持つチップ (S3 / C3 / C6 / C2 / H2)。S2 は SCT を持つが DMA が別系統なので対象外。 -// この判定はクラスのレイアウトを左右するので SDK のヘッダだけで決める (ユーザー定義マクロで変えると翻訳単位間で -// sizeof(Bus_SPI) が食い違う)。無効化したいときは LGFX_SPI_SCT_DISABLE を Bus_SPI.inl を含む翻訳単位に与える -// (実行時の判定 _sct_ok だけが偽になり、レイアウトは変わらない) +// Whether the chip has Segmented-Configure-Transfer (SCT). +// One SPI transaction is limited to an 18-bit length (32 KB); longer transfers otherwise need the CPU to wait for +// and restart each segment. With SCT a CONF buffer placed in the DMA chain lets the hardware move to the next +// segment by itself, so the CPU starts the transfer only once. +// Targets chips with both SCT and GDMA (S3 / C3 / C6 / C2 / H2). S2 has SCT but a different DMA, so it is excluded. +// This decides the class layout, so only SDK headers are used (a user macro could make sizeof(Bus_SPI) differ +// between translation units). To disable SCT, define LGFX_SPI_SCT_DISABLE for the translation unit that includes +// Bus_SPI.inl (only the runtime flag _sct_ok becomes false; the layout does not change). #if __has_include() #include #endif #if __has_include() #include #endif -#undef LGFX_SPI_SCT // 内部用。外から与えられても SDK の判定で上書きする (再定義警告を避ける) +#undef LGFX_SPI_SCT // Internal. Overridden by the SDK check even if given from outside (avoids a redefinition warning) #undef LGFX_SPI_SCT_PERIPH // (host): whether that SPI host supports SCT #if defined ( SOC_GDMA_SUPPORTED ) #if defined ( SOC_SPI_SCT_SUPPORTED ) && SOC_SPI_SCT_SUPPORTED && defined ( SOC_SPI_SCT_SUPPORTED_PERIPH ) @@ -241,8 +241,8 @@ namespace lgfx void _setup_dma_desc_links(const uint8_t *data, int32_t len); #if defined ( LGFX_SPI_SCT ) - // Segmented-Configure-Transfer (SCT): 32 KB を超える 1 本の DMA 転送を、セグメントごとの CONF バッファを - // DMA チェーンに挟むことでハードに連続実行させる (CPU は開始時の 1 回だけ)。 + // Segmented-Configure-Transfer (SCT): runs a single DMA transfer over 32 KB in hardware by placing a CONF buffer + // per segment in the DMA chain (the CPU starts it only once). bool _sct_start(const uint8_t* data, uint32_t length); bool _sct_start_chain(dma_desc_t* first, uint32_t total); void _sct_put_conf(dma_desc_t*& d, uint32_t*& conf, uint32_t user, uint32_t user1, uint32_t seg, bool last); @@ -251,9 +251,9 @@ namespace lgfx volatile uint32_t* _spi_slave_reg = nullptr; dma_desc_t* _sct_desc = nullptr; uint32_t* _sct_conf = nullptr; - uint32_t _sct_desc_capacity = 0; // _sct_desc の確保済み descriptor 数 (_sct_conf はその半分 + 1) - bool _sct_active = false; // SCT で開始した転送が未後始末 (usr_conf を落とす必要がある) - bool _sct_ok = false; // このバスで SCT を使うか (GP-SPI2 のみ、LGFX_SPI_SCT_DISABLE で偽) + uint32_t _sct_desc_capacity = 0; // descriptors allocated for _sct_desc (_sct_conf holds half of that + 1) + bool _sct_active = false; // a transfer started with SCT is not cleaned up yet (usr_conf must be cleared) + bool _sct_ok = false; // whether this bus uses SCT (GP-SPI2 only; false with LGFX_SPI_SCT_DISABLE) #endif config_t _cfg; @@ -265,13 +265,15 @@ namespace lgfx volatile uint32_t* _spi_user_reg = nullptr; volatile uint32_t* _spi_dma_out_link_reg = nullptr; volatile uint32_t* _spi_dma_out_conf0_reg = nullptr; - // 外部 RAM を転送元にするときに先頭を揃える単位。GDMA の外部メモリブロック幅 (16/32/64 B、IDF の版で異なる) の最大値。 - // 揃えるのは転送の先頭 descriptor だけ (後続 descriptor の境界端数は停止しないことを確認済み) + // Alignment of the start of a transfer from external RAM: the largest GDMA external memory block size + // (16/32/64 B, differs by IDF version). Only the first descriptor is aligned (an unaligned tail at later + // descriptor boundaries was confirmed not to stall). static constexpr uint32_t dma_ext_align = 64; - // 外部 RAM を DMA の転送元にしてよいか (init で決める。偽なら外部 RAM は CPU 経路) + // Whether external RAM may be a DMA source (decided in init; if false, external RAM goes through the CPU). bool _psram_dma_ok = true; - // 転送開始前に GDMA チャネルをリセットし、前回の異常終了 (underflow 等) の残留データを持ち越さない。 - // 残留は転送元を問わず次の転送を壊すので、外部 RAM 対応チップに限らず GDMA を持つ全チップ・全転送で行う + // Reset the GDMA channel before each transfer so leftover data from an earlier abort (underflow etc.) is not carried over. + // Leftovers corrupt the next transfer whatever its source, so this is done on every GDMA chip and every transfer, + // not only on chips that support external RAM. void dma_channel_reset(void); // AXI_DMA and the newer AHB_DMA take the descriptor address in a register of its own (unused elsewhere) volatile uint32_t* _spi_dma_out_link2_reg = nullptr; diff --git a/src/lgfx/v1/platforms/esp32/Bus_SPI.inl b/src/lgfx/v1/platforms/esp32/Bus_SPI.inl index dff48cec..b94f9870 100644 --- a/src/lgfx/v1/platforms/esp32/Bus_SPI.inl +++ b/src/lgfx/v1/platforms/esp32/Bus_SPI.inl @@ -86,8 +86,8 @@ Contributors: #include #endif #include -// flash 暗号化の有無 (外部 RAM を DMA に載せてよいかの判定に使う)。IDF 6 では esp_flash_encrypt.h が -// コンポーネント外から見えず、代替 API が efuse 側にある +// Whether flash encryption is enabled (decides whether external RAM may be a DMA source). On IDF 6, +// esp_flash_encrypt.h is not visible outside its component; the replacement API is in efuse. #if defined ( ESP_IDF_VERSION ) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) && __has_include() #include #define LGFX_FLASH_ENCRYPTION_ENABLED() esp_efuse_is_flash_encryption_enabled() @@ -95,7 +95,7 @@ Contributors: #include #define LGFX_FLASH_ENCRYPTION_ENABLED() esp_flash_encryption_enabled() #else - #define LGFX_FLASH_ENCRYPTION_ENABLED() true // 判定できなければ暗号化ありとみなす (安全側) + #define LGFX_FLASH_ENCRYPTION_ENABLED() true // assume encryption when it cannot be determined (safe side) #endif #if defined ESP_IDF_VERSION_MAJOR && ESP_IDF_VERSION_MAJOR >= 5 @@ -184,7 +184,7 @@ Contributors: #endif #if defined ( LGFX_SPI_SCT ) - // GDMA の TX FIFO 残量。S3 は 3 段 (L1/L2/L3)、C3 / C6 / H2 は 1 段; + // Free space in the GDMA TX FIFO. S3 has 3 levels (L1/L2/L3), C3 / C6 / H2 have 1. #if defined ( GDMA_OUTFIFO_CNT_L2_CH0_S ) #define DMA_OUTFIFO_CNT(st) ((((st) >> GDMA_OUTFIFO_CNT_L1_CH0_S) & GDMA_OUTFIFO_CNT_L1_CH0_V) \ + (((st) >> GDMA_OUTFIFO_CNT_L2_CH0_S) & GDMA_OUTFIFO_CNT_L2_CH0_V) \ @@ -194,8 +194,8 @@ Contributors: #define DMA_OUTFIFO_CNT(st) (((st) >> GDMA_OUTFIFO_CNT_CH0_S) & GDMA_OUTFIFO_CNT_CH0_V) #define DMA_OUTFIFO_FULL_CH GDMA_OUTFIFO_FULL_CH0 #endif - // Bus_SPI.hpp が soc_caps から SCT 対象と判定したのにレジスタ定義が揃わない構成 (将来のチップ / SDK) では、 - // 実装の代わりに空の定義を置いて実行時に使わない (hpp 側の inline が _sct_end を参照するのでシンボルは要る) + // When Bus_SPI.hpp selects SCT from soc_caps but the register definitions are missing (a future chip / SDK), + // put empty definitions here instead and never use SCT at runtime (the inline code in the hpp refers to _sct_end, so the symbol is needed). #if !defined ( DMA_OUTFIFO_CNT ) || !defined ( DMA_OUTLINK_START_CH0 ) || !defined ( SPI_USR_CONF ) || !defined ( SPI_DMA_SEG_MAGIC_VALUE_S ) \ || !defined ( SPI_DMA_AFIFO_RST ) || !defined ( SPI_CS_SETUP_TIME_S ) #define LGFX_SPI_SCT_STUB @@ -220,8 +220,8 @@ namespace lgfx { //---------------------------------------------------------------------------- - // DMA が読むディスクリプタ・CONF バッファの書き込みを、DMA を起動するレジスタ書き込みより後ろへ - // コンパイラに動かさせない (IDF の dma_descriptor_t の欄は volatile でない) + // Keep the compiler from moving writes to descriptors and CONF buffers read by DMA after the register write + // that starts DMA (IDF's dma_descriptor_t fields are not volatile). static inline void dma_desc_fence(void) { __asm__ __volatile__ ("" ::: "memory"); } @@ -419,14 +419,14 @@ namespace lgfx #endif #if defined ( LGFX_SPI_SCT ) && !defined ( LGFX_SPI_SCT_STUB ) - // SCT のチェーン構築で共通に使う定数 + // Constants shared by SCT chain construction namespace { - // PREP の長さ (SPI クロック数 - 1)。CONF から DOUT への切替に必要な時間は実測で 4 APB クロック固定 - // (SPI 20 MHz の 1 クロック = 4 APB は成立、26.7/40/50/60 MHz の 1 クロックは不成立で 2 クロックなら成立)。 - // 分周比 1 (SPI = APB) でも 4 クロック必要なので 8 クロックにして 2 倍の余裕を取る + // PREP length (SPI clocks - 1). Switching from CONF to DOUT takes a fixed 4 APB clocks (measured): + // 1 SPI clock at 20 MHz (= 4 APB) works; 1 clock at 26.7/40/50/60 MHz fails and 2 clocks work. + // A divider of 1 (SPI = APB) still needs 4 clocks, so 8 clocks are used for a 2x margin. constexpr uint32_t sct_setup_time = 7; - constexpr uint32_t sct_seg_bytes = (SPI_MS_DATA_BITLEN + 1) >> 3; // 32 KB (1 トランザクションの上限) + constexpr uint32_t sct_seg_bytes = (SPI_MS_DATA_BITLEN + 1) >> 3; // 32 KB (limit of one transaction) constexpr uint32_t sct_conf_words = 4; // bitmap + USER + USER1 + MS_DLEN constexpr uint32_t sct_conf_bytes = sct_conf_words * 4; constexpr uint32_t sct_magic = 0xA; @@ -447,7 +447,7 @@ namespace lgfx #if defined ( LGFX_SPI_SCT_DISABLE ) || defined ( LGFX_SPI_SCT_STUB ) _sct_ok = false; #else - _sct_ok = LGFX_SPI_SCT_PERIPH(_cfg.spi_host) != 0; // S3 / C3 / C6 等は GP-SPI2 のみ + _sct_ok = LGFX_SPI_SCT_PERIPH(_cfg.spi_host) != 0; // S3 / C3 / C6 etc.: GP-SPI2 only #endif #endif _spi_user_reg = reg(SPI_USER_REG( spi_port)); @@ -536,8 +536,8 @@ namespace lgfx _spi_dma_out_conf0_reg = reg(DMA_OUT_CONF0_CH0_REG + assigned_dma_ch * SIZE_OF_DMA_OUT_CH); #endif #if defined ( LGFX_PSRAM_DMA_CAPABLE ) && !(defined ( SOC_AHB_GDMA_VERSION ) && SOC_AHB_GDMA_VERSION == 1) - // AHB GDMA v2 / AXI DMA は flash 暗号化が有効だと外部 RAM の DMA にアドレスと長さの 16 B 整列を要求する - // (IDF の gdma_config_transfer)。長さは揃えないので、その構成では外部 RAM を CPU 経路に回す + // AHB GDMA v2 / AXI DMA require 16-byte alignment of address and length for external RAM DMA when flash + // encryption is enabled (IDF's gdma_config_transfer). Lengths are not aligned here, so external RAM uses the CPU. _psram_dma_ok = !LGFX_FLASH_ENCRYPTION_ENABLED(); #endif #if defined ( DMA_OUT_LINK_ADDR_CH0_REG ) @@ -586,8 +586,8 @@ namespace lgfx #endif if (!_inited) return; _inited = false; - // DMA / SCT の後始末が残っている (= endTransaction を経ずに来た = まだ自分のトランザクション内) ときだけ、 - // 転送完了を待って後始末する。残っていなければバスは既に手放している可能性があるので触らない + // Only when DMA / SCT cleanup is pending (we arrived without endTransaction, so still inside our own transaction), + // wait for the transfer and clean up. Otherwise the bus may already be released, so leave it alone. if (_clear_dma_reg #if defined ( LGFX_SPI_SCT ) || _sct_active @@ -664,7 +664,7 @@ namespace lgfx auto spi_port = _spi_port; (void)spi_port; #if defined ( LGFX_SPI_SCT ) && !defined ( LGFX_SPI_SCT_STUB ) - // PREP の長さ (SPI クロック数 - 1)。DMA 転送と SCT の各セグメントで CS_SETUP を立てて使う + // PREP length (SPI clocks - 1). Set with CS_SETUP for DMA transfers and each SCT segment. if (_sct_ok) { auto user1 = reg(SPI_USER1_REG(spi_port)); @@ -740,7 +740,7 @@ namespace lgfx #endif #if defined LGFX_USE_QSPI - // USER は SPI_USR 待ちの後に読む (SCT 進行中は conf 由来の値が見える) + // Read USER after waiting for SPI_USR (during SCT it shows values loaded from the conf). if( _is_quad_spi) *qspi_user_reg = (*qspi_user_reg & ~(uint32_t)(SPI_FWRITE_QUAD | SPI_CS_SETUP)); #endif @@ -786,7 +786,7 @@ namespace lgfx } #endif #if defined LGFX_USE_QSPI - // USER は SPI_USR 待ちの後に読む (SCT 進行中は conf 由来の値が見える) + // Read USER after waiting for SPI_USR (during SCT it shows values loaded from the conf). if( _is_quad_spi) *qspi_user_reg = (*qspi_user_reg & ~(uint32_t)SPI_CS_SETUP) | SPI_FWRITE_QUAD; #endif @@ -1085,9 +1085,9 @@ namespace lgfx if (!heap_capable_dma(data)) { writeBytes(data, length, dc, false); return; } bool synced = false; #if defined ( LGFX_PSRAM_DMA_CAPABLE ) - // 外部 RAM が転送元のときは、先頭を DMA ブロック境界に揃えてから DMA に載せる。 - // 先頭 descriptor の境界までの端数が 8〜15 バイトだと GDMA が停止する挙動が S3 で確定しており、 - // 端数は CPU 書き込みで先に送る。揃えた後の残りが 1 ブロック未満の短い転送も同じ理由で CPU に回す。 + // When the source is external RAM, align its start to a DMA block boundary before handing it to DMA. + // On S3 the GDMA is confirmed to stall when the first descriptor has 8 to 15 bytes before the boundary, + // so those bytes are written by the CPU first. A short remainder under one block goes to the CPU for the same reason. if (esp_ptr_external_ram(data)) { uint32_t head = (uint32_t)(-(intptr_t)data) & (dma_ext_align - 1); @@ -1130,7 +1130,7 @@ namespace lgfx auto dma = reg(SPI_DMA_CONF_REG(_spi_port)); *dma = 0; /// Clear previous transfer #if defined ( LGFX_SPI_SCT ) && !defined ( LGFX_SPI_SCT_STUB ) - // TRM 30.5.7 の手順どおり転送前に AFIFO をリセットする (IDF の spi_hal も毎転送前に行う) + // Reset AFIFO before the transfer as in TRM 30.5.7 (IDF's spi_hal also does it before every transfer). *dma = SPI_DMA_AFIFO_RST | SPI_BUF_AFIFO_RST | SPI_RX_AFIFO_RST; #endif uint32_t len = ((length - 1) & ((SPI_MS_DATA_BITLEN)>>3)) + 1; @@ -1145,11 +1145,11 @@ namespace lgfx set_write_len(len << 3); *_gpio_reg_dc[dc] = _mask_reg_dc; #if defined ( LGFX_SPI_SCT ) - // SCT の直後に始める通常の DMA 転送は、AFIFO をリセットしても先頭 1 バイトが直前ストリームの残留値に - // 置き換わる (実測)。SCT のセグメントと同じく PREP を挟むと消えるので、この 1 転送だけ CS_SETUP を立てる - // (次の CPU バッファ経路の転送の入口で外す)。CPU バッファ経路の転送を間に挟んだ後は起きない + // A normal DMA transfer started right after SCT replaces its first byte with a leftover from the previous stream, + // even after an AFIFO reset (measured). A PREP removes it as for SCT segments, so CS_SETUP is set for this one + // transfer (cleared at the start of the next CPU-buffer transfer). It does not happen after a CPU-buffer transfer. if (after_sct) { *_spi_user_reg |= SPI_CS_SETUP; } - else if (*_spi_user_reg & SPI_CS_SETUP) { *_spi_user_reg &= ~(uint32_t)SPI_CS_SETUP; } // 直前の DMA 転送が立てた分を引き継がない + else if (*_spi_user_reg & SPI_CS_SETUP) { *_spi_user_reg &= ~(uint32_t)SPI_CS_SETUP; } // do not inherit what the previous DMA transfer set #endif // DMA準備完了待ち; @@ -1312,7 +1312,7 @@ label_start: return; } - // 書き戻しは投入時 (投入後のバッファ変更は DMA 契約上そもそも不可) + // Written back when queued (changing the buffer after queuing is not allowed by the DMA contract anyway). dma_cache_sync(data, length); _dma_queue_bytes += length; size_t index = _dma_queue_size; @@ -1370,8 +1370,8 @@ label_start: // Unused on targets that start the queue some other way. __attribute__((unused)) dma_desc_t* first = &_dma_queue[0]; #if defined ( LGFX_PSRAM_DMA_CAPABLE ) - // 先頭 descriptor が外部 RAM なら、先頭を DMA ブロック境界に揃える (writeBytes と同じ理由)。 - // 端数は CPU で送り、descriptor の先頭と長さを詰める。短い descriptor は丸ごと CPU で送って次へ進む。 + // If the first descriptor is in external RAM, align its start to a DMA block boundary (same reason as writeBytes). + // The head is sent by the CPU and the descriptor's start and length are trimmed. A short descriptor is sent whole by the CPU. for (;;) { auto buf = (const uint8_t*)first->buffer; @@ -1379,7 +1379,7 @@ label_start: uint32_t head = (uint32_t)(-(intptr_t)buf) & (dma_ext_align - 1); uint32_t len = first->dw0.length; if (_psram_dma_ok && head == 0 && len >= dma_ext_align) { break; } - if (!_psram_dma_ok || head >= len || len - head < dma_ext_align) // 短い descriptor は丸ごと CPU + if (!_psram_dma_ok || head >= len || len - head < dma_ext_align) // a short descriptor goes whole to the CPU { // Send in register-sized pieces: a longer non-DMA writeBytes copies through // the flip buffer, which a later queued getDMABuffer() buffer may share. @@ -1392,7 +1392,7 @@ label_start: writeBytes((const uint8_t*)piece, n, true, false); } _dma_queue_bytes -= len; - if (first->dw0.suc_eof) { _dma_queue_bytes = 0; return; } // 全部 CPU で送った + if (first->dw0.suc_eof) { _dma_queue_bytes = 0; return; } // everything was sent by the CPU first = (dma_desc_t*)first->next; continue; } @@ -1412,9 +1412,9 @@ label_start: dma_cache_sync(_dmadesc, sizeof(dma_desc_t) * _dmadesc_size); #if defined ( LGFX_SPI_SCT ) - bool after_sct = _sct_active; // dc_control が後始末で落とすので先に控える + bool after_sct = _sct_active; // dc_control clears it during cleanup, so save it first #endif - dc_control(true); // SCT の後始末 (_sct_end) もここで済む + dc_control(true); // this also finishes SCT cleanup (_sct_end) *_spi_dma_out_link_reg = 0; #if defined ( LGFX_SPI_SCT ) @@ -1423,14 +1423,14 @@ label_start: uint32_t total = _dma_queue_bytes; _dma_queue_bytes = 0; if (_sct_start_chain(first, total)) { return; } - _dma_queue_bytes = total; // 構築に失敗したら従来のループ経路へ + _dma_queue_bytes = total; // fall back to the loop path if building fails } #endif #if defined ( SOC_GDMA_SUPPORTED ) && defined ( DMA_OUTLINK_START_CH0 ) dma_channel_reset(); auto dma = reg(SPI_DMA_CONF_REG(_spi_port)); #if defined ( LGFX_SPI_SCT ) && !defined ( LGFX_SPI_SCT_STUB ) - *dma = SPI_DMA_AFIFO_RST | SPI_BUF_AFIFO_RST | SPI_RX_AFIFO_RST; // writeBytes と同じ理由 + *dma = SPI_DMA_AFIFO_RST | SPI_BUF_AFIFO_RST | SPI_RX_AFIFO_RST; // same reason as writeBytes #endif #if defined ( DMA_OUT_LINK_ADDR_CH0_REG ) *_spi_dma_out_link2_reg = ((uint32_t)(first)); @@ -1443,7 +1443,7 @@ label_start: uint32_t len = ((_dma_queue_bytes - 1) & ((SPI_MS_DATA_BITLEN)>>3)) + 1; set_write_len(len << 3); #if defined ( LGFX_SPI_SCT ) - if (after_sct) { *_spi_user_reg |= SPI_CS_SETUP; } // 理由は writeBytes 側と同じ + if (after_sct) { *_spi_user_reg |= SPI_CS_SETUP; } // same reason as writeBytes else if (*_spi_user_reg & SPI_CS_SETUP) { *_spi_user_reg &= ~(uint32_t)SPI_CS_SETUP; } #endif // DMA準備完了待ち; @@ -1752,17 +1752,18 @@ label_start: bool Bus_SPI::_sct_start_chain(dma_desc_t*, uint32_t) { return false; } bool Bus_SPI::_sct_start(const uint8_t*, uint32_t) { return false; } #elif defined ( LGFX_SPI_SCT ) - // 32 KB を超える 1 本の DMA 転送を Segmented-Configure-Transfer (SCT、TRM 30.5.8.5) で送る。CPU は開始時の 1 回だけ。 - // チェーン = [conf0][data0 ≤32 KB][conf1][data1]...。conf は bitmap + USER + USER1 + MS_DLEN の 4 word - // (bitmap で選んだレジスタだけを詰める compact 形式)。IDF の spi_master と同じく usr_conf は SPI_USR より前に立て、 - // 全セグメントを CONF 経由で始める。最終セグメントの conf だけ CONF_NXT を落とす。 - // データ descriptor はセグメント境界で必ず切る (余りが次の conf として読まれる)。 + // Send a single DMA transfer over 32 KB with Segmented-Configure-Transfer (SCT, TRM 30.5.8.5). The CPU starts it only once. + // Chain = [conf0][data0 <=32 KB][conf1][data1]... Each conf is 4 words: bitmap + USER + USER1 + MS_DLEN + // (compact form holding only the registers selected by the bitmap). As in IDF's spi_master, usr_conf is set before + // SPI_USR and every segment starts through CONF. Only the last segment's conf clears CONF_NXT. + // Data descriptors are always split at segment boundaries (otherwise the rest is read as the next conf). // - // 各 conf で SPI_CS_SETUP を立てて CONF と DOUT の間に PREP (cs_setup、クロック出力無し) を挟む。CONF 直後に - // DOUT が始まると buf_tx_afifo の再充填が間に合わず、先頭 1 バイトが古い内容 (8 バイト前の値) で出る (S3 実測: - // 40 MHz で cs_setup_time=0 は化け、1 以上で消える)。ハード CS は使っていないので待ち時間としてだけ効く。 - // 転送後に残る usr_conf は次の転送の入口で _sct_end が落とす。USER の CS_SETUP は経路ごとに扱う: CPU バッファ経路の - // 入口で落とし、SCT 直後の通常 DMA 転送 1 本には意図的に持ち越す (writeBytes 参照)。 + // Each conf sets SPI_CS_SETUP to put a PREP (cs_setup, no clock output) between CONF and DOUT. If DOUT starts right + // after CONF, buf_tx_afifo is not refilled in time and the first byte is stale (the value 8 bytes earlier) (measured on + // S3: at 40 MHz cs_setup_time=0 corrupts, 1 or more is clean). Hardware CS is not used, so it only acts as a delay. + // The usr_conf left after the transfer is cleared by _sct_end at the start of the next transfer. CS_SETUP in USER is + // handled per path: cleared at the start of a CPU-buffer transfer, and intentionally kept for the one normal DMA + // transfer right after SCT (see writeBytes). void Bus_SPI::_sct_end(void) { @@ -1770,7 +1771,7 @@ label_start: *_spi_slave_reg &= ~(uint32_t)SPI_USR_CONF; } - // 1 セグメント分の conf を書き、その descriptor を繋ぐ + // Write the conf for one segment and link its descriptor void Bus_SPI::_sct_put_conf(dma_desc_t*& d, uint32_t*& conf, uint32_t user, uint32_t user1, uint32_t seg, bool last) { conf[0] = sct_bitmap; @@ -1783,12 +1784,12 @@ label_start: ++d; conf += sct_conf_words; } - // 構築済みのチェーンで SCT を開始する。descriptor の末尾は呼び出し側で閉じておく。 - // 偽を返したときは開始しておらず、呼び出し側は従来経路で送る + // Start SCT with a built chain. The caller closes the end of the descriptor chain. + // On false nothing was started and the caller sends through the normal path. bool Bus_SPI::_sct_exec(void) { dma_desc_fence(); - // IDF の spi_master と同じ順: GDMA リセット → AFIFO リセット → TX_ENA → GDMA 起動 → UPDATE → USR + // Same order as IDF's spi_master: GDMA reset -> AFIFO reset -> TX_ENA -> GDMA start -> UPDATE -> USR dma_channel_reset(); auto dma = reg(SPI_DMA_CONF_REG(_spi_port)); *dma = SPI_DMA_AFIFO_RST | SPI_BUF_AFIFO_RST | SPI_RX_AFIFO_RST; @@ -1798,19 +1799,19 @@ label_start: *reg(SPI_DMA_INT_CLR_REG(_spi_port)) = SPI_DMA_SEG_TRANS_DONE_INT_CLR | SPI_SEG_MAGIC_ERR_INT_CLR; *_spi_slave_reg = (sct_magic << SPI_DMA_SEG_MAGIC_VALUE_S) | SPI_USR_CONF; _sct_active = true; - // magic / usr_conf を SPI クロック域へ同期させてから USR を立てる (同時に書くと magic 不一致で即終了する) + // Sync magic / usr_conf to the SPI clock domain before setting USR (writing them together ends at once on a magic mismatch) *_spi_cmd_reg = SPI_UPDATE; while (*_spi_cmd_reg & SPI_UPDATE) {} - // 先頭の conf だけでなく data0 の先頭が GDMA の FIFO に届くまで待つ。CONF は一瞬で終わるので、転送元が - // 外部 RAM だと data0 の到着前に DOUT が始まって underflow する (実測)。GDMA は消費が無ければ FIFO を - // 満たすまで先読みするので、FIFO 残量が conf を超えるまで待てば data0 が来たと分かる + // Wait until not only the first conf but the start of data0 reaches the GDMA FIFO. CONF finishes instantly, so with + // an external RAM source DOUT can start before data0 arrives and underflow (measured). GDMA prefetches until the FIFO + // is full when nothing is consumed, so once the FIFO holds more than the conf, data0 has arrived. for (int guard = 100000; --guard; ) { uint32_t st = *_spi_dma_outstatus_reg; if (DMA_OUTFIFO_CNT(st) > sct_conf_bytes + 32 || (st & DMA_OUTFIFO_FULL_CH)) { exec_spi(); return true; } } - // data0 が届かない (DMA 側の異常)。SCT を取り消して従来経路 (セグメントごとに CPU が再起動する) に任せる。 - // レジスタは _sct_exec に入る前の状態へ戻す (usr_conf 0、TX_ENA 0、GDMA リセット) + // data0 did not arrive (a DMA fault). Cancel SCT and leave it to the normal path (the CPU restarts each segment). + // Restore the registers to their state before _sct_exec (usr_conf 0, TX_ENA 0, GDMA reset). *_spi_dma_out_link_reg = 0; dma_channel_reset(); *dma = 0; @@ -1820,8 +1821,8 @@ label_start: return false; } - // 行キュー (addDMAQueue で積んだ descriptor の連結) を SCT で送る。32 KB 境界が descriptor の途中に来るときは - // その descriptor を 2 つに割る。失敗したら false を返して従来のループ経路へ落とす + // Send the row queue (descriptors linked by addDMAQueue) with SCT. A descriptor crossing a 32 KB boundary is + // split in two. On failure returns false and the caller falls back to the loop path. bool Bus_SPI::_sct_start_chain(dma_desc_t* first, uint32_t total) { uint32_t src_count = 0; @@ -1860,7 +1861,7 @@ label_start: src_off += n; seg -= n; if (src_off >= src->dw0.length) { src = (dma_desc_t*)src->next; src_off = 0; } } - if (seg) { return false; } // キューの合計長と total が食い違う (起きない想定) + if (seg) { return false; } // the queue total does not match total (not expected) } --d; d->dw0.suc_eof = 1; diff --git a/src/lgfx/v1/platforms/esp32/common.hpp b/src/lgfx/v1/platforms/esp32/common.hpp index 6cac9a41..615ad743 100644 --- a/src/lgfx/v1/platforms/esp32/common.hpp +++ b/src/lgfx/v1/platforms/esp32/common.hpp @@ -53,12 +53,12 @@ Original Source: bool esp_ptr_dma_capable(const void*) { return false; } #endif -// 外部 RAM (PSRAM) を DMA の転送元にできる条件: チップが対応し、書き戻しキャッシュを同期する API がある +// External RAM (PSRAM) can be a DMA source when the chip supports it and the cache writeback sync API exists #if __has_include() #include #endif -// (GDMA を持つチップに限る。SPI 内蔵 DMA の世代は未検証のため従来どおり CPU 経路。 -// esp_cache.h は IDF 5.1 にもあるが方向フラグは 5.2 以降なので、フラグの有無で判定する) +// (chips with GDMA only; generations with the SPI's own DMA are untested and keep the CPU path. +// esp_cache.h exists in IDF 5.1, but its direction flags are 5.2 and later, so the flags are checked) #if defined ( SOC_PSRAM_DMA_CAPABLE ) && defined ( SOC_CACHE_WRITEBACK_SUPPORTED ) && defined ( SOC_GDMA_SUPPORTED ) && __has_include() #include #if defined ( ESP_CACHE_MSYNC_FLAG_DIR_C2M ) @@ -108,7 +108,7 @@ Original Source: #define SPI_DMA_CONF_REG(i) (REG_SPI_BASE(i) + 0x30) #undef SPI_W0_REG #define SPI_W0_REG(i) (REG_SPI_BASE(i) + 0x98) - // SCT (Segmented-Configure-Transfer) で使う分 + // Used by SCT (Segmented-Configure-Transfer) #undef SPI_USER1_REG #define SPI_USER1_REG(i) (REG_SPI_BASE(i) + 0x14) #undef SPI_DMA_INT_CLR_REG @@ -173,10 +173,10 @@ namespace lgfx static inline void* heap_alloc_psram(size_t length) { return heap_caps_malloc((length + 3) & ~3, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } static inline void heap_free(void* buf) { heap_caps_free(buf); } #if defined ( LGFX_PSRAM_DMA_CAPABLE ) - /// DMA が到達できるメモリか (内部 DMA RAM または外部 RAM)。外部 RAM は転送前に dma_cache_sync が必要 + /// Whether DMA can reach this memory (internal DMA RAM or external RAM). External RAM needs dma_cache_sync before a transfer. static inline bool heap_capable_dma(const void* ptr) { return esp_ptr_dma_capable(ptr) || esp_ptr_dma_ext_capable(ptr); } - /// 外部 RAM 上のバッファを DMA が読む前に、CPU キャッシュの内容をメモリへ書き戻す - /// (内部 RAM もキャッシュ越しになるチップでは、DMA に渡すものすべてが対象) + /// Write the CPU cache back to memory before DMA reads a buffer in external RAM + /// (on chips where internal RAM is also cached, everything handed to DMA). static inline void dma_cache_sync(const void* ptr, size_t len) { #if defined ( SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE ) && SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE diff --git a/src/lgfx/v1/platforms/esp32/common.inl b/src/lgfx/v1/platforms/esp32/common.inl index 6090d1f3..74bfa1bc 100644 --- a/src/lgfx/v1/platforms/esp32/common.inl +++ b/src/lgfx/v1/platforms/esp32/common.inl @@ -2085,9 +2085,9 @@ namespace lgfx { #if LGFX_LP_I2C_NUM > 0 && SOC_RTCIO_PIN_COUNT > 0 if (soft_i2c_valid_port(i2c_port)) - { // 低電力ポートのルーティングはリセットを跨いで残るため、前回実行が - // LP ポートとして使ったピンをソフトポートで取り直す場合にも返却が - // 必要になる。(see gpio::release_lp_pad) + { // Low-power port routing survives a reset, so a pin used as an LP port by + // the previous run must also be released when it is reclaimed as a soft + // port. (see gpio::release_lp_pad) auto& ctx = soft_i2c_ctx(i2c_port); gpio::release_lp_pad(ctx.pin_sda); gpio::release_lp_pad(ctx.pin_scl); diff --git a/src/lgfx/v1/platforms/esp32s3/Bus_Parallel16.inl b/src/lgfx/v1/platforms/esp32s3/Bus_Parallel16.inl index 0d44883f..cf0cef24 100644 --- a/src/lgfx/v1/platforms/esp32s3/Bus_Parallel16.inl +++ b/src/lgfx/v1/platforms/esp32s3/Bus_Parallel16.inl @@ -385,7 +385,7 @@ namespace lgfx // Copy inaccessible sources into the internal DMA buffer. if (use_dma && !heap_capable_dma(data)) { use_dma = false; } #if defined ( LGFX_PSRAM_DMA_CAPABLE ) - // 外部 RAM を LCD_CAM の DMA に直接載せる経路は未検証。既存の内部バッファ経由 (memcpy → DMA) に落とす + // Putting external RAM directly on LCD_CAM DMA is untested. Fall back to the internal buffer (memcpy -> DMA). if (use_dma && esp_ptr_external_ram(data)) { use_dma = false; } #endif auto dev = _dev; diff --git a/src/lgfx/v1/platforms/esp32s3/Bus_Parallel8.inl b/src/lgfx/v1/platforms/esp32s3/Bus_Parallel8.inl index 71ca8f2c..657a2b08 100644 --- a/src/lgfx/v1/platforms/esp32s3/Bus_Parallel8.inl +++ b/src/lgfx/v1/platforms/esp32s3/Bus_Parallel8.inl @@ -359,7 +359,7 @@ namespace lgfx // Copy inaccessible sources into the internal DMA buffer. if (use_dma && !heap_capable_dma(data)) { use_dma = false; } #if defined ( LGFX_PSRAM_DMA_CAPABLE ) - // 外部 RAM を LCD_CAM の DMA に直接載せる経路は未検証。既存の内部バッファ経由 (memcpy → DMA) に落とす + // Putting external RAM directly on LCD_CAM DMA is untested. Fall back to the internal buffer (memcpy -> DMA). if (use_dma && esp_ptr_external_ram(data)) { use_dma = false; } #endif if (length <= 4)