Add GPIO support
This commit is contained in:
parent
0def5a4687
commit
41eed8e412
|
@ -11,6 +11,7 @@
|
|||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(global_asm)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(format_args_nl)]
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
|
||||
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
|
||||
*/
|
||||
|
||||
use {
|
||||
super::BcmHost,
|
||||
core::{marker::PhantomData, ops},
|
||||
register::{
|
||||
mmio::{ReadOnly, ReadWrite, WriteOnly},
|
||||
register_bitfields, FieldValue,
|
||||
},
|
||||
};
|
||||
|
||||
// Descriptions taken from
|
||||
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
|
||||
register_bitfields! {
|
||||
u32,
|
||||
|
||||
/// GPIO Pull-up/down Clock Register 0
|
||||
PUDCLK0 [
|
||||
/// Pin 15
|
||||
PUDCLK15 OFFSET(15) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 14
|
||||
PUDCLK14 OFFSET(14) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
/// Generates `pub enums` with no variants for each `ident` passed in.
|
||||
macro states($($name:ident),*) {
|
||||
$(pub enum $name { })*
|
||||
}
|
||||
|
||||
// Possible states for a GPIO pin.
|
||||
states! {
|
||||
Uninitialized, Input, Output, Alt
|
||||
}
|
||||
|
||||
/// A wrapper type that prevents reads or writes to its value.
|
||||
///
|
||||
/// This type implements no methods. It is meant to make the inner type
|
||||
/// inaccessible to prevent accidental reads or writes.
|
||||
#[repr(C)]
|
||||
pub struct Reserved<T>(T);
|
||||
|
||||
/// The offsets for reach register.
|
||||
/// From https://wiki.osdev.org/Raspberry_Pi_Bare_Bones and
|
||||
/// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
pub FSEL: [ReadWrite<u32>; 6], // 0x00-0x14 function select
|
||||
__reserved_0: Reserved<u32>, // 0x18
|
||||
pub SET: [WriteOnly<u32>; 2], // 0x1c-0x20 set output pin
|
||||
__reserved_1: Reserved<u32>, // 0x24
|
||||
pub CLR: [WriteOnly<u32>; 2], // 0x28-0x2c clear output pin
|
||||
__reserved_2: Reserved<u32>, // 0x30
|
||||
pub LEV: [ReadOnly<u32>; 2], // 0x34-0x38 get input pin level
|
||||
__reserved_3: Reserved<u32>, // 0x3C
|
||||
pub EDS: [ReadWrite<u32>; 2], // 0x40-0x44
|
||||
__reserved_4: Reserved<u32>, // 0x48
|
||||
pub REN: [ReadWrite<u32>; 2], // 0x4c-0x50
|
||||
__reserved_5: Reserved<u32>, // 0x54
|
||||
pub FEN: [ReadWrite<u32>; 2], // 0x58-0x5c
|
||||
__reserved_6: Reserved<u32>, // 0x60
|
||||
pub HEN: [ReadWrite<u32>; 2], // 0x64-0x68
|
||||
__reserved_7: Reserved<u32>, // 0x6c
|
||||
pub LEN: [ReadWrite<u32>; 2], // 0x70-0x74
|
||||
__reserved_8: Reserved<u32>, // 0x78
|
||||
pub AREN: [ReadWrite<u32>; 2], // 0x7c-0x80
|
||||
__reserved_9: Reserved<u32>, // 0x84
|
||||
pub AFEN: [ReadWrite<u32>; 2], // 0x88-0x8c
|
||||
__reserved_10: Reserved<u32>, // 0x90
|
||||
pub PUD: ReadWrite<u32>, // 0x94 pull up down
|
||||
pub PUDCLK: [ReadWrite<u32, PUDCLK0::Register>; 2], // 0x98-0x9C -- @todo remove this register
|
||||
}
|
||||
|
||||
/// Public interface to the GPIO MMIO area
|
||||
pub struct GPIO {
|
||||
base_addr: usize,
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.GPPUD.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*GPIO::ptr()).GPPUD.read() }
|
||||
/// ```
|
||||
impl ops::Deref for GPIO {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GPIO {
|
||||
fn default() -> GPIO {
|
||||
// Default RPi3 GPIO base address
|
||||
const GPIO_BASE: usize = BcmHost::get_peripheral_address() + 0x20_0000;
|
||||
GPIO::new(GPIO_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl GPIO {
|
||||
pub fn new(base_addr: usize) -> GPIO {
|
||||
GPIO { base_addr }
|
||||
}
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
}
|
||||
|
||||
pub fn get_pin(&self, pin: usize) -> Pin<Uninitialized> {
|
||||
Pin::new(pin, self.base_addr)
|
||||
}
|
||||
}
|
||||
|
||||
/// An alternative GPIO function.
|
||||
#[repr(u8)]
|
||||
pub enum Function {
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
Alt0 = 0b100,
|
||||
Alt1 = 0b101,
|
||||
Alt2 = 0b110,
|
||||
Alt3 = 0b111,
|
||||
Alt4 = 0b011,
|
||||
Alt5 = 0b010,
|
||||
}
|
||||
|
||||
impl ::core::convert::From<Function> for u32 {
|
||||
fn from(f: Function) -> Self {
|
||||
f as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPIO pin in state `State`.
|
||||
///
|
||||
/// The `State` generic always corresponds to an un-instantiable type that is
|
||||
/// used solely to mark and track the state of a given GPIO pin. A `Pin`
|
||||
/// structure starts in the `Uninitialized` state and must be transitioned into
|
||||
/// one of `Input`, `Output`, or `Alt` via the `into_input`, `into_output`, and
|
||||
/// `into_alt` methods before it can be used.
|
||||
pub struct Pin<State> {
|
||||
pin: usize,
|
||||
base_addr: usize,
|
||||
_state: PhantomData<State>,
|
||||
}
|
||||
|
||||
impl<State> Pin<State> {
|
||||
/// Transitions `self` to state `NewState`, consuming `self` and returning a new
|
||||
/// `Pin` instance in state `NewState`. This method should _never_ be exposed to
|
||||
/// the public!
|
||||
#[inline(always)]
|
||||
fn transition<NewState>(self) -> Pin<NewState> {
|
||||
Pin {
|
||||
pin: self.pin,
|
||||
base_addr: self.base_addr,
|
||||
_state: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a pointer to the register block
|
||||
#[inline(always)]
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
}
|
||||
}
|
||||
|
||||
/// Deref to Pin's Registers
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.PUD.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*Pin::ptr()).PUD.read() }
|
||||
/// ```
|
||||
impl<State> ops::Deref for Pin<State> {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin<Uninitialized> {
|
||||
/// Returns a new GPIO `Pin` structure for pin number `pin`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `pin` > `53`.
|
||||
fn new(pin: usize, base_addr: usize) -> Pin<Uninitialized> {
|
||||
if pin > 53 {
|
||||
panic!("gpio::Pin::new(): pin {} exceeds maximum of 53", pin);
|
||||
}
|
||||
|
||||
Pin {
|
||||
base_addr,
|
||||
pin,
|
||||
_state: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables the alternative function `function` for `self`. Consumes self
|
||||
/// and returns a `Pin` structure in the `Alt` state.
|
||||
pub fn into_alt(self, function: Function) -> Pin<Alt> {
|
||||
let bank = self.pin / 10;
|
||||
let off = self.pin % 10;
|
||||
self.FSEL[bank].modify(FieldValue::<u32, ()>::new(0b111, off * 3, function.into()));
|
||||
self.transition()
|
||||
}
|
||||
|
||||
/// Sets this pin to be an _output_ pin. Consumes self and returns a `Pin`
|
||||
/// structure in the `Output` state.
|
||||
pub fn into_output(self) -> Pin<Output> {
|
||||
self.into_alt(Function::Output).transition()
|
||||
}
|
||||
|
||||
/// Sets this pin to be an _input_ pin. Consumes self and returns a `Pin`
|
||||
/// structure in the `Input` state.
|
||||
pub fn into_input(self) -> Pin<Input> {
|
||||
self.into_alt(Function::Input).transition()
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin<Output> {
|
||||
/// Sets (turns on) this pin.
|
||||
pub fn set(&mut self) {
|
||||
// Guarantees: pin number is between [0; 53] by construction.
|
||||
let bank = self.pin / 32;
|
||||
let shift = self.pin % 32;
|
||||
self.SET[bank].set(1 << shift);
|
||||
}
|
||||
|
||||
/// Clears (turns off) this pin.
|
||||
pub fn clear(&mut self) {
|
||||
// Guarantees: pin number is between [0; 53] by construction.
|
||||
let bank = self.pin / 32;
|
||||
let shift = self.pin % 32;
|
||||
self.CLR[bank].set(1 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
pub type Level = bool;
|
||||
|
||||
impl Pin<Input> {
|
||||
/// Reads the pin's value. Returns `true` if the level is high and `false`
|
||||
/// if the level is low.
|
||||
pub fn level(&self) -> Level {
|
||||
// Guarantees: pin number is between [0; 53] by construction.
|
||||
let bank = self.pin / 32;
|
||||
let off = self.pin % 32;
|
||||
self.LEV[bank].matches_all(FieldValue::<u32, ()>::new(1, off, 1))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test_case]
|
||||
fn test_pin_transitions() {
|
||||
let mut reg = [0u32; 40];
|
||||
let gpio = GPIO::new(&mut reg as *mut _ as usize);
|
||||
|
||||
let _out = gpio.get_pin(1).into_output();
|
||||
assert_eq!(reg[0], 0b001_000);
|
||||
let _inp = gpio.get_pin(12).into_input();
|
||||
assert_eq!(reg[1], 0b000_000_000);
|
||||
let _alt = gpio.get_pin(35).into_alt(Function::Alt1);
|
||||
assert_eq!(reg[3], 0b101_000_000_000_000_000);
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_pin_outputs() {
|
||||
let mut reg = [0u32; 40];
|
||||
let gpio = GPIO::new(&mut reg as *mut _ as usize);
|
||||
|
||||
let pin = gpio.get_pin(1);
|
||||
let mut out = pin.into_output();
|
||||
out.set();
|
||||
assert_eq!(reg[7], 0b10); // SET pin 1 = 1 << 1
|
||||
out.clear();
|
||||
assert_eq!(reg[10], 0b10); // CLR pin 1 = 1 << 1
|
||||
|
||||
let pin = gpio.get_pin(35);
|
||||
let mut out = pin.into_output();
|
||||
out.set();
|
||||
assert_eq!(reg[8], 0b1000); // SET pin 35 = 1 << (35 - 32)
|
||||
out.clear();
|
||||
assert_eq!(reg[11], 0b1000); // CLR pin 35 = 1 << (35 - 32)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_pin_inputs() {
|
||||
let mut reg = [0u32; 40];
|
||||
let gpio = GPIO::new(&mut reg as *mut _ as usize);
|
||||
|
||||
let pin = gpio.get_pin(1);
|
||||
let inp = pin.into_input();
|
||||
assert_eq!(inp.level(), false);
|
||||
|
||||
reg[13] = 0b10;
|
||||
|
||||
assert_eq!(inp.level(), true);
|
||||
|
||||
let pin = gpio.get_pin(35);
|
||||
let inp = pin.into_input();
|
||||
assert_eq!(inp.level(), false);
|
||||
|
||||
reg[14] = 0b1000;
|
||||
|
||||
assert_eq!(inp.level(), true);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod fb;
|
||||
pub mod gpio;
|
||||
pub mod mailbox;
|
||||
|
||||
/// See BCM2835-ARM-Peripherals.pdf
|
||||
|
|
Loading…
Reference in New Issue