From 906b548dfaa2a4e8e0b7564036fc1f664a664391 Mon Sep 17 00:00:00 2001 From: Dylan McKay Date: Fri, 17 Nov 2017 17:18:35 +1300 Subject: [PATCH] Do more work --- Xargo.toml | 8 +++ build.rs | 47 +++++++++++++- build.sh | 1 + examples/spi.rs | 14 ++++ src/.gitignore | 2 + src/lib.rs | 5 +- src/register.rs | 74 ++++++++++++++++++++- src/spi/clock.rs | 60 ++++++++++++++++++ src/{spi.rs => spi/mod.rs} | 23 +++++-- src/spi/settings.rs | 127 +++++++++++++++++++++++++++++++++++++ src/timer0.rs | 12 ++-- 11 files changed, 358 insertions(+), 15 deletions(-) create mode 100644 Xargo.toml create mode 100644 examples/spi.rs create mode 100644 src/.gitignore create mode 100644 src/spi/clock.rs rename src/{spi.rs => spi/mod.rs} (88%) create mode 100644 src/spi/settings.rs diff --git a/Xargo.toml b/Xargo.toml new file mode 100644 index 0000000..72578c6 --- /dev/null +++ b/Xargo.toml @@ -0,0 +1,8 @@ +[dependencies.std] +features = ["panic_unwind"] + +[dependencies.test] +stage = 1 + +[target.avr-atmega328p.dependencies] +core = { git = "https://github.com/avr-rust/libcore", branch = "rust-26015da0" } diff --git a/build.rs b/build.rs index d114de1..6da6b28 100644 --- a/build.rs +++ b/build.rs @@ -6,8 +6,12 @@ use std::io; use std::io::prelude::*; use std::path::{Path, PathBuf}; +fn src_path() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("src") +} + fn cores_path() -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("cores") + src_path().join("cores") } fn core_module_name(mcu: &Mcu) -> String { @@ -21,7 +25,8 @@ fn main() { let current_mcu = avr_mcu::current::mcu() .expect("no target cpu specified"); - generate_cores(&[current_mcu]).unwrap() + generate_config_module().unwrap(); + generate_cores(&[current_mcu]).unwrap(); } fn generate_cores(mcus: &[Mcu]) -> Result<(), io::Error> { @@ -31,6 +36,15 @@ fn generate_cores(mcus: &[Mcu]) -> Result<(), io::Error> { 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"); + writeln!(f, "pub const CPU_FREQUENCY: 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)?; @@ -57,7 +71,7 @@ fn generate_cores_mod_rs(mcus: &[Mcu]) -> Result<(), io::Error> { fn write_core_module(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> { writeln!(w, "//! Core for {}.", mcu.device.name)?; writeln!(w)?; - writeln!(w, "use {{HardwareUsart, Register}};")?; + writeln!(w, "use {{Mask, Bitset, HardwareUsart, Register}};")?; writeln!(w, "use spi::HardwareSpi;")?; writeln!(w)?; @@ -82,6 +96,33 @@ mod gen { 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 {}: Bitset<{}, Self> = Bitset::new(0x{:x});", bitfield.name, ty, bitfield.mask)?; + + // We create masks for the individual bits in the field if there + // is more than one bit in the field. + if bitfield.mask.count_ones() > 1 { + 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 {}{}: Mask<{}, Self> = Mask::new(1<<{});", + bitfield.name, current_mask_bit_num, ty, current_register_bit_num)?; + current_mask_bit_num += 1; + } + + current_mask >>= 1; + } + } + writeln!(w)?; + } + writeln!(w, "}}")?; + writeln!(w)?; + writeln!(w, "impl Register<{}> for {} {{", ty, register.name)?; writeln!(w, " const ADDR: *mut {} = 0x{:x} as *mut {};", ty, register.offset, ty)?; writeln!(w, "}}")?; diff --git a/build.sh b/build.sh index 8ef4b3f..c81f123 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,3 @@ #! /bin/sh +export AVR_CPU_FREQUENCY=16000000 xargo build --target avr-atmega328p $@ 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/lib.rs b/src/lib.rs index 7e1dc59..4970f7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,8 @@ #![feature(no_core)] #![feature(const_fn)] #![feature(associated_consts)] +#![feature(associated_type_defaults)] +#![feature(const_fn)] #![feature(lang_items)] #![feature(unwind_attributes)] @@ -11,7 +13,7 @@ #![no_std] -pub use self::register::{Register, RegisterValue}; +pub use self::register::{Bitset, Mask, Register, RegisterValue}; pub use self::pin::Pin; pub use self::usart::HardwareUsart; @@ -22,6 +24,7 @@ pub mod timer1; pub mod cores; pub mod spi; +pub mod config; mod register; mod pin; diff --git a/src/register.rs b/src/register.rs index 7c18068..b50151e 100644 --- a/src/register.rs +++ b/src/register.rs @@ -1,4 +1,4 @@ -use core::{cmp, convert, ops}; +use core::{cmp, convert, marker, ops}; pub trait RegisterValue : Copy + Clone + ops::BitAnd + @@ -14,7 +14,9 @@ pub trait RegisterValue : Copy + Clone + } /// A register. -pub trait Register { +pub trait Register : Sized { + type Mask = Mask; + /// The address of the register. const ADDR: *mut T; @@ -33,6 +35,8 @@ pub trait Register { } /// Sets a bitmask in a register. + /// + /// This is equivalent to `r |= mask`. #[inline(always)] fn set(mask: T) { unsafe { @@ -41,6 +45,8 @@ pub trait Register { } /// Clears a bitmask from a register. + /// + /// This is equivalent to `r &= !mask`. #[inline(always)] fn unset(mask: T) { unsafe { @@ -49,6 +55,8 @@ pub trait Register { } /// Toggles a mask in the register. + /// + /// This is equivalent to `r ^= mask`. #[inline(always)] fn toggle(mask: T) { unsafe { @@ -57,6 +65,8 @@ pub trait Register { } /// Checks if a mask is set in the register. + /// + /// This is equivalent to `(r & mask) == mask`. #[inline(always)] fn is_set(mask: T) -> bool { unsafe { @@ -65,6 +75,8 @@ pub trait Register { } /// Checks if a mask is clear in the register. + /// + /// This is equivalent to `(r & mask) == 0`. #[inline(always)] fn is_clear(mask: T) -> bool { unsafe { @@ -90,6 +102,64 @@ pub trait Register { } } +/// A register bitmask. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Bitset> { + mask: T, + _phantom: marker::PhantomData, +} + +/// A register bitmask. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Mask> { + mask: T, + _phantom: marker::PhantomData, +} + +impl Bitset + where T: RegisterValue, R: Register { + /// Creates a new register mask. + pub const fn new(mask: T) -> Self { + Bitset { mask, _phantom: marker::PhantomData } + } + + /// Sets the mask in the register. + /// + /// This is equivalent to `r |= mask`. + pub fn set_all(self) { + R::set(self.mask); + } + + /// Clears the mask from the register. + /// + /// This is equivalent to `r &= !mask`. + pub fn unset_all(self) { + R::unset(self.mask); + } + + /// Toggles the masked bits in the register. + /// + /// This is equivalent to `r ^= mask`. + pub fn toggle_all(self) { + R::toggle(self.mask); + } + + /// Checks if the mask is clear. + /// + /// This is equivalent to `(r & mask) == 0`. + pub fn is_clear(self) -> bool { + R::is_clear(self.mask) + } +} + +impl Mask + where T: RegisterValue, R: Register { + /// Creates a new register mask. + pub const fn new(mask: T) -> Self { + Mask { mask, _phantom: marker::PhantomData } + } +} + impl RegisterValue for u8 { } impl RegisterValue for u16 { } diff --git a/src/spi/clock.rs b/src/spi/clock.rs new file mode 100644 index 0000000..aea8ff0 --- /dev/null +++ b/src/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 / 2 { + 0 + } else if spi_clock >= config::CPU_FREQUENCY / 4 { + 1 + } else if spi_clock >= config::CPU_FREQUENCY / 8 { + 2 + } else if spi_clock >= config::CPU_FREQUENCY / 16 { + 3 + } else if spi_clock >= config::CPU_FREQUENCY / 32 { + 4 + } else if spi_clock >= config::CPU_FREQUENCY / 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/spi.rs b/src/spi/mod.rs similarity index 88% rename from src/spi.rs rename to src/spi/mod.rs index fbac626..e0298d9 100644 --- a/src/spi.rs +++ b/src/spi/mod.rs @@ -1,3 +1,6 @@ +mod clock; +mod settings; + use {Register, Pin}; /// An SPI module. @@ -18,7 +21,7 @@ pub trait HardwareSpi { type DataRegister: Register; /// Sets up the SPI as a master. - fn setup_master() { + fn setup_master(clock: u32) { // Setup DDR registers. Self::MasterInSlaveOut::set_input(); Self::MasterOutSlaveIn::set_output(); @@ -27,11 +30,11 @@ pub trait HardwareSpi { Self::set_master(); Self::enable_interrupt(); - Self::enable(); + Self::setup_common(clock) } /// Sets up the SPI as a slave. - fn setup_slave() { + fn setup_slave(clock: u32) { // Setup DDR registers. Self::MasterInSlaveOut::set_output(); Self::MasterOutSlaveIn::set_input(); @@ -39,7 +42,19 @@ pub trait HardwareSpi { Self::SlaveSelect::set_input(); Self::set_slave(); - Self::enable(); + 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.control_register_mask()); + Self::StatusRegister::set(mask.status_register_mask()); } /// Enables interrupts for the spi module. diff --git a/src/spi/settings.rs b/src/spi/settings.rs new file mode 100644 index 0000000..d3563dc --- /dev/null +++ b/src/spi/settings.rs @@ -0,0 +1,127 @@ +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. +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. +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/timer0.rs b/src/timer0.rs index 5869b7c..df6ab9f 100644 --- a/src/timer0.rs +++ b/src/timer0.rs @@ -3,6 +3,8 @@ use core::ptr::write_volatile; use super::*; +use cores::atmega328p as c; + pub enum ClockSource { None, Prescale1, @@ -121,18 +123,18 @@ impl Timer { #[inline] pub fn configure(self) { unsafe { - write_volatile(TCCR0A, self.a); - write_volatile(TCCR0B, self.b); + c::TCCR0A::write(self.a); + c::TCCR0B::write(self.b); // Reset counter to zero - write_volatile(TCNT0, 0); + c::TCNT0::write(0); if let Some(v) = self.output_compare_1 { // Set the match - write_volatile(OCR0A, v); + c::OCR0A::write(v); // Enable compare interrupt - write_volatile(TIMSK0, OCIE0A); + c::TIMSK0::OCIE0A.set_all(); } } }