Support auto-generated cores
This commit is contained in:
parent
a831e48755
commit
d833813374
|
@ -12,3 +12,7 @@ description = """
|
|||
Reusable components for the Arduino Uno.
|
||||
"""
|
||||
keywords = ["avr", "arduino", "uno"]
|
||||
|
||||
[build-dependencies]
|
||||
avr-mcu = "0.2"
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"llvm-target": "avr-unknown-unknown",
|
||||
"cpu": "atmega328p",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "16",
|
||||
"os": "unknown",
|
||||
"target-env": "",
|
||||
"target-vendor": "unknown",
|
||||
"arch": "avr",
|
||||
"data-layout": "e-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
|
||||
|
||||
"executables": true,
|
||||
|
||||
"linker": "avr-gcc",
|
||||
"linker-flavor": "gcc",
|
||||
"pre-link-args": {
|
||||
"gcc": ["-Os", "-mmcu=atmega328p"]
|
||||
},
|
||||
"exe-suffix": ".elf",
|
||||
"post-link-args": {
|
||||
"gcc": ["-Wl,--gc-sections"]
|
||||
},
|
||||
|
||||
"no-default-libraries": false
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
extern crate avr_mcu;
|
||||
|
||||
use avr_mcu::*;
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn cores_path() -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("src").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 = avr_mcu::current::mcu()
|
||||
.expect("no target cpu specified");
|
||||
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_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, "//! Cores")?;
|
||||
writeln!(w)?;
|
||||
for mcu in mcus {
|
||||
writeln!(w, "/// The {}.", mcu.device.name)?;
|
||||
writeln!(w, "pub mod {};", core_module_name(mcu))?;
|
||||
}
|
||||
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 {{HardwareSpi, Pin, Register}};")?;
|
||||
writeln!(w)?;
|
||||
|
||||
gen::write_registers(mcu, w)?;
|
||||
gen::write_pins(mcu, w)?;
|
||||
gen::write_spi_modules(mcu, w)?;
|
||||
|
||||
writeln!(w)
|
||||
}
|
||||
|
||||
mod gen {
|
||||
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, "impl Register<{}> for {} {{", ty, register.name)?;
|
||||
writeln!(w, " const ADDR: *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") {
|
||||
for instance in port.instances.iter() {
|
||||
for signal in instance.signals.iter() {
|
||||
let idx = signal.index.expect("signal with no index");
|
||||
let struct_name = pin_name(instance, signal);
|
||||
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 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);
|
||||
|
||||
writeln!(w, " type {} = {};", spi_signal_name, pin_name)?;
|
||||
}
|
||||
|
||||
for reg in module.registers() {
|
||||
let const_name = match ®.caption[..] {
|
||||
"SPI Data Register" => "SPDR",
|
||||
"SPI Status Register" => "SPSR",
|
||||
"SPI Control Register" => "SPCR",
|
||||
_ => panic!("unknown SPI module register: '{}'", reg.caption),
|
||||
};
|
||||
|
||||
|
||||
writeln!(w, " /// {}.", reg.caption)?;
|
||||
writeln!(w, " type {} = {};", const_name, reg.name)?;
|
||||
}
|
||||
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");
|
||||
format!("{}{}", instance.name, idx)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
*.rs
|
18
src/lib.rs
18
src/lib.rs
|
@ -6,12 +6,26 @@
|
|||
|
||||
#![no_core]
|
||||
|
||||
extern crate core;
|
||||
#![no_std]
|
||||
|
||||
pub use self::reg::Register;
|
||||
pub use self::pin::Pin;
|
||||
pub use self::spi::HardwareSpi;
|
||||
|
||||
pub mod prelude;
|
||||
pub mod serial;
|
||||
pub mod timer0;
|
||||
pub mod timer1;
|
||||
pub mod serial;
|
||||
pub mod cores;
|
||||
|
||||
mod reg;
|
||||
mod pin;
|
||||
mod spi;
|
||||
|
||||
pub enum DataDirection {
|
||||
Input,
|
||||
Output,
|
||||
}
|
||||
|
||||
macro_rules! bit {
|
||||
(-, $pos:expr) => {};
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
use {DataDirection, Register};
|
||||
|
||||
/// An IO pin.
|
||||
pub trait Pin {
|
||||
/// The associated data direction registerr.
|
||||
type DDR: Register<u8>;
|
||||
/// The associated port register.
|
||||
type PORT: Register<u8>;
|
||||
/// The associated pin register.
|
||||
///
|
||||
/// Reads from the register will read input bits.
|
||||
/// Writes to the register will toggle bits.
|
||||
type PIN: Register<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(Self::MASK);
|
||||
}
|
||||
|
||||
/// Sets the pin up as an output.
|
||||
#[inline(always)]
|
||||
fn set_output() {
|
||||
Self::DDR::set(Self::MASK);
|
||||
}
|
||||
|
||||
/// Set the pin to high.
|
||||
///
|
||||
/// The pin must be configured as an output.
|
||||
#[inline(always)]
|
||||
fn set_high() {
|
||||
Self::PORT::set(Self::MASK);
|
||||
}
|
||||
|
||||
/// Set the pin to low.
|
||||
///
|
||||
/// The pin must be configured as an output.
|
||||
#[inline(always)]
|
||||
fn set_low() {
|
||||
Self::PORT::unset(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(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_set(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(Self::MASK)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
use core::{cmp, convert, ops};
|
||||
|
||||
pub trait RegVal : 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<T: RegVal> {
|
||||
/// The address of the register.
|
||||
const ADDR: *mut T;
|
||||
|
||||
/// Writes a value to the register.
|
||||
#[inline(always)]
|
||||
fn write(value: T) {
|
||||
unsafe {
|
||||
*Self::ADDR = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the value of the register.
|
||||
#[inline(always)]
|
||||
fn read() -> T {
|
||||
unsafe { *Self::ADDR }
|
||||
}
|
||||
|
||||
/// Sets a bitmask in a register.
|
||||
#[inline(always)]
|
||||
fn set(mask: T) {
|
||||
unsafe {
|
||||
*Self::ADDR |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears a bitmask from a register.
|
||||
#[inline(always)]
|
||||
fn unset(mask: T) {
|
||||
unsafe {
|
||||
*Self::ADDR &= !mask;
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles a mask in the register.
|
||||
#[inline(always)]
|
||||
fn toggle(mask: T) {
|
||||
unsafe {
|
||||
*Self::ADDR ^= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a mask is set in the register.
|
||||
#[inline(always)]
|
||||
fn is_set(mask: T) -> bool {
|
||||
unsafe {
|
||||
(*Self::ADDR & mask) == mask
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a mask is clear in the register.
|
||||
#[inline(always)]
|
||||
fn is_clear(mask: T) -> bool {
|
||||
unsafe {
|
||||
(*Self::ADDR & mask) == T::from(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until a mask is set.
|
||||
#[inline(always)]
|
||||
fn wait_until_set(mask: T) {
|
||||
Self::wait_until(|| Self::is_set(mask))
|
||||
}
|
||||
}
|
||||
|
||||
impl RegVal for u8 { }
|
||||
impl RegVal for u16 { }
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
use {Register, Pin};
|
||||
|
||||
|
||||
/// An SPI module.
|
||||
///
|
||||
/// Information at
|
||||
/// http://maxembedded.com/2013/11/the-spi-of-the-avr/
|
||||
pub trait HardwareSpi {
|
||||
/// Master-in slave-out pin.
|
||||
type MISO: Pin;
|
||||
/// Master-out slave-in pin.
|
||||
type MOSI: Pin;
|
||||
/// Serial clock pin.
|
||||
type SCK: Pin;
|
||||
/// Slave-select pin.
|
||||
type SS: Pin;
|
||||
|
||||
/// The SPI control register.
|
||||
type SPCR: Register<u8>;
|
||||
/// The SPI status register.
|
||||
type SPSR: Register<u8>;
|
||||
/// The SPI data register.
|
||||
type SPDR: Register<u8>;
|
||||
|
||||
/// Sets up the SPI as a master.
|
||||
fn setup_master() {
|
||||
// Setup DDR registers.
|
||||
Self::MISO::set_input();
|
||||
Self::MOSI::set_output();
|
||||
Self::SCK::set_output();
|
||||
Self::SS::set_input();
|
||||
|
||||
Self::set_master();
|
||||
Self::enable_interrupt();
|
||||
Self::enable();
|
||||
}
|
||||
|
||||
/// Sets up the SPI as a slave.
|
||||
fn setup_slave() {
|
||||
// Setup DDR registers.
|
||||
Self::MISO::set_output();
|
||||
Self::MOSI::set_input();
|
||||
Self::SCK::set_input();
|
||||
Self::SS::set_input();
|
||||
|
||||
Self::set_slave();
|
||||
Self::enable();
|
||||
}
|
||||
|
||||
/// Enables interrupts for the spi module.
|
||||
#[inline(always)]
|
||||
fn enable_interrupt() {
|
||||
Self::SPCR::set(spcr::INTERRUPT_ENABLE);
|
||||
}
|
||||
|
||||
/// Disables interrupts for the spi module.
|
||||
#[inline(always)]
|
||||
fn disable_interrupt() {
|
||||
Self::SPCR::unset(spcr::INTERRUPT_ENABLE);
|
||||
}
|
||||
|
||||
/// Enables the SPI.
|
||||
#[inline(always)]
|
||||
fn enable() {
|
||||
Self::SPCR::set(spcr::ENABLE);
|
||||
}
|
||||
|
||||
/// Disables the SPI.
|
||||
#[inline(always)]
|
||||
fn disable() {
|
||||
Self::SPCR::unset(spcr::ENABLE);
|
||||
}
|
||||
|
||||
/// Enables least-significant-bit first.
|
||||
#[inline(always)]
|
||||
fn set_lsb() {
|
||||
Self::SPCR::set(spcr::DATA_ORDER_LSB);
|
||||
}
|
||||
|
||||
/// Enables most-significant-bit first.
|
||||
#[inline(always)]
|
||||
fn set_msb() {
|
||||
Self::SPCR::unset(spcr::DATA_ORDER_LSB);
|
||||
}
|
||||
|
||||
/// Enables master mode.
|
||||
#[inline(always)]
|
||||
fn set_master() {
|
||||
Self::SPCR::set(spcr::MASTER);
|
||||
}
|
||||
|
||||
/// Enables slave mode.
|
||||
#[inline(always)]
|
||||
fn set_slave() {
|
||||
Self::SPCR::unset(spcr::MASTER);
|
||||
}
|
||||
|
||||
/// Enables double speed mode.
|
||||
#[inline(always)]
|
||||
fn enable_double_speed() {
|
||||
Self::SPSR::set(spsr::SPI2X);
|
||||
}
|
||||
|
||||
/// Disables double speed mode.
|
||||
#[inline(always)]
|
||||
fn disable_double_speed() {
|
||||
Self::SPSR::unset(spsr::SPI2X);
|
||||
}
|
||||
|
||||
/// Checks if there is a write collision.
|
||||
#[inline(always)]
|
||||
fn is_write_collision() -> bool {
|
||||
Self::SPSR::is_set(spsr::WCOL)
|
||||
}
|
||||
|
||||
/// Sends a byte through the serial.
|
||||
#[inline(always)]
|
||||
fn send_byte(byte: u8) {
|
||||
Self::SPDR::write(byte);
|
||||
Self::SPSR::wait_until_set(spsr::SPIF);
|
||||
}
|
||||
|
||||
/// Reads a byte from the serial.
|
||||
#[inline(always)]
|
||||
fn receive_byte() -> u8 {
|
||||
Self::SPSR::wait_until_set(spsr::SPIF);
|
||||
Self::SPDR::read()
|
||||
}
|
||||
|
||||
/// Sends and receives a byte.
|
||||
#[inline(always)]
|
||||
fn send_receive(byte: u8) -> u8 {
|
||||
Self::SPDR::write(byte);
|
||||
Self::SPSR::wait_until_set(spsr::SPIF);
|
||||
Self::SPDR::read()
|
||||
}
|
||||
}
|
||||
|
||||
/// Constants for the control register.
|
||||
#[allow(dead_code)]
|
||||
mod spcr {
|
||||
pub const INTERRUPT_ENABLE: u8 = 1<<7;
|
||||
pub const ENABLE: u8 = 1<<6;
|
||||
pub const DATA_ORDER_LSB: u8 = 1<<5;
|
||||
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)]
|
||||
mod spsr {
|
||||
/// 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue