diff --git a/src/io/mod.rs b/src/io/mod.rs new file mode 100644 index 0000000..5d359f0 --- /dev/null +++ b/src/io/mod.rs @@ -0,0 +1,5 @@ +pub mod port; + +pub const PORT_B: port::Port = port::Port::new(); +pub const PORT_C: port::Port = port::Port::new(); +pub const PORT_D: port::Port = port::Port::new(); diff --git a/src/io/port.rs b/src/io/port.rs new file mode 100644 index 0000000..73c8ee0 --- /dev/null +++ b/src/io/port.rs @@ -0,0 +1,183 @@ +use core::prelude::v1::*; +use core::ptr::{read_volatile, write_volatile}; +use core::marker::PhantomData; + +use Bit; + +pub trait Information { + const DDR: *mut u8; + const IO: *mut u8; + const PIN: *mut u8; +} + +#[derive(Copy, Clone)] +pub struct B; + +impl Information for B { + const DDR: *mut u8 = ::DDRB; + const IO: *mut u8 = ::PORTB; + const PIN: *mut u8 = ::PINB; +} + +#[derive(Copy, Clone)] +pub struct C; + +impl Information for C { + const DDR: *mut u8 = ::DDRC; + const IO: *mut u8 = ::PORTC; + const PIN: *mut u8 = ::PINC; +} + +#[derive(Copy, Clone)] +pub struct D; + +impl Information for D { + const DDR: *mut u8 = ::DDRD; + const IO: *mut u8 = ::PORTD; + const PIN: *mut u8 = ::PIND; +} + +#[derive(Copy, Clone)] +enum Direction { + Output, + Input, +} + +const BITS_IN_BYTE: usize = 8; + +pub struct Configuration { + _port: PhantomData

, + direction: [Option; BITS_IN_BYTE], + pullup: [Option; BITS_IN_BYTE], +} + +impl

Configuration

+ where + P: Information +{ + #[inline] + pub fn new() -> Configuration

{ + Configuration { + _port: PhantomData, + direction: Default::default(), + pullup: Default::default(), + } + } + + #[inline] + pub fn set_all_as_output(&mut self) -> &mut Self { + self.direction = [Some(Direction::Output); 8]; + self + } + + #[inline] + pub fn set_all_as_input(&mut self) -> &mut Self { + self.direction = [Some(Direction::Input); 8]; + self + } + + #[inline] + pub fn set_as_output(&mut self, bit: Bit) -> &mut Self { + self.direction[bit as usize] = Some(Direction::Output); + self + } + + #[inline] + pub fn set_as_input(&mut self, bit: Bit) -> &mut Self { + self.direction[bit as usize] = Some(Direction::Input); + self + } + + #[inline] + pub fn enable_pullup(&mut self, bit: Bit) -> &mut Self { + self.pullup[bit as usize] = Some(true); + self + } + + #[inline] + pub fn disable_pullup(&mut self, bit: Bit) -> &mut Self { + self.pullup[bit as usize] = Some(false); + self + } + + #[inline] + pub fn configure(&self) { + // FIXME: Both of these loops are wasteful if we are + // setting all 8 bits, when we could set the entire IO + // register at once. Is there a way we can track that? + + // We use `zip` instead of `enumerate` to guarantee it's a constant + + for (&p, i) in self.direction.iter().zip(0..BITS_IN_BYTE) { + if let Some(enabled) = p { + if let Direction::Output = enabled { + unsafe { asm!("sbi $0 $1" : : "n"(P::DDR), "n"(i)) } + } else { + unsafe { asm!("cbi $0 $1; A" : : "n"(P::DDR), "n"(i)) } + } + } + } + + for (&p, i) in self.pullup.iter().zip(0..BITS_IN_BYTE) { + if let Some(enabled) = p { + if enabled { + unsafe { asm!("sbi $0 $1" : : "n"(P::IO), "n"(i)) } + } else { + unsafe { asm!("cbi $0 $1; B" : : "n"(P::IO), "n"(i)) } + } + } + } + } +} + +#[derive(Copy, Clone)] +pub struct Data { + _port: PhantomData

, +} + +impl

Data

+ where + P: Information +{ + #[inline] + pub fn new() -> Data

{ Data { _port: PhantomData } } + + #[inline] + pub fn get(&self) -> u8 { + unsafe { read_volatile(P::PIN) } + } + + #[inline] + pub fn set(&self, value: u8) { + unsafe { write_volatile(P::IO, value) }; + } + + #[inline] + pub fn bit_is_set(&self, bit: Bit) -> bool { + bit.is_set(self.get()) + } + + #[inline] + pub fn set_bit(&self, bit: Bit) { + unsafe { asm!("sbi $0 $1" : : "n"(P::IO), "n"(bit as u8)) } + } + + #[inline] + pub fn clear_bit(&self, bit: Bit) { + unsafe { asm!("cbi $0 $1; C" : : "n"(P::IO), "n"(bit as u8)) } + } + + #[inline] + pub fn toggle_bit(&self, bit: Bit) { + unsafe { asm!("sbi $0 $1" : : "n"(P::PIN), "n"(bit as u8)) } + } +} + +#[derive(Copy, Clone)] +pub struct Port

(PhantomData

); + +impl Port

{ + pub(crate) const fn new() -> Port

{ Port(PhantomData) } + pub fn configuration(&self) -> Configuration

{ Configuration::new() } + pub fn data(&self) -> Data

{ Data::new() } +} diff --git a/src/lib.rs b/src/lib.rs index 98cc01b..a81583f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,14 +2,50 @@ #![feature(asm)] #![feature(no_core)] +#![feature(const_fn)] +#![feature(associated_consts)] + #![no_core] extern crate core; +// Look like we have a standard library +#[allow(unused_imports)] +use core::{option, iter, fmt, ops, clone, marker}; + pub mod prelude; pub mod timer0; pub mod timer1; pub mod serial; +pub mod io; + +#[derive(Copy, Clone)] +pub enum Bit { + Bit0 = 0, + Bit1 = 1, + Bit2 = 2, + Bit3 = 3, + Bit4 = 4, + Bit5 = 5, + Bit6 = 6, + Bit7 = 7, +} + +impl Bit { + fn as_mask(&self) -> u8 { 1 << *self as u8 } + + pub fn is_set(&self, value: u8) -> bool { + (value & self.as_mask()) != 0 + } + + pub fn set(&self, value: u8) -> u8 { + value | self.as_mask() + } + + pub fn unset(&self, value: u8) -> u8 { + value & !self.as_mask() + } +} macro_rules! bit { (-, $pos:expr) => {}; diff --git a/src/prelude.rs b/src/prelude.rs index 8895d93..8592448 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,6 +1,8 @@ use core::prelude::v1::*; use core::marker::PhantomData; +pub use io::{PORT_B, PORT_C, PORT_D}; + pub struct DisableInterrupts(PhantomData<()>); impl DisableInterrupts {