diff --git a/avr-hal-generic/Cargo.toml b/avr-hal-generic/Cargo.toml index 0f1f5ea6c9..88bf8f2bf2 100644 --- a/avr-hal-generic/Cargo.toml +++ b/avr-hal-generic/Cargo.toml @@ -15,6 +15,7 @@ features = ["docsrs"] [features] docsrs = ["avr-device/docsrs"] +mega0 = [] [dependencies] nb = "1.1.0" diff --git a/avr-hal-generic/src/clock.rs b/avr-hal-generic/src/clock.rs index f7588747bc..a699c3d206 100644 --- a/avr-hal-generic/src/clock.rs +++ b/avr-hal-generic/src/clock.rs @@ -50,6 +50,13 @@ impl Clock for MHz16 { const FREQ: u32 = 16_000_000; } +/// 16 MHz Clock with prescaler divisor of 6 (used for 4809 peripherals) +#[derive(ufmt::derive::uDebug, Debug)] +pub struct MHz16_6; +impl Clock for MHz16_6 { + const FREQ: u32 = 16_000_000 / 6; +} + /// 12 MHz Clock #[derive(ufmt::derive::uDebug, Debug)] pub struct MHz12; diff --git a/avr-hal-generic/src/port.rs b/avr-hal-generic/src/port.rs index 570b0b8262..ca1a669e13 100644 --- a/avr-hal-generic/src/port.rs +++ b/avr-hal-generic/src/port.rs @@ -559,6 +559,231 @@ impl Pin { } } +#[macro_export] +macro_rules! impl_port_mega0 { + ( + $(#[$pins_attr:meta])* + enum Ports { + $($name:ident: $port:ty = [$($pin:literal),+],)+ + } + ) => { + /// Type-alias for a pin type which can represent any concrete pin. + /// + /// Sometimes it is easier to handle pins if they are all of the same type. By default, + /// each pin gets its own distinct type in `avr-hal`, but by + /// [downgrading][avr_hal_generic::port::Pin#downgrading], you can cast them into this + /// "dynamic" type. Do note, however, that using this dynamic type has a runtime cost. + pub type Pin = $crate::port::Pin; + + $crate::paste::paste! { + $(#[$pins_attr])* + pub struct Pins { + $($(pub [

]: Pin< + mode::Input, + [

], + >,)+)+ + } + + impl Pins { + pub fn new( + $(_: $port,)+ + ) -> Self { + Self { + $($([

]: $crate::port::Pin::new( + [

] { _private: (), } + ),)+)+ + } + } + } + } + + $crate::paste::paste! { + #[repr(u8)] + pub enum DynamicPort { + $([]),+ + } + } + + pub struct Dynamic { + port: DynamicPort, + // We'll store the mask instead of the pin number because this allows much less code to + // be generated for the trait method implementations. + mask: u8, + } + + impl Dynamic { + fn new(port: DynamicPort, num: u8) -> Self { + Self { + port, + mask: 1u8 << num, + } + } + } + + $crate::paste::paste! { + impl $crate::port::PinOps for Dynamic { + type Dynamic = Self; + + #[inline] + fn into_dynamic(self) -> Self::Dynamic { + self + } + + #[inline] + unsafe fn out_set(&mut self) { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).outset().write(|w| { + w.bits(self.mask) + }); + })+ + } + } + + #[inline] + unsafe fn out_clear(&mut self) { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).outclr().write(|w| { + w.bits(self.mask) + }); + })+ + } + } + + #[inline] + unsafe fn out_toggle(&mut self) { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).outtgl().write(|w| { + w.bits(self.mask) + }); + })+ + } + } + + #[inline] + unsafe fn out_get(&self) -> bool { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).out().read().bits() & self.mask != 0 + })+ + } + } + + #[inline] + unsafe fn in_get(&self) -> bool { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).in_().read().bits() & self.mask != 0 + })+ + } + } + + #[inline] + unsafe fn make_output(&mut self) { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).dirset().write(|w| { + w.bits(self.mask) + }); + })+ + } + } + + #[inline] + unsafe fn make_input(&mut self, pull_up: bool) { + match self.port { + $(DynamicPort::[] => { + (*<$port>::ptr()).dirclr().write(|w| { + w.bits(self.mask) + }); + $(if self.mask & (1u8 << $pin) != 0{ + (*<$port>::ptr()).[]().modify(|r, w| { + if pull_up { + w.pullupen().set_bit() + } else { + w.pullupen().clear_bit() + } + }); + })+ + })+ + } + } + } + } + + $crate::paste::paste! { + $($( + pub struct [

] { + _private: () + } + + impl $crate::port::PinOps for [

] { + type Dynamic = Dynamic; + + #[inline] + fn into_dynamic(self) -> Self::Dynamic { + Dynamic::new(DynamicPort::[], $pin) + } + + #[inline] + unsafe fn out_set(&mut self) { + (*<$port>::ptr()).outset().write(|w| { + w.[

]().set_bit() + }); + } + + #[inline] + unsafe fn out_clear(&mut self) { + (*<$port>::ptr()).outclr().modify(|_, w| { + w.[

]().set_bit() + }); + } + + #[inline] + unsafe fn out_toggle(&mut self) { + (*<$port>::ptr()).outtgl().modify(|_, w| { + w.[

]().set_bit() + }); + } + + #[inline] + unsafe fn out_get(&self) -> bool { + (*<$port>::ptr()).out().read().[

]().bit() + } + + #[inline] + unsafe fn in_get(&self) -> bool { + (*<$port>::ptr()).in_().read().[

]().bit() + } + + #[inline] + unsafe fn make_output(&mut self) { + (*<$port>::ptr()).dirset().write(|w| { + w.[

]().set_bit() + }); + } + + #[inline] + unsafe fn make_input(&mut self, pull_up: bool) { + (*<$port>::ptr()).dirclr().write(|w| { + w.[

]().set_bit() + }); + (*<$port>::ptr()).[]().modify(|r, w| { + if pull_up { + w.pullupen().set_bit() + } else { + w.pullupen().clear_bit() + } + }); + } + } + )+)+ + } + }; +} + #[macro_export] macro_rules! impl_port_traditional_base { ( diff --git a/avr-hal-generic/src/usart.rs b/avr-hal-generic/src/usart.rs index 9964e362ad..e521f0dee5 100644 --- a/avr-hal-generic/src/usart.rs +++ b/avr-hal-generic/src/usart.rs @@ -13,9 +13,9 @@ use crate::port; /// Precalculated parameters for configuring a certain USART baudrate. #[derive(Debug, Clone, Copy)] pub struct Baudrate { - /// Value of the `UBRR#` register + /// Value of the `UBRR#` / `BAUD#` register pub ubrr: u16, - /// Value of the `U2X#` bit + /// Value of the `U2X#` / `CLK2X#` bit pub u2x: bool, /// The baudrate calculation depends on the configured clock rate, thus a `CLOCK` generic /// parameter is needed. @@ -49,6 +49,7 @@ impl From for Baudrate { } impl Baudrate { + #[cfg(not(feature = "mega0"))] /// Calculate parameters for a certain baudrate at a certain `CLOCK` speed. pub fn new(baud: u32) -> Baudrate { let mut ubrr = (CLOCK::FREQ / 4 / baud - 1) / 2; @@ -66,6 +67,21 @@ impl Baudrate { } } + #[cfg(feature = "mega0")] + /// Calculate parameters for a certain baudrate at a certain `CLOCK` speed. + pub fn new(baud: u32) -> Baudrate { + let ubrr = CLOCK::FREQ * 64 / 8 / baud; + let u2x = true; + debug_assert!(ubrr <= u16::MAX as u32); + debug_assert!(ubrr >= 64_u32); + + Baudrate { + ubrr: ubrr as u16, + u2x, + _clock: marker::PhantomData, + } + } + /// Construct a `Baudrate` from given `UBRR#` and `U2X#` values. /// /// This provides exact control over the resulting clock speed. @@ -78,10 +94,11 @@ impl Baudrate { } fn compare_value(&self) -> u32 { + // no neet for special computation here since it is only used for internal comparison if self.u2x { - 8 * (self.ubrr as u32 + 1) + self.ubrr as u32 } else { - 16 * (self.ubrr as u32 + 1) + 2 * self.ubrr as u32 } } } @@ -550,3 +567,93 @@ macro_rules! impl_usart_traditional { } }; } + +#[macro_export] +macro_rules! impl_usart_mega0 { + ( + hal: $HAL:ty, + peripheral: $USART:ty, + alt_name: $alt: ident, + rx: $rxpin:ty, + tx: $txpin:ty, + ) => { + $crate::paste::paste! { + impl $crate::usart::UsartOps< + $HAL, + $crate::port::Pin<$crate::port::mode::Input, $rxpin>, + $crate::port::Pin<$crate::port::mode::Output, $txpin>, + > for $USART { + fn raw_init(&mut self, baudrate: $crate::usart::Baudrate) { + .PORTMUX.usartroutea().modify(|_r,w| w.usart3().alt1()); + + self.baud().write(|w| w.set(baudrate.ubrr) ); + self.ctrla().reset(); + self.ctrlb().modify(|_r,w| if baudrate.u2x { + w.rxmode().clk2x() + } else { + w.rxmode().normal() + }); + + // Set frame format to 8n1 for now. At some point, this should be made + // configurable, similar to what is done in other HALs. + self.ctrlc().write(|w| { + w.normal_cmode().asynchronous() + .normal_pmode().disabled() + .normal_sbmode()._1bit() + .normal_chsize()._8bit() + }); + + // Enable receiver and transmitter but leave interrupts disabled. + self.ctrlb().modify(|_r, w| w + .txen().set_bit() + .rxen().set_bit() + ); + } + + fn raw_deinit(&mut self) { + // Wait for any ongoing transfer to finish. + $crate::nb::block!(self.raw_flush()).ok(); + self.ctrlb().reset(); + } + + fn raw_flush(&mut self) -> $crate::nb::Result<(), core::convert::Infallible> { + if self.status().read().dreif().bit_is_clear() { + Err($crate::nb::Error::WouldBlock) + } else { + Ok(()) + } + } + + fn raw_write(&mut self, byte: u8) -> $crate::nb::Result<(), core::convert::Infallible> { + // Call flush to make sure the data-register is empty + self.raw_flush()?; + + self.txdatal().write(|w| w.set(byte)); + Ok(()) + } + + fn raw_read(&mut self) -> $crate::nb::Result { + if self.status().read().rxcif().bit_is_clear() { + return Err($crate::nb::Error::WouldBlock); + } + + Ok(self.rxdatal().read().bits()) + } + + fn raw_interrupt(&mut self, event: $crate::usart::Event, state: bool) { + match event { + $crate::usart::Event::RxComplete => { + self.ctrla().modify(|_, w| w.rxcie().bit(state)); + } + $crate::usart::Event::TxComplete => { + self.ctrla().modify(|_, w| w.txcie().bit(state)); + } + $crate::usart::Event::DataRegisterEmpty => { + self.ctrla().modify(|_, w| w.dreie().bit(state)); + } + } + } + } + } + }; +} diff --git a/mcu/atmega-hal/Cargo.toml b/mcu/atmega-hal/Cargo.toml index 8448e0f66e..fd24eaf49d 100644 --- a/mcu/atmega-hal/Cargo.toml +++ b/mcu/atmega-hal/Cargo.toml @@ -28,6 +28,7 @@ atmega1280 = ["avr-device/atmega1280", "device-selected"] atmega1284p = ["avr-device/atmega1284p", "device-selected"] atmega8 = ["avr-device/atmega8", "device-selected"] atmega88p = ["avr-device/atmega88p", "device-selected"] +atmega4809 = ["avr-hal-generic/mega0", "avr-device/atmega4809", "device-selected"] # Allow certain downstream crates to overwrite the device selection error by themselves. disable-device-selection-error = [] diff --git a/mcu/atmega-hal/src/adc.rs b/mcu/atmega-hal/src/adc.rs index 6dd225a751..1a0ed9dc7f 100644 --- a/mcu/atmega-hal/src/adc.rs +++ b/mcu/atmega-hal/src/adc.rs @@ -231,7 +231,7 @@ avr_hal_generic::impl_adc! { }, } -#[cfg(feature = "atmega32u4")] +#[cfg(any(feature = "atmega32u4",feature = "atmega4809"))] avr_hal_generic::impl_adc! { hal: crate::Atmega, peripheral: crate::pac::ADC, diff --git a/mcu/atmega-hal/src/lib.rs b/mcu/atmega-hal/src/lib.rs index d076052345..87d7e2626e 100644 --- a/mcu/atmega-hal/src/lib.rs +++ b/mcu/atmega-hal/src/lib.rs @@ -19,6 +19,7 @@ #![cfg_attr(feature = "atmega1284p", doc = "**ATmega1284P**.")] #![cfg_attr(feature = "atmega8", doc = "**ATmega8**.")] #![cfg_attr(feature = "atmega88p", doc = "**ATmega88P**.")] +#![cfg_attr(feature = "atmega4809", doc = "**ATmega4809**.")] //! This means that only items which are available for this MCU are visible. If you are using //! a different chip, try building the documentation locally with: //! @@ -48,6 +49,7 @@ compile_error!( * atmega1284p * atmega8 * atmega88p + * atmega4809 " ); @@ -107,6 +109,10 @@ pub use avr_device::atmega8 as pac; /// #[cfg(feature = "atmega88p")] pub use avr_device::atmega88p as pac; +/// Reexport of `atmega4809` from `avr-device` +/// +#[cfg(feature = "atmega4809")] +pub use avr_device::atmega4809 as pac; /// See [`avr_device::entry`](https://docs.rs/avr-device/latest/avr_device/attr.entry.html). #[cfg(feature = "rt")] @@ -119,43 +125,43 @@ pub use avr_hal_generic::clock; pub use avr_hal_generic::delay; pub use avr_hal_generic::prelude; -#[cfg(feature = "device-selected")] -pub mod adc; -#[cfg(feature = "device-selected")] -pub use adc::Adc; - -#[cfg(feature = "device-selected")] -pub mod i2c; -#[cfg(feature = "device-selected")] -pub use i2c::I2c; - -#[cfg(feature = "device-selected")] -pub mod spi; -#[cfg(feature = "device-selected")] -pub use spi::Spi; - +//#[cfg(feature = "device-selected")] +//pub mod adc; +//#[cfg(feature = "device-selected")] +//pub use adc::Adc; +// +//#[cfg(feature = "device-selected")] +//pub mod i2c; +//#[cfg(feature = "device-selected")] +//pub use i2c::I2c; +// +//#[cfg(feature = "device-selected")] +//pub mod spi; +//#[cfg(feature = "device-selected")] +//pub use spi::Spi; +// #[cfg(feature = "device-selected")] pub mod port; #[cfg(feature = "device-selected")] pub use port::Pins; -#[cfg(feature = "device-selected")] -pub mod simple_pwm; - +//#[cfg(feature = "device-selected")] +//pub mod simple_pwm; +// #[cfg(feature = "device-selected")] pub mod usart; #[cfg(feature = "device-selected")] pub use usart::Usart; -#[cfg(feature = "device-selected")] -pub mod wdt; -#[cfg(feature = "device-selected")] -pub use wdt::Wdt; +//#[cfg(feature = "device-selected")] +//pub mod wdt; +//#[cfg(feature = "device-selected")] +//pub use wdt::Wdt; -#[cfg(feature = "device-selected")] -pub mod eeprom; -#[cfg(feature = "device-selected")] -pub use eeprom::Eeprom; +//#[cfg(feature = "device-selected")] +//pub mod eeprom; +//#[cfg(feature = "device-selected")] +//pub use eeprom::Eeprom; pub struct Atmega; @@ -193,6 +199,14 @@ macro_rules! pins { }; } +#[cfg(feature = "atmega4809")] +#[macro_export] +macro_rules! pins { + ($p:expr) => { + $crate::Pins::new($p.PORTA, $p.PORTB, $p.PORTC, $p.PORTD, $p.PORTE, $p.PORTF) + }; +} + #[cfg(any(feature = "atmega128a"))] #[macro_export] macro_rules! pins { diff --git a/mcu/atmega-hal/src/port.rs b/mcu/atmega-hal/src/port.rs index f33d2368a2..4bd386dbdb 100644 --- a/mcu/atmega-hal/src/port.rs +++ b/mcu/atmega-hal/src/port.rs @@ -64,6 +64,18 @@ avr_hal_generic::impl_port_traditional! { } } +#[cfg(feature = "atmega4809")] +avr_hal_generic::impl_port_mega0! { + enum Ports { + A: crate::pac::PORTA = [0, 1, 2, 3, 4, 5, 6, 7], + B: crate::pac::PORTB = [0, 1, 2, 3, 4, 5], + C: crate::pac::PORTC = [0, 1, 2, 3, 4, 5, 6, 7], + D: crate::pac::PORTD = [0, 1, 2, 3, 4, 5, 6, 7], + E: crate::pac::PORTE = [0, 1, 2, 3], + F: crate::pac::PORTF = [0, 1, 2, 3, 4, 5, 6], + } +} + #[cfg(any(feature = "atmega128a"))] avr_hal_generic::impl_port_traditional_old! { enum Ports { diff --git a/mcu/atmega-hal/src/usart.rs b/mcu/atmega-hal/src/usart.rs index a2552aa72e..1d54f01247 100644 --- a/mcu/atmega-hal/src/usart.rs +++ b/mcu/atmega-hal/src/usart.rs @@ -76,6 +76,51 @@ avr_hal_generic::impl_usart_traditional! { rx: port::PD0, tx: port::PD1, } +#[cfg(any(feature = "atmega4809"))] +pub type Usart3 = Usart< + crate::pac::USART3, + port::Pin, + port::Pin, + CLOCK, +>; +#[cfg(any(feature = "atmega4809"))] +avr_hal_generic::impl_usart_mega0! { + hal: crate::Atmega, + peripheral: crate::pac::USART3, + alt_name: default, + rx: port::PB1, + tx: port::PB0, +} +#[cfg(any(feature = "atmega4809"))] +pub type Usart3Alt = Usart< + crate::pac::USART3, + port::Pin, + port::Pin, + CLOCK, +>; +#[cfg(any(feature = "atmega4809"))] +avr_hal_generic::impl_usart_mega0! { + hal: crate::Atmega, + peripheral: crate::pac::USART3, + alt_name: alt1, + rx: port::PB5, + tx: port::PB4, +} +//#[cfg(any(feature = "atmega4809"))] +//pub type Usart1 = Usart< +// crate::pac::USART1, +// port::Pin, +// port::Pin, +// CLOCK, +//>; +//#[cfg(any(feature = "atmega4809"))] +//avr_hal_generic::impl_usart_traditional! { +// hal: crate::Atmega, +// peripheral: crate::pac::USART1, +// register_suffix: 0, +// rx: port::PC5, +// tx: port::PC4, +//} #[cfg(feature = "atmega328pb")] pub type Usart1 = Usart<