Do more work

This commit is contained in:
Dylan McKay 2017-11-17 17:18:35 +13:00
parent 190a3ec8f1
commit 906b548dfa
11 changed files with 358 additions and 15 deletions

8
Xargo.toml Normal file
View File

@ -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" }

View File

@ -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, "}}")?;

View File

@ -1,2 +1,3 @@
#! /bin/sh
export AVR_CPU_FREQUENCY=16000000
xargo build --target avr-atmega328p $@

14
examples/spi.rs Normal file
View File

@ -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() {
}

2
src/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Generated automatically.
config.rs

View File

@ -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;

View File

@ -1,4 +1,4 @@
use core::{cmp, convert, ops};
use core::{cmp, convert, marker, ops};
pub trait RegisterValue : Copy + Clone +
ops::BitAnd<Output=Self> +
@ -14,7 +14,9 @@ pub trait RegisterValue : Copy + Clone +
}
/// A register.
pub trait Register<T: RegisterValue> {
pub trait Register<T: RegisterValue> : Sized {
type Mask = Mask<T, Self>;
/// The address of the register.
const ADDR: *mut T;
@ -33,6 +35,8 @@ pub trait Register<T: RegisterValue> {
}
/// 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<T: RegisterValue> {
}
/// 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<T: RegisterValue> {
}
/// 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<T: RegisterValue> {
}
/// 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<T: RegisterValue> {
}
/// 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<T: RegisterValue> {
}
}
/// A register bitmask.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Bitset<T: RegisterValue, R: Register<T>> {
mask: T,
_phantom: marker::PhantomData<R>,
}
/// A register bitmask.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Mask<T: RegisterValue, R: Register<T>> {
mask: T,
_phantom: marker::PhantomData<R>,
}
impl<T,R> Bitset<T,R>
where T: RegisterValue, R: Register<T> {
/// 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<T,R> Mask<T,R>
where T: RegisterValue, R: Register<T> {
/// 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 { }

60
src/spi/clock.rs Normal file
View File

@ -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
}
}

View File

@ -1,3 +1,6 @@
mod clock;
mod settings;
use {Register, Pin};
/// An SPI module.
@ -18,7 +21,7 @@ pub trait HardwareSpi {
type DataRegister: Register<u8>;
/// 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.

127
src/spi/settings.rs Normal file
View File

@ -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;
}

View File

@ -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();
}
}
}