pimoroni_pico_explorer/
lib.rs

1#![no_std]
2
3pub extern crate rp2040_hal as hal;
4
5#[cfg(feature = "rt")]
6extern crate cortex_m_rt;
7
8#[cfg(feature = "rt")]
9pub use hal::entry;
10
11/// The linker will place this boot block at the start of our program image. We
12/// need this to help the ROM bootloader get our code up and running.
13///
14/// This currently assumes an rp-pico or pimoroni-pico-lipo is used as the brains.
15/// Currently those are the only boards that have the right pin out to be able to be used
16#[cfg(feature = "boot2")]
17#[link_section = ".boot2"]
18#[no_mangle]
19#[used]
20pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
21
22use display_interface_spi::SPIInterface;
23use embedded_graphics::{
24    draw_target::DrawTarget,
25    pixelcolor::{Rgb565, RgbColor},
26};
27use embedded_hal_0_2::{
28    adc::{Channel, OneShot},
29    blocking::delay::DelayUs,
30    digital::v2::{InputPin, OutputPin},
31    spi::MODE_0,
32};
33use fugit::RateExtU32;
34pub use hal::pac;
35use hal::{
36    adc::Adc,
37    gpio::{
38        bank0::{
39            Gpio0, Gpio1, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio2, Gpio22, Gpio23,
40            Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7,
41        },
42        FunctionNull, FunctionPwm, FunctionSioInput, FunctionSioOutput, Pin, PullNone, PullUp,
43    },
44    pac::{RESETS, SPI0},
45    sio::SioGpioBank0,
46    spi::{Enabled, Spi},
47};
48use st7789::ST7789;
49
50pub mod all_pins {
51    hal::bsp_pins!(
52        Gpio0 { name: gpio0 },
53        Gpio1 { name: gpio1 },
54        Gpio2 { name: gpio2 },
55        Gpio3 { name: gpio3 },
56        Gpio4 { name: gpio4 },
57        Gpio5 { name: gpio5 },
58        Gpio6 { name: gpio6 },
59        Gpio7 { name: gpio7 },
60        Gpio8 {
61            name: motor1_neg,
62            aliases: { FunctionPwm, PullNone: Motor1Neg }
63        },
64        Gpio9 {
65            name: motor1_pos,
66            aliases: { FunctionPwm, PullNone: Motor1Pos }
67        },
68        Gpio10 {
69            name: motor2_neg,
70            aliases: { FunctionPwm, PullNone: Motor2Neg }
71        },
72        Gpio11 {
73            name: motor2_pos,
74            aliases: { FunctionPwm, PullNone: Motor2Pos }
75        },
76        Gpio12 { name: switch_a },
77        Gpio13 { name: switch_b },
78        Gpio14 { name: switch_x },
79        Gpio15 { name: switch_y },
80        Gpio16 {
81            name: spi_miso,
82            aliases: { FunctionSpi, PullNone: Miso }
83        },
84        Gpio17 {
85            name: lcd_cs,
86            aliases: { FunctionSpi, PullNone: LcdCs }
87        },
88        Gpio18 {
89            name: spi_sclk,
90            aliases: { FunctionSpi, PullNone: Sclk }
91        },
92        Gpio19 {
93            name: spi_mosi,
94            aliases: { FunctionSpi, PullNone: Mosi }
95        },
96        Gpio20 {
97            name: i2c_sda,
98            aliases: { FunctionI2C, PullUp: Sda }
99        },
100        Gpio21 {
101            name: i2c_scl,
102            aliases: { FunctionI2C, PullUp: Scl }
103        },
104        Gpio22 { name: i2c_int },
105        Gpio23 { name: b_power_save },
106        Gpio24 { name: vbus_detect },
107        Gpio25 { name: led },
108        Gpio26 { name: adc0 },
109        Gpio27 { name: adc1 },
110        Gpio28 { name: adc2 },
111        Gpio29 {
112            name: voltage_monitor
113        },
114    );
115}
116
117// Can't use `hal::bsp_pins!` here because some pins are not set to their reset state
118pub struct Pins {
119    pub gpio0: Pin<Gpio0, FunctionNull, PullNone>,
120    pub gpio1: Pin<Gpio1, FunctionNull, PullNone>,
121    pub gpio2: Pin<Gpio2, FunctionNull, PullNone>,
122    pub gpio3: Pin<Gpio3, FunctionNull, PullNone>,
123    pub gpio4: Pin<Gpio4, FunctionNull, PullNone>,
124    pub gpio5: Pin<Gpio5, FunctionNull, PullNone>,
125    pub gpio6: Pin<Gpio6, FunctionNull, PullNone>,
126    pub gpio7: Pin<Gpio7, FunctionNull, PullNone>,
127    pub i2c_sda: all_pins::Sda,
128    pub i2c_scl: all_pins::Scl,
129    pub i2c_int: Pin<Gpio22, FunctionSioInput, PullUp>,
130    pub b_power_save: Pin<Gpio23, FunctionNull, PullNone>,
131    pub vbus_detect: Pin<Gpio24, FunctionNull, PullNone>,
132    pub led: Pin<Gpio25, FunctionNull, PullNone>,
133    pub adc0: Pin<Gpio26, FunctionNull, PullNone>,
134    pub adc1: Pin<Gpio27, FunctionNull, PullNone>,
135    pub adc2: Pin<Gpio28, FunctionNull, PullNone>,
136    pub voltage_monitor: Pin<Gpio29, FunctionNull, PullNone>,
137}
138
139pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
140pub enum Button {
141    A,
142    B,
143    X,
144    Y,
145}
146
147pub enum Motor {
148    _1,
149    _2,
150}
151
152pub enum MotorAction {
153    Forward(f32),
154    Reverse(f32),
155    Stop,
156}
157
158pub type Screen = ST7789<
159    SPIInterface<
160        Spi<Enabled, SPI0, (all_pins::Mosi, all_pins::Sclk), 8>,
161        Pin<Gpio16, FunctionSioOutput, PullNone>,
162        Pin<Gpio17, FunctionSioOutput, PullNone>,
163    >,
164    DummyPin,
165>;
166
167pub struct PicoExplorer {
168    pub a: Pin<Gpio12, FunctionSioInput, PullUp>,
169    pub b: Pin<Gpio13, FunctionSioInput, PullUp>,
170    pub x: Pin<Gpio14, FunctionSioInput, PullUp>,
171    pub y: Pin<Gpio15, FunctionSioInput, PullUp>,
172    adc: Adc,
173    pub screen: Screen,
174}
175
176pub struct DummyPin;
177
178impl OutputPin for DummyPin {
179    type Error = ();
180    fn set_high(&mut self) -> Result<(), Self::Error> {
181        Ok(())
182    }
183    fn set_low(&mut self) -> Result<(), Self::Error> {
184        Ok(())
185    }
186}
187
188impl PicoExplorer {
189    pub fn new(
190        io: pac::IO_BANK0,
191        pads: pac::PADS_BANK0,
192        sio: SioGpioBank0,
193        spi0: SPI0,
194        adc: Adc,
195        resets: &mut RESETS,
196        delay: &mut impl DelayUs<u32>,
197    ) -> (Self, Pins) {
198        let internal_pins = all_pins::Pins::new(io, pads, sio, resets);
199
200        let a = internal_pins.switch_a.into_pull_up_input();
201        let b = internal_pins.switch_b.into_pull_up_input();
202        let x = internal_pins.switch_x.into_pull_up_input();
203        let y = internal_pins.switch_y.into_pull_up_input();
204
205        internal_pins.motor1_pos.into_function::<FunctionPwm>();
206        internal_pins.motor1_neg.into_function::<FunctionPwm>();
207        internal_pins.motor2_pos.into_function::<FunctionPwm>();
208        internal_pins.motor2_neg.into_function::<FunctionPwm>();
209
210        let dc = internal_pins.spi_miso.reconfigure();
211        let cs = internal_pins.lcd_cs.reconfigure();
212        let spi_sclk = internal_pins.spi_sclk.reconfigure();
213        let spi_mosi = internal_pins.spi_mosi.reconfigure();
214
215        let spi_screen =
216            Spi::new(spi0, (spi_mosi, spi_sclk)).init(resets, 125u32.MHz(), 16u32.MHz(), MODE_0);
217
218        let spii_screen = SPIInterface::new(spi_screen, dc, cs);
219
220        let mut screen = ST7789::new(spii_screen, DummyPin, 240, 240);
221
222        screen.init(delay).unwrap();
223        screen
224            .set_orientation(st7789::Orientation::Portrait)
225            .unwrap();
226        screen.clear(Rgb565::BLACK).unwrap();
227
228        (
229            PicoExplorer {
230                a,
231                b,
232                x,
233                y,
234                adc,
235                screen,
236            },
237            Pins {
238                gpio0: internal_pins.gpio0.reconfigure(),
239                gpio1: internal_pins.gpio1.reconfigure(),
240                gpio2: internal_pins.gpio2.reconfigure(),
241                gpio3: internal_pins.gpio3.reconfigure(),
242                gpio4: internal_pins.gpio4.reconfigure(),
243                gpio5: internal_pins.gpio5.reconfigure(),
244                gpio6: internal_pins.gpio6.reconfigure(),
245                gpio7: internal_pins.gpio7.reconfigure(),
246                i2c_sda: internal_pins.i2c_sda.reconfigure(),
247                i2c_scl: internal_pins.i2c_scl.reconfigure(),
248                i2c_int: internal_pins.i2c_int.reconfigure(),
249                b_power_save: internal_pins.b_power_save.reconfigure(),
250                vbus_detect: internal_pins.vbus_detect.reconfigure(),
251                led: internal_pins.led.reconfigure(),
252                adc0: internal_pins.adc0.reconfigure(),
253                adc1: internal_pins.adc1.reconfigure(),
254                adc2: internal_pins.adc2.reconfigure(),
255                voltage_monitor: internal_pins.voltage_monitor.reconfigure(),
256            },
257        )
258    }
259
260    pub fn is_pressed(&self, button: Button) -> bool {
261        use Button::*;
262        match button {
263            A => self.a.is_low().unwrap(),
264            B => self.b.is_low().unwrap(),
265            X => self.x.is_low().unwrap(),
266            Y => self.y.is_low().unwrap(),
267        }
268    }
269
270    pub fn get_adc<Pin: Channel<Adc, ID = u8>>(&mut self, channel: &mut Pin) -> f32 {
271        // scale raw 12-bit adc value to 0 .. 1 float
272        let adc_value: u16 = self.adc.read(channel).unwrap();
273        let result: f32 = f32::from(adc_value) / f32::from(1u16 << 12);
274        result.clamp(0.0, 1.0)
275    }
276}