mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-23 12:41:31 +11:00
Add input support, examples, SIO/PADS ownership
Sorry this is a large commit :( This adds support for input pins, including pulling them high or low. It also adds two examples: the start of a classic blinky LED example, and an example for reading input.
This commit is contained in:
parent
45580ec4c8
commit
2c3a0956fa
|
@ -1,3 +1,11 @@
|
|||
[build]
|
||||
# Instruction set of Cortex-M0+
|
||||
target = "thumbv6m-none-eabi"
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
rustflags = [
|
||||
"-C", "link-arg=--nmagic",
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
"-C", "inline-threshold=5",
|
||||
"-C", "no-vectorize-loops",
|
||||
]
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
.idea/
|
||||
target
|
||||
Cargo.lock
|
||||
|
|
117
Cargo.lock
generated
117
Cargo.lock
generated
|
@ -1,117 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643a210c1bdc23d0db511e2a576082f4ff4dcae9d0c37f50b431b8f8439d6d6b"
|
||||
dependencies = [
|
||||
"bare-metal",
|
||||
"bitfield",
|
||||
"embedded-hal",
|
||||
"volatile-register",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb"
|
||||
dependencies = [
|
||||
"nb 0.1.3",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||
dependencies = [
|
||||
"nb 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
||||
|
||||
[[package]]
|
||||
name = "rp2040-hal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"embedded-hal",
|
||||
"nb 1.0.0",
|
||||
"rp2040-pac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rp2040-pac"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/rp-rs/rp2040-pac?branch=main#f4339cb856f082970767099a0079259f333cdc7e"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "volatile-register"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||
dependencies = [
|
||||
"vcell",
|
||||
]
|
13
memory.x
Normal file
13
memory.x
Normal file
|
@ -0,0 +1,13 @@
|
|||
MEMORY {
|
||||
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
/* ### Boot loader */
|
||||
.boot2 ORIGIN(BOOT2) :
|
||||
{
|
||||
KEEP(*(.boot2));
|
||||
} > BOOT2
|
||||
} INSERT BEFORE .text;
|
|
@ -11,6 +11,11 @@ license = "MIT OR Apache-2.0"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.1"
|
||||
embedded-hal = "0.2.4"
|
||||
embedded-hal = { version = "0.2.4", features = ["unproven"] }
|
||||
nb = "1.0.0"
|
||||
rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" }
|
||||
|
||||
[dev-dependencies]
|
||||
cortex-m-rt = "0.6.13"
|
||||
panic-halt = "0.2.0"
|
||||
rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" }
|
||||
|
|
31
rp2040-hal/examples/blinky.rs
Normal file
31
rp2040-hal/examples/blinky.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
//! Blinks the LED on a Pico board
|
||||
//!
|
||||
//! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use panic_halt as _;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
|
||||
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use rp2040_hal::prelude::*;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
|
||||
let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS);
|
||||
let mut led_pin = pins.gpio25.into_output();
|
||||
|
||||
loop {
|
||||
led_pin.set_low().unwrap();
|
||||
// TODO: I dare not use delays until we've got clocks running
|
||||
led_pin.set_high().unwrap();
|
||||
// TODO: Other delay
|
||||
}
|
||||
}
|
36
rp2040-hal/examples/gpio_in_out.rs
Normal file
36
rp2040-hal/examples/gpio_in_out.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
//! Toggle LED based on GPIO input
|
||||
//!
|
||||
//! This will control an LED on GP25 based on a button hooked up to GP15. The button should be tied
|
||||
//! to ground, as the input pin is pulled high internally by this example. When the button is
|
||||
//! pressed, the LED will turn off.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use panic_halt as _;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
#[used]
|
||||
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
|
||||
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||
use rp2040_hal::prelude::*;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
|
||||
let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS);
|
||||
let mut led_pin = pins.gpio25.into_output();
|
||||
let mut button_pin = pins.gpio15.into_input();
|
||||
button_pin.pull_high();
|
||||
|
||||
loop {
|
||||
if button_pin.is_high().unwrap() {
|
||||
led_pin.set_high().unwrap();
|
||||
} else {
|
||||
led_pin.set_low().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,97 +7,188 @@
|
|||
//! ```rust
|
||||
//! let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||
//!
|
||||
//! let pins = pac.IO_BANK0.split(&mut pac.RESETS);
|
||||
//! let mut led_pin = pins.gpio25.into_output(&mut pac.PADS_BANK0, &mut pac.SIO);
|
||||
//! led_pin.set_low().unwrap();
|
||||
//! let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS);
|
||||
//! let mut led_pin = pins.gpio25.into_output();
|
||||
//! led_pin.set_high().unwrap();
|
||||
//! ```
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
|
||||
pub struct Floating;
|
||||
pub struct Input<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
pub struct Input;
|
||||
pub struct Output;
|
||||
pub struct Unknown;
|
||||
|
||||
pub trait GpioExt {
|
||||
pub trait GpioExt<PADS, SIO> {
|
||||
type Parts;
|
||||
|
||||
// TODO: Do we need a marker to check that clocks are up?
|
||||
fn split(self, reset: &mut rp2040_pac::RESETS) -> Self::Parts;
|
||||
fn split(self, pads: PADS, sio: SIO, reset: &mut rp2040_pac::RESETS) -> Self::Parts;
|
||||
}
|
||||
|
||||
// Magic numbers from the datasheet
|
||||
// const FUNCTION_SPI: u8 = 1;
|
||||
// const FUNCTION_UART: u8 = 2;
|
||||
// const FUNCTION_I2C: u8 = 3;
|
||||
// const FUNCTION_PWM: u8 = 4;
|
||||
const FUNCTION_SIO: u8 = 5;
|
||||
// const FUNCTION_PIO0: u8 = 6;
|
||||
// const FUNCTION_PIO1: u8 = 7;
|
||||
// const FUNCTION_CLOCK: u8 = 8;
|
||||
// const FUNCTION_USB: u8 = 9;
|
||||
|
||||
macro_rules! gpio {
|
||||
($GPIOX:ident, $gpiox:ident, $PADSX:ident, [
|
||||
($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, [
|
||||
$($PXi:ident: ($pxi:ident, $i:expr),)+
|
||||
]) => {
|
||||
impl GpioExt for pac::$GPIOX {
|
||||
type Parts = Parts;
|
||||
mod $gpiox {
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin};
|
||||
use super::{GpioExt, Input, Output, Unknown, FUNCTION_SIO};
|
||||
|
||||
fn split(self, resets: &mut pac::RESETS) -> Parts {
|
||||
resets.reset.modify(|_, w| w.$gpiox().clear_bit());
|
||||
Parts {
|
||||
$(
|
||||
$pxi: $PXi { _mode: PhantomData },
|
||||
)+
|
||||
impl GpioExt<pac::$PADSX, pac::SIO> for pac::$GPIOX {
|
||||
type Parts = Parts;
|
||||
|
||||
fn split(self, pads: pac::$PADSX, sio: pac::SIO, resets: &mut pac::RESETS) -> Parts {
|
||||
resets.reset.modify(|_, w| w.$gpiox().clear_bit());
|
||||
Parts {
|
||||
_pads: pads,
|
||||
_sio: sio,
|
||||
$(
|
||||
$pxi: $PXi { _mode: PhantomData },
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Parts {
|
||||
pub struct Parts {
|
||||
_pads: pac::$PADSX,
|
||||
_sio: pac::SIO,
|
||||
$(
|
||||
pub $pxi: $PXi<Unknown>,
|
||||
)+
|
||||
}
|
||||
|
||||
// Puts pad in default state as far as this crate is concerned:
|
||||
// - Input is enabled
|
||||
// - Output is enabled (if also set in SIO)
|
||||
// - Pull up/down is disabled
|
||||
//
|
||||
// TODO: Drive strength, smitty, slewing
|
||||
fn setup_pad_io(pads: &pac::$padsx::RegisterBlock, index: usize) {
|
||||
pads.gpio[index].modify(|_, w| w.ie().set_bit().od().clear_bit().pue().clear_bit().pde().clear_bit());
|
||||
}
|
||||
|
||||
fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: u8) {
|
||||
gpios.gpio[index]
|
||||
.gpio_ctrl
|
||||
.write_with_zero(|x| unsafe { x.funcsel().bits(function) });
|
||||
}
|
||||
|
||||
$(
|
||||
pub $pxi: $PXi<Input<Floating>>,
|
||||
pub struct $PXi<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
// Safety: We own our $i slice of padsx, gpiox, and sio because the
|
||||
// construction of Parts assumes ownership of all 3 and will not release
|
||||
// them. Thus several of the methods below will reconstruct these objects
|
||||
// as-needed
|
||||
|
||||
impl<MODE> $PXi<MODE> {
|
||||
pub fn into_output(
|
||||
self,
|
||||
) -> $PXi<Output> {
|
||||
unsafe {
|
||||
setup_pad_io(&*pac::$PADSX::ptr(), $i);
|
||||
}
|
||||
unsafe {
|
||||
set_gpio_function(&*pac::$GPIOX::ptr(), $i, FUNCTION_SIO);
|
||||
}
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_oe_set.write(|x| { x.bits(1 << $i) });
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_input(
|
||||
self,
|
||||
) -> $PXi<Input> {
|
||||
unsafe {
|
||||
setup_pad_io(&*pac::$PADSX::ptr(), $i);
|
||||
}
|
||||
unsafe {
|
||||
set_gpio_function(&*pac::$GPIOX::ptr(), $i, FUNCTION_SIO);
|
||||
}
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_oe_clr.write(|x| { x.bits(1 << $i) });
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputPin for $PXi<Output> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_out_clr.write(|x| x.bits(1 << $i));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_out_set.write(|x| x.bits(1 << $i));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulOutputPin for $PXi<Output> {
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(!self.is_set_high()?)
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
unsafe {
|
||||
Ok((*pac::SIO::ptr()).gpio_out_set.read().bits() & (1 << $i) != 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Don't allow Unknown since we need to fix up the pad at least
|
||||
impl<MODE> InputPin for $PXi<MODE> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(!self.is_high()?)
|
||||
}
|
||||
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
unsafe {
|
||||
Ok((*pac::SIO::ptr()).gpio_in.read().bits() & (1 << $i) != 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $PXi<Input> {
|
||||
pub fn pull_high(&mut self) {
|
||||
unsafe {
|
||||
(*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().set_bit().pde().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pull_low(&mut self) {
|
||||
unsafe {
|
||||
(*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
pub struct $PXi<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<MODE> {
|
||||
pub fn into_output(
|
||||
self,
|
||||
pads: &mut pac::$PADSX,
|
||||
sio: &mut pac::SIO,
|
||||
) -> $PXi<Output> {
|
||||
pads.gpio[$i].modify(|_, w| w.ie().set_bit().od().clear_bit());
|
||||
unsafe {
|
||||
(*pac::$GPIOX::ptr()).gpio[$i]
|
||||
.gpio_ctrl
|
||||
.write_with_zero(|x| x.funcsel().bits(FUNCTION_SIO));
|
||||
}
|
||||
sio.gpio_oe.modify(|_, x| unsafe { x.bits(1 << $i) });
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputPin for $PXi<Output> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_out_clr.write(|x| x.bits(1 << $i));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
(*pac::SIO::ptr()).gpio_out_set.write(|x| x.bits(1 << $i));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
gpio!(
|
||||
IO_BANK0, io_bank0, PADS_BANK0, [
|
||||
IO_BANK0, io_bank0, PADS_BANK0, pads_bank0, [
|
||||
Gpio0: (gpio0, 0),
|
||||
Gpio1: (gpio1, 1),
|
||||
Gpio2: (gpio2, 2),
|
||||
|
|
Loading…
Reference in a new issue