diff --git a/Cargo.toml b/Cargo.toml index 5a1a6d8..e04c30c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,20 @@ version = "0.1.1" authors = [ "The AVR-Rust Project Developers", "Jake Goulding ", + "Dylan McKay ", ] + license = "MIT/Apache-2.0" readme = "README.md" repository = "https://github.com/avr-rust/ruduino" description = """ -Reusable components for the Arduino Uno. +Reusable components for AVR microcontrollers """ + +build = "core_generator/build.rs" + keywords = ["avr", "arduino", "uno"] + +[build-dependencies] +avr-mcu = "0.2" + diff --git a/core_generator/build.rs b/core_generator/build.rs new file mode 100644 index 0000000..e148399 --- /dev/null +++ b/core_generator/build.rs @@ -0,0 +1,97 @@ +extern crate avr_mcu; + +mod gen; + +use avr_mcu::*; +use std::fs::{self, File}; +use std::io; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; + +/// The MCU that will be assumed when running 'cargo doc' targeting +/// archicectures that are not AVR. +const DEFAULT_MCU_FOR_NON_AVR_DOCS: &'static str = "atmega328"; + +fn src_path() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("src") +} + +fn cores_path() -> PathBuf { + src_path().join("cores") +} + +fn core_module_name(mcu: &Mcu) -> String { + mcu.device.name.to_lowercase().to_owned() +} + +fn main() { + if !cores_path().exists() { + fs::create_dir_all(&cores_path()).expect("could not create cores directory"); + } + + let current_mcu = if cfg!(arch = "avr") { + avr_mcu::current::mcu() + .expect("no target cpu specified") + } else { + avr_mcu::microcontroller(DEFAULT_MCU_FOR_NON_AVR_DOCS) + }; + + generate_config_module().unwrap(); + generate_cores(&[current_mcu]).unwrap(); +} + +fn generate_cores(mcus: &[Mcu]) -> Result<(), io::Error> { + for mcu in mcus { + generate_core_module(mcu).expect("failed to generate mcu core"); + } + generate_cores_mod_rs(mcus) +} + +fn generate_config_module() -> Result<(), io::Error> { + let path = src_path().join("config.rs"); + let mut f = File::create(&path)?; + + let clock = env!("AVR_CPU_FREQUENCY_HZ"); + writeln!(f, "/// The clock frequency of device being targeted in Hertz.")?; + writeln!(f, "pub const CPU_FREQUENCY_HZ: u32 = {};", clock)?; + Ok(()) +} + +fn generate_core_module(mcu: &Mcu) -> Result<(), io::Error> { + let path = cores_path().join(format!("{}.rs", core_module_name(mcu))); + let mut file = File::create(&path)?; + write_core_module(mcu, &mut file) +} + +fn generate_cores_mod_rs(mcus: &[Mcu]) -> Result<(), io::Error> { + let path = cores_path().join("mod.rs"); + let mut w = File::create(&path)?; + + writeln!(w)?; + for mcu in mcus { + let module_name = core_module_name(mcu); + writeln!(w, "/// The {}.", mcu.device.name)?; + writeln!(w, "pub mod {};", module_name)?; + + writeln!(w, "#[cfg(all(target_arch = \"avr\", target_cpu = \"{}\"))]", module_name)?; + writeln!(w, "pub use self::{} as current;", module_name)?; + } + writeln!(w) +} + +fn write_core_module(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { + writeln!(w, "//! Core for {}.", mcu.device.name)?; + writeln!(w)?; + writeln!(w, "use {{RegisterBits, Register}};")?; + writeln!(w, "use modules;")?; + writeln!(w)?; + + gen::write_registers(mcu, w)?; + gen::write_pins(mcu, w)?; + gen::write_spi_modules(mcu, w)?; + gen::write_usarts(mcu, w)?; + gen::write_timers(mcu, w)?; + + writeln!(w) +} + diff --git a/core_generator/gen.rs b/core_generator/gen.rs new file mode 100644 index 0000000..4b0d4da --- /dev/null +++ b/core_generator/gen.rs @@ -0,0 +1,240 @@ +use avr_mcu::*; +use std::io; +use std::io::prelude::*; + +pub fn write_registers(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { + for register in mcu.registers() { + let ty = if register.size == 1 { "u8" } else { "u16" }; + + // HACK: Skip, atmeg328p pack defines two of these. + if register.name == "GTCCR" { continue; } + + writeln!(w, "pub struct {};", register.name)?; + writeln!(w)?; + + writeln!(w, "impl {} {{", register.name)?; + for bitfield in register.bitfields.iter() { + // Create a mask for the whole bitset. + writeln!(w, " pub const {}: RegisterBits = RegisterBits::new(0x{:x});", bitfield.name, bitfield.mask)?; + + // We create masks for the individual bits in the field if there + // is more than one bit in the field. + let mut current_mask = bitfield.mask; + let mut current_mask_bit_num = 0; + for current_register_bit_num in 0..15 { + if (current_mask & 0b1) == 0b1 { + writeln!(w, " pub const {}{}: RegisterBits = RegisterBits::new(1<<{});", + bitfield.name, current_mask_bit_num, current_register_bit_num)?; + current_mask_bit_num += 1; + } + + current_mask >>= 1; + } + writeln!(w)?; + } + writeln!(w, "}}")?; + writeln!(w)?; + + writeln!(w, "impl Register for {} {{", register.name)?; + writeln!(w, " type T = {};", ty)?; + writeln!(w, " const ADDRESS: *mut {} = 0x{:x} as *mut {};", ty, register.offset, ty)?; + writeln!(w, "}}")?; + } + + Ok(()) +} + +pub fn write_pins(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { + if let Some(port) = mcu.peripheral("PORT") { + writeln!(w, "pub mod port {{")?; + writeln!(w, " use super::*;")?; + writeln!(w, " use Pin;")?; + writeln!(w)?; + + for instance in port.instances.iter() { + let port_letter = instance.name.chars().rev().next().unwrap(); + + for signal in instance.signals.iter() { + let idx = signal.index.expect("signal with no index"); + let struct_name = format!("{}{}", port_letter, idx); + + let io_module = mcu.modules.iter().find(|m| m.name == "PORT") + .expect("no port io module defined for this port"); + let register_group = io_module.register_groups.iter() + .find(|rg| rg.name == instance.name) + .expect("no register group defined for this port"); + + writeln!(w, " pub struct {};", struct_name)?; + writeln!(w)?; + writeln!(w, " impl Pin for {} {{", struct_name)?; + for reg in register_group.registers.iter() { + let mut const_name = reg.name.clone(); + const_name.pop(); // Pop port character from register name (DDRB/PORTB/etc).. + + writeln!(w, " /// {}.", reg.caption)?; + writeln!(w, " type {} = {};", const_name, reg.name)?; + } + writeln!(w, " /// {}", signal.pad)?; + writeln!(w, " const MASK: u8 = 1<<{};", idx)?; + writeln!(w, " }}")?; + writeln!(w)?; + } + } + + writeln!(w, "}}")?; + writeln!(w)?; + } + Ok(()) +} + +pub fn write_spi_modules(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { + if let Some(module) = mcu.module("SPI") { + let peripheral = mcu.peripheral("SPI").expect("found SPI module but no peripheral"); + let port_peripheral = mcu.port_peripheral(); + + writeln!(w, "pub struct Spi;")?; + writeln!(w)?; + writeln!(w, "impl modules::HardwareSpi for Spi {{")?; + + for spi_signal in peripheral.signals() { + let spi_signal_name = spi_signal.group.clone().expect("spi signal does not have group name"); + let (port_instance, port_signal) = port_peripheral.instance_signal_with_pad(&spi_signal.pad) + .expect("no port signal associated with the spi signal pad"); + let pin_name = self::pin_name(port_instance, port_signal); + + let const_name = match &spi_signal_name[..] { + "MISO" => "MasterInSlaveOut", + "MOSI" => "MasterOutSlaveIn", + "SCK" => "Clock", + "SS" => "SlaveSelect", + _ => panic!("unknown spi signal name: '{}'", spi_signal_name), + }; + + writeln!(w, " type {} = {};", const_name, pin_name)?; + } + + for reg in module.registers() { + let const_name = match ®.caption[..] { + "SPI Data Register" => "DataRegister", + "SPI Status Register" => "StatusRegister", + "SPI Control Register" => "ControlRegister", + _ => panic!("unknown SPI module register: {}", reg.caption), + }; + + writeln!(w, " type {} = {};", const_name, reg.name)?; + } + writeln!(w, "}}")?; + writeln!(w)?; + } + Ok(()) +} + +pub fn write_usarts(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { + if let Some(module) = mcu.module("USART") { + for usart in module.register_groups.iter() { + writeln!(w, "/// The {} module.", usart.name)?; + writeln!(w, "pub struct {};", usart.name)?; + writeln!(w)?; + writeln!(w, "impl modules::HardwareUsart for {} {{", usart.name)?; + for register in usart.registers.iter() { + let reg_ty = if register.name.starts_with("UDR") { // the data register. + "DataRegister".to_owned() + } else if register.name.starts_with("UCSR") { // one of the three control/status registers. + let suffix = register.name.chars().rev().next().unwrap(); + format!("ControlRegister{}", suffix) + } else if register.name.starts_with("UBRR") { // the baud rate register. + "BaudRateRegister".to_owned() + } else { + panic!("unknown usart register '{}'", register.name); + }; + writeln!(w, " type {} = {};", reg_ty, register.name)?; + } + writeln!(w, "}}")?; + writeln!(w)?; + } + } + Ok(()) +} + +pub fn write_timers(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { + if let Some(tc) = mcu.module("TC8") { // Timer/Counter, 8-bit. + const TYPE_NAME: &'static str = "Timer8"; + + let find_reg = |name: &'static str| { + tc.registers().find(|r| r.name.starts_with(name)) + .expect(&format!("could not find '{}' register", name)) + }; + let find_reg_suffix = |name: &'static str, suffix: &'static str| { + tc.registers().find(|r| r.name.starts_with(name) && r.name.ends_with(suffix)) + .expect(&format!("could not find '{}' register", name)) + }; + let timer_number = find_reg("TIMSK").name.chars().last().unwrap() + .to_digit(10).unwrap(); + + writeln!(w, "/// 8-bit timer.")?; + writeln!(w, "pub struct {};", TYPE_NAME)?; + writeln!(w)?; + writeln!(w, "impl modules::Timer8 for {} {{", TYPE_NAME)?; + writeln!(w, " type CompareA = {};", find_reg_suffix("OCR", "A").name)?; + writeln!(w, " type CompareB = {};", find_reg_suffix("OCR", "B").name)?; + writeln!(w, " type Counter = {};", find_reg("TCNT").name)?; + writeln!(w, " type ControlA = {};", find_reg_suffix("TCCR", "A").name)?; + writeln!(w, " type ControlB = {};", find_reg_suffix("TCCR", "B").name)?; + writeln!(w, " type InterruptMask = {};", find_reg("TIMSK").name)?; + writeln!(w, " type InterruptFlag = {};", find_reg("TIFR").name)?; + writeln!(w, " const CS0: RegisterBits = Self::ControlB::CS00;")?; + writeln!(w, " const CS1: RegisterBits = Self::ControlB::CS01;")?; + writeln!(w, " const CS2: RegisterBits = Self::ControlB::CS02;")?; + writeln!(w, " const WGM0: RegisterBits = Self::ControlA::WGM00;")?; + writeln!(w, " const WGM1: RegisterBits = Self::ControlA::WGM01;")?; + writeln!(w, " const WGM2: RegisterBits = Self::ControlB::WGM020;")?; + writeln!(w, " const OCIEA: RegisterBits = Self::InterruptMask::OCIE{}A;", timer_number)?; + writeln!(w, "}}")?; + } + + if let Some(tc) = mcu.module("TC16") { // Timer/Counter, 16-bit. + const TYPE_NAME: &'static str = "Timer16"; + + let find_reg = |name: &'static str| { + tc.registers().find(|r| r.name.starts_with(name)) + .expect(&format!("could not find '{}' register", name)) + }; + let find_reg_suffix = |name: &'static str, suffix: &'static str| { + tc.registers().find(|r| r.name.starts_with(name) && r.name.ends_with(suffix)) + .expect(&format!("could not find '{}' register", name)) + }; + let timer_number = find_reg("TIMSK").name.chars().last().unwrap() + .to_digit(10).unwrap(); + + writeln!(w, "/// 16-bit timer.")?; + writeln!(w, "pub struct {};", TYPE_NAME)?; + writeln!(w)?; + writeln!(w, "impl modules::Timer16 for {} {{", TYPE_NAME)?; + writeln!(w, " type CompareA = {};", find_reg_suffix("OCR", "A").name)?; + writeln!(w, " type CompareB = {};", find_reg_suffix("OCR", "B").name)?; + writeln!(w, " type Counter = {};", find_reg("TCNT").name)?; + writeln!(w, " type ControlA = {};", find_reg_suffix("TCCR", "A").name)?; + writeln!(w, " type ControlB = {};", find_reg_suffix("TCCR", "B").name)?; + writeln!(w, " type ControlC = {};", find_reg_suffix("TCCR", "C").name)?; + writeln!(w, " type InterruptMask = {};", find_reg("TIMSK").name)?; + writeln!(w, " type InterruptFlag = {};", find_reg("TIFR").name)?; + writeln!(w, " const CS0: RegisterBits = Self::ControlB::CS10;")?; + writeln!(w, " const CS1: RegisterBits = Self::ControlB::CS11;")?; + writeln!(w, " const CS2: RegisterBits = Self::ControlB::CS12;")?; + writeln!(w, " const WGM0: RegisterBits = Self::ControlA::WGM10;")?; + writeln!(w, " const WGM1: RegisterBits = Self::ControlA::WGM11;")?; + writeln!(w, " const WGM2: RegisterBits = Self::ControlB::WGM10;")?; + writeln!(w, " const WGM3: RegisterBits = Self::ControlB::WGM11;")?; + writeln!(w, " const OCIEA: RegisterBits = Self::InterruptMask::OCIE{}A;", timer_number)?; + writeln!(w, "}}")?; + } + + Ok(()) +} + +/// Gets the name of a pin. +fn pin_name(instance: &Instance, signal: &Signal) -> String { + let idx = signal.index.expect("signal with no index"); + let letter = instance.name.chars().rev().next().unwrap(); + format!("port::{}{}", letter, idx) +} diff --git a/examples/spi.rs b/examples/spi.rs new file mode 100644 index 0000000..ccecf27 --- /dev/null +++ b/examples/spi.rs @@ -0,0 +1,14 @@ +#![no_std] +#![no_main] + +extern crate arduino; +use arduino::cores::current; + +// Some devices may have multiple SPI modules. +// The ATmega328p only has one. +type Spi = current::Spi; + +#[no_mangle] +pub extern fn main() { +} + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..5c921d9 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +# Generated automatically. +config.rs diff --git a/src/cores/.gitignore b/src/cores/.gitignore new file mode 100644 index 0000000..6f5f3d1 --- /dev/null +++ b/src/cores/.gitignore @@ -0,0 +1 @@ +*.rs diff --git a/src/cores/.gitkeep b/src/cores/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/interrupt.rs b/src/interrupt.rs new file mode 100644 index 0000000..83d358b --- /dev/null +++ b/src/interrupt.rs @@ -0,0 +1,35 @@ +//! Routines for managing interrupts. + +use core::prelude::v1::*; +use core::marker::PhantomData; + +/// Helper struct that automatically restores interrupts +/// on drop. +struct DisableInterrupts(PhantomData<()>); + +/// Executes a closure, disabling interrupts until its completion. +/// +/// Restores interrupts after the closure has completed +/// execution. +pub fn without_interrupts(f: F) -> T + where F: FnOnce() -> T +{ + let _disabled = DisableInterrupts::new(); + f() +} + +impl DisableInterrupts { + #[inline] + pub fn new() -> DisableInterrupts { + unsafe { asm!("CLI") } + DisableInterrupts(PhantomData) + } +} + +impl Drop for DisableInterrupts { + #[inline] + fn drop(&mut self) { + unsafe { asm!("SEI") } + } +} + diff --git a/src/io/mod.rs b/src/io/mod.rs deleted file mode 100644 index 5d359f0..0000000 --- a/src/io/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 73c8ee0..0000000 --- a/src/io/port.rs +++ /dev/null @@ -1,183 +0,0 @@ -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/legacy/mod.rs b/src/legacy/mod.rs new file mode 100644 index 0000000..1cd9e64 --- /dev/null +++ b/src/legacy/mod.rs @@ -0,0 +1,10 @@ +//! Legacy code. +//! +//! The code in here needs to be cleaned up and rewritten to use +//! the new core-based API. +//! +//! Once a replacement exists, these legacy modules may be removed +//! in a major release! + +pub mod serial; + diff --git a/src/serial.rs b/src/legacy/serial.rs similarity index 75% rename from src/serial.rs rename to src/legacy/serial.rs index 76e9643..610cd45 100644 --- a/src/serial.rs +++ b/src/legacy/serial.rs @@ -1,8 +1,10 @@ +//! A serial. +//! +//! *WARNING* The current implementation of this will only work on ATmega328. + use core::prelude::v1::*; use core::ptr::{read_volatile, write_volatile}; -use super::*; - pub enum CharacterSize { FiveBits, SixBits, @@ -107,6 +109,8 @@ impl StopBits { } } +/// A serial connection. +/// *WARNING* The current implementation of this will only work on ATmega328. pub struct Serial { ubrr: u16, a: u8, @@ -224,3 +228,41 @@ pub fn try_receive() -> Option { None } } + +// Dirty hack. +// We should write this out and use the neat build-script method instead. +use self::hack::*; +mod hack { + macro_rules! bit { + (-, $pos:expr) => {}; + ($name:ident, $pos:expr) => { + pub const $name: u8 = 1 << $pos; + }; + } + + macro_rules! register { + ($address:expr, $name:ident, [$b7:tt, $b6:tt, $b5:tt, $b4:tt, $b3:tt, $b2:tt, $b1:tt, $b0:tt]) => { + register!($address, $name); + bit!($b7, 7); + bit!($b6, 6); + bit!($b5, 5); + bit!($b4, 4); + bit!($b3, 3); + bit!($b2, 2); + bit!($b1, 1); + bit!($b0, 0); + }; + ($address:expr, $name:ident) => { + pub const $name: *mut u8 = $address as *mut u8; + }; + } + + register!(0xC6, UDR0); + register!(0xC4, UBRR0L); + register!(0xC2, UCSR0C, [UMSEL01, UMSEL00, UPM01, UPM00, USBS0, UCSZ01, UCSZ00, - ]); + register!(0xC1, UCSR0B, [-, -, -, RXEN0, TXEN0, UCSZ02, -, - ]); + register!(0xC0, UCSR0A, [RXC0, -, UDRE0, -, -, -, -, - ]); + + // 16-bit register pairs + pub const UBRR0: *mut u16 = UBRR0L as *mut u16; +} diff --git a/src/lib.rs b/src/lib.rs index b4e5948..92079e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,174 +1,29 @@ //! Definitions of register addresses and bits within those registers #![feature(asm)] -#![feature(no_core)] #![feature(const_fn)] +#![feature(associated_type_defaults)] +#![feature(lang_items)] +#![feature(panic_handler)] +#![feature(unwind_attributes)] -#![no_core] +#![no_std] -extern crate core; - -// Look like we have a standard library -#[allow(unused_imports)] -use core::{option, iter, fmt, ops, clone, marker}; +pub use self::register::{Register, RegisterBits, RegisterValue}; +pub use self::pin::{DataDirection, Pin}; pub mod prelude; -pub mod timer0; -pub mod timer1; -pub mod serial; -pub mod io; +pub mod legacy; +/// Low level register-based API for device-specific operations. +pub mod cores; +pub mod interrupt; +pub mod modules; -#[derive(Copy, Clone)] -pub enum Bit { - Bit0 = 0, - Bit1 = 1, - Bit2 = 2, - Bit3 = 3, - Bit4 = 4, - Bit5 = 5, - Bit6 = 6, - Bit7 = 7, -} +/// Configuration for the currently-targeted microcontroller. +pub mod config; -impl Bit { - fn as_mask(&self) -> u8 { 1 << *self as u8 } +mod register; +mod pin; +#[doc(hidden)] +pub mod std_stub; - 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) => {}; - ($name:ident, $pos:expr) => { - pub const $name: u8 = 1 << $pos; - }; -} - -macro_rules! register { - ($address:expr, $name:ident, [$b7:tt, $b6:tt, $b5:tt, $b4:tt, $b3:tt, $b2:tt, $b1:tt, $b0:tt]) => { - register!($address, $name); - bit!($b7, 7); - bit!($b6, 6); - bit!($b5, 5); - bit!($b4, 4); - bit!($b3, 3); - bit!($b2, 2); - bit!($b1, 1); - bit!($b0, 0); - }; - ($address:expr, $name:ident) => { - pub const $name: *mut u8 = $address as *mut u8; - }; -} - -register!(0xC6, UDR0 ); -register!(0xC5, UBRR0H ); -register!(0xC4, UBRR0L ); -register!(0xC2, UCSR0C, [UMSEL01, UMSEL00, UPM01, UPM00, USBS0, UCSZ01, UCSZ00, UCPOL0 ]); -register!(0xC1, UCSR0B, [RXCIE0, TXCIE0, UDRIE0, RXEN0, TXEN0, UCSZ02, RXB80, TXB80 ]); -register!(0xC0, UCSR0A, [RXC0, TXC0, UDRE0, FE0, DOR0, UPE0, U2X0, MPCM0 ]); -register!(0xBD, TWAMR, [TWAM6, TWAM5, TWAM4, TWAM3, TWAM2, TWAM1, TWAM0, - ]); -register!(0xBC, TWCR, [TWINT, TWEA, TWSTA, TWSTO, TWWC, TWEN, -, TWIE ]); -register!(0xBB, TWDR ); -register!(0xBA, TWAR, [TWA6, TWA5, TWA4, TWA3, TWA2, TWA1, TWA0, TWGCE ]); -register!(0xB9, TWSR, [TWS7, TWS6, TWS5, TWS4, TWS3, -, TWPS1, TWPS0 ]); -register!(0xB8, TWBR ); -register!(0xB6, ASSR, [-, EXCLK, AS2, TCN2UB, OCR2AUB, OCR2BUB, TCR2AUB, TCR2BUB]); -register!(0xB4, OCR2B ); -register!(0xB3, OCR2A ); -register!(0xB2, TCNT2 ); -register!(0xB1, TCCR2B, [FOC2A, FOC2B, -, -, WGM22, CS22, CS21, CS20 ]); -register!(0xB0, TCCR2A, [COM2A1, COM2A0, COM2B1, COM2B0, -, -, WGM21, WGM20 ]); -register!(0x8B, OCR1BH ); -register!(0x8A, OCR1BL ); -register!(0x89, OCR1AH ); -register!(0x88, OCR1AL ); -register!(0x87, ICR1H ); -register!(0x86, ICR1L ); -register!(0x85, TCNT1H ); -register!(0x84, TCNT1L ); -register!(0x82, TCCR1C, [FOC1A, FOC1B, -, -, -, -, -, - ]); -register!(0x81, TCCR1B, [ICNC1, ICES1, -, WGM13, WGM12, CS12, CS11, CS10 ]); -register!(0x80, TCCR1A, [COM1A1, COM1A0, COM1B1, COM1B0, -, -, WGM11, WGM10 ]); -register!(0x7F, DIDR1, [-, -, -, -, -, -, AIN1D, AIN0D ]); -register!(0x7E, DIDR0, [-, -, ADC5D, ADC4D, ADC3D, ADC2D, ADC1D, ADC0D ]); -register!(0x7C, ADMUX, [REFS1, REFS0, ADLAR, -, MUX3, MUX2, MUX1, MUX0 ]); -register!(0x7B, ADCSRB, [-, ACME, -, -, -, ADTS2, ADTS1, ADTS0 ]); -register!(0x7A, ADCSRA, [ADEN, ADSC, ADATE, ADIF, ADIE, ADPS2, ADPS1, ADPS0 ]); -register!(0x79, ADCH ); -register!(0x78, ADCL ); -register!(0x70, TIMSK2, [-, -, -, -, -, OCIE2B, OCIE2A, TOIE2 ]); -register!(0x6F, TIMSK1, [-, -, ICIE1, -, -, OCIE1B, OCIE1A, TOIE1 ]); -register!(0x6E, TIMSK0, [-, -, -, -, -, OCIE0B, OCIE0A, TOIE0 ]); -register!(0x6D, PCMSK2, [PCINT23, PCINT22, PCINT21, PCINT20, PCINT19, PCINT18, PCINT17, PCINT16]); -register!(0x6C, PCMSK1, [-, PCINT14, PCINT13, PCINT12, PCINT11, PCINT10, PCINT9, PCINT8 ]); -register!(0x6B, PCMSK0, [PCINT7, PCINT6, PCINT5, PCINT4, PCINT3, PCINT2, PCINT1, PCINT0 ]); -register!(0x69, EICRA, [-, -, -, -, ISC11, ISC10, ISC01, ISC00 ]); -register!(0x68, PCICR, [-, -, -, -, -, PCIE2, PCIE1, PCIE0 ]); -register!(0x66, OSCCAL ); -register!(0x64, PRR, [PRTWI, PRTIM2, PRTIM0, -, PRTIM1, PRSPI, PRUSART0,PRADC ]); -register!(0x61, CLKPR, [CLKPCE, -, -, -, CLKPS3, CLKPS2, CLKPS1, CLKPS0 ]); -register!(0x60, WDTCSR, [WDIF, WDIE, WDP3, WDCE, WDE, WDP2, WDP1, WDP0 ]); -register!(0x5F, SREG, [I, T, H, S, V, N, Z, C ]); -register!(0x5E, SPH, [-, -, -, -, -, SP10, SP9, SP8 ]); -register!(0x5D, SPL, [SP7, SP6, SP5, SP4, SP3, SP2, SP1, SP0 ]); -register!(0x57, SPMCSR, [SPMIE, RWWSB, SIGRD, RWWSRE, BLBSET, PGWRT, PGERS, SPMEN ]); -register!(0x55, MCUCR, [-, BODS, BODSE, PUD, -, -, IVSEL, IVCE ]); -register!(0x54, MCUSR, [-, -, -, -, WDRF, BORF, EXTRF, PORF ]); -register!(0x53, SMCR, [-, -, -, -, SM2, SM1, SM0, SE ]); -register!(0x50, ACSR, [ACD, ACBG, ACO, ACI, ACIE, ACIC, ACIS1, ACIS0 ]); -register!(0x4E, SPDR ); -register!(0x4D, SPSR, [SPIF, WCOL, -, -, -, -, -, SPI2X ]); -register!(0x4C, SPCR, [SPIE, SPE, DORD, MSTR, CPOL, CPHA, SPR1, SPR0 ]); -register!(0x4B, GPIOR2 ); -register!(0x4A, GPIOR1 ); -register!(0x48, OCR0B ); -register!(0x47, OCR0A ); -register!(0x46, TCNT0 ); -register!(0x45, TCCR0B, [FOC0A, FOC0B, -, -, WGM02, CS02, CS01, CS00 ]); -register!(0x44, TCCR0A, [COM0A1, COM0A0, COM0B1, COM0B0, -, -, WGM01, WGM00 ]); -register!(0x43, GTCCR, [TSM, -, -, -, -, -, PSRASY, PSRSYNC]); -register!(0x42, EEARH ); -register!(0x41, EEARL ); -register!(0x40, EEDR ); -register!(0x3F, EECR, [-, -, EEPM1, EEPM0, EERIE, EEMPE, EEPE, EERE ]); -register!(0x3E, GPIOR0 ); -register!(0x3D, EIMSK, [-, -, -, -, -, -, INT1, INT0 ]); -register!(0x3C, EIFR, [-, -, -, -, -, -, INTF1, INTF0 ]); -register!(0x3B, PCIFR, [-, -, -, -, -, PCIF2, PCIF1, PCIF0 ]); -register!(0x37, TIFR2, [-, -, -, -, -, OCF2B, OCF2A, TOV2 ]); -register!(0x36, TIFR1, [-, -, ICF1, -, -, OCF1B, OCF1A, TOV1 ]); -register!(0x35, TIFR0, [-, -, -, -, -, OCF0B, OCF0A, TOV0 ]); -register!(0x2B, PORTD, [PORTD7, PORTD6, PORTD5, PORTD4, PORTD3, PORTD2, PORTD1, PORTD0 ]); -register!(0x2A, DDRD, [DDD7, DDD6, DDD5, DDD4, DDD3, DDD2, DDD1, DDD0 ]); -register!(0x29, PIND, [PIND7, PIND6, PIND5, PIND4, PIND3, PIND2, PIND1, PIND0 ]); -register!(0x28, PORTC, [-, PORTC6, PORTC5, PORTC4, PORTC3, PORTC2, PORTC1, PORTC0 ]); -register!(0x27, DDRC, [-, DDC6, DDC5, DDC4, DDC3, DDC2, DDC1, DDC0 ]); -register!(0x26, PINC, [-, PINC6, PINC5, PINC4, PINC3, PINC2, PINC1, PINC0 ]); -register!(0x25, PORTB, [PORTB7, PORTB6, PORTB5, PORTB4, PORTB3, PORTB2, PORTB1, PORTB0 ]); -register!(0x24, DDRB, [DDB7, DDB6, DDB5, DDB4, DDB3, DDB2, DDB1, DDB0 ]); -register!(0x23, PINB, [PINB7, PINB6, PINB5, PINB4, PINB3, PINB2, PINB1, PINB0 ]); - -// 16-bit register pairs -pub const ADC: *mut u16 = ADCL as *mut u16; -pub const EEAR: *mut u16 = EEARL as *mut u16; -pub const ICR1: *mut u16 = ICR1L as *mut u16; -pub const OCR1A: *mut u16 = OCR1AL as *mut u16; -pub const OCR1B: *mut u16 = OCR1BL as *mut u16; -pub const OSCCA: *mut u16 = OSCCAL as *mut u16; -pub const SP: *mut u16 = SPL as *mut u16; -pub const TCNT1: *mut u16 = TCNT1L as *mut u16; -pub const UBRR0: *mut u16 = UBRR0L as *mut u16; - -// Aliases -pub const UDORD0: u8 = UCSZ01; -pub const UCPHA0: u8 = UCSZ00; diff --git a/src/modules/mod.rs b/src/modules/mod.rs new file mode 100644 index 0000000..371c7ac --- /dev/null +++ b/src/modules/mod.rs @@ -0,0 +1,10 @@ +//! Modules that can be implemented for specific cores. + +pub use self::spi::HardwareSpi; +pub use self::timer::{Timer8, Timer8Setup, Timer16, Timer16Setup}; +pub use self::usart::HardwareUsart; + +mod spi; +mod timer; +mod usart; + diff --git a/src/modules/spi/clock.rs b/src/modules/spi/clock.rs new file mode 100644 index 0000000..92fe90c --- /dev/null +++ b/src/modules/spi/clock.rs @@ -0,0 +1,60 @@ +use config; + +/// A clock mask. +/// +/// The format looks like this +/// +/// ``` +/// 0b00000<1><0><2x> +/// ``` +/// +/// Where +/// +/// * `1` is the value of the `SPR1` bit +/// * `0` is the value of the `SPR0` bit +/// * `2x` indicates if double speed mode is enabled +#[derive(Copy, Clone)] +pub struct ClockMask(pub u8); + +impl ClockMask { + /// Gets the clock mask for a specific baute rate. + pub fn with_clock(spi_clock: u32) -> ClockMask { + let mut divider_bits = if spi_clock >= config::CPU_FREQUENCY_HZ / 2 { + 0 + } else if spi_clock >= config::CPU_FREQUENCY_HZ / 4 { + 1 + } else if spi_clock >= config::CPU_FREQUENCY_HZ / 8 { + 2 + } else if spi_clock >= config::CPU_FREQUENCY_HZ / 16 { + 3 + } else if spi_clock >= config::CPU_FREQUENCY_HZ / 32 { + 4 + } else if spi_clock >= config::CPU_FREQUENCY_HZ / 64 { + 5 + } else { + 6 + }; + + // Invert the SPI2X bit + divider_bits ^= 0x1; + + // Compensate for the duplicate F_osc/64 + if divider_bits == 6 { + divider_bits = 7; + } + ClockMask(divider_bits) + } + + pub fn control_register_mask(self) -> u8 { + // SPR1 and SPR0 + // These both form bits 1 and 0 of the control register. + (self.0 & 0b110) >> 1 + } + + pub fn status_register_mask(self) -> u8 { + // SPI2x + // This forms bit 0 of the status register. + self.0 & 0b1 + } +} + diff --git a/src/modules/spi/mod.rs b/src/modules/spi/mod.rs new file mode 100644 index 0000000..73dac79 --- /dev/null +++ b/src/modules/spi/mod.rs @@ -0,0 +1,151 @@ +mod clock; + + +// FIXME: Start using this module or delete!!! +#[allow(dead_code)] mod settings; + +use {Register, Pin}; + +/// An SPI module. +/// +/// Information at +/// http://maxembedded.com/2013/11/the-spi-of-the-avr/ +pub trait HardwareSpi { + type MasterInSlaveOut: Pin; + type MasterOutSlaveIn: Pin; + type Clock: Pin; + type SlaveSelect: Pin; + + /// The SPI control register. + type ControlRegister: Register; + /// The SPI status register. + type StatusRegister: Register; + /// The SPI data register. + type DataRegister: Register; + + /// Sets up the SPI as a master. + fn setup_master(clock: u32) { + // Setup DDR registers. + Self::MasterInSlaveOut::set_input(); + Self::MasterOutSlaveIn::set_output(); + Self::Clock::set_output(); + Self::SlaveSelect::set_input(); + + Self::set_master(); + Self::enable_interrupt(); + Self::setup_common(clock) + } + + /// Sets up the SPI as a slave. + fn setup_slave(clock: u32) { + // Setup DDR registers. + Self::MasterInSlaveOut::set_output(); + Self::MasterOutSlaveIn::set_input(); + Self::Clock::set_input(); + Self::SlaveSelect::set_input(); + + Self::set_slave(); + Self::setup_common(clock) + } + + fn setup_common(clock: u32) { + Self::set_clock(clock); + Self::enable() + } + + /// Sets the clock speed. + fn set_clock(clock: u32) { + let mask = clock::ClockMask::with_clock(clock); + Self::ControlRegister::set_mask_raw(mask.control_register_mask()); + Self::StatusRegister::set_mask_raw(mask.status_register_mask()); + } + + /// Enables interrupts for the spi module. + #[inline(always)] + fn enable_interrupt() { + Self::ControlRegister::set_mask_raw(settings::control_register::INTERRUPT_ENABLE); + } + + /// Disables interrupts for the spi module. + #[inline(always)] + fn disable_interrupt() { + Self::ControlRegister::unset_mask_raw(settings::control_register::INTERRUPT_ENABLE); + } + + /// Enables the SPI. + #[inline(always)] + fn enable() { + Self::ControlRegister::set_mask_raw(settings::control_register::ENABLE); + } + + /// Disables the SPI. + #[inline(always)] + fn disable() { + Self::ControlRegister::unset_mask_raw(settings::control_register::ENABLE); + } + + /// Enables least-significant-bit first. + #[inline(always)] + fn set_lsb() { + Self::ControlRegister::set_mask_raw(settings::control_register::DATA_ORDER_LSB); + } + + /// Enables most-significant-bit first. + #[inline(always)] + fn set_msb() { + Self::ControlRegister::unset_mask_raw(settings::control_register::DATA_ORDER_LSB); + } + + /// Enables master mode. + #[inline(always)] + fn set_master() { + Self::ControlRegister::set_mask_raw(settings::control_register::MASTER); + } + + /// Enables slave mode. + #[inline(always)] + fn set_slave() { + Self::ControlRegister::unset_mask_raw(settings::control_register::MASTER); + } + + /// Enables double speed mode. + #[inline(always)] + fn enable_double_speed() { + Self::StatusRegister::set_mask_raw(settings::status_register::SPI2X); + } + + /// Disables double speed mode. + #[inline(always)] + fn disable_double_speed() { + Self::StatusRegister::unset_mask_raw(settings::status_register::SPI2X); + } + + /// Checks if there is a write collision. + #[inline(always)] + fn is_write_collision() -> bool { + Self::StatusRegister::is_mask_set_raw(settings::status_register::WCOL) + } + + /// Sends a byte through the serial. + #[inline(always)] + fn send_byte(byte: u8) { + Self::DataRegister::write(byte); + Self::StatusRegister::wait_until_mask_set_raw(settings::status_register::SPIF); + } + + /// Reads a byte from the serial. + #[inline(always)] + fn receive_byte() -> u8 { + Self::StatusRegister::wait_until_mask_set_raw(settings::status_register::SPIF); + Self::DataRegister::read() + } + + /// Sends and receives a byte. + #[inline(always)] + fn send_receive(byte: u8) -> u8 { + Self::DataRegister::write(byte); + Self::StatusRegister::wait_until_mask_set_raw(settings::status_register::SPIF); + Self::DataRegister::read() + } +} + diff --git a/src/modules/spi/settings.rs b/src/modules/spi/settings.rs new file mode 100644 index 0000000..45edb9d --- /dev/null +++ b/src/modules/spi/settings.rs @@ -0,0 +1,129 @@ +use super::clock::ClockMask; + +#[derive(Copy, Clone)] +pub enum BitOrder { + /// The least significant bit is sent first. + LeastSignificantBit, + /// The most significant bit is sent first. + MostSignificantBit, +} + +#[derive(Copy, Clone)] +pub enum ClockPhase { + LeadingEdge, + TrailingEdge, +} + +/// SPI settings. +#[derive(Copy, Clone)] +pub struct Settings { + /// Whether the SPI module is enabled. + enabled: bool, + /// Whether to be configured as a master or slave. + master: bool, + /// The clock speed. + clock: u32, + /// The bit ordering. + bit_order: BitOrder, + /// The clock phase. + clock_phase: ClockPhase, + /// Whether interrupts should be enabled. + enable_interrupts: bool, +} + +impl Settings { + /// Gets the default settings for the master. + pub fn master() -> Self { + Settings { + master: true, + ..Default::default() + } + } + + /// Gets the default settings for the slave. + pub fn slave() -> Self { + Settings { + master: false, + ..Default::default() + } + } + + pub fn control_register_bits(self) -> u8 { + let mut bits = 0; + + bits |= self.clock().control_register_mask(); + + if self.enable_interrupts { + bits |= control_register::INTERRUPT_ENABLE + } + if self.enabled { + bits |= control_register::ENABLE + } + if let ClockPhase::LeadingEdge = self.clock_phase { + bits |= control_register::CPHA; + } + + if let BitOrder::LeastSignificantBit = self.bit_order { + bits |= control_register::DATA_ORDER_LSB; + } + bits + } + + pub fn status_register_bits(self) -> u8 { + let mut bits = 0; + + bits |= self.clock().status_register_mask(); + bits + } + + fn clock(self) -> ClockMask { + ClockMask::with_clock(self.clock) + } +} + +impl Default for Settings { + fn default() -> Settings { + Settings { + enabled: true, + master: true, + // same as Arduino default in `SPI.h`. + clock: 4_000_000, + bit_order: BitOrder::MostSignificantBit, + clock_phase: ClockPhase::LeadingEdge, + enable_interrupts: false, + } + } +} + +/// Constants for the control register. +#[allow(dead_code)] +pub mod control_register { + /// Set if interrupts are enabled. + pub const INTERRUPT_ENABLE: u8 = 1<<7; + /// Set if the SPI module is enabled. + pub const ENABLE: u8 = 1<<6; + /// Set if data is sent in LSB format. + pub const DATA_ORDER_LSB: u8 = 1<<5; + /// Set if we are configuring a master. + pub const MASTER: u8 = 1<<4; + /// Clock polarity. + pub const CPOL: u8 = 1<<3; + /// Clock phase. + pub const CPHA: u8 = 1<<2; + /// Clock rate select 1. + pub const SPR1: u8 = 1<<1; + /// Clock rate select 2. + pub const SPR0: u8 = 1<<0; +} + +/// Constants for the status register. +#[allow(dead_code)] +pub mod status_register { + /// SPI interrupt flag. + pub const SPIF: u8 = 1<<7; + /// Write collision flag. + pub const WCOL: u8 = 1<<6; + /// SPI double speed mode. + pub const SPI2X: u8 = 1<<0; +} + diff --git a/src/modules/timer/mod.rs b/src/modules/timer/mod.rs new file mode 100644 index 0000000..11c28c6 --- /dev/null +++ b/src/modules/timer/mod.rs @@ -0,0 +1,6 @@ +pub use self::timer8::{Timer8, Timer8Setup}; +pub use self::timer16::{Timer16, Timer16Setup}; + +mod timer8; +mod timer16; + diff --git a/src/modules/timer/timer16.rs b/src/modules/timer/timer16.rs new file mode 100644 index 0000000..961f75b --- /dev/null +++ b/src/modules/timer/timer16.rs @@ -0,0 +1,211 @@ +use {RegisterBits, Register}; +use core::marker; + +/// A 16-bit timer. +pub trait Timer16 : Sized { + /// The first compare register. + /// For example, OCR0A. + type CompareA: Register; + + /// The second compare register. + /// For example, OCR0B. + type CompareB: Register; + + /// The counter register. + /// + /// For example, TCNT0. + type Counter: Register; + + /// The first control register. + /// + /// For example, TCCR0A. + type ControlA: Register; + + /// The second control register. + /// + /// For example, TCCR0B. + type ControlB: Register; + + /// The third control register. + /// + /// For example, TCCR0C. + type ControlC: Register; + + /// The interrupt mask register. + /// + /// For example, TIMSK0. + type InterruptMask: Register; + + /// The interrupt flag register. + /// + /// For example, TIFR0. + type InterruptFlag: Register; + + const CS0: RegisterBits; + const CS1: RegisterBits; + const CS2: RegisterBits; + + const WGM0: RegisterBits; + const WGM1: RegisterBits; + const WGM2: RegisterBits; + const WGM3: RegisterBits; + + const OCIEA: RegisterBits; + + fn setup() -> Timer16Setup { Timer16Setup::new() } +} + +pub enum ClockSource { + None, + Prescale1, + Prescale8, + Prescale64, + Prescale256, + Prescale1024, + ExternalFalling, + ExternalRising, +} + +impl ClockSource { + fn bits(&self) -> RegisterBits { + use self::ClockSource::*; + + match *self { + None => RegisterBits::zero() | RegisterBits::zero() | RegisterBits::zero(), + Prescale1 => RegisterBits::zero() | RegisterBits::zero() | T::CS0, + Prescale8 => RegisterBits::zero() | T::CS1 | RegisterBits::zero(), + Prescale64 => RegisterBits::zero() | T::CS1 | T::CS0, + Prescale256 => T::CS2 | RegisterBits::zero() | RegisterBits::zero(), + Prescale1024 => T::CS2 | RegisterBits::zero() | T::CS0, + ExternalFalling => T::CS2 | T::CS1 | RegisterBits::zero(), + ExternalRising => T::CS2 | T::CS1 | T::CS0, + } + } + + #[inline] + fn mask() -> RegisterBits { + !(T::CS2 | T::CS1 | T::CS0) + } +} + +pub enum WaveformGenerationMode { + Normal, + PwmPhaseCorrect8Bit, + PwmPhaseCorrect9Bit, + PwmPhaseCorrect10Bit, + ClearOnTimerMatchOutputCompare, + FastPwm8Bit, + FastPwm9Bit, + FastPwm10Bit, + PwmPhaseAndFrequencyCorrectInputCapture, + PwmPhaseAndFrequencyCorrectOutputCompare, + PwmPhaseCorrectInputCapture, + PwmPhaseCorrectOutputCompare, + ClearOnTimerMatchInputCapture, + FastPwmInputCapture, + FastPwmOutputCompare, +} + +impl WaveformGenerationMode { + /// Returns bits for TCCR1A, TCCR1B + #[inline] + fn bits(&self) -> (RegisterBits, RegisterBits) { + use self::WaveformGenerationMode::*; + use RegisterBits as B; + + // It makes more sense to return bytes (A,B), but the manual + // lists the table as (B,A). We match the manual here for + // inspection purposes and flip the values for sanity + // purposes. + let (b, a) = match *self { + Normal => (B::zero() | B::zero(), B::zero() | B::zero()), + PwmPhaseCorrect8Bit => (B::zero() | B::zero(), B::zero() | T::WGM0), + PwmPhaseCorrect9Bit => (B::zero() | B::zero(), T::WGM1 | B::zero()), + PwmPhaseCorrect10Bit => (B::zero() | B::zero(), T::WGM1 | T::WGM0), + ClearOnTimerMatchOutputCompare => (B::zero() | T::WGM2, B::zero() | B::zero()), + FastPwm8Bit => (B::zero() | T::WGM2, B::zero() | T::WGM0), + FastPwm9Bit => (B::zero() | T::WGM2, T::WGM1 | B::zero()), + FastPwm10Bit => (B::zero() | T::WGM2, T::WGM1 | T::WGM0), + PwmPhaseAndFrequencyCorrectInputCapture => (T::WGM3 | B::zero(), B::zero() | B::zero()), + PwmPhaseAndFrequencyCorrectOutputCompare => (T::WGM3 | B::zero(), B::zero() | T::WGM0), + PwmPhaseCorrectInputCapture => (T::WGM3 | B::zero(), T::WGM1 | B::zero()), + PwmPhaseCorrectOutputCompare => (T::WGM3 | B::zero(), T::WGM1 | T::WGM0), + ClearOnTimerMatchInputCapture => (T::WGM3 | T::WGM2, B::zero() | B::zero()), + // Reserved => (T::WGM3 | T::WGM2, B::zero() | T::WGM0), + FastPwmInputCapture => (T::WGM3 | T::WGM2, T::WGM1 | B::zero()), + FastPwmOutputCompare => (T::WGM3 | T::WGM2, T::WGM1 | T::WGM0), + }; + + (a, b) + } + + #[inline] + fn mask() -> (RegisterBits, RegisterBits) { + (!(T::WGM0 | T::WGM1), !(T::WGM2 | T::WGM3)) + } +} + +pub struct Timer16Setup { + a: RegisterBits, + b: RegisterBits, + c: RegisterBits, + output_compare_1: Option, + _phantom: marker::PhantomData, +} + +impl Timer16Setup { + #[inline] + fn new() -> Self { + Timer16Setup { + a: RegisterBits::zero(), + b: RegisterBits::zero(), + c: RegisterBits::zero(), + output_compare_1: None, + _phantom: marker::PhantomData, + } + } + + #[inline] + pub fn clock_source(mut self, source: ClockSource) -> Self { + self.b &= ClockSource::mask::(); + self.b |= source.bits::(); + self + } + + #[inline] + pub fn waveform_generation_mode(mut self, mode: WaveformGenerationMode) -> Self { + let (a, b) = WaveformGenerationMode::mask::(); + self.a &= a; + self.b &= b; + + let (a, b) = mode.bits::(); + self.a |= a; + self.b |= b; + + self + } + + #[inline] + pub fn output_compare_1(mut self, value: Option) -> Self { + self.output_compare_1 = value; + self + } + + #[inline] + pub fn configure(self) { + T::ControlA::write(self.a); + T::ControlB::write(self.b); + T::ControlC::write(self.c); + + // Reset counter to zero + T::Counter::write(0u16); + + if let Some(v) = self.output_compare_1 { + // Set the match + T::CompareA::write(v); + + // Enable compare interrupt + T::InterruptMask::set(T::OCIEA); + } + } +} diff --git a/src/modules/timer/timer8.rs b/src/modules/timer/timer8.rs new file mode 100644 index 0000000..a02f6d5 --- /dev/null +++ b/src/modules/timer/timer8.rs @@ -0,0 +1,189 @@ +use {RegisterBits, Register}; +use core::marker; + +/// A 8-bit timer. +pub trait Timer8 : Sized { + /// The first compare register. + /// For example, OCR0A. + type CompareA: Register; + + /// The second compare register. + /// For example, OCR0B. + type CompareB: Register; + + /// The counter register. + /// + /// For example, TCNT0. + type Counter: Register; + + /// The first control register. + /// + /// For example, TCCR0A. + type ControlA: Register; + + /// The second control register. + /// + /// For example, TCCR0B. + type ControlB: Register; + + /// The interrupt mask register. + /// + /// For example, TIMSK0. + type InterruptMask: Register; + + /// The interrupt flag register. + /// + /// For example, TIFR0. + type InterruptFlag: Register; + + /// Bit 0 of the clock select mask. + const CS0: RegisterBits; + /// Bit 1 of the clock select mask. + const CS1: RegisterBits; + /// Bit 2 of the clock select mask. + const CS2: RegisterBits; + + /// Bit 0 of the waveform generation mode mask. + const WGM0: RegisterBits; + /// Bit 1 of the waveform generation mode mask. + const WGM1: RegisterBits; + /// Bit 2 of the waveform generation mode mask. + const WGM2: RegisterBits; + + /// Output compare interrupt enable flag. + const OCIEA: RegisterBits; +} + +pub enum ClockSource { + None, + Prescale1, + Prescale8, + Prescale64, + Prescale256, + Prescale1024, + ExternalFalling, + ExternalRising, +} + +impl ClockSource { + fn bits(&self) -> RegisterBits { + use self::ClockSource::*; + + match *self { + None => RegisterBits::zero() | RegisterBits::zero() | RegisterBits::zero(), + Prescale1 => RegisterBits::zero() | RegisterBits::zero() | T::CS0, + Prescale8 => RegisterBits::zero() | T::CS1 | RegisterBits::zero(), + Prescale64 => RegisterBits::zero() | T::CS1 | T::CS0, + Prescale256 => T::CS2 | RegisterBits::zero() | RegisterBits::zero(), + Prescale1024 => T::CS2 | RegisterBits::zero() | T::CS0, + ExternalFalling => T::CS2 | T::CS1 | RegisterBits::zero(), + ExternalRising => T::CS2 | T::CS1 | T::CS0, + } + } + + #[inline] + fn mask() -> RegisterBits { + !(T::CS2 | T::CS1 | T::CS0) + } +} + +pub enum WaveformGenerationMode { + Normal, + PwmPhaseCorrect, + ClearOnTimerMatchOutputCompare, + FastPwm , + PwmPhaseCorrectOutputCompare, + FastPwmOutputCompare, +} + +impl WaveformGenerationMode { + /// Returns bits for TCCR0A, TCCR0B + #[inline] + fn bits(&self) -> (RegisterBits, RegisterBits) { + use self::WaveformGenerationMode::*; + + // It makes more sense to return bytes (A,B), but the manual + // lists the table as (B,A). We match the manual here for + // inspection purposes and flip the values for sanity + // purposes. + let (b, a) = match *self { + Normal => (RegisterBits::zero(), RegisterBits::zero() | RegisterBits::zero()), + PwmPhaseCorrect => (RegisterBits::zero(), RegisterBits::zero() | T::WGM0), + ClearOnTimerMatchOutputCompare => (RegisterBits::zero(), T::WGM1 | RegisterBits::zero()), + FastPwm => (RegisterBits::zero(), T::WGM1 | T::WGM0), + // Reserved => (T::WGM2, RegisterBits::zero() | RegisterBits::zero()), + PwmPhaseCorrectOutputCompare => (T::WGM2, RegisterBits::zero() | T::WGM0), + // Reserved => (T::WGM2, T::WGM1 | RegisterBits::zero())), + FastPwmOutputCompare => (T::WGM2, T::WGM1 | T::WGM0), + }; + + (a, b) + } + + #[inline] + fn mask() -> (RegisterBits, RegisterBits) { + (!(T::WGM0 | T::WGM1), !(T::WGM2)) + } +} + +pub struct Timer8Setup { + a: RegisterBits, + b: RegisterBits, + output_compare_1: Option, + _phantom: marker::PhantomData, +} + +impl Timer8Setup { + #[inline] + pub fn new() -> Self { + Timer8Setup { + a: RegisterBits::zero(), + b: RegisterBits::zero(), + output_compare_1: None, + _phantom: marker::PhantomData, + } + } + + #[inline] + pub fn clock_source(mut self, source: ClockSource) -> Self { + self.b &= ClockSource::mask::(); + self.b |= source.bits::(); + self + } + + #[inline] + pub fn waveform_generation_mode(mut self, mode: WaveformGenerationMode) -> Self { + let (a, b) = WaveformGenerationMode::mask::(); + self.a &= a; + self.b &= b; + + let (a, b) = mode.bits::(); + self.a |= a; + self.b |= b; + + self + } + + #[inline] + pub fn output_compare_1(mut self, value: Option) -> Self { + self.output_compare_1 = value; + self + } + + #[inline] + pub fn configure(self) { + T::ControlA::write(self.a); + T::ControlB::write(self.b); + + // Reset counter to zero + T::Counter::write(0); + + if let Some(v) = self.output_compare_1 { + // Set the match + T::CompareA::write(v); + + // Enable compare interrupt + T::InterruptMask::set(T::OCIEA); + } + } +} diff --git a/src/modules/usart.rs b/src/modules/usart.rs new file mode 100644 index 0000000..7e8bc77 --- /dev/null +++ b/src/modules/usart.rs @@ -0,0 +1,14 @@ +use Register; + +pub trait HardwareUsart { + /// The USART data register. + type DataRegister: Register; + /// USART control and status register A. + type ControlRegisterA: Register; + /// USART control and status register B. + type ControlRegisterB: Register; + /// USART control and status register C. + type ControlRegisterC: Register; + /// USART baud rate register. + type BaudRateRegister: Register; +} diff --git a/src/pin.rs b/src/pin.rs new file mode 100644 index 0000000..806ba99 --- /dev/null +++ b/src/pin.rs @@ -0,0 +1,89 @@ +use Register; + +/// Represents whether a pin is an input or an output. +pub enum DataDirection { + /// The pin is exclusively used for reading signals. + Input, + /// The pin is exclusively used for sending signals. + Output, +} + +/// An IO pin. +pub trait Pin { + /// The associated data direction register. + type DDR: Register; + /// The associated port register. + type PORT: Register; + + /// + /// Reads from the register will read input bits. + /// Writes to the register will toggle bits. + type PIN: Register; + /// The mask of the pin used for accessing registers. + const MASK: u8; + + /// Sets the data direction of the pin. + #[inline(always)] + fn set_direction(direction: DataDirection) { + match direction { + DataDirection::Input => Self::set_input(), + DataDirection::Output => Self::set_output(), + } + } + + /// Sets the pin up as an input. + #[inline(always)] + fn set_input() { + Self::DDR::unset_mask_raw(Self::MASK); + } + + /// Sets the pin up as an output. + #[inline(always)] + fn set_output() { + Self::DDR::set_mask_raw(Self::MASK); + } + + /// Set the pin to high. + /// + /// The pin must be configured as an output. + #[inline(always)] + fn set_high() { + Self::PORT::set_mask_raw(Self::MASK); + } + + /// Set the pin to low. + /// + /// The pin must be configured as an output. + #[inline(always)] + fn set_low() { + Self::PORT::unset_mask_raw(Self::MASK); + } + + /// Toggles the pin. + /// + /// The pin must be configured as an output. + #[inline(always)] + fn toggle() { + // FIXME: We can optimise this on post-2006 AVRs. + // http://www.avrfreaks.net/forum/toggle-state-output-pin + // set(Self::PIN, Self::MASK); + Self::PORT::toggle_raw(Self::MASK); + } + + /// Check if the pin is currently high. + /// + /// The pin must be configured as an input. + #[inline(always)] + fn is_high() -> bool { + Self::PIN::is_mask_set_raw(Self::MASK) + } + + /// Checks if the pin is currently low. + /// + /// The pin must be configured as an input. + #[inline(always)] + fn is_low() -> bool { + Self::PIN::is_clear_raw(Self::MASK) + } +} + diff --git a/src/prelude.rs b/src/prelude.rs index 8592448..a5e529e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,28 +1,4 @@ -use core::prelude::v1::*; -use core::marker::PhantomData; +//! Re-exports commonly-used APIs that can be imported at once. -pub use io::{PORT_B, PORT_C, PORT_D}; +pub use interrupt::without_interrupts; -pub struct DisableInterrupts(PhantomData<()>); - -impl DisableInterrupts { - #[inline] - pub fn new() -> DisableInterrupts { - unsafe { asm!("CLI") } - DisableInterrupts(PhantomData) - } -} - -impl Drop for DisableInterrupts { - #[inline] - fn drop(&mut self) { - unsafe { asm!("SEI") } - } -} - -pub fn without_interrupts(f: F) -> T - where F: FnOnce() -> T -{ - let _disabled = DisableInterrupts::new(); - f() -} diff --git a/src/register.rs b/src/register.rs new file mode 100644 index 0000000..2a13f98 --- /dev/null +++ b/src/register.rs @@ -0,0 +1,227 @@ +use core::{cmp, convert, marker, ops}; + +/// A value that a register can store. +/// +/// All registers are either `u8` or `u16`. +pub trait RegisterValue : Copy + Clone + + ops::BitAnd + + ops::BitAndAssign + + ops::BitOr + + ops::BitOrAssign + + ops::BitXor + + ops::BitXorAssign + + ops::Not + + cmp::PartialEq + cmp::Eq + + cmp::PartialOrd + cmp::Ord + + convert::From { +} + +/// A register. +pub trait Register : Sized { + /// The type that can represent the value of the register. + type T: RegisterValue; + /// The type representing a set of bits that may be manipulated + /// within the register. + type RegisterBits = RegisterBits; + + /// The address of the register. + const ADDRESS: *mut Self::T; + + /// Writes a value to the register. + #[inline(always)] + fn write(value: V) where V: Into { + unsafe { + *Self::ADDRESS = value.into(); + } + } + + /// Reads the value of the register. + #[inline(always)] + fn read() -> Self::T { + unsafe { *Self::ADDRESS } + } + + /// Sets a set of bits to `1` in the register. + fn set(bits: RegisterBits) { + Self::set_mask_raw(bits.mask); + } + + /// Sets a bitmask in a register. + /// + /// This is equivalent to `r |= mask`. + #[inline(always)] + fn set_mask_raw(mask: Self::T) { + unsafe { + *Self::ADDRESS |= mask; + } + } + + /// Unsets a set of bits in the register. + /// + /// All of the bits will be set to `0`. + fn unset(bits: RegisterBits) { + Self::unset_mask_raw(bits.mask); + } + + /// Clears a bitmask from a register. + /// + /// This is equivalent to `r &= !mask`. + #[inline(always)] + fn unset_mask_raw(mask: Self::T) { + unsafe { + *Self::ADDRESS &= !mask; + } + } + + /// Toggles a set of bits within the register. + /// + /// All specified bits which were previously `0` will become + /// `1`, and all specified bits that were previous `1` will + /// become `0`. + fn toggle(mask: RegisterBits) { + Self::toggle_raw(mask.mask); + } + + /// Toggles a mask in the register. + /// + /// This is equivalent to `r ^= mask`. + #[inline(always)] + fn toggle_raw(mask: Self::T) { + unsafe { + *Self::ADDRESS ^= mask; + } + } + + /// Checks if a set of bits are enabled. + /// + /// All specifed bits must be set for this function + /// to return `true`. + fn is_set(bits: RegisterBits) -> bool { + Self::is_mask_set_raw(bits.mask) + } + + /// Checks if a mask is set in the register. + /// + /// This is equivalent to `(r & mask) == mask`. + #[inline(always)] + fn is_mask_set_raw(mask: Self::T) -> bool { + unsafe { + (*Self::ADDRESS & mask) == mask + } + } + + /// Checks if a set of bits are not set. + /// + /// All specified bits must be `0` for this + /// function to return `true`. + fn is_clear(mask: RegisterBits) -> bool { + Self::is_clear_raw(mask.mask) + } + + /// Checks if a mask is clear in the register. + /// + /// This is equivalent to `(r & mask) == 0`. + #[inline(always)] + fn is_clear_raw(mask: Self::T) -> bool { + unsafe { + (*Self::ADDRESS & mask) == Self::T::from(0) + } + } + + /// Waits until a set of bits are set in the register. + /// + /// This function will block until all bits that are set in + /// the mask are also set in the register. + fn wait_until_set(bits: RegisterBits) { + Self::wait_until_mask_set_raw(bits.mask); + } + + /// Waits until a bit mask is set in the register. + /// + /// This function will block until all bits that are set in + /// the mask are also set in the register. + #[inline(always)] + fn wait_until_mask_set_raw(mask: Self::T) { + wait_until(|| Self::is_mask_set_raw(mask)) + } +} + +/// Represents a set of bits within a specific register. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RegisterBits { + /// The raw bitmask. + mask: R::T, + _phantom: marker::PhantomData, +} + +impl RegisterBits where R: Register { + /// Creates a new register mask. + pub const fn new(mask: R::T) -> Self { + RegisterBits { mask, _phantom: marker::PhantomData } + } + + pub fn zero() -> Self { + RegisterBits::new(0u8.into()) + } +} + +impl ops::BitOr for RegisterBits where R: Register +{ + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + RegisterBits::new(self.mask | rhs.mask) + } +} + +impl ops::BitOrAssign for RegisterBits where R: Register { + fn bitor_assign(&mut self, rhs: Self) { + self.mask |= rhs.mask; + } +} + +impl ops::BitAnd for RegisterBits where R: Register +{ + type Output = Self; + + fn bitand(self, rhs: Self) -> Self { + RegisterBits::new(self.mask & rhs.mask) + } +} + +impl ops::BitAndAssign for RegisterBits where R: Register { + fn bitand_assign(&mut self, rhs: Self) { + self.mask &= rhs.mask; + } +} + +impl ops::Not for RegisterBits where R: Register { + type Output = Self; + + fn not(self) -> Self { + RegisterBits::new(!self.mask) + } +} + +impl Into for RegisterBits where R: Register { + fn into(self) -> u8 { self.mask } +} + +impl Into for RegisterBits where R: Register { + fn into(self) -> u16 { self.mask } +} + +impl RegisterValue for u8 { } +impl RegisterValue for u16 { } + +/// Waits until some condition is true of the register. +#[inline(always)] +fn wait_until(mut f: F) + where F: FnMut() -> bool { + loop { + if f() { + break; + } + } +} + diff --git a/src/std_stub.rs b/src/std_stub.rs new file mode 100644 index 0000000..53cd096 --- /dev/null +++ b/src/std_stub.rs @@ -0,0 +1,15 @@ +//! Stub methods that `libstd` normally defines. + +// These do not need to be in a module, but we group them here for clarity. +pub mod std { + #[lang = "eh_personality"] + #[no_mangle] + pub unsafe extern "C" fn rust_eh_personality(_state: (), _exception_object: *mut (), _context: *mut ()) -> () { + } + + #[panic_handler] + fn panic(_info: &::core::panic::PanicInfo) -> ! { + loop {} + } +} + diff --git a/src/timer0.rs b/src/timer0.rs deleted file mode 100644 index 5869b7c..0000000 --- a/src/timer0.rs +++ /dev/null @@ -1,139 +0,0 @@ -use core::prelude::v1::*; -use core::ptr::write_volatile; - -use super::*; - -pub enum ClockSource { - None, - Prescale1, - Prescale8, - Prescale64, - Prescale256, - Prescale1024, - ExternalFalling, - ExternalRising, -} - -impl ClockSource { - #[inline] - fn bits(&self) -> u8 { - use self::ClockSource::*; - - match *self { - None => 0 | 0 | 0, - Prescale1 => 0 | 0 | CS00, - Prescale8 => 0 | CS01 | 0, - Prescale64 => 0 | CS01 | CS00, - Prescale256 => CS02 | 0 | 0, - Prescale1024 => CS02 | 0 | CS00, - ExternalFalling => CS02 | CS01 | 0, - ExternalRising => CS02 | CS01 | CS00, - } - } - - #[inline] - fn mask() -> u8 { - !(CS02 | CS01 | CS00) - } -} - -pub enum WaveformGenerationMode { - Normal, - PwmPhaseCorrect, - ClearOnTimerMatchOutputCompare, - FastPwm , - PwmPhaseCorrectOutputCompare, - FastPwmOutputCompare, -} - -impl WaveformGenerationMode { - /// Returns bits for TCCR0A, TCCR0B - #[inline] - fn bits(&self) -> (u8, u8) { - use self::WaveformGenerationMode::*; - - // It makes more sense to return bytes (A,B), but the manual - // lists the table as (B,A). We match the manual here for - // inspection purposes and flip the values for sanity - // purposes. - let (b, a) = match *self { - Normal => ( 0, 0 | 0), - PwmPhaseCorrect => ( 0, 0 | WGM00), - ClearOnTimerMatchOutputCompare => ( 0, WGM01 | 0), - FastPwm => ( 0, WGM01 | WGM00), - // Reserved => (WGM02, 0 | 0), - PwmPhaseCorrectOutputCompare => (WGM02, 0 | WGM00), - // Reserved => (WGM02, WGM01 | 0), - FastPwmOutputCompare => (WGM02, WGM01 | WGM00), - }; - - (a, b) - } - - #[inline] - fn mask() -> (u8, u8) { - (!(WGM00 | WGM01), !(WGM02)) - } -} - -pub struct Timer { - a: u8, - b: u8, - output_compare_1: Option, -} - -impl Timer { - #[inline] - pub fn new() -> Self { - Timer { - a: 0, - b: 0, - output_compare_1: None, - } - } - - #[inline] - pub fn clock_source(mut self, source: ClockSource) -> Self { - self.b &= ClockSource::mask(); - self.b |= source.bits(); - self - } - - #[inline] - pub fn waveform_generation_mode(mut self, mode: WaveformGenerationMode) -> Self { - let (a, b) = WaveformGenerationMode::mask(); - self.a &= a; - self.b &= b; - - let (a, b) = mode.bits(); - self.a |= a; - self.b |= b; - - self - } - - #[inline] - pub fn output_compare_1(mut self, value: Option) -> Self { - self.output_compare_1 = value; - self - } - - #[inline] - pub fn configure(self) { - unsafe { - write_volatile(TCCR0A, self.a); - write_volatile(TCCR0B, self.b); - - // Reset counter to zero - write_volatile(TCNT0, 0); - - if let Some(v) = self.output_compare_1 { - // Set the match - write_volatile(OCR0A, v); - - // Enable compare interrupt - write_volatile(TIMSK0, OCIE0A); - } - } - } -} diff --git a/src/timer1.rs b/src/timer1.rs deleted file mode 100644 index 5c0cfad..0000000 --- a/src/timer1.rs +++ /dev/null @@ -1,159 +0,0 @@ -use core::prelude::v1::*; -use core::ptr::write_volatile; - -use super::*; - -pub enum ClockSource { - None, - Prescale1, - Prescale8, - Prescale64, - Prescale256, - Prescale1024, - ExternalFalling, - ExternalRising, -} - -impl ClockSource { - #[inline] - fn bits(&self) -> u8 { - use self::ClockSource::*; - - match *self { - None => 0 | 0 | 0, - Prescale1 => 0 | 0 | CS10, - Prescale8 => 0 | CS11 | 0, - Prescale64 => 0 | CS11 | CS10, - Prescale256 => CS12 | 0 | 0, - Prescale1024 => CS12 | 0 | CS10, - ExternalFalling => CS12 | CS11 | 0, - ExternalRising => CS12 | CS11 | CS10, - } - } - - #[inline] - fn mask() -> u8 { - !(CS12 | CS11 | CS10) - } -} - -pub enum WaveformGenerationMode { - Normal, - PwmPhaseCorrect8Bit, - PwmPhaseCorrect9Bit, - PwmPhaseCorrect10Bit, - ClearOnTimerMatchOutputCompare, - FastPwm8Bit, - FastPwm9Bit, - FastPwm10Bit, - PwmPhaseAndFrequencyCorrectInputCapture, - PwmPhaseAndFrequencyCorrectOutputCompare, - PwmPhaseCorrectInputCapture, - PwmPhaseCorrectOutputCompare, - ClearOnTimerMatchInputCapture, - FastPwmInputCapture, - FastPwmOutputCompare, -} - -impl WaveformGenerationMode { - /// Returns bits for TCCR1A, TCCR1B - #[inline] - fn bits(&self) -> (u8, u8) { - use self::WaveformGenerationMode::*; - - // It makes more sense to return bytes (A,B), but the manual - // lists the table as (B,A). We match the manual here for - // inspection purposes and flip the values for sanity - // purposes. - let (b, a) = match *self { - Normal => ( 0 | 0, 0 | 0), - PwmPhaseCorrect8Bit => ( 0 | 0, 0 | WGM10), - PwmPhaseCorrect9Bit => ( 0 | 0, WGM11 | 0), - PwmPhaseCorrect10Bit => ( 0 | 0, WGM11 | WGM10), - ClearOnTimerMatchOutputCompare => ( 0 | WGM12, 0 | 0), - FastPwm8Bit => ( 0 | WGM12, 0 | WGM10), - FastPwm9Bit => ( 0 | WGM12, WGM11 | 0), - FastPwm10Bit => ( 0 | WGM12, WGM11 | WGM10), - PwmPhaseAndFrequencyCorrectInputCapture => (WGM13 | 0, 0 | 0), - PwmPhaseAndFrequencyCorrectOutputCompare => (WGM13 | 0, 0 | WGM10), - PwmPhaseCorrectInputCapture => (WGM13 | 0, WGM11 | 0), - PwmPhaseCorrectOutputCompare => (WGM13 | 0, WGM11 | WGM10), - ClearOnTimerMatchInputCapture => (WGM13 | WGM12, 0 | 0), - // Reserved => (WGM13 | WGM12, 0 | WGM10), - FastPwmInputCapture => (WGM13 | WGM12, WGM11 | 0), - FastPwmOutputCompare => (WGM13 | WGM12, WGM11 | WGM10), - }; - - (a, b) - } - - #[inline] - fn mask() -> (u8, u8) { - (!(WGM10 | WGM11), !(WGM12 | WGM13)) - } -} - -pub struct Timer { - a: u8, - b: u8, - c: u8, - output_compare_1: Option, -} - -impl Timer { - #[inline] - pub fn new() -> Self { - Timer { - a: 0, - b: 0, - c: 0, - output_compare_1: None, - } - } - - #[inline] - pub fn clock_source(mut self, source: ClockSource) -> Self { - self.b &= ClockSource::mask(); - self.b |= source.bits(); - self - } - - #[inline] - pub fn waveform_generation_mode(mut self, mode: WaveformGenerationMode) -> Self { - let (a, b) = WaveformGenerationMode::mask(); - self.a &= a; - self.b &= b; - - let (a, b) = mode.bits(); - self.a |= a; - self.b |= b; - - self - } - - #[inline] - pub fn output_compare_1(mut self, value: Option) -> Self { - self.output_compare_1 = value; - self - } - - #[inline] - pub fn configure(self) { - unsafe { - write_volatile(TCCR1A, self.a); - write_volatile(TCCR1B, self.b); - write_volatile(TCCR1C, self.c); - - // Reset counter to zero - write_volatile(TCNT1, 0); - - if let Some(v) = self.output_compare_1 { - // Set the match - write_volatile(OCR1A, v); - - // Enable compare interrupt - write_volatile(TIMSK1, OCIE1A); - } - } - } -}