Merge pull request #4 from dylanmckay/pin-support
WIP: Support for autogenerated mcu cores
This commit is contained in:
commit
7a8572e094
11
Cargo.toml
11
Cargo.toml
|
@ -4,11 +4,20 @@ version = "0.1.1"
|
||||||
authors = [
|
authors = [
|
||||||
"The AVR-Rust Project Developers",
|
"The AVR-Rust Project Developers",
|
||||||
"Jake Goulding <jake.goulding@gmail.com>",
|
"Jake Goulding <jake.goulding@gmail.com>",
|
||||||
|
"Dylan McKay <me@dylanmckay.io>",
|
||||||
]
|
]
|
||||||
|
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/avr-rust/ruduino"
|
repository = "https://github.com/avr-rust/ruduino"
|
||||||
description = """
|
description = """
|
||||||
Reusable components for the Arduino Uno.
|
Reusable components for AVR microcontrollers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
build = "core_generator/build.rs"
|
||||||
|
|
||||||
keywords = ["avr", "arduino", "uno"]
|
keywords = ["avr", "arduino", "uno"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
avr-mcu = "0.2"
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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<Self> = 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<Self> = 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> = Self::ControlB::CS00;")?;
|
||||||
|
writeln!(w, " const CS1: RegisterBits<Self::ControlB> = Self::ControlB::CS01;")?;
|
||||||
|
writeln!(w, " const CS2: RegisterBits<Self::ControlB> = Self::ControlB::CS02;")?;
|
||||||
|
writeln!(w, " const WGM0: RegisterBits<Self::ControlA> = Self::ControlA::WGM00;")?;
|
||||||
|
writeln!(w, " const WGM1: RegisterBits<Self::ControlA> = Self::ControlA::WGM01;")?;
|
||||||
|
writeln!(w, " const WGM2: RegisterBits<Self::ControlB> = Self::ControlB::WGM020;")?;
|
||||||
|
writeln!(w, " const OCIEA: RegisterBits<Self::InterruptMask> = 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> = Self::ControlB::CS10;")?;
|
||||||
|
writeln!(w, " const CS1: RegisterBits<Self::ControlB> = Self::ControlB::CS11;")?;
|
||||||
|
writeln!(w, " const CS2: RegisterBits<Self::ControlB> = Self::ControlB::CS12;")?;
|
||||||
|
writeln!(w, " const WGM0: RegisterBits<Self::ControlA> = Self::ControlA::WGM10;")?;
|
||||||
|
writeln!(w, " const WGM1: RegisterBits<Self::ControlA> = Self::ControlA::WGM11;")?;
|
||||||
|
writeln!(w, " const WGM2: RegisterBits<Self::ControlB> = Self::ControlB::WGM10;")?;
|
||||||
|
writeln!(w, " const WGM3: RegisterBits<Self::ControlB> = Self::ControlB::WGM11;")?;
|
||||||
|
writeln!(w, " const OCIEA: RegisterBits<Self::InterruptMask> = 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)
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
*.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, T>(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") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
pub mod port;
|
|
||||||
|
|
||||||
pub const PORT_B: port::Port<port::B> = port::Port::new();
|
|
||||||
pub const PORT_C: port::Port<port::C> = port::Port::new();
|
|
||||||
pub const PORT_D: port::Port<port::D> = port::Port::new();
|
|
183
src/io/port.rs
183
src/io/port.rs
|
@ -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<P: Information> {
|
|
||||||
_port: PhantomData<P>,
|
|
||||||
direction: [Option<Direction>; BITS_IN_BYTE],
|
|
||||||
pullup: [Option<bool>; BITS_IN_BYTE],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> Configuration<P>
|
|
||||||
where
|
|
||||||
P: Information
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Configuration<P> {
|
|
||||||
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<P: Information> {
|
|
||||||
_port: PhantomData<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> Data<P>
|
|
||||||
where
|
|
||||||
P: Information
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Data<P> { 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<P>(PhantomData<P>);
|
|
||||||
|
|
||||||
impl<P: Information> Port<P> {
|
|
||||||
pub(crate) const fn new() -> Port<P> { Port(PhantomData) }
|
|
||||||
pub fn configuration(&self) -> Configuration<P> { Configuration::new() }
|
|
||||||
pub fn data(&self) -> Data<P> { Data::new() }
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
//! A serial.
|
||||||
|
//!
|
||||||
|
//! *WARNING* The current implementation of this will only work on ATmega328.
|
||||||
|
|
||||||
use core::prelude::v1::*;
|
use core::prelude::v1::*;
|
||||||
use core::ptr::{read_volatile, write_volatile};
|
use core::ptr::{read_volatile, write_volatile};
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub enum CharacterSize {
|
pub enum CharacterSize {
|
||||||
FiveBits,
|
FiveBits,
|
||||||
SixBits,
|
SixBits,
|
||||||
|
@ -107,6 +109,8 @@ impl StopBits {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A serial connection.
|
||||||
|
/// *WARNING* The current implementation of this will only work on ATmega328.
|
||||||
pub struct Serial {
|
pub struct Serial {
|
||||||
ubrr: u16,
|
ubrr: u16,
|
||||||
a: u8,
|
a: u8,
|
||||||
|
@ -224,3 +228,41 @@ pub fn try_receive() -> Option<u8> {
|
||||||
None
|
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;
|
||||||
|
}
|
181
src/lib.rs
181
src/lib.rs
|
@ -1,174 +1,29 @@
|
||||||
//! Definitions of register addresses and bits within those registers
|
//! Definitions of register addresses and bits within those registers
|
||||||
|
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
#![feature(no_core)]
|
|
||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
|
#![feature(associated_type_defaults)]
|
||||||
|
#![feature(lang_items)]
|
||||||
|
#![feature(panic_handler)]
|
||||||
|
#![feature(unwind_attributes)]
|
||||||
|
|
||||||
#![no_core]
|
#![no_std]
|
||||||
|
|
||||||
extern crate core;
|
pub use self::register::{Register, RegisterBits, RegisterValue};
|
||||||
|
pub use self::pin::{DataDirection, Pin};
|
||||||
// Look like we have a standard library
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use core::{option, iter, fmt, ops, clone, marker};
|
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod timer0;
|
pub mod legacy;
|
||||||
pub mod timer1;
|
/// Low level register-based API for device-specific operations.
|
||||||
pub mod serial;
|
pub mod cores;
|
||||||
pub mod io;
|
pub mod interrupt;
|
||||||
|
pub mod modules;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
/// Configuration for the currently-targeted microcontroller.
|
||||||
pub enum Bit {
|
pub mod config;
|
||||||
Bit0 = 0,
|
|
||||||
Bit1 = 1,
|
|
||||||
Bit2 = 2,
|
|
||||||
Bit3 = 3,
|
|
||||||
Bit4 = 4,
|
|
||||||
Bit5 = 5,
|
|
||||||
Bit6 = 6,
|
|
||||||
Bit7 = 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Bit {
|
mod register;
|
||||||
fn as_mask(&self) -> u8 { 1 << *self as u8 }
|
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;
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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<T=u8>;
|
||||||
|
/// The SPI status register.
|
||||||
|
type StatusRegister: Register<T=u8>;
|
||||||
|
/// The SPI data register.
|
||||||
|
type DataRegister: Register<T=u8>;
|
||||||
|
|
||||||
|
/// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
pub use self::timer8::{Timer8, Timer8Setup};
|
||||||
|
pub use self::timer16::{Timer16, Timer16Setup};
|
||||||
|
|
||||||
|
mod timer8;
|
||||||
|
mod timer16;
|
||||||
|
|
|
@ -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<T=u16>;
|
||||||
|
|
||||||
|
/// The second compare register.
|
||||||
|
/// For example, OCR0B.
|
||||||
|
type CompareB: Register<T=u16>;
|
||||||
|
|
||||||
|
/// The counter register.
|
||||||
|
///
|
||||||
|
/// For example, TCNT0.
|
||||||
|
type Counter: Register<T=u16>;
|
||||||
|
|
||||||
|
/// The first control register.
|
||||||
|
///
|
||||||
|
/// For example, TCCR0A.
|
||||||
|
type ControlA: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The second control register.
|
||||||
|
///
|
||||||
|
/// For example, TCCR0B.
|
||||||
|
type ControlB: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The third control register.
|
||||||
|
///
|
||||||
|
/// For example, TCCR0C.
|
||||||
|
type ControlC: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The interrupt mask register.
|
||||||
|
///
|
||||||
|
/// For example, TIMSK0.
|
||||||
|
type InterruptMask: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The interrupt flag register.
|
||||||
|
///
|
||||||
|
/// For example, TIFR0.
|
||||||
|
type InterruptFlag: Register<T=u8>;
|
||||||
|
|
||||||
|
const CS0: RegisterBits<Self::ControlB>;
|
||||||
|
const CS1: RegisterBits<Self::ControlB>;
|
||||||
|
const CS2: RegisterBits<Self::ControlB>;
|
||||||
|
|
||||||
|
const WGM0: RegisterBits<Self::ControlA>;
|
||||||
|
const WGM1: RegisterBits<Self::ControlA>;
|
||||||
|
const WGM2: RegisterBits<Self::ControlB>;
|
||||||
|
const WGM3: RegisterBits<Self::ControlB>;
|
||||||
|
|
||||||
|
const OCIEA: RegisterBits<Self::InterruptMask>;
|
||||||
|
|
||||||
|
fn setup() -> Timer16Setup<Self> { Timer16Setup::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ClockSource {
|
||||||
|
None,
|
||||||
|
Prescale1,
|
||||||
|
Prescale8,
|
||||||
|
Prescale64,
|
||||||
|
Prescale256,
|
||||||
|
Prescale1024,
|
||||||
|
ExternalFalling,
|
||||||
|
ExternalRising,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockSource {
|
||||||
|
fn bits<T: Timer16>(&self) -> RegisterBits<T::ControlB> {
|
||||||
|
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<T: Timer16>() -> RegisterBits<T::ControlB> {
|
||||||
|
!(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<T: Timer16>(&self) -> (RegisterBits<T::ControlA>, RegisterBits<T::ControlB>) {
|
||||||
|
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<T: Timer16>() -> (RegisterBits<T::ControlA>, RegisterBits<T::ControlB>) {
|
||||||
|
(!(T::WGM0 | T::WGM1), !(T::WGM2 | T::WGM3))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Timer16Setup<T: Timer16> {
|
||||||
|
a: RegisterBits<T::ControlA>,
|
||||||
|
b: RegisterBits<T::ControlB>,
|
||||||
|
c: RegisterBits<T::ControlC>,
|
||||||
|
output_compare_1: Option<u16>,
|
||||||
|
_phantom: marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Timer16> Timer16Setup<T> {
|
||||||
|
#[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::<T>();
|
||||||
|
self.b |= source.bits::<T>();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn waveform_generation_mode(mut self, mode: WaveformGenerationMode) -> Self {
|
||||||
|
let (a, b) = WaveformGenerationMode::mask::<T>();
|
||||||
|
self.a &= a;
|
||||||
|
self.b &= b;
|
||||||
|
|
||||||
|
let (a, b) = mode.bits::<T>();
|
||||||
|
self.a |= a;
|
||||||
|
self.b |= b;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn output_compare_1(mut self, value: Option<u16>) -> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<T=u8>;
|
||||||
|
|
||||||
|
/// The second compare register.
|
||||||
|
/// For example, OCR0B.
|
||||||
|
type CompareB: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The counter register.
|
||||||
|
///
|
||||||
|
/// For example, TCNT0.
|
||||||
|
type Counter: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The first control register.
|
||||||
|
///
|
||||||
|
/// For example, TCCR0A.
|
||||||
|
type ControlA: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The second control register.
|
||||||
|
///
|
||||||
|
/// For example, TCCR0B.
|
||||||
|
type ControlB: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The interrupt mask register.
|
||||||
|
///
|
||||||
|
/// For example, TIMSK0.
|
||||||
|
type InterruptMask: Register<T=u8>;
|
||||||
|
|
||||||
|
/// The interrupt flag register.
|
||||||
|
///
|
||||||
|
/// For example, TIFR0.
|
||||||
|
type InterruptFlag: Register<T=u8>;
|
||||||
|
|
||||||
|
/// Bit 0 of the clock select mask.
|
||||||
|
const CS0: RegisterBits<Self::ControlB>;
|
||||||
|
/// Bit 1 of the clock select mask.
|
||||||
|
const CS1: RegisterBits<Self::ControlB>;
|
||||||
|
/// Bit 2 of the clock select mask.
|
||||||
|
const CS2: RegisterBits<Self::ControlB>;
|
||||||
|
|
||||||
|
/// Bit 0 of the waveform generation mode mask.
|
||||||
|
const WGM0: RegisterBits<Self::ControlA>;
|
||||||
|
/// Bit 1 of the waveform generation mode mask.
|
||||||
|
const WGM1: RegisterBits<Self::ControlA>;
|
||||||
|
/// Bit 2 of the waveform generation mode mask.
|
||||||
|
const WGM2: RegisterBits<Self::ControlB>;
|
||||||
|
|
||||||
|
/// Output compare interrupt enable flag.
|
||||||
|
const OCIEA: RegisterBits<Self::InterruptMask>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ClockSource {
|
||||||
|
None,
|
||||||
|
Prescale1,
|
||||||
|
Prescale8,
|
||||||
|
Prescale64,
|
||||||
|
Prescale256,
|
||||||
|
Prescale1024,
|
||||||
|
ExternalFalling,
|
||||||
|
ExternalRising,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockSource {
|
||||||
|
fn bits<T: Timer8>(&self) -> RegisterBits<T::ControlB> {
|
||||||
|
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<T: Timer8>() -> RegisterBits<T::ControlB> {
|
||||||
|
!(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<T: Timer8>(&self) -> (RegisterBits<T::ControlA>, RegisterBits<T::ControlB>) {
|
||||||
|
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<T: Timer8>() -> (RegisterBits<T::ControlA>, RegisterBits<T::ControlB>) {
|
||||||
|
(!(T::WGM0 | T::WGM1), !(T::WGM2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Timer8Setup<T: Timer8> {
|
||||||
|
a: RegisterBits<T::ControlA>,
|
||||||
|
b: RegisterBits<T::ControlB>,
|
||||||
|
output_compare_1: Option<u8>,
|
||||||
|
_phantom: marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Timer8> Timer8Setup<T> {
|
||||||
|
#[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::<T>();
|
||||||
|
self.b |= source.bits::<T>();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn waveform_generation_mode(mut self, mode: WaveformGenerationMode) -> Self {
|
||||||
|
let (a, b) = WaveformGenerationMode::mask::<T>();
|
||||||
|
self.a &= a;
|
||||||
|
self.b &= b;
|
||||||
|
|
||||||
|
let (a, b) = mode.bits::<T>();
|
||||||
|
self.a |= a;
|
||||||
|
self.b |= b;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn output_compare_1(mut self, value: Option<u8>) -> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
use Register;
|
||||||
|
|
||||||
|
pub trait HardwareUsart {
|
||||||
|
/// The USART data register.
|
||||||
|
type DataRegister: Register<T=u8>;
|
||||||
|
/// USART control and status register A.
|
||||||
|
type ControlRegisterA: Register<T=u8>;
|
||||||
|
/// USART control and status register B.
|
||||||
|
type ControlRegisterB: Register<T=u8>;
|
||||||
|
/// USART control and status register C.
|
||||||
|
type ControlRegisterC: Register<T=u8>;
|
||||||
|
/// USART baud rate register.
|
||||||
|
type BaudRateRegister: Register<T=u16>;
|
||||||
|
}
|
|
@ -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<T=u8>;
|
||||||
|
/// The associated port register.
|
||||||
|
type PORT: Register<T=u8>;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Reads from the register will read input bits.
|
||||||
|
/// Writes to the register will toggle bits.
|
||||||
|
type PIN: Register<T=u8>;
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,28 +1,4 @@
|
||||||
use core::prelude::v1::*;
|
//! Re-exports commonly-used APIs that can be imported at once.
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
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, T>(f: F) -> T
|
|
||||||
where F: FnOnce() -> T
|
|
||||||
{
|
|
||||||
let _disabled = DisableInterrupts::new();
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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<Output=Self> +
|
||||||
|
ops::BitAndAssign +
|
||||||
|
ops::BitOr<Output=Self> +
|
||||||
|
ops::BitOrAssign +
|
||||||
|
ops::BitXor<Output=Self> +
|
||||||
|
ops::BitXorAssign +
|
||||||
|
ops::Not<Output=Self> +
|
||||||
|
cmp::PartialEq + cmp::Eq +
|
||||||
|
cmp::PartialOrd + cmp::Ord +
|
||||||
|
convert::From<u8> {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Self>;
|
||||||
|
|
||||||
|
/// The address of the register.
|
||||||
|
const ADDRESS: *mut Self::T;
|
||||||
|
|
||||||
|
/// Writes a value to the register.
|
||||||
|
#[inline(always)]
|
||||||
|
fn write<V>(value: V) where V: Into<Self::T> {
|
||||||
|
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>) {
|
||||||
|
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>) {
|
||||||
|
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>) {
|
||||||
|
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<Self>) -> 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<Self>) -> 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>) {
|
||||||
|
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<R: Register> {
|
||||||
|
/// The raw bitmask.
|
||||||
|
mask: R::T,
|
||||||
|
_phantom: marker::PhantomData<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> RegisterBits<R> 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<R> ops::BitOr for RegisterBits<R> where R: Register
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Self) -> Self {
|
||||||
|
RegisterBits::new(self.mask | rhs.mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> ops::BitOrAssign for RegisterBits<R> where R: Register {
|
||||||
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
|
self.mask |= rhs.mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> ops::BitAnd for RegisterBits<R> where R: Register
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn bitand(self, rhs: Self) -> Self {
|
||||||
|
RegisterBits::new(self.mask & rhs.mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> ops::BitAndAssign for RegisterBits<R> where R: Register {
|
||||||
|
fn bitand_assign(&mut self, rhs: Self) {
|
||||||
|
self.mask &= rhs.mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> ops::Not for RegisterBits<R> where R: Register {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn not(self) -> Self {
|
||||||
|
RegisterBits::new(!self.mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Into<u8> for RegisterBits<R> where R: Register<T=u8> {
|
||||||
|
fn into(self) -> u8 { self.mask }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Into<u16> for RegisterBits<R> where R: Register<T=u16> {
|
||||||
|
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<F>(mut f: F)
|
||||||
|
where F: FnMut() -> bool {
|
||||||
|
loop {
|
||||||
|
if f() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
139
src/timer0.rs
139
src/timer0.rs
|
@ -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<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<u8>) -> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
159
src/timer1.rs
159
src/timer1.rs
|
@ -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<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<u16>) -> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue