mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-23 20:51:31 +11:00
Pico explorer board support
This commit is contained in:
parent
bcfbd72ed1
commit
ae8fe512bd
|
@ -11,9 +11,21 @@ license = "MIT OR Apache-2.0"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
rp2040-hal = { path = "../../rp2040-hal", version = "0.1.0" }
|
||||
cortex-m-rt = { version = "0.6.14", optional = true }
|
||||
rp2040-hal = { path="../../rp2040-hal", version="0.1.0" }
|
||||
cortex-m-rt = { version="0.6.14", optional=true }
|
||||
embedded-hal = { version="0.2.4", features=["unproven"] }
|
||||
st7789 = "0.6.1"
|
||||
display-interface-spi = "0.4.1"
|
||||
embedded-time = "0.12.0"
|
||||
embedded-graphics = "0.7.1"
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["cortex-m-rt","rp2040-hal/rt"]
|
||||
rt = ["cortex-m-rt","rp2040-hal/rt"]
|
||||
|
||||
[dev-dependencies]
|
||||
display-interface = "0.4.1"
|
||||
panic-halt = "0.2.0"
|
||||
arrayvec = { version="0.7.1", default-features=false }
|
||||
rp2040-boot2 = { git="https://github.com/rp-rs/rp2040-boot2-rs", branch="main" }
|
||||
nb = "1.0.0"
|
||||
|
|
111
boards/pico_explorer/examples/pico_explorer_showcase.rs
Normal file
111
boards/pico_explorer/examples/pico_explorer_showcase.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use arrayvec::ArrayString;
|
||||
use core::fmt::Write;
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_graphics::{
|
||||
mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
|
||||
pixelcolor::Rgb565,
|
||||
prelude::*,
|
||||
text::{Alignment, Text},
|
||||
};
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_time::rate::*;
|
||||
use hal::{adc::Adc, clocks::*, sio::Sio, watchdog::Watchdog};
|
||||
use panic_halt as _;
|
||||
use pico_explorer::{hal, pac, Button, PicoExplorer, XOSC_CRYSTAL_FREQ};
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
|
||||
|
||||
// See 4.9.5 from RP2040 datasheet
|
||||
fn calc_temp(adc_value: f32, refv: f64) -> f64 {
|
||||
let vbe: f64 = f64::from(adc_value) * refv;
|
||||
27f64 - (vbe - 0.706) / 0.001721
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut p = pac::Peripherals::take().unwrap();
|
||||
let cp = pac::CorePeripherals::take().unwrap();
|
||||
|
||||
// Enable watchdog and clocks
|
||||
let mut watchdog = Watchdog::new(p.WATCHDOG);
|
||||
let clocks = init_clocks_and_plls(
|
||||
XOSC_CRYSTAL_FREQ,
|
||||
p.XOSC,
|
||||
p.CLOCKS,
|
||||
p.PLL_SYS,
|
||||
p.PLL_USB,
|
||||
&mut p.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let mut delay = cortex_m::delay::Delay::new(cp.SYST, clocks.system_clock.get_freq().integer());
|
||||
|
||||
// Enable adc
|
||||
let mut adc = Adc::new(p.ADC, &mut p.RESETS);
|
||||
let mut temp_sense = adc.enable_temp_sensor();
|
||||
|
||||
let sio = Sio::new(p.SIO);
|
||||
|
||||
let (mut explorer, pins) = PicoExplorer::new(
|
||||
p.IO_BANK0,
|
||||
p.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
p.SPI0,
|
||||
adc,
|
||||
&mut p.RESETS,
|
||||
&mut delay,
|
||||
);
|
||||
|
||||
let mut led = pins.led.into_push_pull_output();
|
||||
|
||||
let mut even = true;
|
||||
loop {
|
||||
delay.delay_ms(500);
|
||||
|
||||
// Set GPIO25 to be low
|
||||
led.set_low().unwrap();
|
||||
|
||||
delay.delay_ms(500);
|
||||
|
||||
// Set GPIO25 to be high
|
||||
led.set_high().unwrap();
|
||||
|
||||
let adc_value = explorer.get_adc(&mut temp_sense);
|
||||
let temp: f64 = calc_temp(adc_value, 3.3);
|
||||
|
||||
// Create a fixed buffer to store screen contents
|
||||
let mut buf = ArrayString::<100>::new();
|
||||
|
||||
// Write to buffer
|
||||
writeln!(&mut buf, "Hello World {}", if even { '|' } else { '-' }).unwrap();
|
||||
writeln!(&mut buf, "Temp: {:.1}", temp).unwrap();
|
||||
writeln!(
|
||||
&mut buf,
|
||||
"A:{:.1} B:{:.1}\nX:{:.1} Y:{:.1}",
|
||||
explorer.is_pressed(Button::A),
|
||||
explorer.is_pressed(Button::B),
|
||||
explorer.is_pressed(Button::X),
|
||||
explorer.is_pressed(Button::Y)
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Draw buffer on screen
|
||||
let style = MonoTextStyleBuilder::new()
|
||||
.font(&FONT_10X20)
|
||||
.text_color(Rgb565::GREEN)
|
||||
.background_color(Rgb565::BLACK)
|
||||
.build();
|
||||
Text::with_alignment(&buf, Point::new(20, 30), style, Alignment::Left)
|
||||
.draw(&mut explorer.screen)
|
||||
.unwrap();
|
||||
|
||||
even = !even;
|
||||
}
|
||||
}
|
|
@ -1,96 +1,276 @@
|
|||
#![no_std]
|
||||
|
||||
extern crate rp2040_hal as hal;
|
||||
pub extern crate rp2040_hal as hal;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
extern crate cortex_m_rt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
pub use cortex_m_rt::entry;
|
||||
|
||||
use display_interface_spi::SPIInterface;
|
||||
use embedded_graphics::{
|
||||
draw_target::DrawTarget,
|
||||
pixelcolor::{Rgb565, RgbColor},
|
||||
};
|
||||
use embedded_hal::{
|
||||
adc::{Channel, OneShot},
|
||||
blocking::delay::DelayUs,
|
||||
digital::v2::{InputPin, OutputPin},
|
||||
spi::MODE_0,
|
||||
};
|
||||
use embedded_time::rate::*;
|
||||
pub use hal::pac;
|
||||
use hal::{
|
||||
adc::Adc,
|
||||
gpio::{
|
||||
bank0::{
|
||||
Gpio0, Gpio1, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, Gpio19, Gpio2,
|
||||
Gpio20, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3,
|
||||
Gpio4, Gpio5, Gpio6, Gpio7,
|
||||
},
|
||||
FunctionI2C, FunctionPwm, FunctionSpi, Pin, PinId, PullUpInput, PushPullOutput,
|
||||
},
|
||||
pac::{RESETS, SPI0},
|
||||
sio::SioGpioBank0,
|
||||
spi::{Enabled, Spi},
|
||||
};
|
||||
use st7789::ST7789;
|
||||
|
||||
hal::bsp_pins!(
|
||||
Gpio0 {
|
||||
name: gpio0,
|
||||
aliases: { FunctionPwm: Audio0 }
|
||||
},
|
||||
Gpio1 {
|
||||
name: gpio1,
|
||||
aliases: { FunctionPwm: Audio1 }
|
||||
},
|
||||
Gpio2 {
|
||||
name: gpio2,
|
||||
aliases: { FunctionPwm: Audio2 }
|
||||
},
|
||||
Gpio3 {
|
||||
name: gpio3,
|
||||
aliases: { FunctionPwm: Audio3 }
|
||||
},
|
||||
Gpio4 {
|
||||
name: gpio4,
|
||||
aliases: { FunctionPwm: Audio4 }
|
||||
},
|
||||
Gpio5 {
|
||||
name: gpio5,
|
||||
aliases: { FunctionPwm: Audio5 }
|
||||
},
|
||||
Gpio6 {
|
||||
name: gpio6,
|
||||
aliases: { FunctionPwm: Audio6 }
|
||||
},
|
||||
Gpio7 {
|
||||
name: gpio7,
|
||||
aliases: { FunctionPwm: Audio7 }
|
||||
},
|
||||
Gpio8 {
|
||||
name: motor1_min,
|
||||
aliases: { FunctionPwm: Motor1Min }
|
||||
},
|
||||
Gpio9 {
|
||||
name: motor1_plus,
|
||||
aliases: { FunctionPwm: Motor1Plus }
|
||||
},
|
||||
Gpio10 {
|
||||
name: motor2_min,
|
||||
aliases: { FunctionPwm: Motor2Min }
|
||||
},
|
||||
Gpio11 {
|
||||
name: motor2_plus,
|
||||
aliases: { FunctionPwm: Motor2Plus }
|
||||
},
|
||||
Gpio12 { name: switch_a },
|
||||
Gpio13 { name: switch_b },
|
||||
Gpio14 { name: switch_x },
|
||||
Gpio15 { name: switch_y },
|
||||
Gpio16 {
|
||||
name: spi_miso,
|
||||
aliases: { FunctionSpi: Miso }
|
||||
},
|
||||
Gpio17 {
|
||||
name: lcd_cs,
|
||||
aliases: { FunctionSpi: LcdCs }
|
||||
},
|
||||
Gpio18 {
|
||||
name: spi_sclk,
|
||||
aliases: { FunctionSpi: Sclk }
|
||||
},
|
||||
Gpio19 {
|
||||
name: spi_mosi,
|
||||
aliases: { FunctionSpi: Mosi }
|
||||
},
|
||||
Gpio20 {
|
||||
name: i2c_sda,
|
||||
aliases: { FunctionI2C: Sda }
|
||||
},
|
||||
Gpio21 {
|
||||
name: i2c_scl,
|
||||
aliases: { FunctionI2C: Scl }
|
||||
},
|
||||
Gpio22 { name: i2c_int },
|
||||
Gpio25 { name: led },
|
||||
Gpio26 { name: adc0 },
|
||||
Gpio27 { name: adc1 },
|
||||
Gpio28 { name: adc2 },
|
||||
);
|
||||
mod internal_pins {
|
||||
hal::bsp_pins!(
|
||||
Gpio0 { name: gpio0 },
|
||||
Gpio1 { name: gpio1 },
|
||||
Gpio2 { name: gpio2 },
|
||||
Gpio3 { name: gpio3 },
|
||||
Gpio4 { name: gpio4 },
|
||||
Gpio5 { name: gpio5 },
|
||||
Gpio6 { name: gpio6 },
|
||||
Gpio7 { name: gpio7 },
|
||||
Gpio8 {
|
||||
name: motor1_neg,
|
||||
aliases: { FunctionPwm: Motor1Neg }
|
||||
},
|
||||
Gpio9 {
|
||||
name: motor1_pos,
|
||||
aliases: { FunctionPwm: Motor1Pos }
|
||||
},
|
||||
Gpio10 {
|
||||
name: motor2_neg,
|
||||
aliases: { FunctionPwm: Motor2Neg }
|
||||
},
|
||||
Gpio11 {
|
||||
name: motor2_pos,
|
||||
aliases: { FunctionPwm: Motor2Pos }
|
||||
},
|
||||
Gpio12 { name: switch_a },
|
||||
Gpio13 { name: switch_b },
|
||||
Gpio14 { name: switch_x },
|
||||
Gpio15 { name: switch_y },
|
||||
Gpio16 {
|
||||
name: spi_miso,
|
||||
aliases: { FunctionSpi: Miso }
|
||||
},
|
||||
Gpio17 {
|
||||
name: lcd_cs,
|
||||
aliases: { FunctionSpi: LcdCs }
|
||||
},
|
||||
Gpio18 {
|
||||
name: spi_sclk,
|
||||
aliases: { FunctionSpi: Sclk }
|
||||
},
|
||||
Gpio19 {
|
||||
name: spi_mosi,
|
||||
aliases: { FunctionSpi: Mosi }
|
||||
},
|
||||
Gpio20 {
|
||||
name: i2c_sda,
|
||||
aliases: { FunctionI2C: Sda }
|
||||
},
|
||||
Gpio21 {
|
||||
name: i2c_scl,
|
||||
aliases: { FunctionI2C: Scl }
|
||||
},
|
||||
Gpio22 { name: i2c_int },
|
||||
Gpio23 { name: bootsel },
|
||||
Gpio24 { name: vbus_detect },
|
||||
Gpio25 { name: led },
|
||||
Gpio26 { name: adc0 },
|
||||
Gpio27 { name: adc1 },
|
||||
Gpio28 { name: adc2 },
|
||||
Gpio29 {
|
||||
name: voltage_monitor
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub struct Pins {
|
||||
pub gpio0: Pin<Gpio0, <Gpio0 as PinId>::Reset>,
|
||||
pub gpio1: Pin<Gpio1, <Gpio1 as PinId>::Reset>,
|
||||
pub gpio2: Pin<Gpio2, <Gpio2 as PinId>::Reset>,
|
||||
pub gpio3: Pin<Gpio3, <Gpio3 as PinId>::Reset>,
|
||||
pub gpio4: Pin<Gpio4, <Gpio4 as PinId>::Reset>,
|
||||
pub gpio5: Pin<Gpio5, <Gpio5 as PinId>::Reset>,
|
||||
pub gpio6: Pin<Gpio6, <Gpio6 as PinId>::Reset>,
|
||||
pub gpio7: Pin<Gpio7, <Gpio7 as PinId>::Reset>,
|
||||
pub spi_sclk: Pin<Gpio18, FunctionSpi>,
|
||||
pub spi_mosi: Pin<Gpio19, FunctionSpi>,
|
||||
pub i2c_sda: Pin<Gpio20, FunctionI2C>,
|
||||
pub i2c_scl: Pin<Gpio21, FunctionI2C>,
|
||||
pub i2c_int: Pin<Gpio22, FunctionI2C>,
|
||||
pub bootsel: Pin<Gpio23, <Gpio23 as PinId>::Reset>,
|
||||
pub vbus_detect: Pin<Gpio24, <Gpio24 as PinId>::Reset>,
|
||||
pub led: Pin<Gpio25, <Gpio25 as PinId>::Reset>,
|
||||
pub adc0: Pin<Gpio26, <Gpio26 as PinId>::Reset>,
|
||||
pub adc1: Pin<Gpio27, <Gpio27 as PinId>::Reset>,
|
||||
pub adc2: Pin<Gpio28, <Gpio28 as PinId>::Reset>,
|
||||
pub voltage_monitor: Pin<Gpio29, <Gpio29 as PinId>::Reset>,
|
||||
}
|
||||
|
||||
pub const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
|
||||
pub enum Button {
|
||||
A,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
pub enum Motor {
|
||||
_1,
|
||||
_2,
|
||||
}
|
||||
|
||||
pub enum MotorAction {
|
||||
Forward(f32),
|
||||
Reverse(f32),
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub type Screen = ST7789<
|
||||
SPIInterface<Spi<Enabled, SPI0, 8>, Pin<Gpio16, PushPullOutput>, Pin<Gpio17, PushPullOutput>>,
|
||||
DummyPin,
|
||||
>;
|
||||
|
||||
pub struct PicoExplorer {
|
||||
a: Pin<Gpio12, PullUpInput>,
|
||||
b: Pin<Gpio13, PullUpInput>,
|
||||
x: Pin<Gpio14, PullUpInput>,
|
||||
y: Pin<Gpio15, PullUpInput>,
|
||||
adc: Adc,
|
||||
pub screen: Screen,
|
||||
}
|
||||
|
||||
pub struct DummyPin;
|
||||
|
||||
impl OutputPin for DummyPin {
|
||||
type Error = ();
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PicoExplorer {
|
||||
pub fn new(
|
||||
io: pac::IO_BANK0,
|
||||
pads: pac::PADS_BANK0,
|
||||
sio: SioGpioBank0,
|
||||
spi0: SPI0,
|
||||
adc: Adc,
|
||||
resets: &mut RESETS,
|
||||
delay: &mut impl DelayUs<u32>,
|
||||
) -> (Self, Pins) {
|
||||
let internal_pins = internal_pins::Pins::new(io, pads, sio, resets);
|
||||
|
||||
let a = internal_pins.switch_a.into_pull_up_input();
|
||||
let b = internal_pins.switch_b.into_pull_up_input();
|
||||
let x = internal_pins.switch_x.into_pull_up_input();
|
||||
let y = internal_pins.switch_y.into_pull_up_input();
|
||||
|
||||
internal_pins.motor1_pos.into_mode::<FunctionPwm>();
|
||||
internal_pins.motor1_neg.into_mode::<FunctionPwm>();
|
||||
internal_pins.motor2_pos.into_mode::<FunctionPwm>();
|
||||
internal_pins.motor2_neg.into_mode::<FunctionPwm>();
|
||||
|
||||
let dc = internal_pins.spi_miso.into_push_pull_output();
|
||||
let cs = internal_pins.lcd_cs.into_push_pull_output();
|
||||
let spi_sclk = internal_pins.spi_sclk.into_mode::<FunctionSpi>();
|
||||
let spi_mosi = internal_pins.spi_mosi.into_mode::<FunctionSpi>();
|
||||
|
||||
let spi_screen = Spi::<_, _, 8>::new(spi0).init(
|
||||
resets,
|
||||
125_000_000u32.Hz(),
|
||||
16_000_000u32.Hz(),
|
||||
&MODE_0,
|
||||
);
|
||||
|
||||
let spii_screen = SPIInterface::new(spi_screen, dc, cs);
|
||||
|
||||
let mut screen = ST7789::new(spii_screen, DummyPin, 240, 240);
|
||||
|
||||
screen.init(delay).unwrap();
|
||||
screen
|
||||
.set_orientation(st7789::Orientation::Portrait)
|
||||
.unwrap();
|
||||
screen.clear(Rgb565::BLACK).unwrap();
|
||||
|
||||
(
|
||||
PicoExplorer {
|
||||
a,
|
||||
b,
|
||||
x,
|
||||
y,
|
||||
adc,
|
||||
screen,
|
||||
},
|
||||
Pins {
|
||||
gpio0: internal_pins.gpio0,
|
||||
gpio1: internal_pins.gpio1,
|
||||
gpio2: internal_pins.gpio2,
|
||||
gpio3: internal_pins.gpio3,
|
||||
gpio4: internal_pins.gpio4,
|
||||
gpio5: internal_pins.gpio5,
|
||||
gpio6: internal_pins.gpio6,
|
||||
gpio7: internal_pins.gpio7,
|
||||
spi_sclk,
|
||||
spi_mosi,
|
||||
i2c_sda: internal_pins.i2c_sda.into_mode(),
|
||||
i2c_scl: internal_pins.i2c_scl.into_mode(),
|
||||
i2c_int: internal_pins.i2c_int.into_mode(),
|
||||
bootsel: internal_pins.bootsel,
|
||||
vbus_detect: internal_pins.vbus_detect,
|
||||
led: internal_pins.led,
|
||||
adc0: internal_pins.adc0,
|
||||
adc1: internal_pins.adc1,
|
||||
adc2: internal_pins.adc2,
|
||||
voltage_monitor: internal_pins.voltage_monitor,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_pressed(&self, button: Button) -> bool {
|
||||
use Button::*;
|
||||
match button {
|
||||
A => self.a.is_low().unwrap(),
|
||||
B => self.b.is_low().unwrap(),
|
||||
X => self.x.is_low().unwrap(),
|
||||
Y => self.y.is_low().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_adc<Pin: Channel<Adc, ID = u8>>(&mut self, channel: &mut Pin) -> f32 {
|
||||
// scale raw 12-bit adc value to 0 .. 1 float
|
||||
let adc_value: u16 = self.adc.read(channel).unwrap();
|
||||
let mut result: f32 = f32::from(adc_value) / f32::from(1u16 << 12);
|
||||
// clamp result to 0 .. 1
|
||||
if result > 1.0 {
|
||||
result = 1.0
|
||||
}
|
||||
|
||||
if result < 0.0 {
|
||||
result = 0.0
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue