Do more work
This commit is contained in:
parent
190a3ec8f1
commit
906b548dfa
|
@ -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" }
|
47
build.rs
47
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, "}}")?;
|
||||
|
|
1
build.sh
1
build.sh
|
@ -1,2 +1,3 @@
|
|||
#! /bin/sh
|
||||
export AVR_CPU_FREQUENCY=16000000
|
||||
xargo build --target avr-atmega328p $@
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# Generated automatically.
|
||||
config.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;
|
||||
|
|
|
@ -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 { }
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue