diff --git a/boards/m5stack/m5stack_cores3/m5stack_cores3-pinctrl.dtsi b/boards/m5stack/m5stack_cores3/m5stack_cores3-pinctrl.dtsi index 30e2222479d60..8764000f7c77f 100644 --- a/boards/m5stack/m5stack_cores3/m5stack_cores3-pinctrl.dtsi +++ b/boards/m5stack/m5stack_cores3/m5stack_cores3-pinctrl.dtsi @@ -57,6 +57,20 @@ }; }; + mipi_dbi_cmd_data_input: mipi_dbi_cmd_data_input { + group1 { + pinmux = ; + input-enable; + }; + }; + + mipi_dbi_cmd_data_output: mipi_dbi_cmd_data_output { + group1 { + pinmux = ; + output-enable; + }; + }; + i2c0_default: i2c0_default { group1 { pinmux = , diff --git a/boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi b/boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi index 3bc1cd3965f18..113747f5c360e 100644 --- a/boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi +++ b/boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi @@ -44,37 +44,41 @@ input = <&ft6336_touch>; }; - mipi_dbi: mipi_dbi { - compatible = "zephyr,mipi-dbi-spi"; - spi-dev = <&spi2>; - write-only; - dc-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; - reset-gpios = <&aw9523b_gpio 13 GPIO_ACTIVE_LOW>; - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - - ili9342c: ili9342c@0 { - compatible = "ilitek,ili9342c"; - reg = <0>; - mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE"; - mipi-max-frequency = <40000000>; - vin-supply = <&vcc_bl>; - pixel-format = ; - ifmode = [e0]; - disctrl = [08 82 1d 04]; - pwctrl1 = [12 12]; - pwctrl2 = [03]; - vmctrl1 = [f2]; - pgamctrl = [00 0c 11 04 11 08 37 89 4c 06 0c 0a 2e 34 0f]; - ngamctrl = [00 0b 11 05 13 09 33 67 48 07 0e 0b 2e 33 0f]; - display-inversion; - width = <320>; - height = <240>; - rotation = <0>; - status = "okay"; - }; - }; + mipi_dbi: mipi_dbi { + compatible = "zephyr,mipi-dbi-spi"; + spi-dev = <&spi2>; + dc-gpios = <&gpio0 35 GPIO_ACTIVE_HIGH>; + cmd-data-tristate; + pinctrl-0 = <&mipi_dbi_cmd_data_input>; + pinctrl-1 = <&mipi_dbi_cmd_data_output>; + pinctrl-names = "cmd_data_input", "cmd_data_output"; + reset-gpios = <&aw9523b_gpio 13 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + ili9342c: ili9342c@0 { + compatible = "ilitek,ili9342c"; + reg = <0>; + mipi-mode = "MIPI_DBI_MODE_SPI_3WIRE"; + mipi-max-frequency = <40000000>; + duplex = ; + vin-supply = <&vcc_bl>; + pixel-format = ; + ifmode = [e0]; + disctrl = [08 82 1d 04]; + pwctrl1 = [12 12]; + pwctrl2 = [03]; + vmctrl1 = [f2]; + pgamctrl = [00 0c 11 04 11 08 37 89 4c 06 0c 0a 2e 34 0f]; + ngamctrl = [00 0b 11 05 13 09 33 67 48 07 0e 0b 2e 33 0f]; + display-inversion; + width = <320>; + height = <240>; + rotation = <0>; + status = "okay"; + }; + }; }; &usb_serial { @@ -245,15 +249,18 @@ }; &spi2 { - status = "okay"; - #address-cells = <1>; - #size-cells = <0>; - pinctrl-0 = <&spim2_default>; - pinctrl-names = "default"; - clock-frequency = <40000000>; - dma-enabled; - cs-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>, /* LCD */ - <&gpio0 4 GPIO_ACTIVE_LOW>; /* TF-CARD */ + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-0 = <&spim2_default>; + pinctrl-names = "default"; + clock-frequency = <40000000>; + half-duplex; + sio; + line-idle-low; + dma-enabled; + cs-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>, /* LCD */ + <&gpio0 4 GPIO_ACTIVE_LOW>; /* TF-CARD */ sd0: sd@1 { compatible = "zephyr,sdhc-spi-slot"; diff --git a/doc/m5gfx_coreS3_spi.cpp b/doc/m5gfx_coreS3_spi.cpp new file mode 100644 index 0000000000000..8a22d59d238cc --- /dev/null +++ b/doc/m5gfx_coreS3_spi.cpp @@ -0,0 +1,112 @@ +/* + * Extracts from M5GFX (https://github.com/m5stack/M5GFX) relevant to the + * CoreS3 SPI wiring and register sequencing. Copied verbatim for + * documentation and comparison purposes. + */ + +// --- src/M5GFX.cpp (panel/bus setup) --- +struct Panel_M5StackCoreS3 : public lgfx::Panel_ILI9342 { + Panel_M5StackCoreS3(void) { + _cfg.pin_cs = GPIO_NUM_3; + _cfg.invert = true; + _cfg.offset_rotation = 3; + + _rotation = 1; // default rotation + } + + void rst_control(bool level) override { + uint8_t bits = level ? (1<<5) : 0; + uint8_t mask = level ? ~0 : ~(1<<5); + // LCD_RST + lgfx::i2c::writeRegister8(i2c_port, aw9523_i2c_addr, 0x03, bits, mask, i2c_freq); + } + + void cs_control(bool flg) override { + lgfx::Panel_ILI9342::cs_control(flg); + // CS操作時にGPIO35の役割を切り替える (MISO or D/C); + *(volatile uint32_t*)( flg + ? GPIO_ENABLE1_W1TC_REG + : GPIO_ENABLE1_W1TS_REG + ) = 1u << (GPIO_NUM_35 & 31); + } +}; + +// Autodetect branch for CoreS3 (bus_cfg setup) + bus_cfg.pin_mosi = GPIO_NUM_37; + bus_cfg.pin_miso = GPIO_NUM_35; + bus_cfg.pin_sclk = GPIO_NUM_36; + bus_cfg.pin_dc = GPIO_NUM_35;// MISOとLCD D/CをGPIO35でシェアしている; + bus_cfg.spi_mode = 0; + bus_cfg.spi_3wire = true; + bus_spi->config(bus_cfg); + bus_spi->init(); + id = _read_panel_id(bus_spi, GPIO_NUM_3); + if ((id & 0xFF) == 0xE3) { + bus_cfg.freq_write = 40000000; + bus_cfg.freq_read = 16000000; + bus_spi->config(bus_cfg); + auto p = new Panel_M5StackCoreS3(); + p->bus(bus_spi); + _panel_last.reset(p); + } + +// --- src/lgfx/v1/platforms/esp32/Bus_SPI.cpp (register writes) --- +void Bus_SPI::beginTransaction(void) { + uint32_t freq_apb = getApbFrequency(); + uint32_t clkdiv_write = _clkdiv_write; + if (_last_freq_apb != freq_apb) { + _last_freq_apb = freq_apb; + _clkdiv_read = FreqToClockDiv(freq_apb, _cfg.freq_read); + clkdiv_write = FreqToClockDiv(freq_apb, _cfg.freq_write); + _clkdiv_write = clkdiv_write; + dc_control(true); + pinMode(_cfg.pin_dc, pin_mode_t::output); + } + + auto spi_mode = _cfg.spi_mode; + uint32_t pin = (spi_mode & 2) ? SPI_CK_IDLE_EDGE : 0; + pin = pin +#if defined ( SPI_CS0_DIS ) + | SPI_CS0_DIS +#endif +#if defined ( SPI_CS1_DIS ) + | SPI_CS1_DIS +#endif +#if defined ( SPI_CS2_DIS ) + | SPI_CS2_DIS +#endif +#if defined ( SPI_CS3_DIS ) + | SPI_CS3_DIS +#endif +#if defined ( SPI_CS4_DIS ) + | SPI_CS4_DIS +#endif +#if defined ( SPI_CS5_DIS ) + | SPI_CS5_DIS +#endif + ; + + if (_cfg.use_lock) spi::beginTransaction(_cfg.spi_host); + + *_spi_user_reg = _user_reg; + auto spi_port = _spi_port; + (void)spi_port; + writereg(SPI_PIN_REG(spi_port), pin); + writereg(SPI_CLOCK_REG(spi_port), clkdiv_write); +#if defined ( SPI_UPDATE ) + *_spi_cmd_reg = SPI_UPDATE; +#endif +} + +void Bus_SPI::beginRead(void) { + uint32_t pin = (_cfg.spi_mode & 2) ? SPI_CK_IDLE_EDGE : 0; + uint32_t user = ((_cfg.spi_mode == 1 || _cfg.spi_mode == 2) ? SPI_CK_OUT_EDGE | SPI_USR_MISO : SPI_USR_MISO) + | (_cfg.spi_3wire ? SPI_SIO : 0); + dc_control(true); + *_spi_user_reg = user; + writereg(SPI_PIN_REG(_spi_port), pin); + writereg(SPI_CLOCK_REG(_spi_port), _clkdiv_read); +#if defined ( SPI_UPDATE ) + *_spi_cmd_reg = SPI_UPDATE; +#endif +} diff --git a/doc/m5stack_core2_spi_comparison.md b/doc/m5stack_core2_spi_comparison.md new file mode 100644 index 0000000000000..3983b73d5ff2f --- /dev/null +++ b/doc/m5stack_core2_spi_comparison.md @@ -0,0 +1,39 @@ +# M5Stack Core2 SPI configuration comparison + +## Zephyr board support (`boards/m5stack/m5stack_core2/m5stack_core2_procpu.dts`) +- The display panel is exposed via a MIPI DBI wrapper on `spi3`, using GPIO0.15 as DC and an AXP192 GPIO line as reset, with the panel bound to chip select 0 on the SPI bus.【F:boards/m5stack/m5stack_core2/m5stack_core2_procpu.dts†L57-L77】 +- `spi3` runs at 20 MHz with DMA enabled and offers two chip-select lines (GPIO0.5 and GPIO0.4). The first CS serves the LCD, while the second drives the `zephyr,sdhc-spi-slot` node for the microSD card, which is also limited to 20 MHz.【F:boards/m5stack/m5stack_core2/m5stack_core2_procpu.dts†L207-L229】 +- Pin control ties `spi3` to SCLK = GPIO18, MOSI = GPIO23 (preset low), and MISO = GPIO38 without additional drive-strength tuning.【F:boards/m5stack/m5stack_core2/m5stack_core2-pinctrl.dtsi†L38-L48】 + +## M5Unified library defaults (`M5Unified/src/M5Unified.cpp`) +- The `_pin_table_spi_sd` table selects SCLK = GPIO18, MOSI = GPIO23, MISO = GPIO38, and CS = GPIO4 for the Core2 SD interface, mirroring the base wiring used in Zephyr: + + ```c++ + { board_t::board_M5StackCore2 , GPIO_NUM_18, GPIO_NUM_23, GPIO_NUM_38, GPIO_NUM_4 }, + ``` + 【F:doc/m5stack_core2_spi_comparison.md†L11-L13】 +- During setup the library boosts the GPIO18/19/23 drive strength to 40 mA and enables pull-ups to keep high-speed SPI links (20 MHz for SD, 80 MHz for external ModuleDisplay) reliable—behaviour not present in the Zephyr device tree: + + ```c++ + for (auto gpio: (const gpio_num_t[]){ GPIO_NUM_18, GPIO_NUM_19, GPIO_NUM_23 }) { + *(volatile uint32_t*)(GPIO_PIN_MUX_REG[gpio]) = tmp | FUN_DRV_M; // gpio drive current set to 40mA. + gpio_pulldown_dis(gpio); + gpio_pullup_en(gpio); + } + ``` + 【F:doc/m5stack_core2_spi_comparison.md†L18-L22】 +- When M5Unified detects Atomic Speaker hardware it remaps the SD SPI pins to GPIO7/6/8 and disables the on-board IMU/RTC to avoid I²C conflicts, showing that its SPI allocation is dynamic depending on accessories: + + ```c++ + _get_pin_table[sd_spi_sclk] = GPIO_NUM_7; + _get_pin_table[sd_spi_copi] = GPIO_NUM_6; + _get_pin_table[sd_spi_cipo] = GPIO_NUM_8; + cfg.internal_imu = false; + cfg.internal_rtc = false; + ``` + 【F:doc/m5stack_core2_spi_comparison.md†L28-L32】 + +## Notable differences +1. **Electrical tuning** – Zephyr leaves the ESP32 pins at their default drive, while M5Unified explicitly increases drive strength and enables pull-ups to stabilise high-speed SPI transfers. +2. **Chip-select usage** – Zephyr reserves two CS lines on `spi3` (LCD and SD); M5Unified manages only the SD CS in its pin table and handles the LCD bus configuration through M5GFX. +3. **Accessory awareness** – Zephyr’s wiring is static, whereas M5Unified can reassign SPI pins (and even disable conflicting peripherals) when certain modular accessories are detected. diff --git a/doc/m5stack_cores3_build_notes.md b/doc/m5stack_cores3_build_notes.md new file mode 100644 index 0000000000000..64bb94933e798 --- /dev/null +++ b/doc/m5stack_cores3_build_notes.md @@ -0,0 +1,50 @@ +# M5Stack CoreS3 Build Notes + +このメモでは、`m5stack_cores3/esp32s3/procpu` ボード向けに Zephyr をビルドする際に必要となった追加ステップと、現状の制限点を整理する。 + +## 事前準備 + +1. 仮想環境を有効化する。 + ```sh + source .venv/bin/activate + ``` +2. ネイティブビルドで必要になる 32bit ライブラリと `file` コマンドをインストールする。 + ```sh + sudo apt-get update + sudo apt-get install -y gcc-multilib file + ``` + これにより `west build -b native_sim ...` が通るようになる。 + +## Zephyr SDK の整備 + +1. ホストツールをインストールする。 + ```sh + /root/zephyr-sdk-0.17.4/setup.sh -h + ``` + 対話モードの場合は、最後の `Install host tools` で `y` を選択する。 + +2. ESP32-S3 用ツールチェーンを追加する。 + ```sh + /root/zephyr-sdk-0.17.4/setup.sh -t xtensa-espressif_esp32s3_zephyr-elf + ``` + +3. Espressif HAL のパスと Python ツールを用意する。 + ```sh + export ESP_IDF_PATH=$PWD/modules/hal/espressif + pip install esptool>=5.0.2 + ``` + +## west build の呼び出し例 + +`ESP_IDF_PATH` を CMake 引数に渡してビルドする。 + +```sh +west build -b m5stack_cores3/esp32s3/procpu samples/basic/blinky --pristine \ + -- -DESP_IDF_PATH=$PWD/modules/hal/espressif +``` + +## 既知の問題 + +上記の手順を踏んでも `xtensa/config/core.h` が見つからないためコンパイルが停止する。Zephyr SDK 0.17.4 同梱の `xtensa-espressif_esp32s3_zephyr-elf` には Xtensa コア設定ヘッダが含まれておらず、Espressif が提供する Xtensa ツールチェーンから `xtensa/config` ツリーを追加で導入する必要がある。 + +現状のリポジトリには当該ヘッダが含まれていないため、Espressif IDF の `tools/xtensa-esp32s3-elf` などから該当ファイル群を取得するまでビルドは完了しない点に留意すること。 diff --git a/doc/m5stack_cores3_display_spi.md b/doc/m5stack_cores3_display_spi.md new file mode 100644 index 0000000000000..cd2006168b6d0 --- /dev/null +++ b/doc/m5stack_cores3_display_spi.md @@ -0,0 +1,55 @@ +# M5Stack CoreS3 display SPI configuration comparison + +## Zephyr board support (`boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi`) +- The on-board ILI9342C panel is exposed through the `mipi_dbi` shim on `spi2`, using chip-select 0 and GPIO35 for the D/C signal; `cmd-data-tristate` forces the pad back to an input between transfers so the shared TF-card on chip-select 1 can still drive MISO, while reset remains on the AW9523 expander.【F:boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi†L20-L64】【F:boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi†L228-L264】 +- `spi2` now advertises half-duplex 3-wire mode via the `half-duplex;` and `sio;` flags alongside `line-idle-low;`, keeping the 40 MHz clock and GPIO routing through the matrix (SCLK = GPIO36, MOSI = GPIO37, MISO/D/C = GPIO35).【F:boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi†L228-L247】【F:boards/m5stack/m5stack_cores3/m5stack_cores3-pinctrl.dtsi†L33-L48】 +- The ESP32-S3 master driver now derives `hal_dev->half_duplex` from `SPI_HALF_DUPLEX`, so `spi_ll_set_half_duplex` clears `USER.doutdin` while `spi_ll_set_sio_mode` honours the three-wire flag; the existing timing calculator still resolves the divider from the 80 MHz APB clock to `SPI_CLOCK_REG = 0x00001001`.【F:drivers/spi/spi_esp32_spim.c†L406-L435】【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L31-L69】【F:modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h†L564-L591】【F:modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h†L702-L735】 +- The `mipi_dbi` SPI backend configures GPIO35 as an input at boot, switches it to an output only while asserting the command/data level, then returns it to high impedance when the transfer finishes—mirroring the CoreS3 wiring where D/C and MISO share the same trace.【F:drivers/mipi_dbi/mipi_dbi_spi.c†L74-L121】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L190-L319】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L520-L573】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L608-L715】 + +### Expected SPI2 register snapshot after `spi_hal_setup_device` + +| Register | Value | Notes | +| --- | --- | --- | +| `SPI_CLOCK_REG(2)` | `0x00001001` | 80 MHz APB source, 40 MHz target -> `clkcnt_n = 1`, `clkcnt_l = 1`, `clkdiv_pre = 0`.【F:modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h†L702-L735】 | +| `SPI_USER_REG(2)` | `0x08020000` | Half-duplex (`doutdin = 0`), SIO enabled (`sio = 1`), write phase active (`usr_mosi = 1`).【F:modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h†L564-L591】【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L181-L205】【F:modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h†L72-L140】 | +| `SPI_USER1_REG(2)` | `0x00000000` | CS setup/hold and dummy/address stages remain disabled until a transfer configures them.【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L31-L108】 | +| `SPI_USER2_REG(2)` | `0x00000000` | Command phase populated by each transaction as needed.【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L70-L108】 | +| `SPI_CTRL_REG(2)` | `0x00000000` | `line-idle-low` keeps MOSI/MISO idle low (`d_pol = q_pol = 0`).【F:drivers/spi/spi_esp32_spim.c†L471-L492】【F:modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h†L40-L80】 | +| `SPI_CMD_REG(2)` | `0x00000000` | No command pending immediately after device setup.【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L31-L69】 | +| `SPI_MS_DLEN_REG(2)` | `0x00000000` | Bit length loaded per transaction, zero when idle.【F:modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h†L120-L140】 | +| `SPI_DMA_CONF_REG(2)` | `0x00180000` | DMA helpers armed; channels remain idle until a transfer starts.【F:modules/hal/espressif/components/hal/spi_hal.c†L45-L76】【F:modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h†L200-L240】 | + +## M5Unified runtime SPI programming (`M5GFX`) +- `Panel_M5StackCoreS3` drives the LCD via GPIO3 for chip-select and reconfigures GPIO35 between output (D/C) and input (MISO) every time CS toggles, matching the board’s shared trace.【F:doc/m5gfx_coreS3_spi.cpp†L9-L26】 +- The CoreS3 autodetect path on `spi2` binds MOSI = GPIO37, SCLK = GPIO36, MISO/D/C = GPIO35, explicitly sets `bus_cfg.spi_3wire = true`, and programs 40 MHz write / 16 MHz read clocks before instantiating the panel.【F:doc/m5gfx_coreS3_spi.cpp†L29-L43】 +- `Bus_SPI::beginTransaction` writes the divider computed by `FreqToClockDiv` and loads `_user_reg = SPI_USR_MOSI` (`0x08000000`), while `SPI_PIN_REG` disables the unused hardware chip-selects because the panel uses a GPIO CS.【F:doc/m5gfx_coreS3_spi.cpp†L46-L74】 +- `Bus_SPI::beginRead` swaps `SPI_USER` to `SPI_USR_MISO | SPI_SIO` (`0x10020000`) so reads also run over the three-wire bus after the D/C line is released.【F:doc/m5gfx_coreS3_spi.cpp†L76-L86】 +- Because the read path toggles `SPI_SIO` rather than leaving the line unused, M5Unified programmes the controller for true three-wire half-duplex operation—the MISO pad actively shares GPIO35 with the D/C latch instead of behaving like a four-wire bus with an unconnected data input.【F:doc/m5gfx_coreS3_spi.cpp†L9-L86】 + +## M5Unified defaults (`src/M5Unified.cpp`) +- The library builds pin maps per board; for CoreS3 it routes the SPI clock/data lines to GPIO36/37/35 and SD CS to GPIO4—the same bus that Zephyr binds to the display/SD pair—while other tables describe how the M-Bus headers and accessory ports share those pins.【F:doc/m5unified_M5Unified.cpp†L54-L143】【F:doc/m5unified_M5Unified.cpp†L200-L246】 +- Board-specific helpers reuse these tables at runtime, allowing the firmware to remap connectors or peripherals (e.g. Module audio or Atomic accessories) before the display driver in M5GFX claims the bus.【F:doc/m5unified_M5Unified.cpp†L247-L318】【F:doc/m5unified_M5Unified.cpp†L1264-L1411】 + +## Notable differences +1. **Static vs dynamic wiring** – Zephyr now mirrors the CoreS3 hardware by sharing GPIO35 between D/C and MISO through `cmd-data-tristate` and SIO mode, while M5Unified still performs runtime remapping to accommodate accessories on the M-Bus connectors.【F:boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi†L20-L64】【F:doc/m5unified_M5Unified.cpp†L54-L143】 +2. **Register sequencing** – Both stacks drive the bus from an 80 MHz APB source, yielding `SPI_CLOCK_REG = 0x00001001` and switching `SPI_USER` between `0x0802_0000` (writes) and `0x1002_0000` (reads) for the three-wire data phases; Zephyr now accomplishes this through DT/HAL configuration, whereas M5Unified programs the registers directly in `Bus_SPI` as it probes the panel.【F:modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h†L702-L735】【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L181-L205】【F:doc/m5gfx_coreS3_spi.cpp†L46-L86】 + +## Register values versus transaction sequencing + +Simply matching the register snapshot is not sufficient to keep the CoreS3 panel alive. Two pieces of runtime sequencing must also track what M5Unified does: + +* **HAL half-duplex bookkeeping.** The ESP32 master driver has to propagate `SPI_HALF_DUPLEX` so that the HAL clears `USER.doutdin`, enables SIO, and sizes the RX bit length before each transaction (`spi_hal_setup_device`/`spi_hal_setup_trans`). Without that handshake the hardware never switches between the write (`0x0802_0000`) and read (`0x1002_0000`) `SPI_USER` patterns even if the static register values are preloaded.【F:drivers/spi/spi_esp32_spim.c†L392-L449】【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L31-L108】 +* **Command/data GPIO choreography.** The MIPI-DBI shim mirrors the M5Unified sequence by packing the command bit into 9-bit writes, then tri-stating GPIO35 before issuing the read phase so the panel can drive the shared trace. After every transfer the driver reverts the GPIO to an input, meaning subsequent reads begin with the line already released. Leaving the GPIO as an output stalls the bus even if the register block is correct.【F:drivers/mipi_dbi/mipi_dbi_spi.c†L63-L144】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L368-L433】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L600-L663】 なお `mipi_dbi_spi_configure_te()` はティアリングエフェクト端子を入力に設定するだけで、D/C 線(GPIO35)の入出力を切り替えるトライステート制御とは別物である点に注意してください。【F:drivers/mipi_dbi/mipi_dbi_spi.c†L661-L715】 + +Consequently, Zephyr needs both the register configuration and the transaction-level choreography; changing register constants alone does not reproduce the working M5Unified behaviour. + +## If the SD card slot is ignored +Even when the TF/SD socket on CS1 is not supported, the CoreS3 panel still depends on two hardware behaviours that mainline Zephyr lacked before these changes: + +* **Half-duplex SIO signalling remains mandatory.** GPIO35 physically multiplexes the D/C latch with the controller’s MISO pad, so the peripheral must be configured for 3-wire operation to match the register programming that M5Unified issues through `spi_hal_setup_device` (`USER.doutdin = 0`, `USER.sio = 1`). This still requires both the devicetree flags (`half-duplex;` and `sio;`) and the ESP32 master driver forwarding `SPI_HALF_DUPLEX` into the HAL state so `spi_ll_set_half_duplex()` and `spi_ll_set_sio_mode()` run.【F:boards/m5stack/m5stack_cores3/m5stack_cores3_procpu_common.dtsi†L228-L247】【F:drivers/spi/spi_esp32_spim.c†L406-L435】【F:modules/hal/espressif/components/hal/spi_hal_iram.c†L181-L205】【F:doc/m5gfx_coreS3_spi.cpp†L29-L86】 +* **The D/C GPIO must still be tri-stated for readback.** CoreS3 ties the panel’s `SDO` pin and the command/data latch to the same copper run on GPIO35; when a write starts, firmware drives the desired D/C level, then releases the pad so the panel can push its status or pixel data back during the read phase. M5Unified achieves this by converting GPIO35 to an input each time chip-select goes high, before reasserting it for the next command.【F:doc/m5gfx_coreS3_spi.cpp†L9-L43】 Without a matching `cmd-data-tristate` path in Zephyr’s MIPI-DBI shim, returning to input mode never happens, so the LCD’s `SDO` output is electrically blocked and read transfers stall even if no SD device is present.【F:drivers/mipi_dbi/mipi_dbi_spi.c†L52-L121】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L520-L573】 `mipi_dbi_spi_configure_te()` が扱うティアリングエフェクト GPIO は常に入力として確保されており、ここで D/C 線を三態化しているわけではない。 + +## Why the tri-state choreography is required +The CoreS3 display wiring collapses the usual four SPI wires (MOSI, MISO, D/C, CS) into three physical signals by double-purposing GPIO35. During command writes the host drives MOSI while holding GPIO35 low for command mode or high for data mode; once the payload is complete the same GPIO must become high impedance so the panel can source its `SDO` signal on the next read. Leaving the pad configured as a push-pull output—even when no SD card is connected—prevents the LCD from ever seizing the line, so panel initialisation fails as soon as the driver tries to poll `TE`, `STATUS`, or read pixel data.【F:doc/m5gfx_coreS3_spi.cpp†L9-L86】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L52-L121】【F:drivers/mipi_dbi/mipi_dbi_spi.c†L520-L573】 Zephyr therefore mirrors M5Unified’s sequencing: drive the D/C level only while CS is asserted, revert to input immediately afterwards, and re-drive it just before the next transfer. + +Consequently, dropping SD card support only avoids the pin-sharing conflict on CS1—the LCD-specific half-duplex sequencing and D/C direction management remain prerequisites for reproducing the proven M5Unified register state. diff --git a/doc/m5unified_M5Unified.cpp b/doc/m5unified_M5Unified.cpp new file mode 100644 index 0000000000000..d276f549a3a69 --- /dev/null +++ b/doc/m5unified_M5Unified.cpp @@ -0,0 +1,2452 @@ +// Copyright (c) M5Stack. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#include "M5Unified.hpp" +#include "utility/PI4IOE5V6408_Class.hpp" + +#if !defined (M5UNIFIED_PC_BUILD) +#include +#include + +#if !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + #if __has_include () + #include + #elif __has_include () + #include + #endif +#endif + +#if __has_include () +#include +#endif + +#if __has_include () + #include + #if ESP_IDF_VERSION_MAJOR >= 4 + /// [[fallthrough]]; + #define NON_BREAK ;[[fallthrough]]; + #endif + +#endif +#endif + +/// [[fallthrough]]; +#ifndef NON_BREAK +#define NON_BREAK ; +#endif + +/// global instance. +m5::M5Unified M5; + +void __attribute((weak)) adc_power_acquire(void) +{ +#if !defined (M5UNIFIED_PC_BUILD) +#if defined (ESP_IDF_VERSION_VAL) + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(3, 3, 4) + adc_power_on(); + #endif +#else + adc_power_on(); +#endif +#endif +} + +namespace m5 +{ +int8_t M5Unified::_get_pin_table[pin_name_max]; + +#if defined (M5UNIFIED_PC_BUILD) + void M5Unified::_setup_pinmap(board_t) + { + std::fill(_get_pin_table, _get_pin_table + pin_name_max, 255); + } +#else +// ピン番号テーブル。 unknownをテーブルの最後に配置する。該当が無い場合はunknownの値が使用される。 +static constexpr const uint8_t _pin_table_i2c_ex_in[][5] = { + // In CL,DA, EX CL,DA +#if defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5StackCoreS3, GPIO_NUM_11,GPIO_NUM_12 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5StackCoreS3SE,GPIO_NUM_11,GPIO_NUM_12, GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5StampS3 , 255 ,255 , GPIO_NUM_15,GPIO_NUM_13 }, +{ board_t::board_M5Capsule , GPIO_NUM_10,GPIO_NUM_8 , GPIO_NUM_15,GPIO_NUM_13 }, +{ board_t::board_M5Dial , GPIO_NUM_12,GPIO_NUM_11 , GPIO_NUM_15,GPIO_NUM_13 }, +{ board_t::board_M5DinMeter , GPIO_NUM_12,GPIO_NUM_11 , GPIO_NUM_15,GPIO_NUM_13 }, +{ board_t::board_M5AirQ , GPIO_NUM_12,GPIO_NUM_11 , GPIO_NUM_15,GPIO_NUM_13 }, +{ board_t::board_M5Cardputer , 255 ,255 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5CardputerADV,GPIO_NUM_9 ,GPIO_NUM_8 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5VAMeter , GPIO_NUM_6 ,GPIO_NUM_5 , GPIO_NUM_9 ,GPIO_NUM_8 }, +{ board_t::board_M5AtomS3R , GPIO_NUM_0 ,GPIO_NUM_45 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5AtomS3RExt , GPIO_NUM_0 ,GPIO_NUM_45 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5AtomEchoS3R, GPIO_NUM_0 ,GPIO_NUM_45 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5AtomS3RCam , GPIO_NUM_0 ,GPIO_NUM_45 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5PaperS3 , GPIO_NUM_42,GPIO_NUM_41 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_M5StampPLC , GPIO_NUM_15,GPIO_NUM_13 , GPIO_NUM_1 ,GPIO_NUM_2 }, +{ board_t::board_unknown , GPIO_NUM_39,GPIO_NUM_38 , GPIO_NUM_1 ,GPIO_NUM_2 }, // AtomS3,AtomS3Lite,AtomS3U +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +{ board_t::board_unknown , 255 ,255 , GPIO_NUM_0 ,GPIO_NUM_1 }, +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +{ board_t::board_M5UnitC6L ,GPIO_NUM_8 ,GPIO_NUM_10 , 255 ,255 }, +{ board_t::board_ArduinoNessoN1,GPIO_NUM_8 ,GPIO_NUM_10 , GPIO_NUM_8 ,GPIO_NUM_10 }, +{ board_t::board_unknown , 255 ,255 , GPIO_NUM_1 ,GPIO_NUM_2 }, // NanoC6 +#elif defined (CONFIG_IDF_TARGET_ESP32P4) +{ board_t::board_M5Tab5 , GPIO_NUM_32,GPIO_NUM_31, GPIO_NUM_54,GPIO_NUM_53 }, // Tab5 +{ board_t::board_unknown , 255 ,255 , 255 ,255 }, +#else +{ board_t::board_M5Stack , GPIO_NUM_22,GPIO_NUM_21 , GPIO_NUM_22,GPIO_NUM_21 }, +{ board_t::board_M5Paper , GPIO_NUM_22,GPIO_NUM_21 , GPIO_NUM_32,GPIO_NUM_25 }, +{ board_t::board_M5TimerCam , GPIO_NUM_14,GPIO_NUM_12 , GPIO_NUM_13,GPIO_NUM_4 }, +{ board_t::board_M5AtomLite , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, +{ board_t::board_M5AtomMatrix , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, +{ board_t::board_M5AtomEcho , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, +{ board_t::board_M5AtomU , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, +{ board_t::board_M5AtomPsram , GPIO_NUM_21,GPIO_NUM_25 , GPIO_NUM_32,GPIO_NUM_26 }, +{ board_t::board_unknown , GPIO_NUM_22,GPIO_NUM_21 , GPIO_NUM_33,GPIO_NUM_32 }, // Core2,Tough,StickC,CoreInk,Station,StampPico +#endif +}; + +static constexpr const uint8_t _pin_table_port_bc[][5] = { + //pB p1,p2, pC p1,p2 +#if defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5StackCoreS3, GPIO_NUM_8 ,GPIO_NUM_9 , GPIO_NUM_18,GPIO_NUM_17 }, +{ board_t::board_M5StackCoreS3SE,GPIO_NUM_8,GPIO_NUM_9 , GPIO_NUM_18,GPIO_NUM_17 }, +{ board_t::board_M5Dial , GPIO_NUM_1 ,GPIO_NUM_2 , 255 ,255 }, +{ board_t::board_M5DinMeter , GPIO_NUM_1 ,GPIO_NUM_2 , 255 ,255 }, +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +{ board_t::board_M5UnitC6L ,GPIO_NUM_4 ,GPIO_NUM_5 , GPIO_NUM_4 ,GPIO_NUM_5 }, +{ board_t::board_ArduinoNessoN1,GPIO_NUM_4 ,GPIO_NUM_5 , GPIO_NUM_4 ,GPIO_NUM_5 }, +#elif defined (CONFIG_IDF_TARGET_ESP32P4) +{ board_t::board_M5Tab5 , GPIO_NUM_17,GPIO_NUM_52, GPIO_NUM_7 ,GPIO_NUM_6 }, // Tab5 +#else +{ board_t::board_M5Stack , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_16,GPIO_NUM_17 }, +{ board_t::board_M5StackCore2 , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_13,GPIO_NUM_14 }, +{ board_t::board_M5Paper , GPIO_NUM_33,GPIO_NUM_26 , GPIO_NUM_19,GPIO_NUM_18 }, +{ board_t::board_M5Station , GPIO_NUM_35,GPIO_NUM_25 , GPIO_NUM_13,GPIO_NUM_14 }, +#endif +{ board_t::board_unknown , 255 ,255 , 255 ,255 }, +}; + +static constexpr const uint8_t _pin_table_port_de[][5] = { + //pD p1,p2, pE p1,p2 +#if defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5StackCoreS3, 14,10, 18,17 }, +{ board_t::board_M5StackCoreS3SE,14,10,18,17 }, +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +#else +{ board_t::board_M5Stack , GPIO_NUM_34,GPIO_NUM_35 , GPIO_NUM_5 ,GPIO_NUM_13 }, +{ board_t::board_M5StackCore2 , GPIO_NUM_34,GPIO_NUM_35 , GPIO_NUM_27,GPIO_NUM_19 }, +{ board_t::board_M5Station , GPIO_NUM_36,GPIO_NUM_26 , GPIO_NUM_16,GPIO_NUM_17 }, // B2 / C2 +#endif +{ board_t::board_unknown , 255 ,255 , 255 ,255 }, +}; + +static constexpr const uint8_t _pin_table_spi_sd[][5] = { + // clk,mosi,miso,cs +#if defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5StackCoreS3, GPIO_NUM_36, GPIO_NUM_37, GPIO_NUM_35, GPIO_NUM_4 }, +{ board_t::board_M5StackCoreS3SE,GPIO_NUM_36,GPIO_NUM_37, GPIO_NUM_35, GPIO_NUM_4 }, +{ board_t::board_M5Capsule , GPIO_NUM_14, GPIO_NUM_12, GPIO_NUM_39, GPIO_NUM_11 }, +{ board_t::board_M5Cardputer , GPIO_NUM_40, GPIO_NUM_14, GPIO_NUM_39, GPIO_NUM_12 }, +{ board_t::board_M5CardputerADV,GPIO_NUM_40, GPIO_NUM_14, GPIO_NUM_39, GPIO_NUM_12 }, +{ board_t::board_M5PaperS3 , GPIO_NUM_39, GPIO_NUM_38, GPIO_NUM_40, GPIO_NUM_47 }, +{ board_t::board_M5StampPLC , GPIO_NUM_7, GPIO_NUM_8, GPIO_NUM_9, GPIO_NUM_10 }, +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +#elif defined (CONFIG_IDF_TARGET_ESP32P4) +{ board_t::board_M5Tab5 , GPIO_NUM_43,GPIO_NUM_44, GPIO_NUM_39, GPIO_NUM_42 }, +#else +{ board_t::board_M5Stack , GPIO_NUM_18, GPIO_NUM_23, GPIO_NUM_19, GPIO_NUM_4 }, +{ board_t::board_M5StackCore2 , GPIO_NUM_18, GPIO_NUM_23, GPIO_NUM_38, GPIO_NUM_4 }, +{ board_t::board_M5Paper , GPIO_NUM_14, GPIO_NUM_12, GPIO_NUM_13, GPIO_NUM_4 }, +#endif +{ board_t::board_unknown , 255 , 255 , 255 , 255 }, +}; + +static constexpr const uint8_t _pin_table_other0[][2] = { + //RGBLED +#if defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5AtomS3U , GPIO_NUM_35 }, +{ board_t::board_M5AtomS3Lite , GPIO_NUM_35 }, +{ board_t::board_M5StampS3 , GPIO_NUM_21 }, +{ board_t::board_M5StampPLC , GPIO_NUM_21 }, +{ board_t::board_M5AirQ , GPIO_NUM_21 }, +{ board_t::board_M5Dial , GPIO_NUM_21 }, +{ board_t::board_M5DinMeter , GPIO_NUM_21 }, +{ board_t::board_M5Capsule , GPIO_NUM_21 }, +{ board_t::board_M5Cardputer , GPIO_NUM_21 }, +{ board_t::board_M5CardputerADV,GPIO_NUM_21 }, +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +{ board_t::board_M5StampC3 , GPIO_NUM_2 }, +{ board_t::board_M5StampC3U , GPIO_NUM_2 }, +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +{ board_t::board_M5NanoC6 , GPIO_NUM_20 }, +{ board_t::board_M5UnitC6L , GPIO_NUM_2 }, +#else +{ board_t::board_M5Stack , GPIO_NUM_15 }, +{ board_t::board_M5StackCore2 , GPIO_NUM_25 }, +{ board_t::board_M5Station , GPIO_NUM_4 }, +{ board_t::board_M5AtomLite , GPIO_NUM_27 }, +{ board_t::board_M5AtomMatrix , GPIO_NUM_27 }, +{ board_t::board_M5AtomEcho , GPIO_NUM_27 }, +{ board_t::board_M5AtomU , GPIO_NUM_27 }, +{ board_t::board_M5AtomPsram , GPIO_NUM_27 }, +{ board_t::board_M5StampPico , GPIO_NUM_27 }, +#endif +{ board_t::board_unknown , 255 }, +}; + +static constexpr const uint8_t _pin_table_other1[][2] = { + //POWER_HOLD +#if defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5Dial , GPIO_NUM_46 }, +{ board_t::board_M5Capsule , GPIO_NUM_46 }, +{ board_t::board_M5AirQ , GPIO_NUM_46 }, +{ board_t::board_M5DinMeter , GPIO_NUM_46 }, +{ board_t::board_M5PaperS3 , GPIO_NUM_44 }, + +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +#else + +{ board_t::board_M5StickCPlus2 , GPIO_NUM_4 }, +{ board_t::board_M5Paper , GPIO_NUM_2 }, +{ board_t::board_M5StackCoreInk, GPIO_NUM_12 }, +{ board_t::board_M5TimerCam , GPIO_NUM_33 }, + +#endif +{ board_t::board_unknown , 255 }, +}; + +static constexpr const uint8_t _pin_table_mbus[][31] = { +#if defined (CONFIG_IDF_TARGET_ESP32P4) +{ board_t::board_M5Tab5 , + 255 , GPIO_NUM_16, + 255 , GPIO_NUM_17, + 255 , 255 , + GPIO_NUM_18, GPIO_NUM_45, + GPIO_NUM_19, GPIO_NUM_52, + GPIO_NUM_5 , 255 , + GPIO_NUM_38, GPIO_NUM_37, + GPIO_NUM_7 , GPIO_NUM_6 , + GPIO_NUM_31, GPIO_NUM_32, + GPIO_NUM_3 , GPIO_NUM_4 , + GPIO_NUM_2 , GPIO_NUM_48, + GPIO_NUM_47, GPIO_NUM_35, + 255 , GPIO_NUM_51, + 255 , 255 , + 255 , 255 , +}, +#elif defined (CONFIG_IDF_TARGET_ESP32S3) +{ board_t::board_M5StackCoreS3, + 255 , GPIO_NUM_10, + 255 , GPIO_NUM_8 , + 255 , 255 , + GPIO_NUM_37, GPIO_NUM_5 , + GPIO_NUM_35, GPIO_NUM_9 , + GPIO_NUM_36, 255 , + GPIO_NUM_44, GPIO_NUM_43, + GPIO_NUM_18, GPIO_NUM_17, + GPIO_NUM_12, GPIO_NUM_11, + GPIO_NUM_2 , GPIO_NUM_1 , + GPIO_NUM_6 , GPIO_NUM_7 , + GPIO_NUM_13, GPIO_NUM_0 , + 255 , GPIO_NUM_14, + 255 , 255 , + 255 , 255 , +}, +{ board_t::board_M5StackCoreS3SE, + 255 , GPIO_NUM_10, + 255 , GPIO_NUM_8 , + 255 , 255 , + GPIO_NUM_37, GPIO_NUM_5 , + GPIO_NUM_35, GPIO_NUM_9 , + GPIO_NUM_36, 255 , + GPIO_NUM_44, GPIO_NUM_43, + GPIO_NUM_18, GPIO_NUM_17, + GPIO_NUM_12, GPIO_NUM_11, + GPIO_NUM_2 , GPIO_NUM_1 , + GPIO_NUM_6 , GPIO_NUM_7 , + GPIO_NUM_13, GPIO_NUM_0 , + 255 , GPIO_NUM_14, + 255 , 255 , + 255 , 255 , +}, +#elif defined (CONFIG_IDF_TARGET_ESP32C3) +#elif defined (CONFIG_IDF_TARGET_ESP32C6) +#else +{ board_t::board_M5Stack , + 255 , GPIO_NUM_35, + 255 , GPIO_NUM_36, + 255 , 255 , + GPIO_NUM_23, GPIO_NUM_25, + GPIO_NUM_19, GPIO_NUM_26, + GPIO_NUM_18, 255 , + GPIO_NUM_3 , GPIO_NUM_1 , + GPIO_NUM_16, GPIO_NUM_17, + GPIO_NUM_21, GPIO_NUM_22, + GPIO_NUM_2 , GPIO_NUM_5 , + GPIO_NUM_12, GPIO_NUM_13, + GPIO_NUM_15, GPIO_NUM_0 , + 255 , GPIO_NUM_34, + 255 , 255 , + 255 , 255 , +}, +{ board_t::board_M5StackCore2, + 255 , GPIO_NUM_35, + 255 , GPIO_NUM_36, + 255 , 255 , + GPIO_NUM_23, GPIO_NUM_25, + GPIO_NUM_38, GPIO_NUM_26, + GPIO_NUM_18, 255 , + GPIO_NUM_3 , GPIO_NUM_1 , + GPIO_NUM_13, GPIO_NUM_14, + GPIO_NUM_21, GPIO_NUM_22, + GPIO_NUM_32, GPIO_NUM_33, + GPIO_NUM_27, GPIO_NUM_19, + GPIO_NUM_2 , GPIO_NUM_0 , + 255 , GPIO_NUM_34, + 255 , 255 , + 255 , 255 , +}, +#endif +{ board_t::board_unknown , 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, +}; + + void M5Unified::_setup_pinmap(board_t id) + { + constexpr const std::pair tbl[] = { + { _pin_table_i2c_ex_in, sizeof(_pin_table_i2c_ex_in[0]) }, + { _pin_table_port_bc, sizeof(_pin_table_port_bc[0]) }, + { _pin_table_port_de, sizeof(_pin_table_port_de[0]) }, + { _pin_table_spi_sd, sizeof(_pin_table_spi_sd[0]) }, + { _pin_table_other0, sizeof(_pin_table_other0[0]) }, + { _pin_table_other1, sizeof(_pin_table_other1[0]) }, + { _pin_table_mbus, sizeof(_pin_table_mbus[0]) }, + }; + + int8_t* dst = _get_pin_table; + for (auto &p : tbl) { + const uint8_t* t = (uint8_t*)p.first; + size_t len = p.second; + while (t[0] != id && t[0] != board_t::board_unknown) { t += len; } + memcpy(dst, &t[1], len - 1); + dst += len - 1; + } + } +#endif + + static void in_i2c_bulk_write(const uint8_t i2c_addr, const uint8_t* bulk_data, const uint32_t i2c_freq = 100000u, const uint8_t retry = 0) + { + // bulk_data example.. + // const uint8_t bulk_data[] = { + // 2, 0x00, 0x00, // <- datalen = 2, reg = 0x00, data = 0x00 + // 3, 0x01, 0x00, 0x02, // <- datalen = 3, reg = 0x01, data = 0x00, 0x02 + // 0 }; // <- datalen 0 is end of data. + + while (*bulk_data) { + uint8_t len = *bulk_data++; + uint8_t r = retry + 1; + while (!M5.In_I2C.writeRegister(i2c_addr, bulk_data[0], &bulk_data[1], len - 1, i2c_freq) && --r) { M5.delay(1); } + bulk_data += len; + } + } + + static constexpr uint8_t es7210_i2c_addr = 0x40; + static constexpr uint8_t es8311_i2c_addr0 = 0x18; + static constexpr uint8_t es8311_i2c_addr1 = 0x19; + static constexpr uint8_t es8388_i2c_addr = 0x10; + static constexpr uint8_t pi4io1_i2c_addr = 0x43; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + static constexpr uint8_t aw88298_i2c_addr = 0x36; + static constexpr uint8_t aw9523_i2c_addr = 0x58; + static void aw88298_write_reg(uint8_t reg, uint16_t value) + { + value = __builtin_bswap16(value); + M5.In_I2C.writeRegister(aw88298_i2c_addr, reg, (const uint8_t*)&value, 2, 400000); + } + + static void es7210_write_reg(uint8_t reg, uint8_t value) + { + M5.In_I2C.writeRegister(es7210_i2c_addr, reg, &value, 1, 400000); + } + +#endif + + bool M5Unified::_speaker_enabled_cb_core2(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32) + auto self = (M5Unified*)args; + auto spk_cfg = self->Speaker.config(); + if (spk_cfg.pin_bck == GPIO_NUM_12 + && spk_cfg.pin_ws == GPIO_NUM_0 + && spk_cfg.pin_data_out == GPIO_NUM_2 + ) { + switch (self->Power.getType()) { + case m5::Power_Class::pmic_axp192: + self->Power.Axp192.setGPIO2(enabled); + break; + case m5::Power_Class::pmic_axp2101: + self->Power.Axp2101.setALDO3(enabled * 3300); + break; + default: + break; + } + } +#endif + return true; + } + + bool M5Unified::_speaker_enabled_cb_cores3(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + auto self = (M5Unified*)args; + auto spk_cfg = self->Speaker.config(); + if (spk_cfg.pin_bck == GPIO_NUM_34 && enabled) + { + self->In_I2C.bitOn(aw9523_i2c_addr, 0x02, 0b00000100, 400000); + /// サンプリングレートに応じてAW88298のレジスタの設定値を変える; + static constexpr uint8_t rate_tbl[] = {4,5,6,8,10,11,15,20,22,44}; + size_t reg0x06_value = 0; + size_t rate = (spk_cfg.sample_rate + 1102) / 2205; + while (rate > rate_tbl[reg0x06_value] && ++reg0x06_value < sizeof(rate_tbl)) {} + + reg0x06_value |= 0x14C0; // I2SBCK=0 (BCK mode 16*2) + aw88298_write_reg( 0x61, 0x0673 ); // boost mode disabled + aw88298_write_reg( 0x04, 0x4040 ); // I2SEN=1 AMPPD=0 PWDN=0 + aw88298_write_reg( 0x05, 0x0008 ); // RMSE=0 HAGCE=0 HDCCE=0 HMUTE=0 + aw88298_write_reg( 0x06, reg0x06_value ); + aw88298_write_reg( 0x0C, 0x0064 ); // volume setting (full volume) + } + else /// disableにする場合および内蔵スピーカ以外を操作対象とした場合、内蔵スピーカを停止する。 + { + aw88298_write_reg( 0x04, 0x4000 ); // I2SEN=0 AMPPD=0 PWDN=0 + self->In_I2C.bitOff(aw9523_i2c_addr, 0x02, 0b00000100, 400000); + } +#endif + return true; + } + + bool M5Unified::_speaker_enabled_cb_tab5(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32P4) + auto self = (M5Unified*)args; + auto spk_cfg = self->Speaker.config(); + if (spk_cfg.pin_data_out != GPIO_NUM_26) { return false; } + + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0, 0x80, // RESET/ CSM POWER ON + 2, 0, 0x00, + 2, 0, 0x00, + 2, 0, 0x0E, + 2, 1, 0x00, + 2, 2, 0x0A, //CHIP POWER: power up all + 2, 3, 0xFF, //ADC POWER: power down all + 2, 4, 0x3C, //DAC POWER: power up and LOUT1/ROUT1/LOUT2/ROUT2 enable + 2, 5, 0x00, //ChipLowPower1 + 2, 6, 0x00, //ChipLowPower2 + 2, 7, 0x7C, //VSEL + 2, 8, 0x00, //set I2S slave mode + // reg9-22 == adc + 2, 23, 0x18, //I2S format (16bit) + 2, 24, 0x00, //I2S MCLK ratio (128) + 2, 25, 0x20, //DAC unmute + 2, 26, 0x00, //LDACVOL 0x00~0xC0 + 2, 27, 0x00, //RDACVOL 0x00~0xC0 + 2, 28, 0x08, //enable digital click free power up and down + 2, 29, 0x00, + 2, 38, 0x00, //DAC CTRL16 + 2, 39, 0xB8, //LEFT Ch MIX + 2, 42, 0xB8, //RIGHTCh MIX + 2, 43, 0x08, //ADC and DAC separate + 2, 45, 0x00, // 0x00=1.5k VREF analog output / 0x10=40kVREF analog output + 2, 46, 0x21, + 2, 47, 0x21, + 2, 48, 0x21, + 2, 49, 0x21, + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 2, 8, 0x00, //set I2S slave mode + 0 + }; + in_i2c_bulk_write(es8388_i2c_addr, enabled ? enabled_bulk_data : disabled_bulk_data); + + if (enabled) + { // AMP on + M5.In_I2C.bitOn(pi4io1_i2c_addr, 0x05, 0b00000010, 400000); + } + else + { // AMP off + M5.In_I2C.bitOff(pi4io1_i2c_addr, 0x05, 0b00000010, 400000); + } +#endif + return true; + } + + bool M5Unified::_speaker_enabled_cb_hat_spk(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32) + auto self = (M5Unified*)args; + gpio_num_t pin_en = self->_board == board_t::board_M5StackCoreInk ? GPIO_NUM_25 : GPIO_NUM_0; + if (enabled) + { + m5gfx::pinMode(pin_en, m5gfx::pin_mode_t::output); + m5gfx::gpio_hi(pin_en); + } + else + { m5gfx::gpio_lo(pin_en); } +#endif + return true; + } + + bool M5Unified::_speaker_enabled_cb_atomic_echo(void* args, bool enabled) + { + (void)args; + (void)enabled; + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0x00, 0x80, // 0x00 RESET/ CSM POWER ON + 2, 0x01, 0xB5, // 0x01 CLOCK_MANAGER/ MCLK=BCLK + 2, 0x02, 0x18, // 0x02 CLOCK_MANAGER/ MULT_PRE=3 + 2, 0x0D, 0x01, // 0x0D SYSTEM/ Power up analog circuitry + 2, 0x12, 0x00, // 0x12 SYSTEM/ power-up DAC - NOT default + 2, 0x13, 0x10, // 0x13 SYSTEM/ Enable output to HP drive - NOT default + 2, 0x32, 0xFF, // 0x32 DAC/ DAC volume (full volume) + 2, 0x37, 0x08, // 0x37 DAC/ Bypass DAC equalizer - NOT default + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 0 + }; + + static constexpr const uint8_t enabled_pi4ioe_bulk_data[] = { + 2, 0x03, 0xFF, // PI4IOE direction:OUTPUT + 2, 0x05, 0xFF, // PI4IOE output HIGH + 2, 0x07, 0x00, // PI4IOE set push-pull + 2, 0x0B, 0x00, // Disable pull (up and down) + 0 + }; + static constexpr const uint8_t disabled_pi4ioe_bulk_data[] = { + 2, 0x05, 0x00, + 0 + }; + +#if defined (CONFIG_IDF_TARGET_ESP32S3) + m5gfx::i2c::i2c_temporary_switcher_t backup_i2c_setting(1, GPIO_NUM_38, GPIO_NUM_39); +#endif + in_i2c_bulk_write(es8311_i2c_addr0, enabled ? enabled_bulk_data : disabled_bulk_data); + in_i2c_bulk_write(pi4io1_i2c_addr, enabled ? enabled_pi4ioe_bulk_data : disabled_pi4ioe_bulk_data); +#if defined (CONFIG_IDF_TARGET_ESP32S3) + backup_i2c_setting.restore(); +#endif + return true; + } + + bool M5Unified::_microphone_enabled_cb_stickc(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32) + auto self = (M5Unified*)args; + self->Power.Axp192.setLDO0(enabled ? 2800 : 0); +#endif + return true; + } + + bool M5Unified::_microphone_enabled_cb_cores3(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + auto self = (M5Unified*)args; + auto cfg = self->Mic.config(); + if (cfg.pin_bck == GPIO_NUM_34) + { + es7210_write_reg(0x00, 0xFF); // RESET_CTL + struct __attribute__((packed)) reg_data_t + { + uint8_t reg; + uint8_t value; + }; + if (enabled) + { + static constexpr reg_data_t data[] = + { + { 0x00, 0x41 }, // RESET_CTL + { 0x01, 0x1f }, // CLK_ON_OFF + { 0x06, 0x00 }, // DIGITAL_PDN + { 0x07, 0x20 }, // ADC_OSR + { 0x08, 0x10 }, // MODE_CFG + { 0x09, 0x30 }, // TCT0_CHPINI + { 0x0A, 0x30 }, // TCT1_CHPINI + { 0x20, 0x0a }, // ADC34_HPF2 + { 0x21, 0x2a }, // ADC34_HPF1 + { 0x22, 0x0a }, // ADC12_HPF2 + { 0x23, 0x2a }, // ADC12_HPF1 + { 0x02, 0xC1 }, + { 0x04, 0x01 }, + { 0x05, 0x00 }, + { 0x11, 0x60 }, + { 0x40, 0x42 }, // ANALOG_SYS + { 0x41, 0x70 }, // MICBIAS12 + { 0x42, 0x70 }, // MICBIAS34 + { 0x43, 0x1B }, // MIC1_GAIN + { 0x44, 0x1B }, // MIC2_GAIN + { 0x45, 0x00 }, // MIC3_GAIN + { 0x46, 0x00 }, // MIC4_GAIN + { 0x47, 0x00 }, // MIC1_LP + { 0x48, 0x00 }, // MIC2_LP + { 0x49, 0x00 }, // MIC3_LP + { 0x4A, 0x00 }, // MIC4_LP + { 0x4B, 0x00 }, // MIC12_PDN + { 0x4C, 0xFF }, // MIC34_PDN + { 0x01, 0x14 }, // CLK_ON_OFF + }; + for (auto& d: data) + { + es7210_write_reg(d.reg, d.value); + } + } + } +#endif + return true; + } + + bool M5Unified::_speaker_enabled_cb_cardputer_adv(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0x00, 0x80, // 0x00 RESET/ CSM POWER ON + 2, 0x01, 0xB5, // 0x01 CLOCK_MANAGER/ MCLK=BCLK + 2, 0x02, 0x18, // 0x02 CLOCK_MANAGER/ MULT_PRE=3 + 2, 0x0D, 0x01, // 0x0D SYSTEM/ Power up analog circuitry + 2, 0x12, 0x00, // 0x12 SYSTEM/ power-up DAC - NOT default + 2, 0x13, 0x10, // 0x13 SYSTEM/ Enable output to HP drive - NOT default + 2, 0x32, 0xBF, // 0x32 DAC/ DAC volume (0xBF == ±0 dB ) + 2, 0x37, 0x08, // 0x37 DAC/ Bypass DAC equalizer - NOT default + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 0 + }; + + in_i2c_bulk_write(es8311_i2c_addr0, enabled ? enabled_bulk_data : disabled_bulk_data); +#endif + return true; + } + + + bool M5Unified::_microphone_enabled_cb_atomic_echo(void* args, bool enabled) + { + (void)args; + (void)enabled; + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0x00, 0x80, // 0x00 RESET/ CSM POWER ON + 2, 0x01, 0xBA, // 0x01 CLOCK_MANAGER/ MCLK=BCLK + 2, 0x02, 0x18, // 0x02 CLOCK_MANAGER/ MULT_PRE=3 + 2, 0x0D, 0x01, // 0x0D SYSTEM/ Power up analog circuitry + 2, 0x0E, 0x02, // 0x0E SYSTEM/ : Enable analog PGA, enable ADC modulator + 2, 0x14, 0x10, // ES8311_ADC_REG14 : select Mic1p-Mic1n / PGA GAIN (minimum) + 2, 0x17, 0xFF, // ES8311_ADC_REG17 : ADC_VOLUME (MAXGAIN) // (0xBF == ± 0 dB ) + 2, 0x1C, 0x6A, // ES8311_ADC_REG1C : ADC Equalizer bypass, cancel DC offset in digital domain + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 2, 0x0D, 0xFC, // 0x0D SYSTEM/ Power down analog circuitry + 2, 0x0E, 0x6A, // 0x0E SYSTEM + 2, 0x00, 0x00, // 0x00 RESET/ CSM POWER DOWN + 0 + }; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + m5gfx::i2c::i2c_temporary_switcher_t backup_i2c_setting(1, GPIO_NUM_38, GPIO_NUM_39); +#endif + in_i2c_bulk_write(es8311_i2c_addr0, enabled ? enabled_bulk_data : disabled_bulk_data); +#if defined (CONFIG_IDF_TARGET_ESP32S3) + backup_i2c_setting.restore(); +#endif + + return true; + } + + bool M5Unified::_microphone_enabled_cb_atom_echos3r(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0x00, 0x80, // 0x00 RESET/ CSM POWER ON + 2, 0x01, 0xBA, // 0x01 CLOCK_MANAGER/ MCLK=BCLK + 2, 0x02, 0x18, // 0x02 CLOCK_MANAGER/ MULT_PRE=3 + 2, 0x0D, 0x01, // 0x0D SYSTEM/ Power up analog circuitry + 2, 0x0E, 0x02, // 0x0E SYSTEM/ : Enable analog PGA, enable ADC modulator + 2, 0x14, 0x10, // ES8311_ADC_REG14 : select Mic1p-Mic1n / PGA GAIN (minimum) + 2, 0x17, 0xFF, // ES8311_ADC_REG17 : ADC_VOLUME (MAXGAIN) // (0xBF == ± 0 dB ) + 2, 0x1C, 0x6A, // ES8311_ADC_REG1C : ADC Equalizer bypass, cancel DC offset in digital domain + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 2, 0x0D, 0xFC, // 0x0D SYSTEM/ Power down analog circuitry + 2, 0x0E, 0x6A, // 0x0E SYSTEM + 2, 0x00, 0x00, // 0x00 RESET/ CSM POWER DOWN + 0 + }; + m5gfx::i2c::i2c_temporary_switcher_t backup_i2c_setting(1, GPIO_NUM_45, GPIO_NUM_0); + in_i2c_bulk_write(es8311_i2c_addr0, enabled ? enabled_bulk_data : disabled_bulk_data); + backup_i2c_setting.restore(); +#endif + return true; + } + + bool M5Unified::_speaker_enabled_cb_atom_echos3r(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0x00, 0x80, // 0x00 RESET/ CSM POWER ON + 2, 0x01, 0xB5, // 0x01 CLOCK_MANAGER/ MCLK=BCLK + 2, 0x02, 0x18, // 0x02 CLOCK_MANAGER/ MULT_PRE=3 + 2, 0x0D, 0x01, // 0x0D SYSTEM/ Power up analog circuitry + 2, 0x12, 0x00, // 0x12 SYSTEM/ power-up DAC - NOT default + 2, 0x13, 0x10, // 0x13 SYSTEM/ Enable output to HP drive - NOT default + 2, 0x32, 0xFF, // 0x32 DAC/ DAC volume (full volume) + 2, 0x37, 0x08, // 0x37 DAC/ Bypass DAC equalizer - NOT default + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 0 + }; + + m5gfx::i2c::i2c_temporary_switcher_t backup_i2c_setting(1, GPIO_NUM_45, GPIO_NUM_0); + in_i2c_bulk_write(es8311_i2c_addr0, enabled ? enabled_bulk_data : disabled_bulk_data); + gpio_num_t pin_en = GPIO_NUM_18; + if (enabled) + { + m5gfx::pinMode(pin_en, m5gfx::pin_mode_t::output); + m5gfx::gpio_hi(pin_en); + } + else + { m5gfx::gpio_lo(pin_en); } + backup_i2c_setting.restore(); +#endif + return true; + } + +#if defined (CONFIG_IDF_TARGET_ESP32) && SOC_TOUCH_SENSOR_SUPPORTED + static void _read_touch_pad(uint32_t* results, const touch_pad_t* channel, const size_t channel_count) + { +#if defined ( TOUCH_SENSOR_DEFAULT_FILTER_CONFIG ) + /* Handles of touch sensor */ + touch_sensor_handle_t sens_handle = nullptr; + touch_channel_handle_t chan_handle[TOUCH_TOTAL_CHAN_NUM]; + + /* Step 1: Create a new touch sensor controller handle with default sample configuration */ + touch_sensor_sample_config_t sample_cfg; + sample_cfg.charge_duration_ms = 5.0f; + sample_cfg.charge_volt_lim_h = TOUCH_VOLT_LIM_H_1V7; + sample_cfg.charge_volt_lim_l = TOUCH_VOLT_LIM_L_0V5; + + touch_sensor_config_t sens_cfg; + sens_cfg.power_on_wait_us = 256; + sens_cfg.meas_interval_us = 320.0; + sens_cfg.intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH; + sens_cfg.intr_trig_group = TOUCH_INTR_TRIG_GROUP_BOTH; + sens_cfg.sample_cfg_num = 1; + sens_cfg.sample_cfg = &sample_cfg; + touch_sensor_new_controller(&sens_cfg, &sens_handle); + + touch_channel_config_t chan_cfg; + chan_cfg.abs_active_thresh[0] = 1024; + chan_cfg.charge_speed = TOUCH_CHARGE_SPEED_7; + chan_cfg.init_charge_volt = TOUCH_INIT_CHARGE_VOLT_DEFAULT; + chan_cfg.group = TOUCH_CHAN_TRIG_GROUP_BOTH; + for (int i = 0; i < channel_count; i++) { + touch_sensor_new_channel(sens_handle, channel[i], &chan_cfg, &chan_handle[i]); + } + touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG(); + touch_sensor_config_filter(sens_handle, &filter_cfg); + touch_sensor_enable(sens_handle); + touch_sensor_trigger_oneshot_scanning(sens_handle, 64); + touch_sensor_disable(sens_handle); + + for (int i = 0; i < channel_count; i++) { + touch_channel_read_data(chan_handle[i], TOUCH_CHAN_DATA_TYPE_SMOOTH, &results[i]); + } + for (int i = 0; i < channel_count; i++) { + touch_sensor_del_channel(chan_handle[i]); + } + touch_sensor_del_controller(sens_handle); +#else + touch_pad_init(); + for (size_t i = 0; i < channel_count; i++) { + touch_pad_config(channel[i], TOUCH_PAD_THRESHOLD_MAX); + } + for (size_t i = 0; i < channel_count; i++) { + uint16_t tmp; + touch_pad_read(channel[i], &tmp); + results[i] = tmp; + } + touch_pad_deinit(); +#endif + } +#endif + + bool M5Unified::_microphone_enabled_cb_tab5(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32P4) + auto self = (M5Unified*)args; + auto cfg = self->Mic.config(); + if (cfg.pin_data_in != GPIO_NUM_28) { return false; } + + M5.In_I2C.writeRegister8(es7210_i2c_addr, 0x00, 0xFF, 400000); + if (enabled) + { + static constexpr uint8_t data[] = + { + 2, 0x00, 0x41, // RESET_CTL + 2, 0x01, 0x1f, // CLK_ON_OFF + 2, 0x06, 0x00, // DIGITAL_PDN + 2, 0x07, 0x20, // ADC_OSR + 2, 0x08, 0x10, // MODE_CFG + 2, 0x09, 0x30, // TCT0_CHPINI + 2, 0x0A, 0x30, // TCT1_CHPINI + 2, 0x20, 0x0a, // ADC34_HPF2 + 2, 0x21, 0x2a, // ADC34_HPF1 + 2, 0x22, 0x0a, // ADC12_HPF2 + 2, 0x23, 0x2a, // ADC12_HPF1 + 2, 0x02, 0xC1, + 2, 0x04, 0x01, + 2, 0x05, 0x00, + 2, 0x11, 0x60, + 2, 0x40, 0x42, // ANALOG_SYS + 2, 0x41, 0x70, // MICBIAS12 + 2, 0x42, 0x70, // MICBIAS34 + 2, 0x43, 0x1B, // MIC1_GAIN + 2, 0x44, 0x1B, // MIC2_GAIN + 2, 0x45, 0x00, // MIC3_GAIN + 2, 0x46, 0x00, // MIC4_GAIN + 2, 0x47, 0x00, // MIC1_LP + 2, 0x48, 0x00, // MIC2_LP + 2, 0x49, 0x00, // MIC3_LP + 2, 0x4A, 0x00, // MIC4_LP + 2, 0x4B, 0x00, // MIC12_PDN + 2, 0x4C, 0xFF, // MIC34_PDN + 2, 0x01, 0x14, // CLK_ON_OFF + 0, + }; + in_i2c_bulk_write(es7210_i2c_addr, data); + } +#endif + return true; + } + + bool M5Unified::_microphone_enabled_cb_cardputer_adv(void* args, bool enabled) + { + (void)args; + (void)enabled; +#if defined (CONFIG_IDF_TARGET_ESP32S3) + static constexpr const uint8_t enabled_bulk_data[] = { + 2, 0x00, 0x80, // 0x00 RESET/ CSM POWER ON + 2, 0x01, 0xBA, // 0x01 CLOCK_MANAGER/ MCLK=BCLK + 2, 0x02, 0x18, // 0x02 CLOCK_MANAGER/ MULT_PRE=3 + 2, 0x0D, 0x01, // 0x0D SYSTEM/ Power up analog circuitry + 2, 0x0E, 0x02, // 0x0E SYSTEM/ : Enable analog PGA, enable ADC modulator + 2, 0x14, 0x10, // ES8311_ADC_REG14 : select Mic1p-Mic1n / PGA GAIN (minimum) + 2, 0x17, 0xBF, // ES8311_ADC_REG17 : ADC_VOLUME 0xBF == ± 0 dB + 2, 0x1C, 0x6A, // ES8311_ADC_REG1C : ADC Equalizer bypass, cancel DC offset in digital domain + 0 + }; + static constexpr const uint8_t disabled_bulk_data[] = { + 2, 0x0D, 0xFC, // 0x0D SYSTEM/ Power down analog circuitry + 2, 0x0E, 0x6A, // 0x0E SYSTEM + 2, 0x00, 0x00, // 0x00 RESET/ CSM POWER DOWN + 0 + }; + + in_i2c_bulk_write(es8311_i2c_addr0, enabled ? enabled_bulk_data : disabled_bulk_data); +#endif + return true; + } + +#if defined (M5UNIFIED_PC_BUILD) +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + static constexpr gpio_num_t TFCARD_CS_PIN = GPIO_NUM_4; + static constexpr gpio_num_t CoreInk_BUTTON_EXT_PIN = GPIO_NUM_5; + static constexpr gpio_num_t CoreInk_BUTTON_PWR_PIN = GPIO_NUM_27; +#endif + + board_t M5Unified::_check_boardtype(board_t board) + { +#if defined (M5UNIFIED_PC_BUILD) +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + if (board == board_t::board_unknown) + { + switch (m5gfx::get_pkg_ver()) + { + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6: + board = board_t::board_M5TimerCam; + break; + + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4: + { + m5gfx::gpio::pin_backup_t pin_backup[] = { GPIO_NUM_2, GPIO_NUM_13, GPIO_NUM_19, GPIO_NUM_22, GPIO_NUM_27, GPIO_NUM_33, GPIO_NUM_34 }; + m5gfx::pinMode(GPIO_NUM_34, m5gfx::pin_mode_t::input); + m5gfx::pinMode(GPIO_NUM_2, m5gfx::pin_mode_t::input_pullup); + board = board_t::board_M5StampPico; + if (m5gfx::gpio_in(GPIO_NUM_2)) // Branches other than StampPico ( StampPico G2 is always LOW ) + { + board = board_t::board_M5AtomU; + if (m5gfx::gpio_in(GPIO_NUM_34)) { // Branches other than AtomU ( AtomU G34 is always LOW ) + board = board_t::board_M5AtomMatrix; +#if SOC_TOUCH_SENSOR_SUPPORTED +/* G27(RGBLED)に対してタッチセンサを用い、容量の差に基づいて Matrix の識別を行う。 + G27に対してタッチセンサを使用すると、得られる値は Lite/ECHOの方が大きく、Matrixの方が小さい。 + なおタッチセンサの値には個体差があるため、判定の基準として絶対値ではなく G13(NC)のタッチセンサ値を比較に用いる。 +*/ + uint32_t results[2] = { 0, 0 }; + static constexpr touch_pad_t s_channel_id[] = { + TOUCH_PAD_NUM4, //Touch pad channel 4 is GPIO13(ESP32) + TOUCH_PAD_NUM7, //Touch pad channel 7 is GPIO27(ESP32) + }; + _read_touch_pad(results, s_channel_id, 2); + + int diff = (results[1] * 3 - results[0]); + // M5_LOGV("G13 = %d / G27 = %d / diff = %d", results[0], results[1], diff); + // true==(Lite/ECHO) / false==AtomMatrix + if (diff >= 0) +#else +/* + タッチセンサAPIが使えない場合の処理 (ESP-IDFのバージョンに依る) + GPIOの立上り速度の差を用いて LiteとMatrix の識別を行う。 + (Matrixの方がinput_pullupでHIGHになるまでの時間が長いため、この性質を利用して判定する) +*/ + portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + uint32_t g27 = 0; + // 8回読み取って立上り速度の差を見る + for (int i = 0; i < 8; ++i) + { + lgfx::pinMode(GPIO_NUM_27, lgfx::pin_mode_t::input_pulldown); + delay(1); + taskENTER_CRITICAL(&mux); + lgfx::pinMode(GPIO_NUM_27, lgfx::pin_mode_t::input_pullup); + g27 += lgfx::gpio_in(GPIO_NUM_27); + taskEXIT_CRITICAL(&mux); + } + + // Branches other than AtomMatrix ( AtomMatrix G27 is delayed from becoming HIGH ) + if (g27 > 4) +#endif + { + auto result = m5gfx::gpio::command( + (const uint8_t[]) { + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_22, + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_19, + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_33, + m5gfx::gpio::command_mode_input_pullup , GPIO_NUM_33, + m5gfx::gpio::command_mode_input_pullup , GPIO_NUM_19, + m5gfx::gpio::command_mode_input_pullup , GPIO_NUM_22, + m5gfx::gpio::command_read , GPIO_NUM_33, + m5gfx::gpio::command_read , GPIO_NUM_19, + m5gfx::gpio::command_read , GPIO_NUM_22, + m5gfx::gpio::command_mode_input , GPIO_NUM_33, + m5gfx::gpio::command_mode_input , GPIO_NUM_19, + m5gfx::gpio::command_mode_input , GPIO_NUM_22, + m5gfx::gpio::command_delay , 1, + m5gfx::gpio::command_read , GPIO_NUM_33, + m5gfx::gpio::command_read , GPIO_NUM_19, + m5gfx::gpio::command_read , GPIO_NUM_22, + } + ); + // G19 G22 G33 = ECHOのI2Sスピーカ用ピン。プルアップを無効化するとすぐにLOWになるため、この性質を利用して判定する。 + // なお当該ピンに何かを外付けしている場合は判定に失敗する可能性がある。 + board = board_t::board_M5AtomLite; + if ((result) == 0b111000) + { // Branches for AtomECHO + board = board_t::board_M5AtomEcho; + } + } + } + } + for (auto &backup : pin_backup) { + backup.restore(); + } + } + break; + + case 6: // EFUSE_RD_CHIP_VER_PKG_ESP32PICOV3_02: // ATOM PSRAM + board = board_t::board_M5AtomPsram; + break; + + default: + +#if defined ( ARDUINO_M5STACK_CORE_ESP32 ) || defined ( ARDUINO_M5STACK_FIRE ) || defined ( ARDUINO_M5Stack_Core_ESP32 ) + + board = board_t::board_M5Stack; + +#elif defined ( ARDUINO_M5STACK_CORE2 ) || defined ( ARDUINO_M5STACK_Core2 ) + + board = board_t::board_M5StackCore2; + +#elif defined ( ARDUINO_M5STICK_C ) || defined ( ARDUINO_M5Stick_C ) + + board = board_t::board_M5StickC; + +#elif defined ( ARDUINO_M5STICK_C_PLUS ) || defined ( ARDUINO_M5Stick_C_Plus ) + + board = board_t::board_M5StickCPlus; + +#elif defined ( ARDUINO_M5STACK_COREINK ) || defined ( ARDUINO_M5Stack_CoreInk ) + + board = board_t::board_M5StackCoreInk; + +#elif defined ( ARDUINO_M5STACK_PAPER ) || defined ( ARDUINO_M5STACK_Paper ) + + board = board_t::board_M5Paper; + +#elif defined ( ARDUINO_M5STACK_TOUGH ) + + board = board_t::board_M5Tough; + +#elif defined ( ARDUINO_M5STACK_ATOM ) || defined ( ARDUINO_M5Stack_ATOM ) + + board = board_t::board_M5AtomLite; + +#elif defined ( ARDUINO_M5STACK_TIMER_CAM ) || defined ( ARDUINO_M5Stack_Timer_CAM ) + + board = board_t::board_M5TimerCam; + +#endif + break; + } + } + +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + + switch (m5gfx::get_pkg_ver()) + { + default: + case 0: // EFUSE_PKG_VERSION_ESP32S3: // QFN56 + if (board == board_t::board_unknown) + { /// StampS3 or AtomS3Lite,S3U ? + /// After setting GPIO38 to INPUT PULL-UP, change to INPUT and read it. + /// In the case of STAMPS3: Returns 0. Charge is sucked by SGM2578. + /// In the case of ATOMS3Lite/S3U : Returns 1. Charge remains. ( Since it is not connected to anywhere. ) + /// + /// AtomS3Lite or AtomS3U ? + /// After setting GPIO4 to INPUT PULL-UP, read it. + /// In the case of ATOMS3Lite : Returns 0. Charge is sucked by InfraRed. + /// In the case of ATOMS3U : Returns 1. Charge remains. ( Since it is not connected to anywhere. ) + + m5gfx::gpio::pin_backup_t pin_backup[] = { GPIO_NUM_4, GPIO_NUM_8, GPIO_NUM_10, GPIO_NUM_12, GPIO_NUM_38 }; + auto result = m5gfx::gpio::command( + (const uint8_t[]) { + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_4, + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_12, + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_38, + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_8, + m5gfx::gpio::command_mode_input_pulldown, GPIO_NUM_10, + m5gfx::gpio::command_mode_input_pullup , GPIO_NUM_4, + m5gfx::gpio::command_mode_input_pullup , GPIO_NUM_12, + m5gfx::gpio::command_mode_input_pullup , GPIO_NUM_38, + m5gfx::gpio::command_read , GPIO_NUM_8, + m5gfx::gpio::command_read , GPIO_NUM_10, + m5gfx::gpio::command_read , GPIO_NUM_4, + m5gfx::gpio::command_read , GPIO_NUM_12, + m5gfx::gpio::command_read , GPIO_NUM_38, + m5gfx::gpio::command_mode_input , GPIO_NUM_38, + m5gfx::gpio::command_delay , 1, + m5gfx::gpio::command_read , GPIO_NUM_38, + m5gfx::gpio::command_end + } + ); + /// result には、command_read で得たGPIOの状態が1bitずつ4回分入っている。 + board = ((const board_t[]) + { // ↓StampS3 pattern↓ + board_t::board_unknown, board_t::board_unknown, board_t::board_M5StampS3, board_t::board_unknown, // ← unknown + board_t::board_M5AtomS3Lite,board_t::board_M5AtomS3Lite,board_t::board_unknown , board_t::board_M5AtomS3Lite, // ← AtomS3Lite pattern + board_t::board_M5AtomS3U, board_t::board_M5AtomS3U, board_t::board_M5StampS3, board_t::board_M5AtomS3U, // ← AtomS3U pattern + board_t::board_unknown, board_t::board_unknown, board_t::board_M5StampS3, board_t::board_unknown, // ← unknown + })[result&15]; + if ((result & 3) == 2) { // StampS3 pattern + if ((result >> 3) == 0b110) { + board = board_t::board_M5Capsule; + // 自動検出の際。PortAに余分な波形が出ているので、一度 I2C STOPコンディションを出しておく。 + // ※ これをしないと正しく動作しないデバイスが存在した。UnitHEART MAX30100 + m5gfx::gpio::command( + (const uint8_t[]) { + m5gfx::gpio::command_mode_output, GPIO_NUM_15, + m5gfx::gpio::command_write_low , GPIO_NUM_15, + m5gfx::gpio::command_mode_output, GPIO_NUM_13, + m5gfx::gpio::command_write_low , GPIO_NUM_13, + m5gfx::gpio::command_write_high , GPIO_NUM_15, + m5gfx::gpio::command_write_high , GPIO_NUM_13, + m5gfx::gpio::command_end + } + ); + } + } + for (auto &backup : pin_backup) { + backup.restore(); + } + } + break; + + case 1: // EFUSE_PKG_VERSION_ESP32S3PICO: // LGA56 + if (board == board_t::board_unknown) { + m5gfx::gpio::pin_backup_t pin_backup[] = { GPIO_NUM_0, GPIO_NUM_45 }; + { + m5gfx::gpio::command((const uint8_t[]) { + m5gfx::gpio::command_write_low, GPIO_NUM_0, + m5gfx::gpio::command_mode_output, GPIO_NUM_0, // SCL + m5gfx::gpio::command_write_low, GPIO_NUM_45, + m5gfx::gpio::command_mode_output, GPIO_NUM_45, // SDA + }); + + delay(50); // 延时 50ms,保证设备上电稳定 + + uint32_t result = 0; + for (uint8_t i2caddr: (const uint8_t[]){ 0x18 << 1 }) { + delay(2); // 小延时 + bool nack = true; + // I2C START + m5gfx::gpio_lo(GPIO_NUM_45); // SDA LOW = START + for (int cycle = 0; cycle < 20; ++cycle) { + // SCL toggle + m5gfx::gpio_hi(GPIO_NUM_0); + delay(1); + m5gfx::gpio_lo(GPIO_NUM_0); + delay(1); + + if (cycle & 1) { + if (cycle == 17) { + nack = m5gfx::gpio_in(GPIO_NUM_45); // 读 ACK + } + } else { + if (i2caddr & 0x80) { + m5gfx::gpio_hi(GPIO_NUM_45); + } else { + m5gfx::gpio_lo(GPIO_NUM_45); + } + i2caddr <<= 1; + if (cycle >= 16) { + m5gfx::pinMode(GPIO_NUM_45, (cycle == 16) ? m5gfx::pin_mode_t::input : m5gfx::pin_mode_t::output); + } + } + } + m5gfx::gpio_hi(GPIO_NUM_45); // SDA HIGH = STOP + result = result << 1 | nack; + } + if (result == 1) { + board = board_t::board_M5AtomEchoS3R; + } + } + + for (auto &backup : pin_backup) { + backup.restore(); + } + } + + if (board == board_t::board_unknown) + { /// AtomS3RCam or AtomS3RExt ? + // Cam = GC0308 = I2C 7bit addr = 0x21 + // CamM12 = OV3660 = I2C 7bit addr = 0x3C + board = board_t::board_M5AtomS3RExt; + m5gfx::gpio_lo(GPIO_NUM_18); + m5gfx::pinMode(GPIO_NUM_18, m5gfx::pin_mode_t::output); + m5gfx::gpio::pin_backup_t pin_backup[] = { GPIO_NUM_9, GPIO_NUM_12, GPIO_NUM_21 }; + { // G9=SCL, G12=SDA, G21=XCLK + m5gfx::gpio::command( + (const uint8_t[]) { + m5gfx::gpio::command_write_low, GPIO_NUM_9, + m5gfx::gpio::command_mode_output, GPIO_NUM_9, // SCL + m5gfx::gpio::command_write_low, GPIO_NUM_12, + m5gfx::gpio::command_mode_output, GPIO_NUM_12, // SDA + m5gfx::gpio::command_mode_output, GPIO_NUM_21, // XCL + m5gfx::gpio::command_write_high, GPIO_NUM_9, + m5gfx::gpio::command_write_high, GPIO_NUM_21, + m5gfx::gpio::command_write_high, GPIO_NUM_12, + }); + auto lo_reg = m5gfx::get_gpio_lo_reg(GPIO_NUM_21); + auto hi_reg = m5gfx::get_gpio_hi_reg(GPIO_NUM_21); + + // prepare camera module (need XCLK signal) + for (int xclk = 32768 * 54; xclk != 0; --xclk) + { + *lo_reg = 1 << GPIO_NUM_21; + *hi_reg = 1 << GPIO_NUM_21; + } + uint32_t result = 0; + for (uint8_t i2caddr: (const uint8_t[]){ 0x3C << 1, 0x21 << 1 }) { + for (int xclk = 32768 * 2; xclk != 0; --xclk) { + *lo_reg = 1 << GPIO_NUM_21; + *hi_reg = 1 << GPIO_NUM_21; + } + bool nack = true; + // The camera module is identified using I2C communication via GPIO self-operation. + *lo_reg = 1 << GPIO_NUM_12; // SDA LOW = START + for (int cycle = 0; cycle < 20; ++cycle) { + for (int j = 0; j < 2; ++j) { + for (int xclk = 8; xclk != 0; --xclk) { + *lo_reg = 1 << GPIO_NUM_21; + *hi_reg = 1 << GPIO_NUM_21; + } + *((cycle & 1) ? hi_reg : lo_reg) = 1 << GPIO_NUM_9; // SCL + } + if (cycle & 1) { + if (cycle == 17) { + nack = m5gfx::gpio_in(GPIO_NUM_12); + } + } else { + *((i2caddr & 0x80) ? hi_reg : lo_reg) = 1 << GPIO_NUM_12; // SDA + i2caddr <<= 1; + if (cycle >= 16) { + m5gfx::pinMode(GPIO_NUM_12, (cycle == 16) ? m5gfx::pin_mode_t::input : m5gfx::pin_mode_t::output); + } + } + } + *hi_reg = 1 << GPIO_NUM_12; // SDA HIGH = STOP + result = result << 1 | nack; + } + // printf("CAM TEST RESULT: %08x \r\n", (int)result); + if (result == 1 || result == 2) { + // result == 1 : OV3660 + // result == 2 : GC0308 + board = board_t::board_M5AtomS3RCam; + } + } + for (auto &backup : pin_backup) { + backup.restore(); + } + } + } + + +#elif defined (CONFIG_IDF_TARGET_ESP32C3) + if (board == board_t::board_unknown) + { // StampC3 or StampC3U ? + uint32_t tmp = *((volatile uint32_t *)(IO_MUX_GPIO20_REG)); + m5gfx::pinMode(GPIO_NUM_20, m5gfx::pin_mode_t::input_pulldown); + // StampC3 has a strong external pull-up on GPIO20, which is HIGH even when input_pulldown is set. + // Therefore, if it is LOW, it is not StampC3 and can be assumed to be StampC3U. + // However, even if it goes HIGH, something may be connected to GPIO20 by StampC3U, so it is treated as unknown. + // The StampC3U determination uses the fallback_board setting. + if (m5gfx::gpio_in(GPIO_NUM_20) == false) + { + board = board_t::board_M5StampC3U; + } + *((volatile uint32_t *)(IO_MUX_GPIO20_REG)) = tmp; + } + +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + if (board == board_t::board_unknown) + { // NanoC6 + board = board_t::board_M5NanoC6; + } + +#elif defined (CONFIG_IDF_TARGET_ESP32P4) + if (board == board_t::board_unknown) + { + board = board_t::board_M5Tab5; + } + +#endif + + return board; + } + + void M5Unified::_setup_i2c(board_t board) + { +#if defined (M5UNIFIED_PC_BUILD) + (void)board; +#else + + gpio_num_t in_scl = (gpio_num_t)getPin(pin_name_t::in_i2c_scl); + gpio_num_t in_sda = (gpio_num_t)getPin(pin_name_t::in_i2c_sda); + gpio_num_t ex_scl = (gpio_num_t)getPin(pin_name_t::ex_i2c_scl); + gpio_num_t ex_sda = (gpio_num_t)getPin(pin_name_t::ex_i2c_sda); + + i2c_port_t ex_port = I2C_NUM_0; +#if SOC_I2C_NUM == 1 || defined (CONFIG_IDF_TARGET_ESP32C6) + i2c_port_t in_port = I2C_NUM_0; +#else + i2c_port_t in_port = I2C_NUM_1; + if (in_scl == ex_scl && in_sda == ex_sda) { + in_port = ex_port; + } +#endif + if ((uint_fast8_t)in_scl < GPIO_NUM_MAX) + { + In_I2C.begin(in_port, in_sda, in_scl); + } + else + { + In_I2C.setPort(I2C_NUM_MAX, in_sda, in_scl); + } + + if ((uint_fast8_t)ex_scl < GPIO_NUM_MAX) + { + if ((in_port != ex_port) || (in_sda == ex_sda && in_scl == ex_scl) || ((uint_fast8_t)in_scl >= GPIO_NUM_MAX)) { + Ex_I2C.setPort(ex_port, ex_sda, ex_scl); + } + } + + switch (board) { +#if defined (CONFIG_IDF_TARGET_ESP32P4) + case board_t::board_M5Tab5: + for (int i = 0; i < 2; ++i) + { + auto ioexp = new PI4IOE5V6408_Class(0x43 + i); + ioexp->begin(); + _io_expander[i].reset(ioexp); + } + break; +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + case board_t::board_M5UnitC6L: + { + auto ioexp = new PI4IOE5V6408_Class(0x43); + ioexp->begin(); + _io_expander[0].reset(ioexp); + } + break; + case board_t::board_ArduinoNessoN1: + for (int i = 0; i < 2; ++i) + { + auto ioexp = new PI4IOE5V6408_Class(0x43 + i); + ioexp->begin(); + _io_expander[i].reset(ioexp); + } + break; +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + case board_t::board_M5StampPLC: + { + auto ioexp = new PI4IOE5V6408_Class; + ioexp->begin(); + _io_expander[0].reset(ioexp); + } + break; +#endif + default: + break; + } +#endif + } + + void M5Unified::_begin(const config_t& cfg) + { + /// setup power management ic + Power.begin(); + Power.setExtOutput(cfg.output_power); + if (cfg.led_brightness) + { + M5.Power.setLed(cfg.led_brightness); + } + auto pmic_type = Power.getType(); + if (pmic_type == Power_Class::pmic_t::pmic_axp2101 + || pmic_type == Power_Class::pmic_t::pmic_axp192) + { + _use_pmic_button = cfg.pmic_button; + /// Slightly lengthen the acceptance time of the AXP192 power button multiclick. + BtnPWR.setHoldThresh(BtnPWR.getHoldThresh() * 1.2); + } + + if (cfg.clear_display) + { + Display.clear(); + } + +#if defined (M5UNIFIED_PC_BUILD) +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + switch (_board) + { + case board_t::board_M5Stack: + // Countermeasure to the problem that GPIO15 affects WiFi sensitivity when M5GO bottom is connected. + m5gfx::pinMode(GPIO_NUM_15, m5gfx::pin_mode_t::output); + m5gfx::gpio_lo(GPIO_NUM_15); + + // M5Stack Core v2.6 has a problem that SPI communication speed cannot be increased. + // This problem can be solved by increasing the GPIO drive current. + // ※ This allows SunDisk SD cards to communicate at 20 MHz. (without M5GO bottom.) + // This allows communication with ModuleDisplay at 80 MHz. + for (auto gpio: (const gpio_num_t[]){ GPIO_NUM_18, GPIO_NUM_19, GPIO_NUM_23 }) + { + uint32_t tmp = *(volatile uint32_t*)(GPIO_PIN_MUX_REG[gpio]); + *(volatile uint32_t*)(GPIO_PIN_MUX_REG[gpio]) = tmp | FUN_DRV_M; // gpio drive current set to 40mA. + gpio_pulldown_dis(gpio); // disable pulldown. + gpio_pullup_en(gpio); // enable pullup. + } + break; + + case board_t::board_M5StickC: + case board_t::board_M5StickCPlus: + case board_t::board_M5AtomLite: + case board_t::board_M5AtomMatrix: + case board_t::board_M5AtomEcho: + case board_t::board_M5AtomU: + // Countermeasure to the problem that CH552 applies 4v to GPIO0, thus reducing WiFi sensitivity. + // Setting output_high adds a bias of 3.3v and suppresses overvoltage. + m5gfx::pinMode(GPIO_NUM_0, m5gfx::pin_mode_t::output); + m5gfx::gpio_hi(GPIO_NUM_0); + break; + + default: + break; + } +#endif + + switch (_board) /// setup Hardware Buttons + { +#if defined (M5UNIFIED_PC_BUILD) +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + case board_t::board_M5StackCoreInk: + m5gfx::pinMode(CoreInk_BUTTON_EXT_PIN, m5gfx::pin_mode_t::input); // TopButton + m5gfx::pinMode(CoreInk_BUTTON_PWR_PIN, m5gfx::pin_mode_t::input); // PowerButton + NON_BREAK; /// don't break; + + case board_t::board_M5Paper: + case board_t::board_M5Station: + case board_t::board_M5Stack: + m5gfx::pinMode(GPIO_NUM_38, m5gfx::pin_mode_t::input); + NON_BREAK; /// don't break; + + case board_t::board_M5StickC: + case board_t::board_M5StickCPlus: + m5gfx::pinMode(GPIO_NUM_37, m5gfx::pin_mode_t::input); + NON_BREAK; /// don't break; + + case board_t::board_M5AtomLite: + case board_t::board_M5AtomMatrix: + case board_t::board_M5AtomEcho: + case board_t::board_M5AtomPsram: + case board_t::board_M5AtomU: + case board_t::board_M5StampPico: + m5gfx::pinMode(GPIO_NUM_39, m5gfx::pin_mode_t::input); + NON_BREAK; /// don't break; + + case board_t::board_M5StackCore2: + case board_t::board_M5Tough: + /// for GPIO 36,39 Chattering prevention. + adc_power_acquire(); + break; + + case board_t::board_M5StickCPlus2: + m5gfx::pinMode(GPIO_NUM_35, m5gfx::pin_mode_t::input); + m5gfx::pinMode(GPIO_NUM_37, m5gfx::pin_mode_t::input); + m5gfx::pinMode(GPIO_NUM_39, m5gfx::pin_mode_t::input); + break; + +#elif defined (CONFIG_IDF_TARGET_ESP32C3) + + case board_t::board_M5StampC3: + m5gfx::pinMode(GPIO_NUM_3, m5gfx::pin_mode_t::input_pullup); + break; + + case board_t::board_M5StampC3U: + m5gfx::pinMode(GPIO_NUM_9, m5gfx::pin_mode_t::input_pullup); + break; + +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + + case board_t::board_M5NanoC6: + m5gfx::pinMode(GPIO_NUM_9, m5gfx::pin_mode_t::input_pullup); + break; + +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + case board_t::board_M5AtomS3: + case board_t::board_M5AtomS3Lite: + case board_t::board_M5AtomS3U: + case board_t::board_M5AtomS3R: + case board_t::board_M5AtomEchoS3R: + m5gfx::pinMode(GPIO_NUM_41, m5gfx::pin_mode_t::input); + break; + + case board_t::board_M5AirQ: + m5gfx::pinMode(GPIO_NUM_0, m5gfx::pin_mode_t::input); + m5gfx::pinMode(GPIO_NUM_8, m5gfx::pin_mode_t::input); + break; + + case board_t::board_M5VAMeter: + m5gfx::pinMode(GPIO_NUM_0, m5gfx::pin_mode_t::input); + m5gfx::pinMode(GPIO_NUM_2, m5gfx::pin_mode_t::input); + break; + + case board_t::board_M5StampS3: + case board_t::board_M5Cardputer: + case board_t::board_M5CardputerADV: + m5gfx::pinMode(GPIO_NUM_0, m5gfx::pin_mode_t::input); + break; + + case board_t::board_M5Capsule: + case board_t::board_M5Dial: + case board_t::board_M5DinMeter: + m5gfx::pinMode(GPIO_NUM_42, m5gfx::pin_mode_t::input); + break; + + case board_t::board_M5StampPLC: + { + auto& ioexp = getIOExpander(0); + // lcd backlight + ioexp.setDirection(7, true); + ioexp.setPullMode(7, false); + ioexp.setHighImpedance(7, false); + + for (int i = 0; i < 3; ++i) { + // button a~c + ioexp.setDirection(i, false); + ioexp.setPullMode(i, true); + ioexp.setHighImpedance(i, false); + } + delay(100); + } + break; + +#endif + + default: + break; + } + +#if defined ( ARDUINO ) + + if (cfg.serial_baudrate) + { // Wait with delay to prevent startup log output from disappearing. + delay(16); + Serial.begin(cfg.serial_baudrate); + } + +#endif + } + + void M5Unified::_begin_spk(config_t& cfg) + { + bool(*mic_enable_cb)(void*, bool) = nullptr; + auto mic_cfg = Mic.config(); + + bool(*spk_enable_cb)(void*, bool) = nullptr; + auto spk_cfg = Speaker.config(); + + if (cfg.internal_mic) + { + mic_cfg.over_sampling = 1; + mic_cfg.i2s_port = I2S_NUM_0; + switch (_board) + { +#if defined (M5UNIFIED_PC_BUILD) +#elif defined (CONFIG_IDF_TARGET_ESP32P4) + case board_t::board_M5Tab5: + if (cfg.internal_mic) + { + mic_cfg.pin_mck = GPIO_NUM_30; + mic_cfg.pin_bck = GPIO_NUM_27; + mic_cfg.pin_ws = GPIO_NUM_29; + mic_cfg.pin_data_in = GPIO_NUM_28; + // mic_cfg.pin_data_out = GPIO_NUM_26; + mic_cfg.magnification = 2; + mic_cfg.input_channel = input_channel_t::input_stereo; + mic_cfg.i2s_port = I2S_NUM_0; + mic_enable_cb = _microphone_enabled_cb_tab5; + } + break; + +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + case board_t::board_M5StackCoreS3: + case board_t::board_M5StackCoreS3SE: + if (cfg.internal_mic) + { + mic_cfg.magnification = 2; + mic_cfg.over_sampling = 1; + mic_cfg.pin_mck = GPIO_NUM_0; + mic_cfg.pin_bck = GPIO_NUM_34; + mic_cfg.pin_ws = GPIO_NUM_33; + mic_cfg.pin_data_in = GPIO_NUM_14; + mic_cfg.i2s_port = I2S_NUM_1; + mic_cfg.input_channel = input_channel_t::input_stereo; + mic_enable_cb = _microphone_enabled_cb_cores3; + } + break; + + case board_t::board_M5AtomS3U: + if (cfg.internal_mic) + { + mic_cfg.pin_data_in = GPIO_NUM_38; + mic_cfg.pin_ws = GPIO_NUM_39; + } + break; + + case board_t::board_M5Cardputer: + if (cfg.internal_mic) + { + mic_cfg.pin_data_in = GPIO_NUM_46; + mic_cfg.pin_ws = GPIO_NUM_43; + } + break; + + case board_t::board_M5CardputerADV: + if (cfg.internal_mic) + { + mic_cfg.pin_data_in = GPIO_NUM_46; + mic_cfg.pin_ws = GPIO_NUM_43; + mic_cfg.pin_bck = GPIO_NUM_41; + mic_enable_cb = _microphone_enabled_cb_cardputer_adv; + } + break; + + case board_t::board_M5Capsule: + if (cfg.internal_mic) + { + mic_cfg.pin_data_in = GPIO_NUM_41; + mic_cfg.pin_ws = GPIO_NUM_40; + } + break; + +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + case board_t::board_M5Stack: + if (cfg.internal_mic) + { + mic_cfg.pin_data_in = GPIO_NUM_34; // M5GO bottom MIC + mic_cfg.i2s_port = I2S_NUM_0; + mic_cfg.use_adc = true; // use ADC analog input + mic_cfg.over_sampling = 4; + } + break; + + case board_t::board_M5StickC: + case board_t::board_M5StickCPlus: + if (cfg.internal_mic) + { /// builtin PDM mic + mic_cfg.pin_data_in = GPIO_NUM_34; + mic_cfg.pin_ws = GPIO_NUM_0; + mic_enable_cb = _microphone_enabled_cb_stickc; + } + break; + + case board_t::board_M5StickCPlus2: + case board_t::board_M5Tough: + case board_t::board_M5StackCore2: + if (cfg.internal_mic) + { /// builtin PDM mic + mic_cfg.pin_data_in = GPIO_NUM_34; + mic_cfg.pin_ws = GPIO_NUM_0; + } + break; + + case board_t::board_M5AtomU: + { /// ATOM U builtin PDM mic + mic_cfg.pin_data_in = GPIO_NUM_19; + mic_cfg.pin_ws = GPIO_NUM_5; + } + break; + + case board_t::board_M5AtomEcho: + { /// ATOM ECHO builtin PDM mic + mic_cfg.pin_data_in = GPIO_NUM_23; + mic_cfg.pin_ws = GPIO_NUM_33; + } + break; +#endif + default: + break; + } + } + + if (cfg.external_spk_detail.enabled && cfg.external_speaker_value == 0) { + cfg.external_speaker.atomic_spk = false==cfg.external_spk_detail.omit_atomic_spk; + cfg.external_speaker.hat_spk = false==cfg.external_spk_detail.omit_spk_hat; + } + + if (cfg.internal_spk || cfg.external_speaker_value) + { + // set default speaker gain. + spk_cfg.magnification = 16; +#if defined SOC_I2S_NUM + spk_cfg.i2s_port = (i2s_port_t)(SOC_I2S_NUM - 1); +#else + spk_cfg.i2s_port = (i2s_port_t)(I2S_NUM_MAX - 1); +#endif + switch (_board) + { +#if defined (M5UNIFIED_PC_BUILD) +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + case board_t::board_M5UnitC6L: + case board_t::board_ArduinoNessoN1: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_11; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + +#elif defined (CONFIG_IDF_TARGET_ESP32P4) + case board_t::board_M5Tab5: + if (cfg.internal_spk) + { + spk_cfg.pin_mck = GPIO_NUM_30; + spk_cfg.pin_bck = GPIO_NUM_27; + spk_cfg.pin_ws = GPIO_NUM_29; +// spk_cfg.pin_data_in = GPIO_NUM_28; + spk_cfg.pin_data_out = GPIO_NUM_26; + spk_cfg.magnification = 4; + spk_cfg.i2s_port = I2S_NUM_0; + spk_enable_cb = _speaker_enabled_cb_tab5; + } + break; + +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + case board_t::board_M5StackCoreS3: + case board_t::board_M5StackCoreS3SE: + if (cfg.internal_spk) + { + spk_cfg.pin_bck = GPIO_NUM_34; + spk_cfg.pin_ws = GPIO_NUM_33; + spk_cfg.pin_data_out = GPIO_NUM_13; + spk_cfg.magnification = 4; + spk_cfg.i2s_port = I2S_NUM_1; + spk_enable_cb = _speaker_enabled_cb_cores3; + } + break; + + case board_t::board_M5AtomS3: + case board_t::board_M5AtomS3Lite: + case board_t::board_M5AtomS3R: + case board_t::board_M5AtomS3RCam: + case board_t::board_M5AtomS3RExt: + if (cfg.external_speaker.atomic_spk || cfg.external_speaker.atomic_echo) + { // for ATOMIC SPK / ATOMIC ECHO BASE + bool atomdisplay = false; + for (int i = 0; i < getDisplayCount(); ++i) { + if (Displays(i).getBoard() == board_t::board_M5AtomDisplay) { + atomdisplay = true; + break; + } + } + if (!atomdisplay) { + bool flg_atomic_spk = false; + if (cfg.external_speaker.atomic_spk) { + m5gfx::pinMode(GPIO_NUM_6, m5gfx::pin_mode_t::input_pulldown); // MOSI + m5gfx::pinMode(GPIO_NUM_7, m5gfx::pin_mode_t::input_pulldown); // SCLK + if (m5gfx::gpio_in(GPIO_NUM_6) + && m5gfx::gpio_in(GPIO_NUM_7)) + { + flg_atomic_spk = true; + ESP_LOGD("M5Unified", "ATOMIC SPK"); + // atomic_spkのSDカード用ピンを割当 + _get_pin_table[sd_spi_sclk] = GPIO_NUM_7; + _get_pin_table[sd_spi_copi] = GPIO_NUM_6; + _get_pin_table[sd_spi_cipo] = GPIO_NUM_8; + cfg.internal_imu = false; /// avoid conflict with i2c + cfg.internal_rtc = false; /// avoid conflict with i2c + spk_cfg.pin_bck = GPIO_NUM_5; + spk_cfg.pin_ws = GPIO_NUM_39; + spk_cfg.pin_data_out = GPIO_NUM_38; + spk_cfg.magnification = 16; + } + } + if (cfg.external_speaker.atomic_echo && !flg_atomic_spk) { + spk_cfg.pin_bck = GPIO_NUM_8; + spk_cfg.pin_ws = GPIO_NUM_6; + spk_cfg.pin_data_out = GPIO_NUM_5; + spk_cfg.magnification = 1; + spk_enable_cb = _speaker_enabled_cb_atomic_echo; + + mic_cfg.i2s_port = spk_cfg.i2s_port; + mic_cfg.pin_bck = GPIO_NUM_8; + mic_cfg.pin_ws = GPIO_NUM_6; + mic_cfg.pin_data_in = GPIO_NUM_7; + mic_cfg.magnification = 1; + mic_cfg.over_sampling = 1; + mic_cfg.pin_mck = GPIO_NUM_NC; + mic_cfg.stereo = false; + mic_enable_cb = _microphone_enabled_cb_atomic_echo; + } + } + } + break; + + case board_t::board_M5AtomEchoS3R: + if (cfg.internal_mic) { + cfg.internal_imu = false; + + // spk_cfg.pin_mck = GPIO_NUM_11; + spk_cfg.pin_bck = GPIO_NUM_17; + spk_cfg.pin_ws = GPIO_NUM_3; + spk_cfg.pin_data_out = GPIO_NUM_48; + spk_cfg.magnification = 1; + spk_cfg.i2s_port = I2S_NUM_1; + spk_enable_cb = _speaker_enabled_cb_atom_echos3r; + + mic_cfg.i2s_port = spk_cfg.i2s_port; + mic_cfg.pin_mck = GPIO_NUM_11; + mic_cfg.pin_bck = GPIO_NUM_17; + mic_cfg.pin_ws = GPIO_NUM_3; + mic_cfg.pin_data_in = GPIO_NUM_4; + mic_cfg.magnification = 1; + mic_cfg.over_sampling = 1; + mic_cfg.stereo = true; + mic_enable_cb = _microphone_enabled_cb_atom_echos3r; + } + break; + + case board_t::board_M5Capsule: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_2; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + + case board_t::board_M5Dial: + case board_t::board_M5DinMeter: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_3; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + + case board_t::board_M5AirQ: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_9; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + + case board_t::board_M5VAMeter: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_14; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + + case board_t::board_M5PaperS3: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_21; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + + case board_t::board_M5Cardputer: + case board_t::board_M5CardputerADV: + if (cfg.internal_spk) + { + spk_cfg.pin_bck = GPIO_NUM_41; + spk_cfg.pin_ws = GPIO_NUM_43; + spk_cfg.pin_data_out = GPIO_NUM_42; + spk_cfg.magnification = 16; + spk_cfg.i2s_port = I2S_NUM_1; + if (_board == board_t::board_M5CardputerADV) { + spk_enable_cb = _speaker_enabled_cb_cardputer_adv; + } + } + break; + + case board_t::board_M5StampPLC: + if (cfg.internal_spk) + { + spk_cfg.pin_data_out = GPIO_NUM_44; + spk_cfg.buzzer = true; + spk_cfg.magnification = 48; + } + break; + +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + case board_t::board_M5Stack: + if (cfg.internal_spk) + { + m5gfx::gpio_lo(GPIO_NUM_25); + m5gfx::pinMode(GPIO_NUM_25, m5gfx::pin_mode_t::output); + spk_cfg.i2s_port = I2S_NUM_0; + spk_cfg.use_dac = true; + spk_cfg.pin_data_out = GPIO_NUM_25; + spk_cfg.magnification = 8; + spk_cfg.sample_rate *= 2; + } + break; + + case board_t::board_M5StackCoreInk: + case board_t::board_M5StickCPlus: + case board_t::board_M5StickCPlus2: + if (cfg.internal_spk) + { + spk_cfg.buzzer = true; + spk_cfg.pin_data_out = GPIO_NUM_2; + spk_cfg.magnification = 48; + } + NON_BREAK; + + case board_t::board_M5StickC: + if (cfg.external_speaker.hat_spk2 && (_board != board_t::board_M5StackCoreInk)) + { /// for HAT SPK2 (for StickC/StickCPlus. CoreInk does not support.) + spk_cfg.pin_data_out = GPIO_NUM_25; + spk_cfg.pin_bck = GPIO_NUM_26; + spk_cfg.pin_ws = GPIO_NUM_0; + spk_cfg.i2s_port = I2S_NUM_1; + spk_cfg.use_dac = false; + spk_cfg.buzzer = false; + spk_cfg.magnification = 16; + } + else if (cfg.external_speaker.hat_spk) + { /// for HAT SPK + gpio_num_t pin_en = _board == board_t::board_M5StackCoreInk ? GPIO_NUM_25 : GPIO_NUM_0; + m5gfx::gpio_lo(pin_en); + m5gfx::pinMode(pin_en, m5gfx::pin_mode_t::output); + m5gfx::gpio_lo(GPIO_NUM_26); + m5gfx::pinMode(GPIO_NUM_26, m5gfx::pin_mode_t::output); + spk_cfg.pin_data_out = GPIO_NUM_26; + spk_cfg.i2s_port = I2S_NUM_0; + spk_cfg.use_dac = true; + spk_cfg.buzzer = false; + spk_cfg.magnification = 32; + spk_enable_cb = _speaker_enabled_cb_hat_spk; + } + break; + + case board_t::board_M5Tough: + // The magnification is set higher than Core2 here because the waterproof case reduces the sound.; + spk_cfg.magnification = 24; + NON_BREAK; + case board_t::board_M5StackCore2: + if (cfg.internal_spk) + { + spk_cfg.pin_bck = GPIO_NUM_12; + spk_cfg.pin_ws = GPIO_NUM_0; + spk_cfg.pin_data_out = GPIO_NUM_2; + spk_enable_cb = _speaker_enabled_cb_core2; + } + break; + + case board_t::board_M5AtomEcho: + if (cfg.internal_spk && (Display.getBoard() != board_t::board_M5AtomDisplay)) + { // for ATOM ECHO + spk_cfg.pin_bck = GPIO_NUM_19; + spk_cfg.pin_ws = GPIO_NUM_33; + spk_cfg.pin_data_out = GPIO_NUM_22; + spk_cfg.magnification = 12; + } + NON_BREAK; + case board_t::board_M5AtomLite: + case board_t::board_M5AtomMatrix: + case board_t::board_M5AtomPsram: + if (cfg.external_speaker.atomic_spk || cfg.external_speaker.atomic_echo) + { // for ATOMIC SPK / ATOMIC ECHO BASE + bool atomdisplay = false; + for (int i = 0; i < getDisplayCount(); ++i) { + if (Displays(i).getBoard() == board_t::board_M5AtomDisplay) { + atomdisplay = true; + break; + } + } + if (!atomdisplay) { + bool flg_atomic_spk = false; + if (cfg.external_speaker.atomic_spk) { + // 19,23 pulldown read check ( all high = ATOMIC_SPK ? ) // MISO is not used for judgment as it changes depending on the state of the SD card. + gpio_num_t pin = (_board == board_t::board_M5AtomPsram) ? GPIO_NUM_5 : GPIO_NUM_23; + m5gfx::pinMode(GPIO_NUM_19, m5gfx::pin_mode_t::input_pulldown); // MOSI + m5gfx::pinMode(pin , m5gfx::pin_mode_t::input_pulldown); // SCLK + if (m5gfx::gpio_in(GPIO_NUM_19) + && m5gfx::gpio_in(pin )) + { + flg_atomic_spk = true; + ESP_LOGD("M5Unified", "ATOMIC SPK"); + // atomic_spkのSDカード用ピンを割当 + _get_pin_table[sd_spi_sclk] = pin; + _get_pin_table[sd_spi_copi] = GPIO_NUM_19; + _get_pin_table[sd_spi_cipo] = GPIO_NUM_33; + cfg.internal_imu = false; /// avoid conflict with i2c + cfg.internal_rtc = false; /// avoid conflict with i2c + spk_cfg.pin_bck = GPIO_NUM_22; + spk_cfg.pin_ws = GPIO_NUM_21; + spk_cfg.pin_data_out = GPIO_NUM_25; + spk_cfg.magnification = 16; + auto mic = Mic.config(); + mic.pin_data_in = -1; // disable mic for ATOMECHO + Mic.config(mic); + } + } + if (cfg.external_speaker.atomic_echo && !flg_atomic_spk) { + spk_cfg.pin_bck = GPIO_NUM_33; + spk_cfg.pin_ws = GPIO_NUM_19; + spk_cfg.pin_data_out = GPIO_NUM_22; + spk_cfg.magnification = 1; + spk_enable_cb = _speaker_enabled_cb_atomic_echo; + + mic_cfg.i2s_port = spk_cfg.i2s_port; + mic_cfg.pin_bck = GPIO_NUM_33; + mic_cfg.pin_ws = GPIO_NUM_19; + mic_cfg.pin_data_in = GPIO_NUM_23; + mic_cfg.magnification = 1; + mic_cfg.over_sampling = 1; + mic_cfg.pin_mck = GPIO_NUM_NC; + mic_cfg.stereo = false; + mic_enable_cb = _microphone_enabled_cb_atomic_echo; + } + } + } + break; +#endif + default: + break; + } + + if (cfg.external_speaker_value) + { +#if defined (M5UNIFIED_PC_BUILD) +#elif defined ( CONFIG_IDF_TARGET_ESP32P4 ) + #define ENABLE_M5MODULE + if (_board == board_t::board_M5Tab5) +#elif defined ( CONFIG_IDF_TARGET_ESP32S3 ) + #define ENABLE_M5MODULE + if (_board == board_t::board_M5StackCoreS3 + || _board == board_t::board_M5StackCoreS3SE) +#elif defined ( CONFIG_IDF_TARGET_ESP32 ) || !defined ( CONFIG_IDF_TARGET ) + #define ENABLE_M5MODULE + if ( _board == board_t::board_M5Stack + || _board == board_t::board_M5StackCore2 + || _board == board_t::board_M5Tough) +#endif + { +#ifdef ENABLE_M5MODULE + bool use_module_display = cfg.external_speaker.module_display + && (0 <= getDisplayIndex(m5gfx::board_M5ModuleDisplay)); + if (use_module_display || cfg.external_speaker.module_rca) + { + if (use_module_display) { + spk_cfg.sample_rate = 48000; // Module Display audio output is fixed at 48 kHz + } + // ModuleDisplay or Module RCA + spk_cfg.pin_bck = getPin(use_module_display ? pin_name_t::mbus_pin21 : pin_name_t::mbus_pin22); + spk_cfg.pin_data_out = getPin(pin_name_t::mbus_pin23); + spk_cfg.pin_ws = getPin(pin_name_t::mbus_pin24); // LRCK + + spk_cfg.i2s_port = I2S_NUM_1; + spk_cfg.magnification = 16; + spk_cfg.stereo = true; + spk_cfg.buzzer = false; + spk_cfg.use_dac = false; + spk_enable_cb = nullptr; + } + #undef ENABLE_M5MODULE +#endif + } + } + } + if (mic_cfg.pin_data_in >= 0) + { + Mic.setCallback(this, mic_enable_cb); + Mic.config(mic_cfg); + } + if (spk_cfg.pin_data_out >= 0) + { + Speaker.setCallback(this, spk_enable_cb); + Speaker.config(spk_cfg); + } + } + + bool M5Unified::_begin_rtc_imu(const config_t& cfg) + { + bool port_a_used = false; + if (cfg.external_rtc || cfg.external_imu) + { + M5.Ex_I2C.begin(); + } + + if (cfg.internal_rtc && In_I2C.isEnabled()) + { + M5.Rtc.begin(&M5.In_I2C, M5.getBoard()); + } + if (!M5.Rtc.isEnabled() && cfg.external_rtc && Ex_I2C.isEnabled()) + { + port_a_used = M5.Rtc.begin(&M5.Ex_I2C); + } + if (M5.Rtc.isEnabled()) + { + M5.Rtc.setSystemTimeFromRtc(); + if (cfg.disable_rtc_irq) { + M5.Rtc.disableIRQ(); + } + } + + if (cfg.internal_imu && In_I2C.isEnabled()) + { + M5.Imu.begin(&M5.In_I2C, M5.getBoard()); + } + if (!M5.Imu.isEnabled() && cfg.external_imu && Ex_I2C.isEnabled()) + { + port_a_used = M5.Imu.begin(&M5.Ex_I2C) || port_a_used; + } + return port_a_used; + } + + void M5Unified::update( void ) + { + auto ms = m5gfx::millis(); + _updateMsec = ms; + + // 1=BtnA / 2=BtnB / 4=BtnC / 8=BtnEXT / 16=BtnPWR + uint_fast8_t use_rawstate_bits = 0; + uint_fast8_t btn_rawstate_bits = 0; + + if (Touch.isEnabled()) + { + Touch.update(ms); + + int tb_y = 0; + int tb_k = 0; + switch (_board) + { + case board_t::board_M5StackCore2: + case board_t::board_M5Tough: + case board_t::board_M5StackCoreS3SE: + case board_t::board_M5StackCoreS3: + tb_y = 240; + tb_k = 614; // (65536*3/320) + break; + case board_t::board_M5Paper: + case board_t::board_M5PaperS3: + tb_y = 960; + tb_k = 364; // (65536*3/540) + break; + case board_t::board_M5Tab5: + tb_y = 1280; + tb_k = 273; // (65536*3/540) + break; + default: + break; + } + + if (tb_k) + { + tb_y -= _touch_button_height; + if (tb_y < 0) { tb_y = 0; } + + use_rawstate_bits = 0b00111; + int i = Touch.getCount(); + while (--i >= 0) + { + auto raw = Touch.getTouchPointRaw(i); + if (raw.y >= tb_y) + { + auto det = Touch.getDetail(i); + if (det.state & touch_state_t::touch) + { + if (BtnA.isPressed()) { btn_rawstate_bits |= 1 << 0; } + if (BtnB.isPressed()) { btn_rawstate_bits |= 1 << 1; } + if (BtnC.isPressed()) { btn_rawstate_bits |= 1 << 2; } + if (btn_rawstate_bits || !(det.state & touch_state_t::mask_moving)) + { + btn_rawstate_bits |= 1 << ((raw.x * tb_k) >> 16); + } + } + } + } + } + } + +#if defined (M5UNIFIED_PC_BUILD) + use_rawstate_bits = 0b10111; + btn_rawstate_bits = (!m5gfx::gpio_in(39) ? 0b00001 : 0) // LEFT=BtnA + | (!m5gfx::gpio_in(38) ? 0b00010 : 0) // DOWN=BtnB + | (!m5gfx::gpio_in(37) ? 0b00100 : 0) // RIGHT=BtnC + | (!m5gfx::gpio_in(36) ? 0b10000 : 0) // UP=BtnPWR + ; +#elif !defined (CONFIG_IDF_TARGET) || defined (CONFIG_IDF_TARGET_ESP32) + + uint_fast8_t raw_gpio32_39 = ~GPIO.in1.data; + switch (_board) + { + case board_t::board_M5StackCoreInk: + { + uint32_t raw_gpio0_31 = ~GPIO.in; + use_rawstate_bits = 0b11000; + btn_rawstate_bits = (((raw_gpio0_31 >> CoreInk_BUTTON_EXT_PIN) & 1) << 3) + | (((raw_gpio0_31 >> CoreInk_BUTTON_PWR_PIN) & 1) << 4); + } + NON_BREAK; /// don't break; + + case board_t::board_M5Paper: + case board_t::board_M5Station: + use_rawstate_bits |= 0b00111; + btn_rawstate_bits |= (raw_gpio32_39 >> (GPIO_NUM_37 & 31)) & 0x07; // gpio37 A / gpio38 B / gpio39 C + break; + + case board_t::board_M5Stack: + use_rawstate_bits = 0b00111; + btn_rawstate_bits = (((raw_gpio32_39 >> (GPIO_NUM_38 & 31)) & 1) << 1) // gpio38 B + | (((raw_gpio32_39 >> (GPIO_NUM_37 & 31)) & 1) << 2); // gpio37 C + NON_BREAK; /// don't break; + + case board_t::board_M5AtomLite: + case board_t::board_M5AtomMatrix: + case board_t::board_M5AtomEcho: + case board_t::board_M5AtomPsram: + case board_t::board_M5AtomU: + case board_t::board_M5StampPico: + use_rawstate_bits |= 0b00001; + btn_rawstate_bits |= (raw_gpio32_39 >> (GPIO_NUM_39 & 31)) & 1; // gpio39 A + break; + + case board_t::board_M5StickCPlus2: + use_rawstate_bits = 0b10000; + btn_rawstate_bits = (((raw_gpio32_39 >> (GPIO_NUM_35 & 31)) & 1)<<4); // gpio35 PWR + NON_BREAK; /// don't break; + + case board_t::board_M5StickC: + case board_t::board_M5StickCPlus: + use_rawstate_bits |= 0b00011; + btn_rawstate_bits |= (( raw_gpio32_39 >> (GPIO_NUM_37 & 31)) & 1 ) // gpio37 A + | (((raw_gpio32_39 >> (GPIO_NUM_39 & 31)) & 1)<<1); // gpio39 B + break; + + default: + break; + } + +#elif defined (CONFIG_IDF_TARGET_ESP32S3) + + switch (_board) + { + case board_t::board_M5AirQ: + use_rawstate_bits = 0b00011; + btn_rawstate_bits = ((!m5gfx::gpio_in(GPIO_NUM_0)) & 1) + | ((!m5gfx::gpio_in(GPIO_NUM_8)) & 1) << 1; + break; + + case board_t::board_M5VAMeter: + use_rawstate_bits = 0b00011; + btn_rawstate_bits = ((!m5gfx::gpio_in(GPIO_NUM_2)) & 1) + | ((!m5gfx::gpio_in(GPIO_NUM_0)) & 1) << 1; + break; + + case board_t::board_M5StampS3: + case board_t::board_M5Cardputer: + case board_t::board_M5CardputerADV: + use_rawstate_bits = 0b00001; + btn_rawstate_bits = (!m5gfx::gpio_in(GPIO_NUM_0)) & 1; + break; + + case board_t::board_M5AtomS3: + case board_t::board_M5AtomS3Lite: + case board_t::board_M5AtomS3U: + case board_t::board_M5AtomS3R: + case board_t::board_M5AtomEchoS3R: + use_rawstate_bits = 0b00001; + btn_rawstate_bits = (!m5gfx::gpio_in(GPIO_NUM_41)) & 1; + break; + + case board_t::board_M5Capsule: + case board_t::board_M5Dial: + case board_t::board_M5DinMeter: + use_rawstate_bits = 0b00011; + btn_rawstate_bits = ((!m5gfx::gpio_in(GPIO_NUM_42)) & 1) + | ((!m5gfx::gpio_in(GPIO_NUM_0)) & 1) << 1; + break; + + case board_t::board_M5StampPLC: + { + use_rawstate_bits = 0b00111; + auto value = _io_expander[0]->readRegister8(0x0F); + btn_rawstate_bits = (!(value & 0b100) ? 0b00001 : 0) // BtnA + | (!(value & 0b010) ? 0b00010 : 0) // BtnB + | (!(value & 0b001) ? 0b00100 : 0) // BtnC + ; + } + break; + + default: + + break; + } + +#elif defined (CONFIG_IDF_TARGET_ESP32C3) + + switch (_board) + { + case board_t::board_M5StampC3: + use_rawstate_bits = 0b00001; + btn_rawstate_bits = (!m5gfx::gpio_in(GPIO_NUM_3)) & 1; + break; + + case board_t::board_M5StampC3U: + use_rawstate_bits = 0b00001; + btn_rawstate_bits = (!m5gfx::gpio_in(GPIO_NUM_9)) & 1; + break; + + default: + break; + } + +#elif defined (CONFIG_IDF_TARGET_ESP32C6) + + switch (_board) + { + case board_t::board_M5NanoC6: + use_rawstate_bits = 0b00001; + btn_rawstate_bits = (!m5gfx::gpio_in(GPIO_NUM_9) ? 0b00001 : 0); + break; + + case board_t::board_M5UnitC6L: + { + use_rawstate_bits = 0b00001; + auto value = _io_expander[0]->readRegister8(0x0F); + btn_rawstate_bits = (!(value & 0b001) ? 0b00001 : 0); // BtnA + } + break; + + case board_t::board_ArduinoNessoN1: + { + use_rawstate_bits = 0b00011; + auto value = _io_expander[0]->readRegister8(0x0F); + btn_rawstate_bits = (!(value & 0b001) ? 0b00001 : 0) // BtnA + | (!(value & 0b010) ? 0b00010 : 0) // BtnB + ; + } + break; + + default: + break; + } + +#endif + + if (use_rawstate_bits) { + for (int i = 0; i < 5; ++i) { + if (use_rawstate_bits & (1 << i)) { + _buttons[i].setRawState(ms, btn_rawstate_bits & (1 << i)); + } + } + } + +#if defined (CONFIG_IDF_TARGET_ESP32) || defined (CONFIG_IDF_TARGET_ESP32S3) + if (_use_pmic_button) + { + Button_Class::button_state_t state = Button_Class::button_state_t::state_nochange; + bool read_axp = (ms - BtnPWR.getUpdateMsec()) >= BTNPWR_MIN_UPDATE_MSEC; + if (read_axp || BtnPWR.getState()) + { + switch (Power.getKeyState()) + { + case 0: break; + case 2: state = Button_Class::button_state_t::state_clicked; break; + default: state = Button_Class::button_state_t::state_hold; break; + } + BtnPWR.setState(ms, state); + } + } +#endif + } + + void M5Unified::setTouchButtonHeightByRatio(uint8_t ratio) + { + uint32_t height = 0; + switch (_board) + { + case board_t::board_M5StackCore2: + case board_t::board_M5Tough: + case board_t::board_M5StackCoreS3SE: + case board_t::board_M5StackCoreS3: + height = 240; + break; + case board_t::board_M5Paper: + case board_t::board_M5PaperS3: + height = 960; + break; + case board_t::board_M5Tab5: + height = 1280; + break; + default: + break; + } + _touch_button_height = height * ratio / 255; + } + + M5GFX& M5Unified::getDisplay(size_t index) + { + return index != _primary_display_index && index < this->_displays.size() ? this->_displays[index] : Display; + } + + std::size_t M5Unified::addDisplay(M5GFX& dsp) + { + this->_displays.push_back(dsp); + auto res = this->_displays.size() - 1; + setPrimaryDisplay(res == 0 ? 0 : _primary_display_index); + + // Touch screen operation is always limited to the first display. + Touch.begin(_displays.front().touch() ? &_displays.front() : nullptr); + + return res; + } + + int32_t M5Unified::getDisplayIndex(m5gfx::board_t board) { + int i = 0; + for (auto &d : _displays) + { + if (board == d.getBoard()) { return i; } + ++i; + } + return -1; + } + + int32_t M5Unified::getDisplayIndex(std::initializer_list board_list) + { + for (auto b : board_list) + { + int32_t i = getDisplayIndex(b); + if (i >= 0) { return i; } + } + return -1; + } + + bool M5Unified::setPrimaryDisplay(std::size_t index) + { + if (index >= _displays.size()) { return false; } + std::size_t pdi = _primary_display_index; + + if (pdi < _displays.size()) + { + _displays[pdi] = Display; + } + _primary_display_index = index; + Display = _displays[index]; + return true; + } + + bool M5Unified::setPrimaryDisplayType(std::initializer_list board_list) + { + auto i = getDisplayIndex(board_list); + bool res = (i >= 0); + if (res) { setPrimaryDisplay(i); } + return res; + } + + void M5Unified::setLogDisplayIndex(size_t index) + { + Log.setDisplay(getDisplay(index)); + } + + void M5Unified::setLogDisplayType(std::initializer_list board_list) + { + auto i = getDisplayIndex(board_list); + if (i >= 0) { Log.setDisplay(getDisplay(i)); } + } +} diff --git a/drivers/mipi_dbi/mipi_dbi_spi.c b/drivers/mipi_dbi/mipi_dbi_spi.c index 003ba73ab3118..191d98a627367 100644 --- a/drivers/mipi_dbi/mipi_dbi_spi.c +++ b/drivers/mipi_dbi/mipi_dbi_spi.c @@ -10,11 +10,20 @@ #include #include #include +#if defined(CONFIG_PINCTRL) +#include +#endif #include +#include #include LOG_MODULE_REGISTER(mipi_dbi_spi, CONFIG_MIPI_DBI_LOG_LEVEL); +#if defined(CONFIG_PINCTRL) +#define PINCTRL_STATE_CMD_DATA_INPUT PINCTRL_STATE_PRIV_START +#define PINCTRL_STATE_CMD_DATA_OUTPUT (PINCTRL_STATE_PRIV_START + 1U) +#endif + /* Expands to 1 if the node does not have the `write-only` property */ #define MIPI_DBI_SPI_WRITE_ONLY_ABSENT(n) (!DT_INST_PROP(n, write_only)) | @@ -58,35 +67,108 @@ uint32_t var = MIPI_DBI_SPI_READ_REQUIRED; #define MIPI_DBI_DC_BIT BIT(8) struct mipi_dbi_spi_config { - /* SPI hardware used to send data */ - const struct device *spi_dev; - /* Command/Data gpio */ - const struct gpio_dt_spec cmd_data; - /* Tearing Effect GPIO */ - const struct gpio_dt_spec tearing_effect; - /* Reset GPIO */ - const struct gpio_dt_spec reset; - /* Minimum transfer bits */ - const uint8_t xfr_min_bits; + /* SPI hardware used to send data */ + const struct device *spi_dev; + /* Command/Data gpio */ + const struct gpio_dt_spec cmd_data; + /* Tearing Effect GPIO */ + const struct gpio_dt_spec tearing_effect; + /* Reset GPIO */ + const struct gpio_dt_spec reset; + /* Minimum transfer bits */ + const uint8_t xfr_min_bits; + /* Tri-state D/C pin between transfers */ + const bool cmd_data_tristate; +#if defined(CONFIG_PINCTRL) + /* Optional pinctrl configuration for command/data tristate */ + const struct pinctrl_dev_config *pinctrl; +#endif }; struct mipi_dbi_spi_data { - struct k_mutex lock; + struct k_mutex lock; #if MIPI_DBI_SPI_TE_REQUIRED - struct k_sem te_signal; - k_timeout_t te_delay; - atomic_t in_active_area; - struct gpio_callback te_cb_data; + struct k_sem te_signal; + k_timeout_t te_delay; + atomic_t in_active_area; + struct gpio_callback te_cb_data; +#endif + /* Used for 3 wire mode */ + uint16_t spi_byte; + bool cmd_data_is_output; +#if defined(CONFIG_PINCTRL) + const struct pinctrl_state *cmd_data_input_state; + const struct pinctrl_state *cmd_data_output_state; + bool use_pinctrl_cmd_data; #endif - /* Used for 3 wire mode */ - uint16_t spi_byte; }; +static int mipi_dbi_spi_cmd_data_set(const struct device *dev, int value) +{ + const struct mipi_dbi_spi_config *config = dev->config; + struct mipi_dbi_spi_data *data = dev->data; + int ret = 0; + + if (!mipi_dbi_has_pin(&config->cmd_data)) { + return 0; + } + + if (config->cmd_data_tristate && !data->cmd_data_is_output) { +#if defined(CONFIG_PINCTRL) + if (data->use_pinctrl_cmd_data) { + ret = pinctrl_apply_state_direct(config->pinctrl, + data->cmd_data_output_state); + } else +#endif + { + ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT); + } + + if (ret < 0) { + return ret; + } + + data->cmd_data_is_output = true; + } + + return gpio_pin_set_dt(&config->cmd_data, value); +} + +static void mipi_dbi_spi_cmd_data_release(const struct device *dev) +{ + const struct mipi_dbi_spi_config *config = dev->config; + struct mipi_dbi_spi_data *data = dev->data; + + if (!config->cmd_data_tristate || !data->cmd_data_is_output || + !mipi_dbi_has_pin(&config->cmd_data)) { + return; + } + + int ret; + +#if defined(CONFIG_PINCTRL) + if (data->use_pinctrl_cmd_data) { + ret = pinctrl_apply_state_direct(config->pinctrl, + data->cmd_data_input_state); + } else +#endif + { + ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_INPUT); + } + + if (ret < 0) { + LOG_WRN("Failed to tri-state D/C GPIO (%d)", ret); + return; + } + + data->cmd_data_is_output = false; +} + #if MIPI_DBI_SPI_TE_REQUIRED static void mipi_dbi_spi_te_cb(const struct device *dev, - struct gpio_callback *cb, - uint32_t pins) + struct gpio_callback *cb, + uint32_t pins) { ARG_UNUSED(dev); ARG_UNUSED(pins); @@ -178,33 +260,39 @@ mipi_dbi_spi_write_helper_4wire_8bit(const struct device *dev, LOG_WRN("4wire_8bit"); - if (cmd_present) { - /* Set CD pin low for command */ - LOG_WRN("4wire_8bit 1"); - gpio_pin_set_dt(&config->cmd_data, 0); - LOG_WRN("4wire_8bit %p", config->spi_dev); - ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set); - if (ret < 0) { - goto out; - } - } + if (cmd_present) { + /* Set CD pin low for command */ + LOG_WRN("4wire_8bit 1"); + ret = mipi_dbi_spi_cmd_data_set(dev, 0); + if (ret < 0) { + goto out; + } + LOG_WRN("4wire_8bit %p", config->spi_dev); + ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set); + if (ret < 0) { + goto out; + } + } if (len > 0) { buffer.buf = (void *)data_buf; buffer.len = len; - LOG_WRN("4wire_8bit 3"); - /* Set CD pin high for data */ - gpio_pin_set_dt(&config->cmd_data, 1); - LOG_WRN("4wire_8bit 4"); - ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set); - if (ret < 0) { - goto out; - } - } + LOG_WRN("4wire_8bit 3"); + /* Set CD pin high for data */ + ret = mipi_dbi_spi_cmd_data_set(dev, 1); + if (ret < 0) { + goto out; + } + LOG_WRN("4wire_8bit 4"); + ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set); + if (ret < 0) { + goto out; + } + } out: - LOG_WRN("4wire_8bit 5"); - return ret; + LOG_WRN("4wire_8bit 5"); + return ret; } #endif /* MIPI_DBI_SPI_WRITE_8BIT_REQUIRED */ @@ -232,23 +320,29 @@ mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev, * but send 16-bit blocks (with bit stuffing). */ - if (cmd_present) { - data16 = sys_cpu_to_be16(cmd); - buffer.buf = &data16; - buffer.len = sizeof(data16); - - /* Set CD pin low for command */ - gpio_pin_set_dt(&config->cmd_data, 0); - ret = spi_write(config->spi_dev, &dbi_config->config, - &buf_set); - if (ret < 0) { - goto out; - } - - /* Set CD pin high for data, if there are any */ - if (len > 0) { - gpio_pin_set_dt(&config->cmd_data, 1); - } + if (cmd_present) { + data16 = sys_cpu_to_be16(cmd); + buffer.buf = &data16; + buffer.len = sizeof(data16); + + /* Set CD pin low for command */ + ret = mipi_dbi_spi_cmd_data_set(dev, 0); + if (ret < 0) { + goto out; + } + ret = spi_write(config->spi_dev, &dbi_config->config, + &buf_set); + if (ret < 0) { + goto out; + } + + /* Set CD pin high for data, if there are any */ + if (len > 0) { + ret = mipi_dbi_spi_cmd_data_set(dev, 1); + if (ret < 0) { + goto out; + } + } /* iterate command data */ for (int i = 0; i < len; i++) { @@ -263,10 +357,13 @@ mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev, } else { int stuffing = len % sizeof(data16); - /* Set CD pin high for data, if there are any */ - if (len > 0) { - gpio_pin_set_dt(&config->cmd_data, 1); - } + /* Set CD pin high for data, if there are any */ + if (len > 0) { + ret = mipi_dbi_spi_cmd_data_set(dev, 1); + if (ret < 0) { + goto out; + } + } /* pass through generic device data */ if (len - stuffing > 0) { @@ -294,7 +391,7 @@ mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev, } } out: - return ret; + return ret; } #endif /* MIPI_DBI_SPI_WRITE_16BIT_REQUIRED */ @@ -354,9 +451,10 @@ static int mipi_dbi_spi_write_helper(const struct device *dev, ret = -ENOTSUP; out: - LOG_WRN("write_helper end"); - k_mutex_unlock(&data->lock); - return ret; + LOG_WRN("write_helper end"); + mipi_dbi_spi_cmd_data_release(dev); + k_mutex_unlock(&data->lock); + return ret; } static int mipi_dbi_spi_command_write(const struct device *dev, @@ -485,35 +583,41 @@ mipi_dbi_spi_read_helper_4wire(const struct device *dev, memcpy(&tmp_config, &dbi_config->config, sizeof(tmp_config)); tmp_config.operation |= SPI_HOLD_ON_CS; - if (num_cmds > 0) { - buffer.buf = cmds; - buffer.len = num_cmds; - - /* Set CD pin low for command */ - gpio_pin_set_dt(&config->cmd_data, 0); - - ret = spi_write(config->spi_dev, &tmp_config, &buf_set); - if (ret < 0) { - goto out; - } - } - - if (len > 0) { - buffer.buf = (void *)response; - buffer.len = len; - - /* Set CD pin high for data */ - gpio_pin_set_dt(&config->cmd_data, 1); - - ret = spi_read(config->spi_dev, &tmp_config, &buf_set); - if (ret < 0) { - goto out; - } - } + if (num_cmds > 0) { + buffer.buf = cmds; + buffer.len = num_cmds; + + /* Set CD pin low for command */ + ret = mipi_dbi_spi_cmd_data_set(dev, 0); + if (ret < 0) { + goto out; + } + + ret = spi_write(config->spi_dev, &tmp_config, &buf_set); + if (ret < 0) { + goto out; + } + } + + if (len > 0) { + buffer.buf = (void *)response; + buffer.len = len; + + /* Set CD pin high for data */ + ret = mipi_dbi_spi_cmd_data_set(dev, 1); + if (ret < 0) { + goto out; + } + + ret = spi_read(config->spi_dev, &tmp_config, &buf_set); + if (ret < 0) { + goto out; + } + } out: - spi_release(config->spi_dev, &tmp_config); - return ret; + spi_release(config->spi_dev, &tmp_config); + return ret; } static int mipi_dbi_spi_command_read(const struct device *dev, @@ -548,8 +652,9 @@ static int mipi_dbi_spi_command_read(const struct device *dev, ret = -ENOTSUP; } out: - k_mutex_unlock(&data->lock); - return ret; + mipi_dbi_spi_cmd_data_release(dev); + k_mutex_unlock(&data->lock); + return ret; } #endif /* MIPI_DBI_SPI_READ_REQUIRED */ @@ -580,11 +685,12 @@ static int mipi_dbi_spi_reset(const struct device *dev, k_timeout_t delay) } static int mipi_dbi_spi_release(const struct device *dev, - const struct mipi_dbi_config *dbi_config) + const struct mipi_dbi_config *dbi_config) { - const struct mipi_dbi_spi_config *config = dev->config; + const struct mipi_dbi_spi_config *config = dev->config; - return spi_release(config->spi_dev, &dbi_config->config); + mipi_dbi_spi_cmd_data_release(dev); + return spi_release(config->spi_dev, &dbi_config->config); } #if MIPI_DBI_SPI_TE_REQUIRED @@ -649,25 +755,64 @@ static int mipi_dbi_spi_configure_te(const struct device *dev, static int mipi_dbi_spi_init(const struct device *dev) { - const struct mipi_dbi_spi_config *config = dev->config; - struct mipi_dbi_spi_data *data = dev->data; - int ret; + const struct mipi_dbi_spi_config *config = dev->config; + struct mipi_dbi_spi_data *data = dev->data; + int ret; if (!device_is_ready(config->spi_dev)) { LOG_ERR("SPI device is not ready"); return -ENODEV; } - if (mipi_dbi_has_pin(&config->cmd_data)) { - if (!gpio_is_ready_dt(&config->cmd_data)) { - return -ENODEV; - } - ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT); - if (ret < 0) { - LOG_ERR("Could not configure command/data GPIO (%d)", ret); - return ret; - } - } + if (mipi_dbi_has_pin(&config->cmd_data)) { + if (!gpio_is_ready_dt(&config->cmd_data)) { + return -ENODEV; + } + if (config->cmd_data_tristate) { +#if defined(CONFIG_PINCTRL) + if (config->pinctrl != NULL) { + ret = pinctrl_lookup_state(config->pinctrl, + PINCTRL_STATE_CMD_DATA_INPUT, + &data->cmd_data_input_state); + if (ret == 0) { + ret = pinctrl_lookup_state(config->pinctrl, + PINCTRL_STATE_CMD_DATA_OUTPUT, + &data->cmd_data_output_state); + } + if (ret == 0) { + ret = pinctrl_apply_state_direct(config->pinctrl, + data->cmd_data_input_state); + } + if (ret == 0) { + data->use_pinctrl_cmd_data = true; + } else if (ret != -ENOENT) { + LOG_ERR("Failed to prepare D/C pinctrl state (%d)", ret); + return ret; + } + } +#endif + +#if defined(CONFIG_PINCTRL) + if (!data->use_pinctrl_cmd_data) { +#endif + ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure command/data GPIO (%d)", ret); + return ret; + } +#if defined(CONFIG_PINCTRL) + } +#endif + data->cmd_data_is_output = false; + } else { + ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT); + if (ret < 0) { + LOG_ERR("Could not configure command/data GPIO (%d)", ret); + return ret; + } + data->cmd_data_is_output = true; + } + } if (mipi_dbi_has_pin(&config->reset)) { if (!gpio_is_ready_dt(&config->reset)) { @@ -698,23 +843,26 @@ static DEVICE_API(mipi_dbi, mipi_dbi_spi_driver_api) = { #endif }; -#define MIPI_DBI_SPI_INIT(n) \ - static const struct mipi_dbi_spi_config \ - mipi_dbi_spi_config_##n = { \ - .spi_dev = DEVICE_DT_GET( \ - DT_INST_PHANDLE(n, spi_dev)), \ - .cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}), \ - .tearing_effect = GPIO_DT_SPEC_INST_GET_OR(n, te_gpios, {}), \ - .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \ - .xfr_min_bits = DT_INST_STRING_UPPER_TOKEN(n, xfr_min_bits) \ - }; \ - static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n; \ - \ - DEVICE_DT_INST_DEFINE(n, mipi_dbi_spi_init, NULL, \ - &mipi_dbi_spi_data_##n, \ - &mipi_dbi_spi_config_##n, \ - POST_KERNEL, \ - CONFIG_MIPI_DBI_INIT_PRIORITY, \ - &mipi_dbi_spi_driver_api); +#define MIPI_DBI_SPI_INIT(n)\ + static const struct mipi_dbi_spi_config\ + mipi_dbi_spi_config_##n = {\ + .spi_dev = DEVICE_DT_GET(\ + DT_INST_PHANDLE(n, spi_dev)),\ + .cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}),\ + .tearing_effect = GPIO_DT_SPEC_INST_GET_OR(n, te_gpios, {}), \ + .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}),\ + .xfr_min_bits = DT_INST_STRING_UPPER_TOKEN(n, xfr_min_bits), \ + .cmd_data_tristate = DT_INST_PROP_OR(n, cmd_data_tristate, false), \ +#if defined(CONFIG_PINCTRL)\ + .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ +#endif\ + };\ + static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n;\ + DEVICE_DT_INST_DEFINE(n, mipi_dbi_spi_init, NULL,\ + &mipi_dbi_spi_data_##n,\ + &mipi_dbi_spi_config_##n,\ + POST_KERNEL,\ + CONFIG_MIPI_DBI_INIT_PRIORITY,\ + &mipi_dbi_spi_driver_api); DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_SPI_INIT) diff --git a/drivers/spi/spi_esp32_spim.c b/drivers/spi/spi_esp32_spim.c index c41cd30842e58..9c851da956889 100644 --- a/drivers/spi/spi_esp32_spim.c +++ b/drivers/spi/spi_esp32_spim.c @@ -406,12 +406,8 @@ static int IRAM_ATTR spi_esp32_configure(const struct device *dev, return 0; } - ctx->config = spi_cfg; - - if (spi_cfg->operation & SPI_HALF_DUPLEX) { - LOG_ERR("Half-duplex not supported"); - return -ENOTSUP; - } + ctx->config = spi_cfg; + hal_dev->half_duplex = (spi_cfg->operation & SPI_HALF_DUPLEX) ? 1 : 0; if (spi_cfg->operation & SPI_OP_MODE_SLAVE) { LOG_ERR("Slave mode not supported"); diff --git a/dts/bindings/mipi-dbi/zephyr,mipi-dbi-spi.yaml b/dts/bindings/mipi-dbi/zephyr,mipi-dbi-spi.yaml index 21793683482bb..96bad2950dba4 100644 --- a/dts/bindings/mipi-dbi/zephyr,mipi-dbi-spi.yaml +++ b/dts/bindings/mipi-dbi/zephyr,mipi-dbi-spi.yaml @@ -33,6 +33,18 @@ properties: description: | Reset GPIO pin. Set high to reset the display + cmd-data-tristate: + type: boolean + description: | + Tri-state the command/data GPIO when the display chip select is + inactive. This is required on hardware that multiplexes the D/C + signal onto an SPI data line shared with other peripherals. When + pin control states named ``cmd_data_input`` and ``cmd_data_output`` + are provided, the driver will apply them using + ``PINCTRL_STATE_CMD_DATA_INPUT`` and + ``PINCTRL_STATE_CMD_DATA_OUTPUT`` to avoid reconfiguring the pin + in software for every transfer. + xfr-min-bits: type: string default: "MIPI_DBI_SPI_XFR_8BIT" diff --git a/modules/hal/espressif/components/hal/esp32s3/clk_tree_hal.c b/modules/hal/espressif/components/hal/esp32s3/clk_tree_hal.c new file mode 100644 index 0000000000000..6e62a377a01c8 --- /dev/null +++ b/modules/hal/espressif/components/hal/esp32s3/clk_tree_hal.c @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/clk_tree_hal.h" +#include "hal/clk_tree_ll.h" +#include "soc/rtc.h" +#include "hal/assert.h" +#include "hal/log.h" + +static const char *CLK_HAL_TAG = "clk_hal"; + +uint32_t clk_hal_soc_root_get_freq_mhz(soc_cpu_clk_src_t cpu_clk_src) +{ + switch (cpu_clk_src) { + case SOC_CPU_CLK_SRC_XTAL: + return clk_hal_xtal_get_freq_mhz(); + case SOC_CPU_CLK_SRC_PLL: + return clk_ll_bbpll_get_freq_mhz(); + case SOC_CPU_CLK_SRC_RC_FAST: + return SOC_CLK_RC_FAST_FREQ_APPROX / MHZ; + default: + // Unknown CPU_CLK mux input + HAL_ASSERT(false); + return 0; + } +} + +uint32_t clk_hal_cpu_get_freq_hz(void) +{ + soc_cpu_clk_src_t source = clk_ll_cpu_get_src(); + switch (source) { + case SOC_CPU_CLK_SRC_PLL: { + // PLL 320MHz, CPU 240MHz is an undetermined state + uint32_t pll_freq_mhz = clk_ll_bbpll_get_freq_mhz(); + uint32_t cpu_freq_mhz = clk_ll_cpu_get_freq_mhz_from_pll(); + if (pll_freq_mhz == CLK_LL_PLL_320M_FREQ_MHZ && cpu_freq_mhz == CLK_LL_PLL_240M_FREQ_MHZ) { + HAL_LOGE(CLK_HAL_TAG, "Invalid cpu config"); + return 0; + } + return cpu_freq_mhz * MHZ; + } + default: // SOC_CPU_CLK_SRC_XTAL, SOC_CPU_CLK_SRC_RC_FAST... + return clk_hal_soc_root_get_freq_mhz(source) * MHZ / clk_ll_cpu_get_divider(); + } +} + +uint32_t clk_hal_ahb_get_freq_hz(void) +{ + // AHB_CLK path is highly dependent on CPU_CLK path + switch (clk_ll_cpu_get_src()) { + case SOC_CPU_CLK_SRC_PLL: + // AHB_CLK is a fixed value when CPU_CLK is clocked from PLL + return CLK_LL_AHB_MAX_FREQ_MHZ * MHZ; + default: // SOC_CPU_CLK_SRC_XTAL, SOC_CPU_CLK_SRC_RC_FAST... + return clk_hal_cpu_get_freq_hz(); + } +} + +uint32_t clk_hal_apb_get_freq_hz(void) +{ + return clk_hal_ahb_get_freq_hz(); +} + +uint32_t clk_hal_lp_slow_get_freq_hz(void) +{ + switch (clk_ll_rtc_slow_get_src()) { + case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: + return SOC_CLK_RC_SLOW_FREQ_APPROX; + case SOC_RTC_SLOW_CLK_SRC_XTAL32K: + return SOC_CLK_XTAL32K_FREQ_APPROX; + case SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256: + return SOC_CLK_RC_FAST_D256_FREQ_APPROX; + default: + // Unknown RTC_SLOW_CLK mux input + HAL_ASSERT(false); + return 0; + } +} + +uint32_t clk_hal_xtal_get_freq_mhz(void) +{ + uint32_t freq = clk_ll_xtal_load_freq_mhz(); + if (freq == 0) { + HAL_LOGW(CLK_HAL_TAG, "invalid RTC_XTAL_FREQ_REG value, assume 40MHz"); + return (uint32_t)RTC_XTAL_FREQ_40M; + } + return freq; +} diff --git a/modules/hal/espressif/components/hal/esp32s3/include/hal/clk_tree_ll.h b/modules/hal/espressif/components/hal/esp32s3/include/hal/clk_tree_ll.h new file mode 100644 index 0000000000000..f2170a70f557b --- /dev/null +++ b/modules/hal/espressif/components/hal/esp32s3/include/hal/clk_tree_ll.h @@ -0,0 +1,723 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc.h" +#include "soc/clk_tree_defs.h" +#include "soc/rtc.h" +#include "soc/system_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/regi2c_defs.h" +#include "hal/regi2c_ctrl.h" +#include "soc/regi2c_bbpll.h" +#include "hal/assert.h" +#include "hal/log.h" +#include "esp32s3/rom/rtc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#undef MHZ +#define MHZ (1000000) + +#define CLK_LL_PLL_80M_FREQ_MHZ (80) +#define CLK_LL_PLL_160M_FREQ_MHZ (160) +#define CLK_LL_PLL_240M_FREQ_MHZ (240) + +#define CLK_LL_PLL_320M_FREQ_MHZ (320) +#define CLK_LL_PLL_480M_FREQ_MHZ (480) + +#define CLK_LL_AHB_MAX_FREQ_MHZ CLK_LL_PLL_80M_FREQ_MHZ + +#define CLK_LL_XTAL32K_CONFIG_DEFAULT() { \ + .dac = 3, \ + .dres = 3, \ + .dgm = 3, \ + .dbuf = 1, \ +} + +/** + * @brief XTAL32K_CLK enable modes + */ +typedef enum { + CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL, //!< Enable the external 32kHz crystal for XTAL32K_CLK + CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL, //!< Enable the external clock signal for XTAL32K_CLK + CLK_LL_XTAL32K_ENABLE_MODE_BOOTSTRAP, //!< Bootstrap the crystal oscillator for faster XTAL32K_CLK start up */ +} clk_ll_xtal32k_enable_mode_t; + +/** + * @brief XTAL32K_CLK configuration structure + */ +typedef struct { + uint32_t dac : 6; + uint32_t dres : 3; + uint32_t dgm : 3; + uint32_t dbuf: 1; +} clk_ll_xtal32k_config_t; + +/** + * @brief Power up BBPLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_bbpll_enable(void) +{ + REG_CLR_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); +} + +/** + * @brief Power down BBPLL circuit + */ +static inline __attribute__((always_inline)) void clk_ll_bbpll_disable(void) +{ + REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); +} + +/** + * @brief Enable the 32kHz crystal oscillator + * + * @param mode Used to determine the xtal32k configuration parameters + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode) +{ + if (mode == CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL) { + SET_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XTAL32K_GPIO_SEL); + } else { + // Configure xtal32k + CLEAR_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XTAL32K_GPIO_SEL); + clk_ll_xtal32k_config_t cfg = CLK_LL_XTAL32K_CONFIG_DEFAULT(); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DAC_XTAL_32K, cfg.dac); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DRES_XTAL_32K, cfg.dres); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DGM_XTAL_32K, cfg.dgm); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DBUF_XTAL_32K, cfg.dbuf); + // Enable xtal32k xpd status + SET_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K); + } +} + +/** + * @brief Disable the 32kHz crystal oscillator + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_disable(void) +{ + // Set xtal32k xpd to be controlled by software + SET_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XTAL32K_XPD_FORCE); + // Disable xtal32k xpd status + CLEAR_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K); +} + +/** + * @brief Get the state of the 32kHz crystal clock + * + * @return True if the 32kHz XTAL is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_xtal32k_is_enabled(void) +{ + uint32_t xtal_conf = READ_PERI_REG(RTC_CNTL_EXT_XTL_CONF_REG); + /* If xtal xpd is controlled by software */ + bool xtal_xpd_sw = (xtal_conf & RTC_CNTL_XTAL32K_XPD_FORCE) >> RTC_CNTL_XTAL32K_XPD_FORCE_S; + /* If xtal xpd software control is on */ + bool xtal_xpd_st = (xtal_conf & RTC_CNTL_XPD_XTAL_32K) >> RTC_CNTL_XPD_XTAL_32K_S; + // disabled = xtal_xpd_sw && !xtal_xpd_st; enabled = !disbaled + bool enabled = !xtal_xpd_sw || xtal_xpd_st; + return enabled; +} + +/** + * @brief Enable the internal oscillator output for RC_FAST_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_enable(void) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CK8M_ENABLE_WAIT_DEFAULT); +} + +/** + * @brief Disable the internal oscillator output for RC_FAST_CLK + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_disable(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT); +} + +/** + * @brief Get the state of the internal oscillator for RC_FAST_CLK + * + * @return True if the oscillator is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc_fast_is_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M) == 0; +} + +/** + * @brief Enable the output from the internal oscillator to be passed into a configurable divider, + * which by default divides the input clock frequency by 256. i.e. RC_FAST_D256_CLK = RC_FAST_CLK / 256 + * + * Divider values other than 256 may be configured, but this facility is not currently needed, + * so is not exposed in the code. + * The output of the divider, RC_FAST_D256_CLK, is referred as 8md256 or simply d256 in reg. descriptions. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_enable(void) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV); +} + +/** + * @brief Disable the output from the internal oscillator to be passed into a configurable divider. + * i.e. RC_FAST_D256_CLK = RC_FAST_CLK / 256 + * + * Disabling this divider could reduce power consumption. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_disable(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV); +} + +/** + * @brief Get the state of the divider which is applied to the output from the internal oscillator (RC_FAST_CLK) + * + * @return True if the divided output is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc_fast_d256_is_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV) == 0; +} + +/** + * @brief Enable the digital RC_FAST_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_enable(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M); +} + +/** + * @brief Disable the digital RC_FAST_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_disable(void) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M); +} + +/** + * @brief Get the state of the digital RC_FAST_CLK + * + * @return True if the digital RC_FAST_CLK is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_rc_fast_digi_is_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M); +} + +/** + * @brief Enable the digital RC_FAST_D256_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_digi_enable(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN_M); +} + +/** + * @brief Disable the digital RC_FAST_D256_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_digi_disable(void) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN_M); +} + +/** + * @brief Enable the digital XTAL32K_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_enable(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN_M); +} + +/** + * @brief Disable the digital XTAL32K_CLK, which is used to support peripherals. + */ +static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_disable(void) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN_M); +} + +/** + * @brief Get the state of the digital XTAL32K_CLK + * + * @return True if the digital XTAL32K_CLK is enabled + */ +static inline __attribute__((always_inline)) bool clk_ll_xtal32k_digi_is_enabled(void) +{ + return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN); +} + +/** + * @brief Get PLL_CLK frequency + * + * @return PLL clock frequency, in MHz. Returns 0 if register field value is invalid. + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_bbpll_get_freq_mhz(void) +{ + uint32_t pll_freq_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); + switch (pll_freq_sel) { + case 0: // PLL_320M + return CLK_LL_PLL_320M_FREQ_MHZ; + case 1: // PLL_480M + return CLK_LL_PLL_480M_FREQ_MHZ; + default: + return 0; + } +} + +/** + * @brief Set BBPLL frequency from XTAL source (Digital part) + * + * @param pll_freq_mhz PLL frequency, in MHz + */ +static inline __attribute__((always_inline)) void clk_ll_bbpll_set_freq_mhz(uint32_t pll_freq_mhz) +{ + switch (pll_freq_mhz) { + case CLK_LL_PLL_320M_FREQ_MHZ: // PLL_320M + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL, 0); + break; + case CLK_LL_PLL_480M_FREQ_MHZ: // PLL_480M + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL, 1); + break; + default: + abort(); + } +} + +/** + * @brief Set BBPLL frequency from XTAL source (Analog part) + * + * @param pll_freq_mhz PLL frequency, in MHz + * @param xtal_freq_mhz XTAL frequency, in MHz + */ +static inline __attribute__((always_inline)) void clk_ll_bbpll_set_config(uint32_t pll_freq_mhz, uint32_t xtal_freq_mhz) +{ + uint8_t div_ref; + uint8_t div7_0; + uint8_t dr1; + uint8_t dr3; + uint8_t dchgp; + uint8_t dcur; + uint8_t dbias = 3; + + if (pll_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) { + /* Configure 480M PLL */ + switch (xtal_freq_mhz) { + case RTC_XTAL_FREQ_40M: + div_ref = 0; + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + break; + case RTC_XTAL_FREQ_32M: + div_ref = 1; + div7_0 = 26; + dr1 = 1; + dr3 = 1; + dchgp = 4; + dcur = 0; + break; + default: + div_ref = 0; + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + break; + } + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6B); + } else { + /* Configure 320M PLL */ + switch (xtal_freq_mhz) { + case RTC_XTAL_FREQ_40M: + div_ref = 0; + div7_0 = 4; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + break; + case RTC_XTAL_FREQ_32M: + div_ref = 1; + div7_0 = 6; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + break; + default: + div_ref = 0; + div7_0 = 4; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + break; + } + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69); + } + uint8_t i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | (div_ref); + uint8_t i2c_bbpll_div_7_0 = div7_0; + uint8_t i2c_bbpll_dcur = (1 << I2C_BBPLL_OC_DLREF_SEL_LSB ) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias); +} + +/** + * @brief Select the clock source for CPU_CLK + * + * @param in_sel One of the clock sources in soc_cpu_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel) +{ + switch (in_sel) { + case SOC_CPU_CLK_SRC_XTAL: + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, 0); + break; + case SOC_CPU_CLK_SRC_PLL: + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, 1); + break; + case SOC_CPU_CLK_SRC_RC_FAST: + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, 2); + break; + default: + // Unsupported CPU_CLK mux input sel + abort(); + } +} + +/** + * @brief Get the clock source for CPU_CLK + * + * @return Currently selected clock source (one of soc_cpu_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_cpu_clk_src_t clk_ll_cpu_get_src(void) +{ + uint32_t clk_sel = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); + switch (clk_sel) { + case 0: + return SOC_CPU_CLK_SRC_XTAL; + case 1: + return SOC_CPU_CLK_SRC_PLL; + case 2: + return SOC_CPU_CLK_SRC_RC_FAST; + default: + // Invalid SOC_CLK_SEL value + return SOC_CPU_CLK_SRC_INVALID; + } +} + +/** + * @brief Set CPU frequency from PLL clock + * + * @param cpu_mhz CPU frequency value, in MHz + */ +static inline __attribute__((always_inline)) void clk_ll_cpu_set_freq_mhz_from_pll(uint32_t cpu_mhz) +{ + switch (cpu_mhz) { + case CLK_LL_PLL_80M_FREQ_MHZ: + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, 0); + break; + case CLK_LL_PLL_160M_FREQ_MHZ: + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, 1); + break; + case CLK_LL_PLL_240M_FREQ_MHZ: + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, 2); + break; + default: + // Unsupported CPU_CLK freq from PLL + abort(); + } +} + +/** + * @brief Get CPU_CLK frequency from PLL_CLK source + * + * @return CPU clock frequency, in MHz. Returns 0 if register field value is invalid. + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_freq_mhz_from_pll(void) +{ + uint32_t cpu_freq_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL); + switch (cpu_freq_sel) { + case 0: + return CLK_LL_PLL_80M_FREQ_MHZ; + case 1: + return CLK_LL_PLL_160M_FREQ_MHZ; + case 2: + // When PLL frequency selection is 320MHz but CPU frequency selection is 240MHz, it is an undetermined state. + // It is checked in the upper layer. + return CLK_LL_PLL_240M_FREQ_MHZ; + default: + // Invalid CPUPERIOD_SEL value + return 0; + } +} + +/** + * @brief Set CPU_CLK's XTAL/FAST_RC clock source path divider + * + * @param divider Divider. Usually this divider is set to 1 in bootloader stage. PRE_DIV_CNT = divider - 1. + */ +static inline __attribute__((always_inline)) void clk_ll_cpu_set_divider(uint32_t divider) +{ + HAL_ASSERT(divider > 0); + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, divider - 1); +} + +/** + * @brief Get CPU_CLK's XTAL/FAST_RC clock source path divider + * + * @return Divider. Divider = (PRE_DIV_CNT + 1). + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_divider(void) +{ + return REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT) + 1; +} + +/** + * @brief Select the clock source for RTC_SLOW_CLK + * + * @param in_sel One of the clock sources in soc_rtc_slow_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel) +{ + switch (in_sel) { + case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, 0); + break; + case SOC_RTC_SLOW_CLK_SRC_XTAL32K: + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, 1); + break; + case SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256: + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, 2); + break; + default: + // Unsupported RTC_SLOW_CLK mux input sel + abort(); + } +} + +/** + * @brief Get the clock source for RTC_SLOW_CLK + * + * @return Currently selected clock source (one of soc_rtc_slow_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_rtc_slow_clk_src_t clk_ll_rtc_slow_get_src(void) +{ + uint32_t clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); + switch (clk_sel) { + case 0: + return SOC_RTC_SLOW_CLK_SRC_RC_SLOW; + case 1: + return SOC_RTC_SLOW_CLK_SRC_XTAL32K; + case 2: + return SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256; + default: + // Invalid ANA_CLK_RTC_SEL value + HAL_ASSERT(false); + return SOC_RTC_SLOW_CLK_SRC_INVALID; + } +} + +/** + * @brief Select the clock source for RTC_FAST_CLK + * + * @param in_sel One of the clock sources in soc_rtc_fast_clk_src_t + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel) +{ + switch (in_sel) { + case SOC_RTC_FAST_CLK_SRC_XTAL_D2: + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, 0); + break; + case SOC_RTC_FAST_CLK_SRC_RC_FAST: + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, 1); + break; + default: + // Unsupported RTC_FAST_CLK mux input sel + abort(); + } +} + +/** + * @brief Get the clock source for RTC_FAST_CLK + * + * @return Currently selected clock source (one of soc_rtc_fast_clk_src_t values) + */ +static inline __attribute__((always_inline)) soc_rtc_fast_clk_src_t clk_ll_rtc_fast_get_src(void) +{ + uint32_t clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL); + switch (clk_sel) { + case 0: + return SOC_RTC_FAST_CLK_SRC_XTAL_D2; + case 1: + return SOC_RTC_FAST_CLK_SRC_RC_FAST; + default: + return SOC_RTC_FAST_CLK_SRC_INVALID; + } +} + +/** + * @brief Set RC_FAST_CLK divider. The output from the divider is passed into rtc_fast_clk MUX. + * + * @param divider Divider of RC_FAST_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_fast_set_divider(uint32_t divider) +{ + HAL_ASSERT(divider > 0); + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, divider - 1); + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD); +} + +/** + * @brief Get RC_FAST_CLK divider + * + * @return Divider. Divider = (CK8M_DIV_SEL + 1). + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_rc_fast_get_divider(void) +{ + return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL) + 1; +} + +/** + * @brief Set RC_SLOW_CLK divider + * + * @param divider Divider of RC_SLOW_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage. + */ +static inline __attribute__((always_inline)) void clk_ll_rc_slow_set_divider(uint32_t divider) +{ + HAL_ASSERT(divider > 0); + CLEAR_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV_VLD); + REG_SET_FIELD(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV, divider - 1); + SET_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV_VLD); +} + +/************************* RTC STORAGE REGISTER STORE/LOAD **************************/ +/** + * @brief Store XTAL_CLK frequency in RTC storage register + * + * Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit + * halves. These are the routines to work with that representation. + * + * @param xtal_freq_mhz XTAL frequency, in MHz. The frequency must necessarily be even, + * otherwise there will be a conflict with the low bit, which is used to disable logs + * in the ROM code. + */ +static inline __attribute__((always_inline)) void clk_ll_xtal_store_freq_mhz(uint32_t xtal_freq_mhz) +{ + // Read the status of whether disabling logging from ROM code + uint32_t reg = READ_PERI_REG(RTC_XTAL_FREQ_REG) & RTC_DISABLE_ROM_LOG; + // If so, need to write back this setting + if (reg == RTC_DISABLE_ROM_LOG) { + xtal_freq_mhz |= 1; + } + WRITE_PERI_REG(RTC_XTAL_FREQ_REG, (xtal_freq_mhz & UINT16_MAX) | ((xtal_freq_mhz & UINT16_MAX) << 16)); +} + +/** + * @brief Load XTAL_CLK frequency from RTC storage register + * + * Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit + * halves. These are the routines to work with that representation. + * + * @return XTAL frequency, in MHz. Returns 0 if value in reg is invalid. + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_xtal_load_freq_mhz(void) +{ + // Read from the RTC storage register + uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG); + if ((xtal_freq_reg & 0xFFFF) == ((xtal_freq_reg >> 16) & 0xFFFF) && + xtal_freq_reg != 0 && xtal_freq_reg != UINT32_MAX) { + return xtal_freq_reg & ~RTC_DISABLE_ROM_LOG & UINT16_MAX; + } + // If the format in reg is invalid + return 0; +} + +/** + * @brief Store RTC_SLOW_CLK calibration value in RTC storage register + * + * Value of RTC_SLOW_CLK_CAL_REG has to be in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * + * @param cal_value The calibration value of slow clock period in microseconds, in Q13.19 fixed point format + */ +static inline __attribute__((always_inline)) void clk_ll_rtc_slow_store_cal(uint32_t cal_value) +{ + REG_WRITE(RTC_SLOW_CLK_CAL_REG, cal_value); +} + +/** + * @brief Load the calibration value of RTC_SLOW_CLK frequency from RTC storage register + * + * This value gets updated (i.e. rtc slow clock gets calibrated) every time RTC_SLOW_CLK source switches + * + * @return The calibration value of slow clock period in microseconds, in Q13.19 fixed point format + */ +static inline __attribute__((always_inline)) uint32_t clk_ll_rtc_slow_load_cal(void) +{ + return REG_READ(RTC_SLOW_CLK_CAL_REG); +} + +/** + * @brief Configure PLL frequency for MSPI timing tuning + * @note Only used by the MSPI Timing tuning driver + * + * @param xtal_freq Xtal frequency + * @param pll_freq PLL frequency + * @param oc_div OC divider + * @param oc_ref_div OC ref divider + */ +static inline __attribute__((always_inline)) +void clk_ll_bbpll_set_frequency_for_mspi_tuning(rtc_xtal_freq_t xtal_freq, int pll_freq, uint8_t oc_div, uint8_t oc_ref_div) +{ + HAL_ASSERT(xtal_freq == RTC_XTAL_FREQ_40M); + uint32_t pll_reg = GET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); + HAL_ASSERT(pll_reg == 0); + + /* Set this register to let the digital part know 480M PLL is used */ + SET_PERI_REG_MASK(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); + uint8_t dr1 = 0; + uint8_t dr3 = 0; + uint8_t dchgp = 5; + uint8_t dcur = 3; + uint8_t dbias = 2; + uint8_t i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | (oc_ref_div); + uint8_t i2c_bbpll_div_7_0 = oc_div; + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6B); + + uint8_t i2c_bbpll_dcur = (1 << I2C_BBPLL_OC_DLREF_SEL_LSB ) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); + REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 3); + REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h b/modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h new file mode 100644 index 0000000000000..154f7734688ea --- /dev/null +++ b/modules/hal/espressif/components/hal/esp32s3/include/hal/spi_ll.h @@ -0,0 +1,1244 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The hal is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +// The LL layer for ESP32-S3 SPI register operations + +#pragma once + +#include //for abs() +#include +#include "esp_attr.h" +#include "esp_types.h" +#include "soc/spi_periph.h" +#include "soc/spi_struct.h" +#include "soc/lldesc.h" +#include "hal/assert.h" +#include "hal/misc.h" +#include "hal/spi_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Interrupt not used. Don't use in app. +#define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA) +/// These 2 masks together will set SPI transaction to one line mode +#define SPI_LL_ONE_LINE_CTRL_MASK (SPI_FREAD_OCT | SPI_FREAD_QUAD | SPI_FREAD_DUAL | SPI_FCMD_OCT | \ + SPI_FCMD_QUAD | SPI_FCMD_DUAL | SPI_FADDR_OCT | SPI_FADDR_QUAD | SPI_FADDR_DUAL) +#define SPI_LL_ONE_LINE_USER_MASK (SPI_FWRITE_OCT | SPI_FWRITE_QUAD | SPI_FWRITE_DUAL) + +/// Swap the bit order to its correct place to send +#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len)) +#define SPI_LL_GET_HW(ID) ((ID)==0? ({abort();NULL;}):((ID)==1? &GPSPI2 : &GPSPI3)) + +#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits +#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words +#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized + +/** + * The data structure holding calculated clock configuration. Since the + * calculation needs long time, it should be calculated during initialization and + * stored somewhere to be quickly used. + */ +typedef uint32_t spi_ll_clock_val_t; +typedef spi_dev_t spi_dma_dev_t; + +// Type definition of all supported interrupts +typedef enum { + SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done + SPI_LL_INTR_RDBUF = BIT(6), ///< Has received RDBUF command. Only available in slave HD. + SPI_LL_INTR_WRBUF = BIT(7), ///< Has received WRBUF command. Only available in slave HD. + SPI_LL_INTR_RDDMA = BIT(8), ///< Has received RDDMA command. Only available in slave HD. + SPI_LL_INTR_WRDMA = BIT(9), ///< Has received WRDMA command. Only available in slave HD. + SPI_LL_INTR_CMD7 = BIT(10), ///< Has received CMD7 command. Only available in slave HD. + SPI_LL_INTR_CMD8 = BIT(11), ///< Has received CMD8 command. Only available in slave HD. + SPI_LL_INTR_CMD9 = BIT(12), ///< Has received CMD9 command. Only available in slave HD. + SPI_LL_INTR_CMDA = BIT(13), ///< Has received CMDA command. Only available in slave HD. + SPI_LL_INTR_SEG_DONE = BIT(14), +} spi_ll_intr_t; +FLAG_ATTR(spi_ll_intr_t) + +// Flags for conditions under which the transaction length should be recorded +typedef enum { + SPI_LL_TRANS_LEN_COND_WRBUF = BIT(0), ///< WRBUF length will be recorded + SPI_LL_TRANS_LEN_COND_RDBUF = BIT(1), ///< RDBUF length will be recorded + SPI_LL_TRANS_LEN_COND_WRDMA = BIT(2), ///< WRDMA length will be recorded + SPI_LL_TRANS_LEN_COND_RDDMA = BIT(3), ///< RDDMA length will be recorded +} spi_ll_trans_len_cond_t; +FLAG_ATTR(spi_ll_trans_len_cond_t) + +// SPI base command in esp32s3 +typedef enum { + /* Slave HD Only */ + SPI_LL_BASE_CMD_HD_WRBUF = 0x01, + SPI_LL_BASE_CMD_HD_RDBUF = 0x02, + SPI_LL_BASE_CMD_HD_WRDMA = 0x03, + SPI_LL_BASE_CMD_HD_RDDMA = 0x04, + SPI_LL_BASE_CMD_HD_SEG_END = 0x05, + SPI_LL_BASE_CMD_HD_EN_QPI = 0x06, + SPI_LL_BASE_CMD_HD_WR_END = 0x07, + SPI_LL_BASE_CMD_HD_INT0 = 0x08, + SPI_LL_BASE_CMD_HD_INT1 = 0x09, + SPI_LL_BASE_CMD_HD_INT2 = 0x0A, +} spi_ll_base_command_t; + +/*------------------------------------------------------------------------------ + * Control + *----------------------------------------------------------------------------*/ + +/** + * Select SPI peripheral clock source (master). + * + * @param hw Beginning address of the peripheral registers. + * @param clk_source clock source to select, see valid sources in type `spi_clock_source_t` + */ +static inline void spi_ll_set_clk_source(spi_dev_t *hw, spi_clock_source_t clk_source){ + switch (clk_source) + { + case SPI_CLK_SRC_XTAL: + hw->clk_gate.mst_clk_sel = 0; + break; + default: + hw->clk_gate.mst_clk_sel = 1; + break; + } +} + +/** + * Initialize SPI peripheral (master). + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_master_init(spi_dev_t *hw) +{ + //Reset timing + hw->user1.cs_setup_time = 0; + hw->user1.cs_hold_time = 0; + + //use all 64 bytes of the buffer + hw->user.usr_miso_highpart = 0; + hw->user.usr_mosi_highpart = 0; + + //Disable unneeded ints + hw->slave.val = 0; + hw->user.val = 0; + + hw->clk_gate.clk_en = 1; + hw->clk_gate.mst_clk_active = 1; + hw->clk_gate.mst_clk_sel = 1; + + hw->dma_conf.val = 0; + hw->dma_conf.tx_seg_trans_clr_en = 1; + hw->dma_conf.rx_seg_trans_clr_en = 1; + hw->dma_conf.dma_seg_trans_en = 0; +} + +/** + * Initialize SPI peripheral (slave). + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_slave_init(spi_dev_t *hw) +{ + //Configure slave + hw->clock.val = 0; + hw->user.val = 0; + hw->ctrl.val = 0; + hw->user.doutdin = 1; //we only support full duplex + hw->user.sio = 0; + hw->slave.slave_mode = 1; + hw->slave.soft_reset = 1; + hw->slave.soft_reset = 0; + //use all 64 bytes of the buffer + hw->user.usr_miso_highpart = 0; + hw->user.usr_mosi_highpart = 0; + + // Configure DMA In-Link to not be terminated when transaction bit counter exceeds + hw->dma_conf.rx_eof_en = 0; + hw->dma_conf.dma_seg_trans_en = 0; + + //Disable unneeded ints + hw->dma_int_ena.val &= ~SPI_LL_UNUSED_INT_MASK; +} + +/** + * Initialize SPI peripheral (slave half duplex mode) + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_slave_hd_init(spi_dev_t *hw) +{ + hw->clock.val = 0; + hw->user.val = 0; + hw->ctrl.val = 0; + hw->user.doutdin = 0; + hw->user.sio = 0; + + hw->slave.soft_reset = 1; + hw->slave.soft_reset = 0; + hw->slave.slave_mode = 1; +} + +/** + * Determine and unify the default level of mosi line when bus free + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level) +{ + hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state +} + +/** + * Apply the register configurations and wait until it's done + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_apply_config(spi_dev_t *hw) +{ + hw->cmd.update = 1; + while (hw->cmd.update); //waiting config applied +} + +/** + * Check whether user-defined transaction is done. + * + * @param hw Beginning address of the peripheral registers. + * + * @return True if transaction is done, otherwise false. + */ +static inline bool spi_ll_usr_is_done(spi_dev_t *hw) +{ + return hw->dma_int_raw.trans_done; +} + +/** + * Trigger start of user-defined transaction. + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_user_start(spi_dev_t *hw) +{ + hw->cmd.usr = 1; +} + +/** + * Get current running command bit-mask. (Preview) + * + * @param hw Beginning address of the peripheral registers. + * + * @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command. + */ +static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) +{ + return hw->cmd.val; +} + +/** + * Reset the slave peripheral before next transaction. + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_slave_reset(spi_dev_t *hw) +{ + hw->slave.soft_reset = 1; + hw->slave.soft_reset = 0; +} + +/** + * Reset SPI CPU TX FIFO + * + * On ESP32S3, this function is not seperated + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_cpu_tx_fifo_reset(spi_dev_t *hw) +{ + hw->dma_conf.buf_afifo_rst = 1; + hw->dma_conf.buf_afifo_rst = 0; +} + +/** + * Reset SPI CPU RX FIFO + * + * On ESP32S3, this function is not seperated + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_cpu_rx_fifo_reset(spi_dev_t *hw) +{ + hw->dma_conf.rx_afifo_rst = 1; + hw->dma_conf.rx_afifo_rst = 0; +} + +/** + * Reset SPI DMA TX FIFO + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_dma_tx_fifo_reset(spi_dev_t *hw) +{ + hw->dma_conf.dma_afifo_rst = 1; + hw->dma_conf.dma_afifo_rst = 0; +} + +/** + * Reset SPI DMA RX FIFO + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_dma_rx_fifo_reset(spi_dev_t *hw) +{ + hw->dma_conf.rx_afifo_rst = 1; + hw->dma_conf.rx_afifo_rst = 0; +} + +/** + * Clear in fifo full error + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_infifo_full_clr(spi_dev_t *hw) +{ + hw->dma_int_clr.infifo_full_err = 1; +} + +/** + * Clear out fifo empty error + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_outfifo_empty_clr(spi_dev_t *hw) +{ + hw->dma_int_clr.outfifo_empty_err = 1; +} + +/*------------------------------------------------------------------------------ + * DMA + *----------------------------------------------------------------------------*/ +/** + * Enable/Disable RX DMA (Peripherals->DMA->RAM) + * + * @param hw Beginning address of the peripheral registers. + * @param enable 1: enable; 2: disable + */ +static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable) +{ + hw->dma_conf.dma_rx_ena = enable; +} + +/** + * Enable/Disable TX DMA (RAM->DMA->Peripherals) + * + * @param hw Beginning address of the peripheral registers. + * @param enable 1: enable; 2: disable + */ +static inline void spi_ll_dma_tx_enable(spi_dev_t *hw, bool enable) +{ + hw->dma_conf.dma_tx_ena = enable; +} + +/** + * Configuration of RX DMA EOF interrupt generation way + * + * @param hw Beginning address of the peripheral registers. + * @param enable 1: spi_dma_inlink_eof is set when the number of dma pushed data bytes is equal to the value of spi_slv/mst_dma_rd_bytelen[19:0] in spi dma transition. 0: spi_dma_inlink_eof is set by spi_trans_done in non-seg-trans or spi_dma_seg_trans_done in seg-trans. + */ +static inline void spi_ll_dma_set_rx_eof_generation(spi_dev_t *hw, bool enable) +{ + hw->dma_conf.rx_eof_en = enable; +} + +/*------------------------------------------------------------------------------ + * Buffer + *----------------------------------------------------------------------------*/ +/** + * Write to SPI hardware data buffer. + * + * @param hw Beginning address of the peripheral registers. + * @param buffer_to_send Address of the data to be written to the hardware data buffer. + * @param bitlen Length to write, in bits. + */ +static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_send, size_t bitlen) +{ + for (size_t x = 0; x < bitlen; x += 32) { + //Use memcpy to get around alignment issues for txdata + uint32_t word; + memcpy(&word, &buffer_to_send[x / 8], 4); + hw->data_buf[(x / 32)] = word; + } +} + +/** + * Write to SPI hardware data buffer by buffer ID (address) + * + * @param hw Beginning address of the peripheral registers + * @param byte_id Start ID (address) of the hardware buffer to be written + * @param data Address of the data to be written to the hardware data buffer. + * @param len Length to write, in bytes. + */ +static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *data, int len) +{ + HAL_ASSERT(byte_id + len <= 64); + HAL_ASSERT(len > 0); + HAL_ASSERT(byte_id >= 0); + + while (len > 0) { + uint32_t word; + int offset = byte_id % 4; + int copy_len = 4 - offset; + if (copy_len > len) { + copy_len = len; + } + + //read-modify-write + if (copy_len != 4) { + word = hw->data_buf[byte_id / 4]; //read + } + memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify + hw->data_buf[byte_id / 4] = word; //write + + data += copy_len; + byte_id += copy_len; + len -= copy_len; + } +} + +/** + * Read from SPI hardware data buffer. + * + * @param hw Beginning address of the peripheral registers. + * @param buffer_to_rcv Address of a buffer to read data from hardware data buffer + * @param bitlen Length to read, in bits. + */ +static inline void spi_ll_read_buffer(spi_dev_t *hw, uint8_t *buffer_to_rcv, size_t bitlen) +{ + for (size_t x = 0; x < bitlen; x += 32) { + //Do a memcpy to get around possible alignment issues in rx_buffer + uint32_t word = hw->data_buf[x / 32]; + int len = bitlen - x; + if (len > 32) { + len = 32; + } + memcpy(&buffer_to_rcv[x / 8], &word, (len + 7) / 8); + } +} + +/** + * Read from SPI hardware data buffer by buffer ID (address) + * + * @param hw Beginning address of the peripheral registers + * @param byte_id Start ID (address) of the hardware buffer to be read + * @param data Address of a buffer to read data from hardware data buffer + * @param len Length to read, in bytes. + */ +static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *out_data, int len) +{ + while (len > 0) { + uint32_t word = hw->data_buf[byte_id / 4]; + int offset = byte_id % 4; + int copy_len = 4 - offset; + if (copy_len > len) { + copy_len = len; + } + + memcpy(out_data, ((uint8_t *)&word) + offset, copy_len); + byte_id += copy_len; + out_data += copy_len; + len -= copy_len; + } +} + +/*------------------------------------------------------------------------------ + * Configs: mode + *----------------------------------------------------------------------------*/ +/** + * Enable/disable the postive-cs feature. + * + * @param hw Beginning address of the peripheral registers. + * @param cs One of the CS (0-2) to enable/disable the feature. + * @param pos_cs True to enable the feature, otherwise disable (default). + */ +static inline void spi_ll_master_set_pos_cs(spi_dev_t *hw, int cs, uint32_t pos_cs) +{ + if (pos_cs) { + hw->misc.master_cs_pol |= (1 << cs); + } else { + hw->misc.master_cs_pol &= ~(1 << cs); + } +} + +/** + * Enable/disable the LSBFIRST feature for TX data. + * + * @param hw Beginning address of the peripheral registers. + * @param lsbfirst True if LSB of TX data to be sent first, otherwise MSB is sent first (default). + */ +static inline void spi_ll_set_tx_lsbfirst(spi_dev_t *hw, bool lsbfirst) +{ + hw->ctrl.wr_bit_order = lsbfirst; +} + +/** + * Enable/disable the LSBFIRST feature for RX data. + * + * @param hw Beginning address of the peripheral registers. + * @param lsbfirst True if first bit received as LSB, otherwise as MSB (default). + */ +static inline void spi_ll_set_rx_lsbfirst(spi_dev_t *hw, bool lsbfirst) +{ + hw->ctrl.rd_bit_order = lsbfirst; +} + +/** + * Set SPI mode for the peripheral as master. + * + * @param hw Beginning address of the peripheral registers. + * @param mode SPI mode to work at, 0-3. + */ +static inline void spi_ll_master_set_mode(spi_dev_t *hw, uint8_t mode) +{ + //Configure polarity + if (mode == 0) { + hw->misc.ck_idle_edge = 0; + hw->user.ck_out_edge = 0; + } else if (mode == 1) { + hw->misc.ck_idle_edge = 0; + hw->user.ck_out_edge = 1; + } else if (mode == 2) { + hw->misc.ck_idle_edge = 1; + hw->user.ck_out_edge = 1; + } else if (mode == 3) { + hw->misc.ck_idle_edge = 1; + hw->user.ck_out_edge = 0; + } +} + +/** + * Set SPI mode for the peripheral as slave. + * + * @param hw Beginning address of the peripheral registers. + * @param mode SPI mode to work at, 0-3. + */ +static inline void spi_ll_slave_set_mode(spi_dev_t *hw, const int mode, bool dma_used) +{ + if (mode == 0) { + hw->misc.ck_idle_edge = 0; + hw->user.rsck_i_edge = 0; + hw->user.tsck_i_edge = 0; + hw->slave.clk_mode_13 = 0; + } else if (mode == 1) { + hw->misc.ck_idle_edge = 0; + hw->user.rsck_i_edge = 1; + hw->user.tsck_i_edge = 1; + hw->slave.clk_mode_13 = 1; + } else if (mode == 2) { + hw->misc.ck_idle_edge = 1; + hw->user.rsck_i_edge = 1; + hw->user.tsck_i_edge = 1; + hw->slave.clk_mode_13 = 0; + } else if (mode == 3) { + hw->misc.ck_idle_edge = 1; + hw->user.rsck_i_edge = 0; + hw->user.tsck_i_edge = 0; + hw->slave.clk_mode_13 = 1; + } + hw->slave.rsck_data_out = 0; +} + +/** + * Set SPI to work in full duplex or half duplex mode. + * + * @param hw Beginning address of the peripheral registers. + * @param half_duplex True to work in half duplex mode, otherwise in full duplex mode. + */ +static inline void spi_ll_set_half_duplex(spi_dev_t *hw, bool half_duplex) +{ + hw->user.doutdin = !half_duplex; +} + +/** + * Set SPI to work in SIO mode or not. + * + * SIO is a mode which MOSI and MISO share a line. The device MUST work in half-duplexmode. + * + * @param hw Beginning address of the peripheral registers. + * @param sio_mode True to work in SIO mode, otherwise false. + */ +static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode) +{ + hw->user.sio = sio_mode; +} + +/** + * Configure the SPI transaction line mode for the master to use. + * + * @param hw Beginning address of the peripheral registers. + * @param line_mode SPI transaction line mode to use, see ``spi_line_mode_t``. + */ +static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t line_mode) +{ + hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK; + hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK; + hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2); + hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4); + hw->ctrl.fcmd_oct = (line_mode.cmd_lines == 8); + hw->ctrl.faddr_dual = (line_mode.addr_lines == 2); + hw->ctrl.faddr_quad = (line_mode.addr_lines == 4); + hw->ctrl.faddr_oct = (line_mode.addr_lines == 8); + hw->ctrl.fread_dual = (line_mode.data_lines == 2); + hw->user.fwrite_dual = (line_mode.data_lines == 2); + hw->ctrl.fread_quad = (line_mode.data_lines == 4); + hw->user.fwrite_quad = (line_mode.data_lines == 4); + hw->ctrl.fread_oct = (line_mode.data_lines == 8); + hw->user.fwrite_oct = (line_mode.data_lines == 8); +} + +/** + * Set the SPI slave to work in segment transaction mode + * + * @param hw Beginning address of the peripheral registers. + * @param seg_trans True to work in seg mode, otherwise false. + */ +static inline void spi_ll_slave_set_seg_mode(spi_dev_t *hw, bool seg_trans) +{ + hw->dma_conf.dma_seg_trans_en = seg_trans; +} + +/** + * Select one of the CS to use in current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param cs_id The cs to use, 0-2, otherwise none of them is used. + */ +static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) +{ + if (hw == &GPSPI2) { + hw->misc.cs0_dis = (cs_id == 0) ? 0 : 1; + hw->misc.cs1_dis = (cs_id == 1) ? 0 : 1; + hw->misc.cs2_dis = (cs_id == 2) ? 0 : 1; + hw->misc.cs3_dis = (cs_id == 3) ? 0 : 1; + hw->misc.cs4_dis = (cs_id == 4) ? 0 : 1; + hw->misc.cs5_dis = (cs_id == 5) ? 0 : 1; + } + + if (hw == &GPSPI3) { + hw->misc.cs0_dis = (cs_id == 0) ? 0 : 1; + hw->misc.cs1_dis = (cs_id == 1) ? 0 : 1; + hw->misc.cs2_dis = (cs_id == 2) ? 0 : 1; + } +} + +/** + * Keep Chip Select activated after the current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param keep_active if 0 don't keep CS activated, else keep CS activated + */ +static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) +{ + hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; +} + +/*------------------------------------------------------------------------------ + * Configs: parameters + *----------------------------------------------------------------------------*/ +/** + * Set the standard clock mode for master. + * + * @param hw Beginning address of the peripheral registers. + * @param enable_std True for std timing, False for half cycle delay sampling. + */ +static inline void spi_ll_master_set_rx_timing_mode(spi_dev_t *hw, spi_sampling_point_t sample_point) +{ + //This is not supported +} + +/** + * Get if standard clock mode is supported. + */ +static inline bool spi_ll_master_is_rx_std_sample_supported(void) +{ + return false; +} + +/** + * Set the clock for master by stored value. + * + * @param hw Beginning address of the peripheral registers. + * @param val Stored clock configuration calculated before (by ``spi_ll_cal_clock``). + */ +static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_clock_val_t *val) +{ + hw->clock.val = *(uint32_t *)val; +} + +/** + * Get the frequency of given dividers. Don't use in app. + * + * @param fapb APB clock of the system. + * @param pre Pre devider. + * @param n Main divider. + * + * @return Frequency of given dividers. + */ +static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n) +{ + return (fapb / (pre * n)); +} + +/** + * Calculate the nearest frequency avaliable for master. + * + * @param fapb APB clock of the system. + * @param hz Frequncy desired. + * @param duty_cycle Duty cycle desired. + * @param out_reg Output address to store the calculated clock configurations for the return frequency. + * + * @return Actual (nearest) frequency. + */ +static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg) +{ + typeof(GPSPI2.clock) reg = {.val = 0}; + int eff_clk; + + //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value. + if (hz > ((fapb / 4) * 3)) { + //Using Fapb directly will give us the best result here. + reg.clkcnt_l = 0; + reg.clkcnt_h = 0; + reg.clkcnt_n = 0; + reg.clkdiv_pre = 0; + reg.clk_equ_sysclk = 1; + eff_clk = fapb; + } else { + //For best duty cycle resolution, we want n to be as close to 32 as possible, but + //we also need a pre/n combo that gets us as close as possible to the intended freq. + //To do this, we bruteforce n and calculate the best pre to go along with that. + //If there's a choice between pre/n combos that give the same result, use the one + //with the higher n. + int pre, n, h, l; + int bestn = -1; + int bestpre = -1; + int besterr = 0; + int errval; + for (n = 2; n <= 64; n++) { //Start at 2: we need to be able to set h/l so we have at least one high and one low pulse. + //Effectively, this does pre=round((fapb/n)/hz). + pre = ((fapb / n) + (hz / 2)) / hz; + if (pre <= 0) { + pre = 1; + } + if (pre > 16) { + pre = 16; + } + errval = abs(spi_ll_freq_for_pre_n(fapb, pre, n) - hz); + if (bestn == -1 || errval <= besterr) { + besterr = errval; + bestn = n; + bestpre = pre; + } + } + + n = bestn; + pre = bestpre; + l = n; + //This effectively does round((duty_cycle*n)/256) + h = (duty_cycle * n + 127) / 256; + if (h <= 0) { + h = 1; + } + + reg.clk_equ_sysclk = 0; + reg.clkcnt_n = n - 1; + reg.clkdiv_pre = pre - 1; + reg.clkcnt_h = h - 1; + reg.clkcnt_l = l - 1; + eff_clk = spi_ll_freq_for_pre_n(fapb, pre, n); + } + if (out_reg != NULL) { + *(uint32_t *)out_reg = reg.val; + } + return eff_clk; +} + +/** + * Calculate and set clock for SPI master according to desired parameters. + * + * This takes long, suggest to calculate the configuration during + * initialization by ``spi_ll_master_cal_clock`` and store the result, then + * configure the clock by stored value when used by + * ``spi_ll_msater_set_clock_by_reg``. + * + * @param hw Beginning address of the peripheral registers. + * @param fapb APB clock of the system. + * @param hz Frequncy desired. + * @param duty_cycle Duty cycle desired. + * + * @return Actual frequency that is used. + */ +static inline int spi_ll_master_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) +{ + spi_ll_clock_val_t reg_val; + int freq = spi_ll_master_cal_clock(fapb, hz, duty_cycle, ®_val); + spi_ll_master_set_clock_by_reg(hw, ®_val); + return freq; +} + +/** + * Set the mosi delay after the output edge to the signal. (Preview) + * + * The delay mode/num is a Espressif conception, may change in the new chips. + * + * @param hw Beginning address of the peripheral registers. + * @param delay_mode Delay mode, see TRM. + * @param delay_num APB clocks to delay. + */ +static inline void spi_ll_set_mosi_delay(spi_dev_t *hw, int delay_mode, int delay_num) +{ +} + +/** + * Set the miso delay applied to the input signal before the internal peripheral. (Preview) + * + * The delay mode/num is a Espressif conception, may change in the new chips. + * + * @param hw Beginning address of the peripheral registers. + * @param delay_mode Delay mode, see TRM. + * @param delay_num APB clocks to delay. + */ +static inline void spi_ll_set_miso_delay(spi_dev_t *hw, int delay_mode, int delay_num) +{ +} + +/** + * Set the delay of SPI clocks before the CS inactive edge after the last SPI clock. + * + * @param hw Beginning address of the peripheral registers. + * @param hold Delay of SPI clocks after the last clock, 0 to disable the hold phase. + */ +static inline void spi_ll_master_set_cs_hold(spi_dev_t *hw, int hold) +{ + hw->user1.cs_hold_time = hold; + hw->user.cs_hold = hold ? 1 : 0; +} + +/** + * Set the delay of SPI clocks before the first SPI clock after the CS active edge. + * + * Note ESP32 doesn't support to use this feature when command/address phases + * are used in full duplex mode. + * + * @param hw Beginning address of the peripheral registers. + * @param setup Delay of SPI clocks after the CS active edge, 0 to disable the setup phase. + */ +static inline void spi_ll_master_set_cs_setup(spi_dev_t *hw, uint8_t setup) +{ + hw->user1.cs_setup_time = setup - 1; + hw->user.cs_setup = setup ? 1 : 0; +} + +/*------------------------------------------------------------------------------ + * Configs: data + *----------------------------------------------------------------------------*/ +/** + * Set the output length (master). + * This should be called before master setting MISO(input) length + * + * @param hw Beginning address of the peripheral registers. + * @param bitlen output length, in bits. + */ +static inline void spi_ll_set_mosi_bitlen(spi_dev_t *hw, size_t bitlen) +{ + if (bitlen > 0) { + hw->ms_dlen.ms_data_bitlen = bitlen - 1; + } +} + +/** + * Set the input length (master). + * + * @param hw Beginning address of the peripheral registers. + * @param bitlen input length, in bits. + */ +static inline void spi_ll_set_miso_bitlen(spi_dev_t *hw, size_t bitlen) +{ + if (bitlen > 0) { + hw->ms_dlen.ms_data_bitlen = bitlen - 1; + } +} + +/** + * Set the maximum input length (slave). + * + * @param hw Beginning address of the peripheral registers. + * @param bitlen Input length, in bits. + */ +static inline void spi_ll_slave_set_rx_bitlen(spi_dev_t *hw, size_t bitlen) +{ + //This is not used in esp32s3 +} + +/** + * Set the maximum output length (slave). + * + * @param hw Beginning address of the peripheral registers. + * @param bitlen Output length, in bits. + */ +static inline void spi_ll_slave_set_tx_bitlen(spi_dev_t *hw, size_t bitlen) +{ + //This is not used in esp32s3 +} + +/** + * Set the length of command phase. + * + * When in 4-bit mode, the SPI cycles of the phase will be shorter. E.g. 16-bit + * command phases takes 4 cycles in 4-bit mode. + * + * @param hw Beginning address of the peripheral registers. + * @param bitlen Length of command phase, in bits. 0 to disable the command phase. + */ +static inline void spi_ll_set_command_bitlen(spi_dev_t *hw, int bitlen) +{ + hw->user2.usr_command_bitlen = bitlen - 1; + hw->user.usr_command = bitlen ? 1 : 0; +} + +/** + * Set the length of address phase. + * + * When in 4-bit mode, the SPI cycles of the phase will be shorter. E.g. 16-bit + * address phases takes 4 cycles in 4-bit mode. + * + * @param hw Beginning address of the peripheral registers. + * @param bitlen Length of address phase, in bits. 0 to disable the address phase. + */ +static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen) +{ + hw->user1.usr_addr_bitlen = bitlen - 1; + hw->user.usr_addr = bitlen ? 1 : 0; +} + +/** + * Set the address value in an intuitive way. + * + * The length and lsbfirst is required to shift and swap the address to the right place. + * + * @param hw Beginning address of the peripheral registers. + * @param address Address to set + * @param addrlen Length of the address phase + * @param lsbfirst Whether the LSB first feature is enabled. + */ +static inline void spi_ll_set_address(spi_dev_t *hw, uint64_t addr, int addrlen, uint32_t lsbfirst) +{ + if (lsbfirst) { + /* The output address start from the LSB of the highest byte, i.e. + * addr[24] -> addr[31] + * ... + * addr[0] -> addr[7] + * So swap the byte order to let the LSB sent first. + */ + addr = HAL_SWAP32(addr); + //otherwise only addr register is sent + hw->addr = addr; + } else { + // shift the address to MSB of addr register. + // output address will be sent from MSB to LSB of addr register + hw->addr = addr << (32 - addrlen); + } +} + +/** + * Set the command value in an intuitive way. + * + * The length and lsbfirst is required to shift and swap the command to the right place. + * + * @param hw Beginning command of the peripheral registers. + * @param command Command to set + * @param addrlen Length of the command phase + * @param lsbfirst Whether the LSB first feature is enabled. + */ +static inline void spi_ll_set_command(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst) +{ + if (lsbfirst) { + // The output command start from bit0 to bit 15, kept as is. + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->user2, usr_command_value, cmd); + } else { + /* Output command will be sent from bit 7 to 0 of command_value, and + * then bit 15 to 8 of the same register field. Shift and swap to send + * more straightly. + */ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->user2, usr_command_value, HAL_SPI_SWAP_DATA_TX(cmd, cmdlen)); + + } +} + +/** + * Set dummy clocks to output before RX phase (master), or clocks to skip + * before the data phase and after the address phase (slave). + * + * Note this phase is also used to compensate RX timing in half duplex mode. + * + * @param hw Beginning address of the peripheral registers. + * @param dummy_n Dummy cycles used. 0 to disable the dummy phase. + */ +static inline void spi_ll_set_dummy(spi_dev_t *hw, int dummy_n) +{ + hw->user.usr_dummy = dummy_n ? 1 : 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->user1, usr_dummy_cyclelen, dummy_n - 1); +} + +/** + * Enable/disable the RX data phase. + * + * @param hw Beginning address of the peripheral registers. + * @param enable True if RX phase exist, otherwise false. + */ +static inline void spi_ll_enable_miso(spi_dev_t *hw, int enable) +{ + hw->user.usr_miso = enable; +} + +/** + * Enable/disable the TX data phase. + * + * @param hw Beginning address of the peripheral registers. + * @param enable True if TX phase exist, otherwise false. + */ +static inline void spi_ll_enable_mosi(spi_dev_t *hw, int enable) +{ + hw->user.usr_mosi = enable; +} + +/** + * Get the received bit length of the slave. + * + * @param hw Beginning address of the peripheral registers. + * + * @return Received bits of the slave. + */ +static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw) +{ + return hw->slave1.data_bitlen; +} + +/*------------------------------------------------------------------------------ + * Interrupts + *----------------------------------------------------------------------------*/ +//helper macros to generate code for each interrupts +#define FOR_EACH_ITEM(op, list) do { list(op) } while(0) +#define INTR_LIST(item) \ + item(SPI_LL_INTR_TRANS_DONE, dma_int_ena.trans_done, dma_int_raw.trans_done, dma_int_clr.trans_done, dma_int_set.trans_done_int_set) \ + item(SPI_LL_INTR_RDBUF, dma_int_ena.rd_buf_done, dma_int_raw.rd_buf_done, dma_int_clr.rd_buf_done, dma_int_set.rd_buf_done_int_set) \ + item(SPI_LL_INTR_WRBUF, dma_int_ena.wr_buf_done, dma_int_raw.wr_buf_done, dma_int_clr.wr_buf_done, dma_int_set.wr_buf_done_int_set) \ + item(SPI_LL_INTR_RDDMA, dma_int_ena.rd_dma_done, dma_int_raw.rd_dma_done, dma_int_clr.rd_dma_done, dma_int_set.rd_dma_done_int_set) \ + item(SPI_LL_INTR_WRDMA, dma_int_ena.wr_dma_done, dma_int_raw.wr_dma_done, dma_int_clr.wr_dma_done, dma_int_set.wr_dma_done_int_set) \ + item(SPI_LL_INTR_SEG_DONE, dma_int_ena.dma_seg_trans_done, dma_int_raw.dma_seg_trans_done, dma_int_clr.dma_seg_trans_done, dma_int_set.dma_seg_trans_done_int_set) \ + item(SPI_LL_INTR_CMD7, dma_int_ena.cmd7, dma_int_raw.cmd7, dma_int_clr.cmd7, dma_int_set.cmd7_int_set) \ + item(SPI_LL_INTR_CMD8, dma_int_ena.cmd8, dma_int_raw.cmd8, dma_int_clr.cmd8, dma_int_set.cmd8_int_set) \ + item(SPI_LL_INTR_CMD9, dma_int_ena.cmd9, dma_int_raw.cmd9, dma_int_clr.cmd9, dma_int_set.cmd9_int_set) \ + item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda, dma_int_set.cmda_int_set) + + +static inline void spi_ll_enable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask) +{ +#define ENA_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 1; + FOR_EACH_ITEM(ENA_INTR, INTR_LIST); +#undef ENA_INTR +} + +static inline void spi_ll_disable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask) +{ +#define DIS_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 0; + FOR_EACH_ITEM(DIS_INTR, INTR_LIST); +#undef DIS_INTR +} + +static inline void spi_ll_set_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask) +{ +#define SET_INTR(intr_bit, _, __, ___, set_reg) if (intr_mask & (intr_bit)) hw->set_reg = 1; + FOR_EACH_ITEM(SET_INTR, INTR_LIST); +#undef SET_INTR +} + +static inline void spi_ll_clear_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask) +{ +#define CLR_INTR(intr_bit, _, __, clr_reg, ...) if (intr_mask & (intr_bit)) hw->clr_reg = 1; + FOR_EACH_ITEM(CLR_INTR, INTR_LIST); +#undef CLR_INTR +} + +static inline bool spi_ll_get_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask) +{ +#define GET_INTR(intr_bit, _, raw_reg, ...) if (intr_mask & (intr_bit) && hw->raw_reg) return true; + FOR_EACH_ITEM(GET_INTR, INTR_LIST); + return false; +#undef GET_INTR +} + +#undef FOR_EACH_ITEM +#undef INTR_LIST + +/** + * Disable the trans_done interrupt. + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_disable_int(spi_dev_t *hw) +{ + hw->dma_int_ena.trans_done = 0; +} + +/** + * Clear the trans_done interrupt. + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_clear_int_stat(spi_dev_t *hw) +{ + hw->dma_int_clr.trans_done = 1; +} + +/** + * Set the trans_done interrupt. + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_set_int_stat(spi_dev_t *hw) +{ + hw->dma_int_set.trans_done_int_set = 1; +} + +/** + * Enable the trans_done interrupt. + * + * @param hw Beginning address of the peripheral registers. + */ +static inline void spi_ll_enable_int(spi_dev_t *hw) +{ + hw->dma_int_ena.trans_done = 1; +} + +/*------------------------------------------------------------------------------ + * Slave HD + *----------------------------------------------------------------------------*/ +static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t *hw, spi_ll_trans_len_cond_t cond_mask) +{ + hw->slave.rdbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_RDBUF) ? 1 : 0; + hw->slave.wrbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRBUF) ? 1 : 0; + hw->slave.rddma_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_RDDMA) ? 1 : 0; + hw->slave.wrdma_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRDMA) ? 1 : 0; +} + +static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t *hw) +{ + return hw->slave1.data_bitlen / 8; +} + +static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw) +{ + return hw->slave1.last_addr; +} + +#undef SPI_LL_RST_MASK +#undef SPI_LL_UNUSED_INT_MASK + +/** + * Get the base spi command in esp32s3 + * + * @param cmd_t Command value + */ +static inline uint8_t spi_ll_get_slave_hd_base_command(spi_command_t cmd_t) +{ + uint8_t cmd_base = 0x00; + switch (cmd_t) + { + case SPI_CMD_HD_WRBUF: + cmd_base = SPI_LL_BASE_CMD_HD_WRBUF; + break; + case SPI_CMD_HD_RDBUF: + cmd_base = SPI_LL_BASE_CMD_HD_RDBUF; + break; + case SPI_CMD_HD_WRDMA: + cmd_base = SPI_LL_BASE_CMD_HD_WRDMA; + break; + case SPI_CMD_HD_RDDMA: + cmd_base = SPI_LL_BASE_CMD_HD_RDDMA; + break; + case SPI_CMD_HD_SEG_END: + cmd_base = SPI_LL_BASE_CMD_HD_SEG_END; + break; + case SPI_CMD_HD_EN_QPI: + cmd_base = SPI_LL_BASE_CMD_HD_EN_QPI; + break; + case SPI_CMD_HD_WR_END: + cmd_base = SPI_LL_BASE_CMD_HD_WR_END; + break; + case SPI_CMD_HD_INT0: + cmd_base = SPI_LL_BASE_CMD_HD_INT0; + break; + case SPI_CMD_HD_INT1: + cmd_base = SPI_LL_BASE_CMD_HD_INT1; + break; + case SPI_CMD_HD_INT2: + cmd_base = SPI_LL_BASE_CMD_HD_INT2; + break; + default: + HAL_ASSERT(cmd_base); + } + return cmd_base; +} + +/** + * Get the spi communication command + * + * @param cmd_t Base command value + * @param line_mode Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN. + */ +static inline uint16_t spi_ll_get_slave_hd_command(spi_command_t cmd_t, spi_line_mode_t line_mode) +{ + uint8_t cmd_base = spi_ll_get_slave_hd_base_command(cmd_t); + uint8_t cmd_mod = 0x00; //CMD:1-bit, ADDR:1-bit, DATA:1-bit + + if (line_mode.data_lines == 2) { + if (line_mode.addr_lines == 2) { + cmd_mod = 0x50; //CMD:1-bit, ADDR:2-bit, DATA:2-bit + } else { + cmd_mod = 0x10; //CMD:1-bit, ADDR:1-bit, DATA:2-bit + } + } else if (line_mode.data_lines == 4) { + if (line_mode.addr_lines == 4) { + cmd_mod = 0xA0; //CMD:1-bit, ADDR:4-bit, DATA:4-bit + } else { + cmd_mod = 0x20; //CMD:1-bit, ADDR:1-bit, DATA:4-bit + } + } + if (cmd_base == SPI_LL_BASE_CMD_HD_SEG_END || cmd_base == SPI_LL_BASE_CMD_HD_EN_QPI) { + cmd_mod = 0x00; + } + + return cmd_base | cmd_mod; +} + +/** + * Get the dummy bits + * + * @param line_mode Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN. + */ +static inline int spi_ll_get_slave_hd_dummy_bits(spi_line_mode_t line_mode) +{ + return 8; +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/hal/espressif/components/hal/spi_hal.c b/modules/hal/espressif/components/hal/spi_hal.c new file mode 100644 index 0000000000000..3c983dabc083d --- /dev/null +++ b/modules/hal/espressif/components/hal/spi_hal.c @@ -0,0 +1,169 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The HAL layer for SPI (common part) + +#include "hal/spi_hal.h" +#include "hal/log.h" +#include "hal/assert.h" +#include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" + +//This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros. +#if SOC_GDMA_SUPPORTED +#include "soc/gdma_struct.h" +#include "hal/gdma_ll.h" + +#define spi_dma_ll_rx_enable_burst_data(dev, chan, enable) gdma_ll_rx_enable_data_burst(&GDMA, chan, enable); +#define spi_dma_ll_tx_enable_burst_data(dev, chan, enable) gdma_ll_tx_enable_data_burst(&GDMA, chan, enable); +#define spi_dma_ll_rx_enable_burst_desc(dev, chan, enable) gdma_ll_rx_enable_descriptor_burst(&GDMA, chan, enable); +#define spi_dma_ll_tx_enable_burst_desc(dev, chan, enable) gdma_ll_tx_enable_descriptor_burst(&GDMA, chan, enable); +#define spi_dma_ll_enable_out_auto_wrback(dev, chan, enable) gdma_ll_tx_enable_auto_write_back(&GDMA, chan, enable); +#define spi_dma_ll_set_out_eof_generation(dev, chan, enable) gdma_ll_tx_set_eof_mode(&GDMA, chan, enable); +#endif + +/* The tag may be unused if log level is set to NONE */ +static const __attribute__((unused)) char SPI_HAL_TAG[] = "spi_hal"; + +#define SPI_HAL_CHECK(a, str, ret_val, ...) \ + if (!(a)) { \ + HAL_LOGE(SPI_HAL_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return (ret_val); \ + } + +static void s_spi_hal_dma_init_config(const spi_hal_context_t *hal) +{ + spi_dma_ll_rx_enable_burst_data(hal->dma_in, hal->rx_dma_chan, 1); + spi_dma_ll_tx_enable_burst_data(hal->dma_out, hal->tx_dma_chan, 1); + spi_dma_ll_rx_enable_burst_desc(hal->dma_in, hal->rx_dma_chan, 1); + spi_dma_ll_tx_enable_burst_desc(hal->dma_out, hal->tx_dma_chan, 1); +} + +void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_config_t *config) +{ + memset(hal, 0, sizeof(spi_hal_context_t)); + spi_dev_t *hw = SPI_LL_GET_HW(host_id); + hal->hw = hw; + hal->dma_in = config->dma_in; + hal->dma_out = config->dma_out; + hal->dma_enabled = config->dma_enabled; + hal->dmadesc_tx = config->dmadesc_tx; + hal->dmadesc_rx = config->dmadesc_rx; + hal->tx_dma_chan = config->tx_dma_chan; + hal->rx_dma_chan = config->rx_dma_chan; + hal->dmadesc_n = config->dmadesc_n; + +#if SPI_LL_MOSI_FREE_LEVEL + // Change default data line level to low which same as esp32 + spi_ll_set_mosi_free_level(hw, 0); +#endif + spi_ll_master_init(hw); + if (config->dma_enabled) { + s_spi_hal_dma_init_config(hal); + } + + //Force a transaction done interrupt. This interrupt won't fire yet because + //we initialized the SPI interrupt as disabled. This way, we can just + //enable the SPI interrupt and the interrupt handler will kick in, handling + //any transactions that are queued. + spi_ll_enable_int(hw); + spi_ll_set_int_stat(hw); + spi_ll_set_mosi_delay(hw, 0, 0); + spi_ll_apply_config(hw); +} + +void spi_hal_deinit(spi_hal_context_t *hal) +{ + spi_dev_t *hw = hal->hw; + if (hw) { + spi_ll_disable_int(hw); + spi_ll_clear_int_stat(hw); + } +} + +esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf) +{ + spi_hal_timing_conf_t temp_conf = {}; + + int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, timing_param->expected_freq, timing_param->duty_cycle, &temp_conf.clock_reg); + + //When the speed is too fast, we may need to use dummy cycles to compensate the reading. + //But these don't work for full-duplex connections. + spi_hal_cal_timing(timing_param->clk_src_hz, eff_clk_n, timing_param->use_gpio, timing_param->input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay); + +#ifdef CONFIG_IDF_TARGET_ESP32 + const int freq_limit = spi_hal_get_freq_limit(timing_param->use_gpio, timing_param->input_delay_ns); + + SPI_HAL_CHECK(timing_param->half_duplex || temp_conf.timing_dummy == 0 || timing_param->no_compensate, + "When work in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\ +Try to use IOMUX pins to increase the frequency limit, or use the half duplex mode.\n\ +Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\ +Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.", + ESP_ERR_NOT_SUPPORTED, freq_limit / 1000. / 1000 ); +#endif + + if (timing_conf) { + *timing_conf = temp_conf; + } + if (out_freq) { + *out_freq = eff_clk_n; + } + return ESP_OK; +} + +int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle) +{ + return spi_ll_master_cal_clock(fapb, hz, duty_cycle, NULL); +} + + +void spi_hal_cal_timing(int source_freq_hz, int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n) +{ + const int apbclk_kHz = source_freq_hz / 1000; + //how many apb clocks a period has + const int spiclk_apb_n = source_freq_hz / eff_clk; + const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0; + + //how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off. + int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000; + if (delay_apb_n < 0) { + delay_apb_n = 0; + } + + int dummy_required = delay_apb_n / spiclk_apb_n; + + int miso_delay = 0; + if (dummy_required > 0) { + //due to the clock delay between master and slave, there's a range in which data is random + //give MISO a delay if needed to make sure we sample at the time MISO is stable + miso_delay = (dummy_required + 1) * spiclk_apb_n - delay_apb_n - 1; + } else { + //if the dummy is not required, maybe we should also delay half a SPI clock if the data comes too early + if (delay_apb_n * 4 <= spiclk_apb_n) { + miso_delay = -1; + } + } + *dummy_n = dummy_required; + *miso_delay_n = miso_delay; + HAL_LOGD(SPI_HAL_TAG, "eff: %d, limit: %dk(/%d), %d dummy, %d delay", eff_clk / 1000, apbclk_kHz / (delay_apb_n + 1), delay_apb_n, dummy_required, miso_delay); +} + +#ifdef CONFIG_IDF_TARGET_ESP32 +//TODO: IDF-6578 +int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns) +{ + const int apbclk_kHz = APB_CLK_FREQ / 1000; + const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0; + + //how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off. + int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000; + if (delay_apb_n < 0) { + delay_apb_n = 0; + } + + return APB_CLK_FREQ / (delay_apb_n + 1); +} +#endif diff --git a/modules/hal/espressif/components/hal/spi_hal_iram.c b/modules/hal/espressif/components/hal/spi_hal_iram.c new file mode 100644 index 0000000000000..2280b1ec2a1f3 --- /dev/null +++ b/modules/hal/espressif/components/hal/spi_hal_iram.c @@ -0,0 +1,207 @@ +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The HAL layer for SPI (common part, in iram) +// make these functions in a seperate file to make sure all LL functions are in the IRAM. + +#include "hal/spi_hal.h" +#include "hal/assert.h" +#include "soc/soc_caps.h" + +//This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros. +#if SOC_GDMA_SUPPORTED +#include "soc/gdma_struct.h" +#include "hal/gdma_ll.h" + +#define spi_dma_ll_rx_reset(dev, chan) gdma_ll_rx_reset_channel(&GDMA, chan) +#define spi_dma_ll_tx_reset(dev, chan) gdma_ll_tx_reset_channel(&GDMA, chan); +#define spi_dma_ll_rx_start(dev, chan, addr) do {\ + gdma_ll_rx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\ + gdma_ll_rx_start(&GDMA, chan);\ + } while (0) +#define spi_dma_ll_tx_start(dev, chan, addr) do {\ + gdma_ll_tx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\ + gdma_ll_tx_start(&GDMA, chan);\ + } while (0) +#endif + +void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev) +{ + //Configure clock settings + spi_dev_t *hw = hal->hw; +#if SOC_SPI_AS_CS_SUPPORTED + spi_ll_master_set_cksel(hw, dev->cs_pin_id, dev->as_cs); +#endif + spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs); + spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg); + spi_ll_master_set_rx_timing_mode(hw, dev->timing_conf.rx_sample_point); + spi_ll_set_clk_source(hw, dev->timing_conf.clock_source); + //Configure bit order + spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst); + spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst); + spi_ll_master_set_mode(hw, dev->mode); + //Configure misc stuff + spi_ll_set_half_duplex(hw, dev->half_duplex); + spi_ll_set_sio_mode(hw, dev->sio); + //Configure CS pin and timing + spi_ll_master_set_cs_setup(hw, dev->cs_setup); + spi_ll_master_set_cs_hold(hw, dev->cs_hold); + spi_ll_master_select_cs(hw, dev->cs_pin_id); +} + +void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans) +{ + spi_dev_t *hw = hal->hw; + + //clear int bit + spi_ll_clear_int_stat(hal->hw); + //We should be done with the transmission. + HAL_ASSERT(spi_ll_get_running_cmd(hw) == 0); + //set transaction line mode + spi_ll_master_set_line_mode(hw, trans->line_mode); + + int extra_dummy = 0; + //when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist + if (trans->rcv_buffer && !dev->no_compensate && dev->half_duplex) { + extra_dummy = dev->timing_conf.timing_dummy; + } + + //SPI iface needs to be configured for a delay in some cases. + //configure dummy bits + spi_ll_set_dummy(hw, extra_dummy + trans->dummy_bits); + + uint32_t miso_delay_num = 0; + uint32_t miso_delay_mode = 0; + if (dev->timing_conf.timing_miso_delay < 0) { + //if the data comes too late, delay half a SPI clock to improve reading + switch (dev->mode) { + case 0: + miso_delay_mode = 2; + break; + case 1: + miso_delay_mode = 1; + break; + case 2: + miso_delay_mode = 1; + break; + case 3: + miso_delay_mode = 2; + break; + } + miso_delay_num = 0; + } else { + //if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing + miso_delay_num = extra_dummy ? dev->timing_conf.timing_miso_delay : 0; + miso_delay_mode = 0; + } + spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num); + + spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen); + + if (dev->half_duplex) { + spi_ll_set_miso_bitlen(hw, trans->rx_bitlen); + } else { + //rxlength is not used in full-duplex mode + spi_ll_set_miso_bitlen(hw, trans->tx_bitlen); + } + + //Configure bit sizes, load addr and command + int cmdlen = trans->cmd_bits; + int addrlen = trans->addr_bits; + if (!dev->half_duplex && dev->cs_setup != 0) { + /* The command and address phase is not compatible with cs_ena_pretrans + * in full duplex mode. + */ + cmdlen = 0; + addrlen = 0; + } + + spi_ll_set_addr_bitlen(hw, addrlen); + spi_ll_set_command_bitlen(hw, cmdlen); + + spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst); + spi_ll_set_address(hw, trans->addr, addrlen, dev->tx_lsbfirst); + + //Configure keep active CS + spi_ll_master_keep_cs(hw, trans->cs_keep_active); + + //Save the transaction attributes for internal usage. + memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t)); +} + +void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans) +{ + spi_dev_t *hw = hal->hw; + + //Fill DMA descriptors + if (trans->rcv_buffer) { + if (!hal->dma_enabled) { + //No need to setup anything; we'll copy the result out of the work registers directly later. + } else { + lldesc_setup_link(hal->dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true); + + spi_dma_ll_rx_reset(hal->dma_in, hal->rx_dma_chan); + spi_ll_dma_rx_fifo_reset(hal->hw); + spi_ll_infifo_full_clr(hal->hw); + spi_ll_dma_rx_enable(hal->hw, 1); + spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, hal->dmadesc_rx); + } + + } +#if CONFIG_IDF_TARGET_ESP32 + else { + //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon + if (hal->dma_enabled && !dev->half_duplex) { + spi_ll_dma_rx_enable(hal->hw, 1); + spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, 0); + } + } +#endif + + if (trans->send_buffer) { + if (!hal->dma_enabled) { + //Need to copy data to registers manually + spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen); + } else { + lldesc_setup_link(hal->dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false); + + spi_dma_ll_tx_reset(hal->dma_out, hal->tx_dma_chan); + spi_ll_dma_tx_fifo_reset(hal->hw); + spi_ll_outfifo_empty_clr(hal->hw); + spi_ll_dma_tx_enable(hal->hw, 1); + spi_dma_ll_tx_start(hal->dma_out, hal->tx_dma_chan, hal->dmadesc_tx); + } + } + + //in ESP32 these registers should be configured after the DMA is set + if ((!dev->half_duplex && trans->rcv_buffer) || trans->send_buffer) { + spi_ll_enable_mosi(hw, 1); + } else { + spi_ll_enable_mosi(hw, 0); + } + spi_ll_enable_miso(hw, (trans->rcv_buffer) ? 1 : 0); +} + +void spi_hal_user_start(const spi_hal_context_t *hal) +{ + spi_ll_apply_config(hal->hw); + spi_ll_user_start(hal->hw); +} + +bool spi_hal_usr_is_done(const spi_hal_context_t *hal) +{ + return spi_ll_usr_is_done(hal->hw); +} + +void spi_hal_fetch_result(const spi_hal_context_t *hal) +{ + const spi_hal_trans_config_t *trans = &hal->trans_config; + + if (trans->rcv_buffer && !hal->dma_enabled) { + //Need to copy from SPI regs to result buffer. + spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen); + } +} diff --git a/modules/hal/espressif/components/soc/esp32s3/include/soc/clk_tree_defs.h b/modules/hal/espressif/components/soc/esp32s3/include/soc/clk_tree_defs.h new file mode 100644 index 0000000000000..a6765a348564e --- /dev/null +++ b/modules/hal/espressif/components/soc/esp32s3/include/soc/clk_tree_defs.h @@ -0,0 +1,476 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ************************* ESP32S3 Root Clock Source **************************** + * 1) Internal 17.5MHz RC Oscillator: RC_FAST (usually referred as FOSC or CK8M/CLK8M in TRM and reg. description) + * + * This RC oscillator generates a ~17.5MHz clock signal output as the RC_FAST_CLK. + * The ~17.5MHz signal output is also passed into a configurable divider, which by default divides the input clock + * frequency by 256, to generate a RC_FAST_D256_CLK (usually referred as 8md256 or simply d256 in reg. description). + * + * The exact frequency of RC_FAST_CLK can be computed in runtime through calibration on the RC_FAST_D256_CLK. + * + * 2) External 40MHz Crystal Clock: XTAL + * + * 3) Internal 136kHz RC Oscillator: RC_SLOW (usually referrred as RTC in TRM or reg. description) + * + * This RC oscillator generates a ~136kHz clock signal output as the RC_SLOW_CLK. The exact frequency of this clock + * can be computed in runtime through calibration. + * + * 4) External 32kHz Crystal Clock (optional): XTAL32K + * + * The clock source for this XTAL32K_CLK can be either a 32kHz crystal connecting to the XTAL_32K_P and XTAL_32K_N + * pins or a 32kHz clock signal generated by an external circuit. The external signal must be connected to the + * XTAL_32K_P pin. + * + * XTAL32K_CLK can also be calibrated to get its exact frequency. + */ + +/* With the default value of CK8M_DFREQ = 100, RC_FAST clock frequency is 17.5 MHz +/- 7% */ +#define SOC_CLK_RC_FAST_FREQ_APPROX 17500000 /*!< Approximate RC_FAST_CLK frequency in Hz */ +#define SOC_CLK_RC_SLOW_FREQ_APPROX 136000 /*!< Approximate RC_SLOW_CLK frequency in Hz */ +#define SOC_CLK_RC_FAST_D256_FREQ_APPROX (SOC_CLK_RC_FAST_FREQ_APPROX / 256) /*!< Approximate RC_FAST_D256_CLK frequency in Hz */ +#define SOC_CLK_XTAL32K_FREQ_APPROX 32768 /*!< Approximate XTAL32K_CLK frequency in Hz */ + +// Naming convention: SOC_ROOT_CLK_{loc}_{type}_[attr] +// {loc}: EXT, INT +// {type}: XTAL, RC +// [attr] - optional: [frequency], FAST, SLOW +/** + * @brief Root clock + */ +typedef enum { + SOC_ROOT_CLK_INT_RC_FAST, /*!< Internal 17.5MHz RC oscillator */ + SOC_ROOT_CLK_INT_RC_SLOW, /*!< Internal 136kHz RC oscillator */ + SOC_ROOT_CLK_EXT_XTAL, /*!< External 40MHz crystal */ + SOC_ROOT_CLK_EXT_XTAL32K, /*!< External 32kHz crystal/clock signal */ +} soc_root_clk_t; + +/** + * @brief CPU_CLK mux inputs, which are the supported clock sources for the CPU_CLK + * @note Enum values are matched with the register field values on purpose + */ +typedef enum { + SOC_CPU_CLK_SRC_XTAL = 0, /*!< Select XTAL_CLK as CPU_CLK source */ + SOC_CPU_CLK_SRC_PLL = 1, /*!< Select PLL_CLK as CPU_CLK source (PLL_CLK is the output of 40MHz crystal oscillator frequency multiplier, can be 480MHz or 320MHz) */ + SOC_CPU_CLK_SRC_RC_FAST = 2, /*!< Select RC_FAST_CLK as CPU_CLK source */ + SOC_CPU_CLK_SRC_INVALID, /*!< Invalid CPU_CLK source */ +} soc_cpu_clk_src_t; + +/** + * @brief RTC_SLOW_CLK mux inputs, which are the supported clock sources for the RTC_SLOW_CLK + * @note Enum values are matched with the register field values on purpose + */ +typedef enum { + SOC_RTC_SLOW_CLK_SRC_RC_SLOW = 0, /*!< Select RC_SLOW_CLK as RTC_SLOW_CLK source */ + SOC_RTC_SLOW_CLK_SRC_XTAL32K = 1, /*!< Select XTAL32K_CLK as RTC_SLOW_CLK source */ + SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256 = 2, /*!< Select RC_FAST_D256_CLK (referred as FOSC_DIV or 8m_d256/8md256 in TRM and reg. description) as RTC_SLOW_CLK source */ + SOC_RTC_SLOW_CLK_SRC_INVALID, /*!< Invalid RTC_SLOW_CLK source */ +} soc_rtc_slow_clk_src_t; + +/** + * @brief RTC_FAST_CLK mux inputs, which are the supported clock sources for the RTC_FAST_CLK + * @note Enum values are matched with the register field values on purpose + */ +typedef enum { + SOC_RTC_FAST_CLK_SRC_XTAL_D2 = 0, /*!< Select XTAL_D2_CLK (may referred as XTAL_CLK_DIV_2) as RTC_FAST_CLK source */ + SOC_RTC_FAST_CLK_SRC_XTAL_DIV = SOC_RTC_FAST_CLK_SRC_XTAL_D2, /*!< Alias name for `SOC_RTC_FAST_CLK_SRC_XTAL_D2` */ + SOC_RTC_FAST_CLK_SRC_RC_FAST = 1, /*!< Select RC_FAST_CLK as RTC_FAST_CLK source */ + SOC_RTC_FAST_CLK_SRC_INVALID, /*!< Invalid RTC_FAST_CLK source */ +} soc_rtc_fast_clk_src_t; + +/** + * @brief Possible main XTAL frequency options on the target + * @note Enum values equal to the frequency value in MHz + * @note Not all frequency values listed here are supported in IDF. Please check SOC_XTAL_SUPPORT_XXX in soc_caps.h for + * the supported ones. + */ +typedef enum { + SOC_XTAL_FREQ_32M = 32, /*!< 32MHz XTAL */ + SOC_XTAL_FREQ_40M = 40, /*!< 40MHz XTAL */ +} soc_xtal_freq_t; + +// Naming convention: SOC_MOD_CLK_{[upstream]clock_name}_[attr] +// {[upstream]clock_name}: APB, (BB)PLL, etc. +// [attr] - optional: FAST, SLOW, D, F +/** + * @brief Supported clock sources for modules (CPU, peripherals, RTC, etc.) + * + * @note enum starts from 1, to save 0 for special purpose + */ +typedef enum { + // For CPU domain + SOC_MOD_CLK_CPU = 1, /*!< CPU_CLK can be sourced from XTAL, PLL, or RC_FAST by configuring soc_cpu_clk_src_t */ + // For RTC domain + SOC_MOD_CLK_RTC_FAST, /*!< RTC_FAST_CLK can be sourced from XTAL_D2 or RC_FAST by configuring soc_rtc_fast_clk_src_t */ + SOC_MOD_CLK_RTC_SLOW, /*!< RTC_SLOW_CLK can be sourced from RC_SLOW, XTAL32K, or RC_FAST_D256 by configuring soc_rtc_slow_clk_src_t */ + // For digital domain: peripherals, WIFI, BLE + SOC_MOD_CLK_APB, /*!< APB_CLK is highly dependent on the CPU_CLK source */ + SOC_MOD_CLK_PLL_F80M, /*!< PLL_F80M_CLK is derived from PLL, and has a fixed frequency of 80MHz */ + SOC_MOD_CLK_PLL_F160M, /*!< PLL_F160M_CLK is derived from PLL, and has a fixed frequency of 160MHz */ + SOC_MOD_CLK_PLL_D2, /*!< PLL_D2_CLK is derived from PLL, it has a fixed divider of 2 */ + SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */ + SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 20MHz rc oscillator, passing a clock gating to the peripherals */ + SOC_MOD_CLK_RC_FAST_D256, /*!< RC_FAST_D256_CLK comes from the internal 20MHz rc oscillator, divided by 256, and passing a clock gating to the peripherals */ + SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 40MHz crystal */ + SOC_MOD_CLK_TEMP_SENSOR, /*!< TEMP_SENSOR_CLK comes directly from the internal 20MHz rc oscillator */ + SOC_MOD_CLK_INVALID, /*!< Indication of the end of the available module clock sources */ +} soc_module_clk_t; + +//////////////////////////////////////////////////SYSTIMER/////////////////////////////////////////////////////////////// + +/** + * @brief Type of SYSTIMER clock source + */ +typedef enum { + SYSTIMER_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< SYSTIMER source clock is XTAL */ + SYSTIMER_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, /*!< SYSTIMER source clock default choice is XTAL */ +} soc_periph_systimer_clk_src_t; + +//////////////////////////////////////////////////GPTimer/////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of GPTimer + * + * The following code can be used to iterate all possible clocks: + * @code{c} + * soc_periph_gptimer_clk_src_t gptimer_clks[] = (soc_periph_gptimer_clk_src_t)SOC_GPTIMER_CLKS; + * for (size_t i = 0; i< sizeof(gptimer_clks) / sizeof(gptimer_clks[0]); i++) { + * soc_periph_gptimer_clk_src_t clk = gptimer_clks[i]; + * // Test GPTimer with the clock `clk` + * } + * @endcode + */ +#define SOC_GPTIMER_CLKS {SOC_MOD_CLK_APB, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of GPTimer clock source + */ +typedef enum { + GPTIMER_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + GPTIMER_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + GPTIMER_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default choice */ +} soc_periph_gptimer_clk_src_t; + +/** + * @brief Type of Timer Group clock source, reserved for the legacy timer group driver + */ +typedef enum { + TIMER_SRC_CLK_APB = SOC_MOD_CLK_APB, /*!< Timer group source clock is APB */ + TIMER_SRC_CLK_XTAL = SOC_MOD_CLK_XTAL, /*!< Timer group source clock is XTAL */ + TIMER_SRC_CLK_DEFAULT = SOC_MOD_CLK_APB, /*!< Timer group source clock default choice is APB */ +} soc_periph_tg_clk_src_legacy_t; + +//////////////////////////////////////////////////LCD/////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of LCD + */ +#define SOC_LCD_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of LCD clock source + */ +typedef enum { + LCD_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + LCD_CLK_SRC_PLL240M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */ + LCD_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */ +} soc_periph_lcd_clk_src_t; + +//////////////////////////////////////////////////RMT/////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of RMT + */ +#define SOC_RMT_CLKS {SOC_MOD_CLK_APB, SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of RMT clock source + */ +typedef enum { + RMT_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + RMT_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */ + RMT_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + RMT_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default choice */ +} soc_periph_rmt_clk_src_t; + +/** + * @brief Type of RMT clock source, reserved for the legacy RMT driver + */ +typedef enum { + RMT_BASECLK_APB = SOC_MOD_CLK_APB, /*!< RMT source clock is APB */ + RMT_BASECLK_XTAL = SOC_MOD_CLK_XTAL, /*!< RMT source clock is XTAL */ + RMT_BASECLK_DEFAULT = SOC_MOD_CLK_APB, /*!< RMT source clock default choice is APB */ +} soc_periph_rmt_clk_src_legacy_t; + +//////////////////////////////////////////////////Temp Sensor/////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of Temperature Sensor + */ +#define SOC_TEMP_SENSOR_CLKS {SOC_MOD_CLK_TEMP_SENSOR} + +/** + * @brief Type of Temp Sensor clock source + */ +typedef enum { + TEMPERATURE_SENSOR_CLK_SRC_RC_FAST = SOC_MOD_CLK_TEMP_SENSOR, /*!< Select RC_FAST as the source clock */ + TEMPERATURE_SENSOR_CLK_SRC_DEFAULT = SOC_MOD_CLK_TEMP_SENSOR, /*!< Select RC_FAST as the default choice */ +} soc_periph_temperature_sensor_clk_src_t; + +///////////////////////////////////////////////////UART///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of UART + */ +#define SOC_UART_CLKS {SOC_MOD_CLK_APB, SOC_MOD_CLK_XTAL, SOC_MOD_CLK_RC_FAST} + +/** + * @brief Type of UART clock source, reserved for the legacy UART driver + */ +typedef enum { + UART_SCLK_APB = SOC_MOD_CLK_APB, /*!< UART source clock is APB CLK */ + UART_SCLK_RTC = SOC_MOD_CLK_RC_FAST, /*!< UART source clock is RC_FAST */ + UART_SCLK_XTAL = SOC_MOD_CLK_XTAL, /*!< UART source clock is XTAL */ + UART_SCLK_DEFAULT = SOC_MOD_CLK_APB, /*!< UART source clock default choice is APB */ +} soc_periph_uart_clk_src_legacy_t; + +//////////////////////////////////////////////////MCPWM///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of MCPWM Timer + */ +#define SOC_MCPWM_TIMER_CLKS {SOC_MOD_CLK_PLL_F160M} + +/** + * @brief Type of MCPWM timer clock source + */ +typedef enum { + MCPWM_TIMER_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + MCPWM_TIMER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ +} soc_periph_mcpwm_timer_clk_src_t; + +/** + * @brief Array initializer for all supported clock sources of MCPWM Capture Timer + */ +#define SOC_MCPWM_CAPTURE_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief Type of MCPWM capture clock source + */ +typedef enum { + MCPWM_CAPTURE_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_mcpwm_capture_clk_src_t; + +/** + * @brief Array initializer for all supported clock sources of MCPWM Carrier + */ +#define SOC_MCPWM_CARRIER_CLKS {SOC_MOD_CLK_PLL_F160M} + +/** + * @brief Type of MCPWM carrier clock source + */ +typedef enum { + MCPWM_CARRIER_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + MCPWM_CARRIER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ +} soc_periph_mcpwm_carrier_clk_src_t; + +///////////////////////////////////////////////////// I2S ////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of I2S + */ +#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_XTAL, I2S_CLK_SRC_EXTERNAL} + +/** + * @brief I2S clock source enum + */ +typedef enum { + I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */ + I2S_CLK_SRC_PLL_240M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */ + I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + I2S_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */ +} soc_periph_i2s_clk_src_t; + +/////////////////////////////////////////////////I2C//////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of I2C + */ +#define SOC_I2C_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_RC_FAST} + +/** + * @brief Type of I2C clock source. + */ +typedef enum { + I2C_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, + I2C_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, + I2C_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, +} soc_periph_i2c_clk_src_t; + +/////////////////////////////////////////////////SPI//////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of SPI + */ +#define SOC_SPI_CLKS {SOC_MOD_CLK_APB, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of SPI clock source. + */ +typedef enum { + SPI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as SPI source clock */ + SPI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as SPI source clock */ + SPI_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as SPI source clock */ +} soc_periph_spi_clk_src_t; + +//////////////////////////////////////////////////SDM////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of SDM + */ +#define SOC_SDM_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief Sigma Delta Modulator clock source + */ +typedef enum { + SDM_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_sdm_clk_src_t; + +//////////////////////////////////////////////////GPIO Glitch Filter//////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of Glitch Filter + */ +#define SOC_GLITCH_FILTER_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief Glitch filter clock source + */ + +typedef enum { + GLITCH_FILTER_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB clock as the source clock */ + GLITCH_FILTER_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB clock as the default clock choice */ +} soc_periph_glitch_filter_clk_src_t; + +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_twai_clk_src_t; + +//////////////////////////////////////////////////ADC/////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of ADC digital controller + */ +#define SOC_ADC_DIGI_CLKS {SOC_MOD_CLK_APB, SOC_MOD_CLK_PLL_D2} + +/** + * @brief ADC digital controller clock source + */ +typedef enum { + ADC_DIGI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + ADC_DIGI_CLK_SRC_PLL_F240M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 (default value PLL_F240M) as the source clock */ + ADC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_adc_digi_clk_src_t; + +/** + * @brief Array initializer for all supported clock sources of ADC RTC controller + */ +#define SOC_ADC_RTC_CLKS {SOC_MOD_CLK_RC_FAST} + +/** + * @brief ADC RTC controller clock source + */ +typedef enum { + ADC_RTC_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */ + ADC_RTC_CLK_SRC_DEFAULT = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the default clock choice */ +} soc_periph_adc_rtc_clk_src_t; + +//////////////////////////////////////////////////MWDT///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of MWDT + */ +#define SOC_MWDT_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief MWDT clock source + */ +typedef enum { + MWDT_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + MWDT_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_mwdt_clk_src_t; + +//////////////////////////////////////////////////LEDC///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of LEDC + */ +#define SOC_LEDC_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_APB, SOC_MOD_CLK_RC_FAST} + +/** + * @brief Type of LEDC clock source, reserved for the legacy LEDC driver + */ +typedef enum { + LEDC_AUTO_CLK = 0, /*!< LEDC source clock will be automatically selected based on the giving resolution and duty parameter when init the timer*/ + LEDC_USE_APB_CLK = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + LEDC_USE_RC_FAST_CLK = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */ + LEDC_USE_XTAL_CLK = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + + LEDC_USE_RTC8M_CLK __attribute__((deprecated("please use 'LEDC_USE_RC_FAST_CLK' instead"))) = LEDC_USE_RC_FAST_CLK, /*!< Alias of 'LEDC_USE_RC_FAST_CLK' */ +} soc_periph_ledc_clk_src_legacy_t; + +//////////////////////////////////////////////////SDMMC/////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of SDMMC + */ +#define SOC_SDMMC_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of SDMMC clock source + */ +typedef enum { + SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the default choice */ + SDMMC_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the source clock */ + SDMMC_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ +} soc_periph_sdmmc_clk_src_t; + +//////////////////////////////////////////////CLOCK OUTPUT/////////////////////////////////////////////////////////// +typedef enum { + CLKOUT_SIG_PLL = 1, /*!< PLL_CLK is the output of crystal oscillator frequency multiplier */ + CLKOUT_SIG_RC_SLOW = 4, /*!< RC slow clock, depends on the RTC_CLK_SRC configuration */ + CLKOUT_SIG_XTAL = 5, /*!< Main crystal oscillator clock */ + CLKOUT_SIG_PLL_F80M = 13, /*!< From PLL, usually be 80MHz */ + CLKOUT_SIG_RC_FAST = 14, /*!< RC fast clock, about 17.5MHz */ + CLKOUT_SIG_INVALID = 0xFF, +} soc_clkout_sig_id_t; + +#ifdef __cplusplus +} +#endif diff --git a/modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h b/modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h new file mode 100644 index 0000000000000..2a3abd257c178 --- /dev/null +++ b/modules/hal/espressif/components/soc/esp32s3/include/soc/spi_struct.h @@ -0,0 +1,428 @@ +// Copyright 2017-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _SOC_SPI_STRUCT_H_ +#define _SOC_SPI_STRUCT_H_ + + +#include +#ifdef __cplusplus +extern "C" { +#endif + +typedef volatile struct spi_dev_s { + union { + struct { + uint32_t conf_bitlen : 18; /*Define the APB cycles of SPI_CONF state. Can be configured in CONF state.*/ + uint32_t reserved18 : 5; /*reserved*/ + uint32_t update : 1; /*Set this bit to synchronize SPI registers from APB clock domain into SPI module clock domain, which is only used in SPI master mode.*/ + uint32_t usr : 1; /*User define command enable. An operation will be triggered when the bit is set. The bit will be cleared once the operation done.1: enable 0: disable. Can not be changed by CONF_buf.*/ + uint32_t reserved25 : 7; /*reserved*/ + }; + uint32_t val; + } cmd; + uint32_t addr; + union { + struct { + uint32_t reserved0 : 3; /*reserved*/ + uint32_t dummy_out : 1; /*0: In the dummy phase, the FSPI bus signals are not output. 1: In the dummy phase, the FSPI bus signals are output. Can be configured in CONF state.*/ + uint32_t reserved4 : 1; /*reserved*/ + uint32_t faddr_dual : 1; /*Apply 2 signals during addr phase 1:enable 0: disable. Can be configured in CONF state.*/ + uint32_t faddr_quad : 1; /*Apply 4 signals during addr phase 1:enable 0: disable. Can be configured in CONF state.*/ + uint32_t faddr_oct : 1; /*Apply 8 signals during addr phase 1:enable 0: disable. Can be configured in CONF state.*/ + uint32_t fcmd_dual : 1; /*Apply 2 signals during command phase 1:enable 0: disable. Can be configured in CONF state.*/ + uint32_t fcmd_quad : 1; /*Apply 4 signals during command phase 1:enable 0: disable. Can be configured in CONF state.*/ + uint32_t fcmd_oct : 1; /*Apply 8 signals during command phase 1:enable 0: disable. Can be configured in CONF state.*/ + uint32_t reserved11 : 3; /*reserved*/ + uint32_t fread_dual : 1; /*In the read operations, read-data phase apply 2 signals. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t fread_quad : 1; /*In the read operations read-data phase apply 4 signals. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t fread_oct : 1; /*In the read operations read-data phase apply 8 signals. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t reserved17 : 1; /*reserved*/ + uint32_t q_pol : 1; /*The bit is used to set MISO line polarity, 1: high 0, low. Can be configured in CONF state.*/ + uint32_t d_pol : 1; /*The bit is used to set MOSI line polarity, 1: high 0, low. Can be configured in CONF state.*/ + uint32_t hold_pol : 1; /*SPI_HOLD output value when SPI is idle. 1: output high, 0: output low. Can be configured in CONF state.*/ + uint32_t wp_pol : 1; /*Write protect signal output when SPI is idle. 1: output high, 0: output low. Can be configured in CONF state.*/ + uint32_t reserved22 : 1; /*reserved*/ + uint32_t rd_bit_order : 2; /*In read-data (MISO) phase 1: LSB first 0: MSB first. Can be configured in CONF state.*/ + uint32_t wr_bit_order : 2; /*In command address write-data (MOSI) phases 1: LSB firs 0: MSB first. Can be configured in CONF state.*/ + uint32_t reserved27 : 5; /*reserved*/ + }; + uint32_t val; + } ctrl; + union { + struct { + uint32_t clkcnt_l : 6; /*In the master mode it must be equal to spi_clkcnt_N. In the slave mode it must be 0. Can be configured in CONF state.*/ + uint32_t clkcnt_h : 6; /*In the master mode it must be floor((spi_clkcnt_N+1)/2-1). In the slave mode it must be 0. Can be configured in CONF state.*/ + uint32_t clkcnt_n : 6; /*In the master mode it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1). Can be configured in CONF state.*/ + uint32_t clkdiv_pre : 4; /*In the master mode it is pre-divider of spi_clk. Can be configured in CONF state.*/ + uint32_t reserved22 : 9; /*reserved*/ + uint32_t clk_equ_sysclk : 1; /*In the master mode 1: spi_clk is eqaul to system 0: spi_clk is divided from system clock. Can be configured in CONF state.*/ + }; + uint32_t val; + } clock; + union { + struct { + uint32_t doutdin : 1; /*Set the bit to enable full duplex communication. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t reserved1 : 2; /*reserved*/ + uint32_t qpi_mode : 1; /*Both for master mode and slave mode. 1: spi controller is in QPI mode. 0: others. Can be configured in CONF state.*/ + uint32_t opi_mode : 1; /*Just for master mode. 1: spi controller is in OPI mode (all in 8-b-m). 0: others. Can be configured in CONF state.*/ + uint32_t tsck_i_edge : 1; /*In the slave mode, this bit can be used to change the polarity of tsck. 0: tsck = spi_ck_i. 1:tsck = !spi_ck_i.*/ + uint32_t cs_hold : 1; /*spi cs keep low when spi is in done phase. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t cs_setup : 1; /*spi cs is enable when spi is in prepare phase. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t rsck_i_edge : 1; /*In the slave mode, this bit can be used to change the polarity of rsck. 0: rsck = !spi_ck_i. 1:rsck = spi_ck_i.*/ + uint32_t ck_out_edge : 1; /*the bit combined with spi_mosi_delay_mode bits to set mosi signal delay mode. Can be configured in CONF state.*/ + uint32_t reserved10 : 2; /*reserved*/ + uint32_t fwrite_dual : 1; /*In the write operations read-data phase apply 2 signals. Can be configured in CONF state.*/ + uint32_t fwrite_quad : 1; /*In the write operations read-data phase apply 4 signals. Can be configured in CONF state.*/ + uint32_t fwrite_oct : 1; /*In the write operations read-data phase apply 8 signals. Can be configured in CONF state.*/ + uint32_t usr_conf_nxt : 1; /*1: Enable the DMA CONF phase of next seg-trans operation, which means seg-trans will continue. 0: The seg-trans will end after the current SPI seg-trans or this is not seg-trans mode. Can be configured in CONF state.*/ + uint32_t reserved16 : 1; /*reserved*/ + uint32_t sio : 1; /*Set the bit to enable 3-line half duplex communication mosi and miso signals share the same pin. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t reserved18 : 6; /*reserved*/ + uint32_t usr_miso_highpart : 1; /*read-data phase only access to high-part of the buffer spi_w8~spi_w15. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t usr_mosi_highpart : 1; /*write-data phase only access to high-part of the buffer spi_w8~spi_w15. 1: enable 0: disable. Can be configured in CONF state.*/ + uint32_t usr_dummy_idle : 1; /*spi clock is disable in dummy phase when the bit is enable. Can be configured in CONF state.*/ + uint32_t usr_mosi : 1; /*This bit enable the write-data phase of an operation. Can be configured in CONF state.*/ + uint32_t usr_miso : 1; /*This bit enable the read-data phase of an operation. Can be configured in CONF state.*/ + uint32_t usr_dummy : 1; /*This bit enable the dummy phase of an operation. Can be configured in CONF state.*/ + uint32_t usr_addr : 1; /*This bit enable the address phase of an operation. Can be configured in CONF state.*/ + uint32_t usr_command : 1; /*This bit enable the command phase of an operation. Can be configured in CONF state.*/ + }; + uint32_t val; + } user; + union { + struct { + uint32_t usr_dummy_cyclelen : 8; /*The length in spi_clk cycles of dummy phase. The register value shall be (cycle_num-1). Can be configured in CONF state.*/ + uint32_t reserved8 : 8; /*reserved*/ + uint32_t mst_wfull_err_end_en : 1; /*1: SPI transfer is ended when SPI RX AFIFO wfull error is valid in GP-SPI master FD/HD-mode. 0: SPI transfer is not ended when SPI RX AFIFO wfull error is valid in GP-SPI master FD/HD-mode.*/ + uint32_t cs_setup_time : 5; /*(cycles+1) of prepare phase by spi clock this bits are combined with spi_cs_setup bit. Can be configured in CONF state.*/ + uint32_t cs_hold_time : 5; /*delay cycles of cs pin by spi clock this bits are combined with spi_cs_hold bit. Can be configured in CONF state.*/ + uint32_t usr_addr_bitlen : 5; /*The length in bits of address phase. The register value shall be (bit_num-1). Can be configured in CONF state.*/ + }; + uint32_t val; + } user1; + union { + struct { + uint32_t usr_command_value : 16; /*The value of command. Can be configured in CONF state.*/ + uint32_t reserved16 : 11; /*reserved*/ + uint32_t mst_rempty_err_end_en : 1; /*1: SPI transfer is ended when SPI TX AFIFO read empty error is valid in GP-SPI master FD/HD-mode. 0: SPI transfer is not ended when SPI TX AFIFO read empty error is valid in GP-SPI master FD/HD-mode.*/ + uint32_t usr_command_bitlen : 4; /*The length in bits of command phase. The register value shall be (bit_num-1). Can be configured in CONF state.*/ + }; + uint32_t val; + } user2; + union { + struct { + uint32_t ms_data_bitlen : 18; /*The value of these bits is the configured SPI transmission data bit length in master mode DMA controlled transfer or CPU controlled transfer. The value is also the configured bit length in slave mode DMA RX controlled transfer. The register value shall be (bit_num-1). Can be configured in CONF state.*/ + uint32_t reserved18 : 14; /*reserved*/ + }; + uint32_t val; + } ms_dlen; + union { + struct { + uint32_t cs0_dis : 1; /*SPI CS$n pin enable, 1: disable CS$n, 0: spi_cs$n signal is from/to CS$n pin. Can be configured in CONF state.*/ + uint32_t cs1_dis : 1; /*SPI CS$n pin enable, 1: disable CS$n, 0: spi_cs$n signal is from/to CS$n pin. Can be configured in CONF state.*/ + uint32_t cs2_dis : 1; /*SPI CS$n pin enable, 1: disable CS$n, 0: spi_cs$n signal is from/to CS$n pin. Can be configured in CONF state.*/ + uint32_t cs3_dis : 1; /*SPI CS$n pin enable, 1: disable CS$n, 0: spi_cs$n signal is from/to CS$n pin. Can be configured in CONF state.*/ + uint32_t cs4_dis : 1; /*SPI CS$n pin enable, 1: disable CS$n, 0: spi_cs$n signal is from/to CS$n pin. Can be configured in CONF state.*/ + uint32_t cs5_dis : 1; /*SPI CS$n pin enable, 1: disable CS$n, 0: spi_cs$n signal is from/to CS$n pin. Can be configured in CONF state.*/ + uint32_t ck_dis : 1; /*1: spi clk out disable, 0: spi clk out enable. Can be configured in CONF state.*/ + uint32_t master_cs_pol : 6; /*In the master mode the bits are the polarity of spi cs line, the value is equivalent to spi_cs ^ spi_master_cs_pol. Can be configured in CONF state.*/ + uint32_t reserved13 : 3; /*reserved*/ + uint32_t clk_data_dtr_en : 1; /*1: SPI master DTR mode is applied to SPI clk, data and spi_dqs. 0: SPI master DTR mode is only applied to spi_dqs. This bit should be used with bit 17/18/19. */ + uint32_t data_dtr_en : 1; /*1: SPI clk and data of SPI_DOUT and SPI_DIN state are in DTR mode, including master 1/2/4/8-bm. 0: SPI clk and data of SPI_DOUT and SPI_DIN state are in STR mode. Can be configured in CONF state.*/ + uint32_t addr_dtr_en : 1; /*1: SPI clk and data of SPI_SEND_ADDR state are in DTR mode, including master 1/2/4/8-bm. 0: SPI clk and data of SPI_SEND_ADDR state are in STR mode. Can be configured in CONF state.*/ + uint32_t cmd_dtr_en : 1; /*1: SPI clk and data of SPI_SEND_CMD state are in DTR mode, including master 1/2/4/8-bm. 0: SPI clk and data of SPI_SEND_CMD state are in STR mode. Can be configured in CONF state.*/ + uint32_t reserved20 : 3; /*reserved*/ + uint32_t slave_cs_pol : 1; /*spi slave input cs polarity select. 1: inv 0: not change. Can be configured in CONF state.*/ + uint32_t dqs_idle_edge : 1; /*The default value of spi_dqs. Can be configured in CONF state.*/ + uint32_t reserved25 : 4; /*reserved*/ + uint32_t ck_idle_edge : 1; /*1: spi clk line is high when idle 0: spi clk line is low when idle. Can be configured in CONF state.*/ + uint32_t cs_keep_active : 1; /*spi cs line keep low when the bit is set. Can be configured in CONF state.*/ + uint32_t quad_din_pin_swap : 1; /*1: SPI quad input swap enable, swap FSPID with FSPIQ, swap FSPIWP with FSPIHD. 0: spi quad input swap disable. Can be configured in CONF state.*/ + }; + uint32_t val; + } misc; + union { + struct { + uint32_t din0_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din1_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din2_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din3_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din4_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din5_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din6_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t din7_mode : 2; /*the input signals are delayed by SPI module clock cycles, 0: input without delayed, 1: input with the posedge of clk_apb,2 input with the negedge of clk_apb, 3: input with the spi_clk. Can be configured in CONF state.*/ + uint32_t timing_hclk_active : 1; /*1:enable hclk in SPI input timing module. 0: disable it. Can be configured in CONF state.*/ + uint32_t reserved17 : 15; /*reserved*/ + }; + uint32_t val; + } din_mode; + union { + struct { + uint32_t din0_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din1_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din2_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din3_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din4_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din5_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din6_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t din7_num : 2; /*the input signals are delayed by SPI module clock cycles, 0: delayed by 1 cycle, 1: delayed by 2 cycles,... Can be configured in CONF state.*/ + uint32_t reserved16 : 16; /*reserved*/ + }; + uint32_t val; + } din_num; + union { + struct { + uint32_t dout0_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout1_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout2_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout3_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout4_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout5_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout6_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t dout7_mode : 1; /*The output signal $n is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t d_dqs_mode : 1; /*The output signal SPI_DQS is delayed by the SPI module clock, 0: output without delayed, 1: output delay for a SPI module clock cycle at its negative edge. Can be configured in CONF state.*/ + uint32_t reserved9 : 23; /*reserved*/ + }; + uint32_t val; + } dout_mode; + union { + struct { + uint32_t outfifo_empty : 1; /*Records the status of DMA TX FIFO. 1: DMA TX FIFO is not ready for sending data. 0: DMA TX FIFO is ready for sending data.*/ + uint32_t infifo_full : 1; /*Records the status of DMA RX FIFO. 1: DMA RX FIFO is not ready for receiving data. 0: DMA RX FIFO is ready for receiving data.*/ + uint32_t reserved2 : 16; /*reserved*/ + uint32_t dma_seg_trans_en : 1; /*Enable dma segment transfer in spi dma half slave mode. 1: enable. 0: disable.*/ + uint32_t rx_seg_trans_clr_en : 1; /*1: spi_dma_infifo_full_vld is cleared by spi slave cmd 5. 0: spi_dma_infifo_full_vld is cleared by spi_trans_done.*/ + uint32_t tx_seg_trans_clr_en : 1; /*1: spi_dma_outfifo_empty_vld is cleared by spi slave cmd 6. 0: spi_dma_outfifo_empty_vld is cleared by spi_trans_done.*/ + uint32_t rx_eof_en : 1; /*1: spi_dma_inlink_eof is set when the number of dma pushed data bytes is equal to the value of spi_slv/mst_dma_rd_bytelen[19:0] in spi dma transition. 0: spi_dma_inlink_eof is set by spi_trans_done in non-seg-trans or spi_dma_seg_trans_done in seg-trans.*/ + uint32_t reserved22 : 5; /*reserved*/ + uint32_t dma_rx_ena : 1; /*Set this bit to enable SPI DMA controlled receive data mode.*/ + uint32_t dma_tx_ena : 1; /*Set this bit to enable SPI DMA controlled send data mode.*/ + uint32_t rx_afifo_rst : 1; /*Set this bit to reset RX AFIFO, which is used to receive data in SPI master and slave mode transfer.*/ + uint32_t buf_afifo_rst : 1; /*Set this bit to reset BUF TX AFIFO, which is used send data out in SPI slave CPU controlled mode transfer and master mode transfer.*/ + uint32_t dma_afifo_rst : 1; /*Set this bit to reset DMA TX AFIFO, which is used to send data out in SPI slave DMA controlled mode transfer.*/ + }; + uint32_t val; + } dma_conf; + union { + struct { + uint32_t infifo_full_err : 1; /*The enable bit for SPI_DMA_INFIFO_FULL_ERR_INT interrupt.*/ + uint32_t outfifo_empty_err : 1; /*The enable bit for SPI_DMA_OUTFIFO_EMPTY_ERR_INT interrupt.*/ + uint32_t ex_qpi : 1; /*The enable bit for SPI slave Ex_QPI interrupt.*/ + uint32_t en_qpi : 1; /*The enable bit for SPI slave En_QPI interrupt.*/ + uint32_t cmd7 : 1; /*The enable bit for SPI slave CMD7 interrupt.*/ + uint32_t cmd8 : 1; /*The enable bit for SPI slave CMD8 interrupt.*/ + uint32_t cmd9 : 1; /*The enable bit for SPI slave CMD9 interrupt.*/ + uint32_t cmda : 1; /*The enable bit for SPI slave CMDA interrupt.*/ + uint32_t rd_dma_done : 1; /*The enable bit for SPI_SLV_RD_DMA_DONE_INT interrupt.*/ + uint32_t wr_dma_done : 1; /*The enable bit for SPI_SLV_WR_DMA_DONE_INT interrupt.*/ + uint32_t rd_buf_done : 1; /*The enable bit for SPI_SLV_RD_BUF_DONE_INT interrupt.*/ + uint32_t wr_buf_done : 1; /*The enable bit for SPI_SLV_WR_BUF_DONE_INT interrupt.*/ + uint32_t trans_done : 1; /*The enable bit for SPI_TRANS_DONE_INT interrupt.*/ + uint32_t dma_seg_trans_done : 1; /*The enable bit for SPI_DMA_SEG_TRANS_DONE_INT interrupt.*/ + uint32_t seg_magic_err : 1; /*The enable bit for SPI_SEG_MAGIC_ERR_INT interrupt.*/ + uint32_t buf_addr_err : 1; /*The enable bit for SPI_SLV_BUF_ADDR_ERR_INT interrupt.*/ + uint32_t cmd_err : 1; /*The enable bit for SPI_SLV_CMD_ERR_INT interrupt.*/ + uint32_t mst_rx_afifo_wfull_err : 1; /*The enable bit for SPI_MST_RX_AFIFO_WFULL_ERR_INT interrupt.*/ + uint32_t mst_tx_afifo_rempty_err : 1; /*The enable bit for SPI_MST_TX_AFIFO_REMPTY_ERR_INT interrupt.*/ + uint32_t app2 : 1; /*The enable bit for SPI_APP2_INT interrupt.*/ + uint32_t app1 : 1; /*The enable bit for SPI_APP1_INT interrupt.*/ + uint32_t reserved21 : 11; /*reserved*/ + }; + uint32_t val; + } dma_int_ena; + union { + struct { + uint32_t infifo_full_err : 1; /*The clear bit for SPI_DMA_INFIFO_FULL_ERR_INT interrupt.*/ + uint32_t outfifo_empty_err : 1; /*The clear bit for SPI_DMA_OUTFIFO_EMPTY_ERR_INT interrupt.*/ + uint32_t ex_qpi : 1; /*The clear bit for SPI slave Ex_QPI interrupt.*/ + uint32_t en_qpi : 1; /*The clear bit for SPI slave En_QPI interrupt.*/ + uint32_t cmd7 : 1; /*The clear bit for SPI slave CMD7 interrupt.*/ + uint32_t cmd8 : 1; /*The clear bit for SPI slave CMD8 interrupt.*/ + uint32_t cmd9 : 1; /*The clear bit for SPI slave CMD9 interrupt.*/ + uint32_t cmda : 1; /*The clear bit for SPI slave CMDA interrupt.*/ + uint32_t rd_dma_done : 1; /*The clear bit for SPI_SLV_RD_DMA_DONE_INT interrupt.*/ + uint32_t wr_dma_done : 1; /*The clear bit for SPI_SLV_WR_DMA_DONE_INT interrupt.*/ + uint32_t rd_buf_done : 1; /*The clear bit for SPI_SLV_RD_BUF_DONE_INT interrupt.*/ + uint32_t wr_buf_done : 1; /*The clear bit for SPI_SLV_WR_BUF_DONE_INT interrupt.*/ + uint32_t trans_done : 1; /*The clear bit for SPI_TRANS_DONE_INT interrupt.*/ + uint32_t dma_seg_trans_done : 1; /*The clear bit for SPI_DMA_SEG_TRANS_DONE_INT interrupt.*/ + uint32_t seg_magic_err : 1; /*The clear bit for SPI_SEG_MAGIC_ERR_INT interrupt.*/ + uint32_t buf_addr_err : 1; /*The clear bit for SPI_SLV_BUF_ADDR_ERR_INT interrupt.*/ + uint32_t cmd_err : 1; /*The clear bit for SPI_SLV_CMD_ERR_INT interrupt.*/ + uint32_t mst_rx_afifo_wfull_err : 1; /*The clear bit for SPI_MST_RX_AFIFO_WFULL_ERR_INT interrupt.*/ + uint32_t mst_tx_afifo_rempty_err : 1; /*The clear bit for SPI_MST_TX_AFIFO_REMPTY_ERR_INT interrupt.*/ + uint32_t app2 : 1; /*The clear bit for SPI_APP2_INT interrupt.*/ + uint32_t app1 : 1; /*The clear bit for SPI_APP1_INT interrupt.*/ + uint32_t reserved21 : 11; /*reserved*/ + }; + uint32_t val; + } dma_int_clr; + union { + struct { + uint32_t infifo_full_err : 1; /*1: The current data rate of DMA Rx is smaller than that of SPI, which will lose the receive data. 0: Others. */ + uint32_t outfifo_empty_err : 1; /*1: The current data rate of DMA TX is smaller than that of SPI. SPI will stop in master mode and send out all 0 in slave mode. 0: Others. */ + uint32_t ex_qpi : 1; /*The raw bit for SPI slave Ex_QPI interrupt. 1: SPI slave mode Ex_QPI transmission is ended. 0: Others.*/ + uint32_t en_qpi : 1; /*The raw bit for SPI slave En_QPI interrupt. 1: SPI slave mode En_QPI transmission is ended. 0: Others.*/ + uint32_t cmd7 : 1; /*The raw bit for SPI slave CMD7 interrupt. 1: SPI slave mode CMD7 transmission is ended. 0: Others.*/ + uint32_t cmd8 : 1; /*The raw bit for SPI slave CMD8 interrupt. 1: SPI slave mode CMD8 transmission is ended. 0: Others.*/ + uint32_t cmd9 : 1; /*The raw bit for SPI slave CMD9 interrupt. 1: SPI slave mode CMD9 transmission is ended. 0: Others.*/ + uint32_t cmda : 1; /*The raw bit for SPI slave CMDA interrupt. 1: SPI slave mode CMDA transmission is ended. 0: Others.*/ + uint32_t rd_dma_done : 1; /*The raw bit for SPI_SLV_RD_DMA_DONE_INT interrupt. 1: SPI slave mode Rd_DMA transmission is ended. 0: Others.*/ + uint32_t wr_dma_done : 1; /*The raw bit for SPI_SLV_WR_DMA_DONE_INT interrupt. 1: SPI slave mode Wr_DMA transmission is ended. 0: Others.*/ + uint32_t rd_buf_done : 1; /*The raw bit for SPI_SLV_RD_BUF_DONE_INT interrupt. 1: SPI slave mode Rd_BUF transmission is ended. 0: Others.*/ + uint32_t wr_buf_done : 1; /*The raw bit for SPI_SLV_WR_BUF_DONE_INT interrupt. 1: SPI slave mode Wr_BUF transmission is ended. 0: Others.*/ + uint32_t trans_done : 1; /*The raw bit for SPI_TRANS_DONE_INT interrupt. 1: SPI master mode transmission is ended. 0: others.*/ + uint32_t dma_seg_trans_done : 1; /*The raw bit for SPI_DMA_SEG_TRANS_DONE_INT interrupt. 1: spi master DMA full-duplex/half-duplex seg-conf-trans ends or slave half-duplex seg-trans ends. And data has been pushed to corresponding memory. 0: seg-conf-trans or seg-trans is not ended or not occurred. */ + uint32_t seg_magic_err : 1; /*The raw bit for SPI_SEG_MAGIC_ERR_INT interrupt. 1: The magic value in CONF buffer is error in the DMA seg-conf-trans. 0: others.*/ + uint32_t buf_addr_err : 1; /*The raw bit for SPI_SLV_BUF_ADDR_ERR_INT interrupt. 1: The accessing data address of the current SPI slave mode CPU controlled FD, Wr_BUF or Rd_BUF transmission is bigger than 63. 0: Others.*/ + uint32_t cmd_err : 1; /*The raw bit for SPI_SLV_CMD_ERR_INT interrupt. 1: The slave command value in the current SPI slave HD mode transmission is not supported. 0: Others.*/ + uint32_t mst_rx_afifo_wfull_err : 1; /*The raw bit for SPI_MST_RX_AFIFO_WFULL_ERR_INT interrupt. 1: There is a RX AFIFO write-full error when SPI inputs data in master mode. 0: Others.*/ + uint32_t mst_tx_afifo_rempty_err : 1; /*The raw bit for SPI_MST_TX_AFIFO_REMPTY_ERR_INT interrupt. 1: There is a TX BUF AFIFO read-empty error when SPI outputs data in master mode. 0: Others.*/ + uint32_t app2 : 1; /*The raw bit for SPI_APP2_INT interrupt. The value is only controlled by software.*/ + uint32_t app1 : 1; /*The raw bit for SPI_APP1_INT interrupt. The value is only controlled by software.*/ + uint32_t reserved21 : 11; /*reserved*/ + }; + uint32_t val; + } dma_int_raw; + union { + struct { + uint32_t infifo_full_err : 1; /*The status bit for SPI_DMA_INFIFO_FULL_ERR_INT interrupt.*/ + uint32_t outfifo_empty_err : 1; /*The status bit for SPI_DMA_OUTFIFO_EMPTY_ERR_INT interrupt.*/ + uint32_t ex_qpi : 1; /*The status bit for SPI slave Ex_QPI interrupt.*/ + uint32_t en_qpi : 1; /*The status bit for SPI slave En_QPI interrupt.*/ + uint32_t cmd7 : 1; /*The status bit for SPI slave CMD7 interrupt.*/ + uint32_t cmd8 : 1; /*The status bit for SPI slave CMD8 interrupt.*/ + uint32_t cmd9 : 1; /*The status bit for SPI slave CMD9 interrupt.*/ + uint32_t cmda : 1; /*The status bit for SPI slave CMDA interrupt.*/ + uint32_t rd_dma_done : 1; /*The status bit for SPI_SLV_RD_DMA_DONE_INT interrupt.*/ + uint32_t wr_dma_done : 1; /*The status bit for SPI_SLV_WR_DMA_DONE_INT interrupt.*/ + uint32_t rd_buf_done : 1; /*The status bit for SPI_SLV_RD_BUF_DONE_INT interrupt.*/ + uint32_t wr_buf_done : 1; /*The status bit for SPI_SLV_WR_BUF_DONE_INT interrupt.*/ + uint32_t trans_done : 1; /*The status bit for SPI_TRANS_DONE_INT interrupt.*/ + uint32_t dma_seg_trans_done : 1; /*The status bit for SPI_DMA_SEG_TRANS_DONE_INT interrupt.*/ + uint32_t seg_magic_err : 1; /*The status bit for SPI_SEG_MAGIC_ERR_INT interrupt.*/ + uint32_t buf_addr_err : 1; /*The status bit for SPI_SLV_BUF_ADDR_ERR_INT interrupt.*/ + uint32_t cmd_err : 1; /*The status bit for SPI_SLV_CMD_ERR_INT interrupt.*/ + uint32_t mst_rx_afifo_wfull_err : 1; /*The status bit for SPI_MST_RX_AFIFO_WFULL_ERR_INT interrupt.*/ + uint32_t mst_tx_afifo_rempty_err : 1; /*The status bit for SPI_MST_TX_AFIFO_REMPTY_ERR_INT interrupt.*/ + uint32_t app2 : 1; /*The status bit for SPI_APP2_INT interrupt.*/ + uint32_t app1 : 1; /*The status bit for SPI_APP1_INT interrupt.*/ + uint32_t reserved21 : 11; /*reserved*/ + }; + uint32_t val; + } dma_int_st; + union { + struct { + uint32_t infifo_full_err_int_set : 1; /*The software set bit for SPI_DMA_INFIFO_FULL_ERR_INT interrupt.*/ + uint32_t outfifo_empty_err_int_set : 1; /*The software set bit for SPI_DMA_OUTFIFO_EMPTY_ERR_INT interrupt.*/ + uint32_t ex_qpi_int_set : 1; /*The software set bit for SPI slave Ex_QPI interrupt.*/ + uint32_t en_qpi_int_set : 1; /*The software set bit for SPI slave En_QPI interrupt.*/ + uint32_t cmd7_int_set : 1; /*The software set bit for SPI slave CMD7 interrupt.*/ + uint32_t cmd8_int_set : 1; /*The software set bit for SPI slave CMD8 interrupt.*/ + uint32_t cmd9_int_set : 1; /*The software set bit for SPI slave CMD9 interrupt.*/ + uint32_t cmda_int_set : 1; /*The software set bit for SPI slave CMDA interrupt.*/ + uint32_t rd_dma_done_int_set : 1; /*The software set bit for SPI_SLV_RD_DMA_DONE_INT interrupt.*/ + uint32_t wr_dma_done_int_set : 1; /*The software set bit for SPI_SLV_WR_DMA_DONE_INT interrupt.*/ + uint32_t rd_buf_done_int_set : 1; /*The software set bit for SPI_SLV_RD_BUF_DONE_INT interrupt.*/ + uint32_t wr_buf_done_int_set : 1; /*The software set bit for SPI_SLV_WR_BUF_DONE_INT interrupt.*/ + uint32_t trans_done_int_set : 1; /*The software set bit for SPI_TRANS_DONE_INT interrupt.*/ + uint32_t dma_seg_trans_done_int_set : 1; /*The software set bit for SPI_DMA_SEG_TRANS_DONE_INT interrupt.*/ + uint32_t seg_magic_err_int_set : 1; /*The software set bit for SPI_SEG_MAGIC_ERR_INT interrupt.*/ + uint32_t buf_addr_err_int_set : 1; /*The software set bit for SPI_SLV_BUF_ADDR_ERR_INT interrupt.*/ + uint32_t cmd_err_int_set : 1; /*The software set bit for SPI_SLV_CMD_ERR_INT interrupt.*/ + uint32_t mst_rx_afifo_wfull_err_int_set: 1; /*The software set bit for SPI_MST_RX_AFIFO_WFULL_ERR_INT interrupt.*/ + uint32_t mst_tx_afifo_rempty_err_int_set: 1; /*The software set bit for SPI_MST_TX_AFIFO_REMPTY_ERR_INT interrupt.*/ + uint32_t app2_int_set : 1; /*The software set bit for SPI_APP2_INT interrupt.*/ + uint32_t app1_int_set : 1; /*The software set bit for SPI_APP1_INT interrupt.*/ + uint32_t reserved21 : 11; /*reserved*/ + }; + uint32_t val; + } dma_int_set; + uint32_t reserved_48; + uint32_t reserved_4c; + uint32_t reserved_50; + uint32_t reserved_54; + uint32_t reserved_58; + uint32_t reserved_5c; + uint32_t reserved_60; + uint32_t reserved_64; + uint32_t reserved_68; + uint32_t reserved_6c; + uint32_t reserved_70; + uint32_t reserved_74; + uint32_t reserved_78; + uint32_t reserved_7c; + uint32_t reserved_80; + uint32_t reserved_84; + uint32_t reserved_88; + uint32_t reserved_8c; + uint32_t reserved_90; + uint32_t reserved_94; + uint32_t data_buf[16]; /*SPI CPU-controlled buffer0*/ + uint32_t reserved_d8; + uint32_t reserved_dc; + union { + struct { + uint32_t clk_mode : 2; /*SPI clock mode bits. 0: SPI clock is off when CS inactive 1: SPI clock is delayed one cycle after CS inactive 2: SPI clock is delayed two cycles after CS inactive 3: SPI clock is alwasy on. Can be configured in CONF state.*/ + uint32_t clk_mode_13 : 1; /*{CPOL, CPHA},1: support spi clk mode 1 and 3, first edge output data B[0]/B[7]. 0: support spi clk mode 0 and 2, first edge output data B[1]/B[6].*/ + uint32_t rsck_data_out : 1; /*It saves half a cycle when tsck is the same as rsck. 1: output data at rsck posedge 0: output data at tsck posedge */ + uint32_t reserved4 : 4; /*reserved*/ + uint32_t rddma_bitlen_en : 1; /*1: SPI_SLV_DATA_BITLEN stores data bit length of master-read-slave data length in DMA controlled mode(Rd_DMA). 0: others*/ + uint32_t wrdma_bitlen_en : 1; /*1: SPI_SLV_DATA_BITLEN stores data bit length of master-write-to-slave data length in DMA controlled mode(Wr_DMA). 0: others*/ + uint32_t rdbuf_bitlen_en : 1; /*1: SPI_SLV_DATA_BITLEN stores data bit length of master-read-slave data length in CPU controlled mode(Rd_BUF). 0: others*/ + uint32_t wrbuf_bitlen_en : 1; /*1: SPI_SLV_DATA_BITLEN stores data bit length of master-write-to-slave data length in CPU controlled mode(Wr_BUF). 0: others*/ + uint32_t reserved12 : 10; /*reserved*/ + uint32_t dma_seg_magic_value : 4; /*The magic value of BM table in master DMA seg-trans.*/ + uint32_t slave_mode : 1; /*Set SPI work mode. 1: slave mode 0: master mode.*/ + uint32_t soft_reset : 1; /*Software reset enable, reset the spi clock line cs line and data lines. Can be configured in CONF state.*/ + uint32_t usr_conf : 1; /*1: Enable the DMA CONF phase of current seg-trans operation, which means seg-trans will start. 0: This is not seg-trans mode.*/ + uint32_t reserved29 : 3; /*reserved*/ + }; + uint32_t val; + } slave; + union { + struct { + uint32_t data_bitlen : 18; /*The transferred data bit length in SPI slave FD and HD mode. */ + uint32_t last_command : 8; /*In the slave mode it is the value of command.*/ + uint32_t last_addr : 6; /*In the slave mode it is the value of address.*/ + }; + uint32_t val; + } slave1; + union { + struct { + uint32_t clk_en : 1; /*Set this bit to enable clk gate*/ + uint32_t mst_clk_active : 1; /*Set this bit to power on the SPI module clock.*/ + uint32_t mst_clk_sel : 1; /*This bit is used to select SPI module clock source in master mode. 1: PLL_CLK_80M. 0: XTAL CLK.*/ + uint32_t reserved3 : 29; /*reserved*/ + }; + uint32_t val; + } clk_gate; + uint32_t reserved_ec; + union { + struct { + uint32_t date : 28; /*SPI register version.*/ + uint32_t reserved28 : 4; /*reserved*/ + }; + uint32_t val; + } date; +} spi_dev_t; +extern spi_dev_t GPSPI2; +extern spi_dev_t GPSPI3; +#ifdef __cplusplus +} +#endif + + + +#endif /*_SOC_SPI_STRUCT_H_ */