Skip to main content

m5stack_core/board/
fire27.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! M5Stack Fire v2.7 (ESP32) board-level pin wiring.
3
4use esp_hal::{
5    Blocking,
6    dma::AnySpiDmaChannel,
7    gpio::AnyPin,
8    i2c::master::{BusTimeout, Config as I2cConfig, I2c},
9    interrupt::software::SoftwareInterruptControl,
10    peripherals::{BT, PCNT, Peripherals, RMT, UART0, WIFI},
11    spi::master::AnySpi,
12    time::Rate,
13    timer::{AnyTimer, timg::TimerGroup},
14};
15
16use crate::board::SystemResources;
17use crate::board::spi2::Spi2Resources;
18use crate::io::buttons::ButtonResources;
19
20/// External M5-Bus pins the BSP does not claim for on-board functions. What
21/// hangs off them (1-Wire sensors, RPM pickups, ...) is application wiring.
22/// Not exhaustive — extend as needed.
23pub struct M5Bus<'a> {
24    pub gpio5: AnyPin<'a>,
25    pub gpio26: AnyPin<'a>,
26}
27
28/// The Fire27's peripherals, split into board-fact groups. See
29/// [`Board::split`].
30pub struct Board {
31    /// Display + SD-card shared SPI2 bus (see [`super::spi2`]).
32    pub spi2: Spi2Resources<'static>,
33    /// Internal I2C bus (SDA=GPIO21, SCL=GPIO22) — IP5306 fuel gauge (0x75,
34    /// M5GO bottom) and any Grove/external I2C devices.
35    ///
36    /// Returned BLOCKING: defer `into_async()` to the core that runs the I2C
37    /// tasks — `into_async()` binds the I2C peripheral IRQ to the calling
38    /// core, and on a BLE app it must stay off the core running RWBLE's
39    /// level-1 IRQ (cumulative IRQ blocking otherwise desyncs the BLE
40    /// controller blob and wedges that core's executor).
41    pub i2c0: I2c<'static, Blocking>,
42    /// Front-panel buttons A/B/C (GPIO39/38/37, active-low). Use
43    /// [`ButtonResources::into_buttons`] (feature `buttons`).
44    pub buttons: ButtonResources<'static>,
45    pub bt: BT<'static>,
46    pub wifi: WIFI<'static>,
47    /// UART0 — console + serial-cmd
48    /// ([`crate::io::serial_cmd::SerialCmdResources`] +
49    /// [`crate::io::console`]).
50    pub uart0: UART0<'static>,
51    /// UART0 RX pin (GPIO3).
52    pub uart0_rx: AnyPin<'static>,
53    /// UART0 TX pin (GPIO1).
54    pub uart0_tx: AnyPin<'static>,
55    /// SK6812 LED bars data pin (M-Bus pin 23 → GPIO15), driven over RMT
56    /// ([`crate::driver::sk6812::Sk6812Driver`]).
57    pub sk6812: AnyPin<'static>,
58    /// RMT — SK6812 LED bars or the 1-Wire driver (one owner;
59    /// [`crate::io::ow_temp::OnewireResources`]).
60    pub rmt: RMT<'static>,
61    /// PCNT — e.g. pulse counting ([`crate::io::rpm::RpmResources`]).
62    pub pcnt: PCNT<'static>,
63    /// Spare SPI3 unit — the BSP does not drive it, listed for the app to
64    /// claim (same idea as [`Self::rmt`]/[`Self::pcnt`]). Wiring (which
65    /// [`Self::m5bus`] pin is SCK/MOSI/MISO/CS) and the interrupt priority /
66    /// core allocation are application knowledge. Attach the CS pin as the
67    /// peripheral's **hardware** chip-select (`with_cs`), not a
68    /// software-toggled [`esp_hal::gpio::Output`] — some designs depend on a
69    /// sub-microsecond CS gap that software toggling cannot hold. See #27.
70    pub spi3: AnySpi<'static>,
71    /// PDMA channel for [`Self::spi3`] — SPI3's own dedicated PDMA unit, not
72    /// shared with `spi2` (`DMA_SPI2`).
73    pub spi3_dma: AnySpiDmaChannel<'static>,
74    /// External SPI PSRAM (~4 MB) — feed to `mem::psram_map` / `mem::psram_split`
75    /// (the ESP32 cannot DMA from PSRAM). [feature `psram`]
76    pub psram: esp_hal::peripherals::PSRAM<'static>,
77    pub m5bus: M5Bus<'static>,
78    pub system: SystemResources<'static>,
79}
80
81impl Board {
82    /// Wire the Fire27 pin map. Call once with the output of
83    /// [`crate::board::init`] (or `esp_hal::init`); heap setup stays with the
84    /// app (sizing is application policy).
85    pub fn split(peripherals: Peripherals) -> Self {
86        let spi2 = Spi2Resources {
87            spi2: AnySpi::from(peripherals.SPI2),
88            spi2_dma: AnySpiDmaChannel::from(peripherals.DMA_SPI2),
89            sck: AnyPin::from(peripherals.GPIO18),
90            mosi: AnyPin::from(peripherals.GPIO23),
91            miso: AnyPin::from(peripherals.GPIO19),
92            display_cs: AnyPin::from(peripherals.GPIO14),
93            display_dc: AnyPin::from(peripherals.GPIO27),
94            display_rst: AnyPin::from(peripherals.GPIO33),
95            display_bl: AnyPin::from(peripherals.GPIO32),
96            card_cs: AnyPin::from(peripherals.GPIO4),
97        };
98
99        let i2c0 = I2c::new(
100            peripherals.I2C0,
101            I2cConfig::default()
102                .with_frequency(Rate::from_khz(400))
103                .with_timeout(BusTimeout::BusCycles(20)),
104        )
105        .expect("I2C0 init failed")
106        .with_sda(AnyPin::from(peripherals.GPIO21))
107        .with_scl(AnyPin::from(peripherals.GPIO22));
108
109        let buttons = ButtonResources {
110            left: AnyPin::from(peripherals.GPIO39),
111            center: AnyPin::from(peripherals.GPIO38),
112            right: AnyPin::from(peripherals.GPIO37),
113        };
114
115        let tg0 = TimerGroup::new(peripherals.TIMG0);
116        let tg1 = TimerGroup::new(peripherals.TIMG1);
117        let system = SystemResources {
118            sw_int: SoftwareInterruptControl::new(peripherals.SW_INTERRUPT),
119            timer0_0: AnyTimer::from(tg0.timer0),
120            timer0_1: AnyTimer::from(tg0.timer1),
121            timer1_0: AnyTimer::from(tg1.timer0),
122            timer1_1: AnyTimer::from(tg1.timer1),
123            cpu_ctrl: peripherals.CPU_CTRL,
124            lpwr: peripherals.LPWR,
125        };
126
127        Board {
128            spi2,
129            i2c0,
130            buttons,
131            bt: peripherals.BT,
132            wifi: peripherals.WIFI,
133            uart0: peripherals.UART0,
134            uart0_rx: AnyPin::from(peripherals.GPIO3),
135            uart0_tx: AnyPin::from(peripherals.GPIO1),
136            sk6812: AnyPin::from(peripherals.GPIO15),
137            rmt: peripherals.RMT,
138            pcnt: peripherals.PCNT,
139            spi3: AnySpi::from(peripherals.SPI3),
140            spi3_dma: AnySpiDmaChannel::from(peripherals.DMA_SPI3),
141            psram: peripherals.PSRAM,
142            m5bus: M5Bus {
143                gpio5: AnyPin::from(peripherals.GPIO5),
144                gpio26: AnyPin::from(peripherals.GPIO26),
145            },
146            system,
147        }
148    }
149}