diff --git a/avr-hal-generic/src/lib.rs b/avr-hal-generic/src/lib.rs index 41d3c9d688..524d73d00e 100644 --- a/avr-hal-generic/src/lib.rs +++ b/avr-hal-generic/src/lib.rs @@ -21,6 +21,7 @@ pub mod port; pub mod simple_pwm; pub mod spi; pub mod usart; +pub mod usart_spi; pub mod wdt; /// Prelude containing all HAL traits diff --git a/avr-hal-generic/src/usart_spi.rs b/avr-hal-generic/src/usart_spi.rs new file mode 100644 index 0000000000..acf86a608a --- /dev/null +++ b/avr-hal-generic/src/usart_spi.rs @@ -0,0 +1,132 @@ +//! MSPIM Implimentation +use crate::{port::PinOps, spi}; + +// This module just impliments a macro for SpiOps, since underlyingly, the Spi type can still be used since it just needs SpiOps + +/// Dummy Pin for MPSPIM +pub struct UsartSPIDummyPin; + +impl PinOps for UsartSPIDummyPin { + type Dynamic = todo!(); + + fn into_dynamic(self) -> Self::Dynamic { + todo!() + } + + unsafe fn out_set(&mut self) {} + + unsafe fn out_clear(&mut self) {} + + unsafe fn out_toggle(&mut self) {} + + unsafe fn out_get(&self) -> bool { + false + } + + unsafe fn in_get(&self) -> bool { + true + } + + unsafe fn make_output(&mut self) {} + + unsafe fn make_input(&mut self, pull_up: bool) {} +} + +pub type UsartSpi = + spi::Spi; + +// Impliment SpiOps trait for USART +#[macro_export] +macro_rules! add_usart_spi { + ( + hal: $HAL:ty, + peripheral: $USART_SPI:ty, + register_suffix: $n:expr, + sclk: $sclkpin:ty, + mosi: $mosipin:ty, + miso: $misopin:ty, + ) => { + $crate::paste::paste! { + // This is quite a messy way to get the doc string working properly... but it works! + #[doc = concat!("**Clock:** `", stringify!($sclkpin), "`
**MOSI:** `", stringify!($mosipin), "`
**MISO:** `", stringify!($misopin), "`
**CS:** `", stringify!($cspin), "`")] + pub type [] = avr_hal_generic::usart_spi::UsartSpi<$HAL, $USART_SPI, $sclkpin, $mosipin, $misopin>; + + impl $crate::spi::SpiOps<$HAL, $sclkpin, $mosipin, $misopin, $cspin> for $USART_SPI { + fn raw_setup(&mut self, settings: &crate::spi::Settings) { + use $crate::hal::spi; + + // Setup control registers + // We start by setting the UBBRn to 0 + self.[].write(|w| unsafe {w.bits(0)}); + + // We have to translate the character size register into the 2 bits which are the MSB/LSB and the phase + // 5 Bit Char = MSB and 1st + // 6 Bit Char = MSB and 2nd + // 7 Bit Char = LSB and 1st + // 8 Bit Char = LSB and 2nd + self.[].write(|w| { + w.[]().spi_master(); + + match settings.data_order { + crate::spi::DataOrder::MostSignificantFirst => match settings.mode.phase { + spi::Phase::CaptureOnFirstTransition => w.[]().chr5(), + spi::Phase::CaptureOnSecondTransition => w.[]().chr6(), + }, + crate::spi::DataOrder::LeastSignificantFirst => match settings.mode.phase { + spi::Phase::CaptureOnFirstTransition => w.[]().chr7(), + spi::Phase::CaptureOnSecondTransition => w.[]().chr8(), + }, + }; + + match settings.mode.polarity { + spi::Polarity::IdleLow => w.[]().clear_bit(), + spi::Polarity::IdleHigh => w.[]().set_bit(), + } + }); + + // Enable receiver and transmitter, and also the rec interrupt. + self.[].write(|w| w + .[]().set_bit() + .[]().set_bit() + ); + + // Set the clock divider for SPI clock. + self.[].write(|w| { + match settings.clock { + crate::spi::SerialClockRate::OscfOver2 => w.bits(0), + crate::spi::SerialClockRate::OscfOver4 => w.bits(1), + crate::spi::SerialClockRate::OscfOver8 => w.bits(3), + crate::spi::SerialClockRate::OscfOver16 => w.bits(7), + crate::spi::SerialClockRate::OscfOver32 => w.bits(15), + crate::spi::SerialClockRate::OscfOver64 => w.bits(31), + crate::spi::SerialClockRate::OscfOver128 => w.bits(63), + } + }); + } + + fn raw_release(&mut self) { + self.[].write(|w| w.[]().usart_async()); + self.[].reset(); + } + + fn raw_check_iflag(&self) -> bool { + self.[].read().[]().bit_is_set() + } + + fn raw_read(&self) -> u8 { + self.[].read().bits() + } + + fn raw_write(&mut self, byte: u8) { + self.[].write(|w| unsafe { w.bits(byte) }); + } + + fn raw_transaction(&mut self, byte: u8) -> u8 { + self.raw_write(byte); + while !self.raw_check_iflag() {} + self.raw_read() + } + } + } + }; +} diff --git a/examples/atmega2560/src/bin/atmega2560-usart_spi-feedback.rs b/examples/atmega2560/src/bin/atmega2560-usart_spi-feedback.rs new file mode 100644 index 0000000000..9ebec3639d --- /dev/null +++ b/examples/atmega2560/src/bin/atmega2560-usart_spi-feedback.rs @@ -0,0 +1,78 @@ +//! This example demonstrates how to set up a SPI interface and communicate +//! over it. The physical hardware configuration consists of connecting a +//! jumper directly from pin `PB2` to pin `PB3`. +//! +//! Run the program using `cargo run`. +//! You should see it output the line `data: 42` repeatedly. +//! If the output you see is `data: 255`, you may need to check your jumper. + +#![no_std] +#![no_main] + +use atmega_hal::delay::Delay; +use atmega_hal::usart::{Baudrate, Usart}; +use atmega_hal::usart_spi; +use embedded_hal::delay::DelayNs; +use embedded_hal::spi::SpiBus; +use panic_halt as _; + +// Define core clock. This can be used in the rest of the project. +type CoreClock = atmega_hal::clock::MHz16; + +#[avr_device::entry] +fn main() -> ! { + let dp = atmega_hal::Peripherals::take().unwrap(); + let pins = atmega_hal::pins!(dp); + + let mut delay = Delay::::new(); + + // set up serial interface for text output + let mut serial = Usart::new( + dp.USART0, + pins.pe0, + pins.pe1.into_output(), + Baudrate::::new(57600), + ); + + // Create SPI interface. + let (mut spi, _) = usart_spi::Usart1Spi::new( + dp.USART1, + pins.pd5.into_output(), + pins.pd3.into_output(), + pins.pd2.into_pull_up_input(), + pins.pd4.into_output().downgrade(), + atmega_hal::spi::Settings::default(), + ); + + /* Other SPI examples for other USART's + let (mut spi, _) = usart_spi::Usart2Spi::new( + dp.USART2, + pins.ph2.into_output(), + pins.ph1.into_output(), + pins.ph0.into_pull_up_input(), + pins.pd4.into_output().downgrade(), + atmega_hal::spi::Settings::default(), + ); + + let (mut spi, _) = usart_spi::Usart3Spi::new( + dp.USART3, + pins.pj2.into_output(), + pins.pj1.into_output(), + pins.pj0.into_pull_up_input(), + pins.pd4.into_output().downgrade(), + atmega_hal::spi::Settings::default(), + ); + */ + + loop { + // Send a byte + let data_out: [u8; 1] = [42]; + let mut data_in: [u8; 1] = [0]; + // Send a byte + // Because MISO is connected to MOSI, the read data should be the same + spi.transfer(&mut data_in, &data_out).unwrap(); + + ufmt::uwriteln!(&mut serial, "data: {}\r", data_in[0]).unwrap(); + delay.delay_ms(1000); + } +} diff --git a/mcu/atmega-hal/src/lib.rs b/mcu/atmega-hal/src/lib.rs index e2ce9bf2e1..53f75426cf 100644 --- a/mcu/atmega-hal/src/lib.rs +++ b/mcu/atmega-hal/src/lib.rs @@ -145,6 +145,9 @@ pub mod eeprom; #[cfg(feature = "device-selected")] pub use eeprom::Eeprom; +#[cfg(feature = "device-selected")] +pub mod usart_spi; + pub struct Atmega; #[cfg(any(feature = "atmega48p", feature = "atmega168", feature = "atmega328p"))] diff --git a/mcu/atmega-hal/src/usart.rs b/mcu/atmega-hal/src/usart.rs index 3fbd8514cc..adea8344a5 100644 --- a/mcu/atmega-hal/src/usart.rs +++ b/mcu/atmega-hal/src/usart.rs @@ -14,7 +14,8 @@ pub type UsartReader = feature = "atmega328p", feature = "atmega328pb", feature = "atmega1284p", - feature = "atmega164pa" + feature = "atmega164pa", + feature = "atmega48p" ))] pub type Usart0 = Usart< crate::pac::USART0, @@ -27,7 +28,8 @@ pub type Usart0 = Usart< feature = "atmega328p", feature = "atmega328pb", feature = "atmega1284p", - feature = "atmega164pa" + feature = "atmega164pa", + feature = "atmega48p" ))] avr_hal_generic::impl_usart_traditional! { hal: crate::Atmega, diff --git a/mcu/atmega-hal/src/usart_spi.rs b/mcu/atmega-hal/src/usart_spi.rs new file mode 100644 index 0000000000..be3f98fe7c --- /dev/null +++ b/mcu/atmega-hal/src/usart_spi.rs @@ -0,0 +1,104 @@ +//! USART MSPIM implimentations +//! +//! The following list details how many USARTs and if the USARTs support MSPIM for each board choosable. +//! +//! | Board | USARTs | SPI | +//! |-------|--------|-----| +//! | `atmega48p` | 1 | Yes | +//! | `atmega164pa`| 2 | Yes | +//! | `atmega168` | 1 | Yes | +//! | `atmega328p` | 1 | Yes | +//! | `atmega328pb` | 1 | Yes | +//! | `atmega32a` | 1 | No | +//! | `atmega32u4` | 1 | Yes | +//! | `atmega2560` | 4 | Yes | +//! | `atmega128a` | 2 | No | +//! | `atmega1280` | 4 | Yes | +//! | `atmega1284p` | 2 | Yes | +//! | `atmega8` | 1 | No | + +// Supress warning because it doesn't recognise us using it in macros properly. +#[allow(unused_imports)] +use crate::port; + +#[cfg(any(feature = "atmega1280", feature = "atmega2560"))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART0, + register_suffix: 0, + sclk: port::PE2, + mosi: port::PE1, + miso: port::PE0, + cs: port::Dynamic, +} + +#[cfg(any(feature = "atmega1280", feature = "atmega2560"))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART1, + register_suffix: 1, + sclk: port::PD5, + mosi: port::PD3, + miso: port::PD2, + cs: port::Dynamic, +} + +#[cfg(any(feature = "atmega1280", feature = "atmega2560"))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART2, + register_suffix: 2, + sclk: port::PH2, + mosi: port::PH1, + miso: port::PH0, + cs: port::Dynamic, +} + +#[cfg(any(feature = "atmega1280", feature = "atmega2560"))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART3, + register_suffix: 3, + sclk: port::PJ2, + mosi: port::PJ1, + miso: port::PJ0, + cs: port::Dynamic, +} + +#[cfg(any( + feature = "atmega168", + feature = "atmega328p", + feature = "atmega328pb", + feature = "atmega48p" +))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART0, + register_suffix: 0, + sclk: port::PD4, + mosi: port::PD1, + miso: port::PD0, + cs: port::Dynamic, +} + +#[cfg(any(feature = "atmega1284p", feature = "atmega164pa",))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART0, + register_suffix: 0, + sclk: port::PB0, + mosi: port::PD1, + miso: port::PD0, + cs: port::Dynamic, +} + +#[cfg(any(feature = "atmega1284p", feature = "atmega164pa",))] +avr_hal_generic::add_usart_spi! { + hal: crate::Atmega, + peripheral: crate::pac::USART1, + register_suffix: 1, + sclk: port::PD4, + mosi: port::PD3, + miso: port::PD2, + cs: port::Dynamic, +}