diff --git a/avr-hal-generic/src/clock.rs b/avr-hal-generic/src/clock.rs index 66d498a7f4..5d04d5bc9e 100644 --- a/avr-hal-generic/src/clock.rs +++ b/avr-hal-generic/src/clock.rs @@ -34,6 +34,13 @@ impl Clock for MHz12 { const FREQ: u32 = 12_000_000; } +/// 10 MHz Clock +#[derive(ufmt::derive::uDebug, Debug)] +pub struct MHz10; +impl Clock for MHz10 { + const FREQ: u32 = 10_000_000; +} + /// 8 MHz Clock #[derive(ufmt::derive::uDebug, Debug)] pub struct MHz8; diff --git a/avr-hal-generic/src/delay.rs b/avr-hal-generic/src/delay.rs index 7a480ec384..7571f80094 100644 --- a/avr-hal-generic/src/delay.rs +++ b/avr-hal-generic/src/delay.rs @@ -182,6 +182,23 @@ impl delay_v0::DelayUs for Delay { } } +impl delay_v0::DelayUs for Delay { + fn delay_us(&mut self, mut us: u16) { + // for the 10 crate::clock::MHz clock if somebody is working with USB + + // for a 1 microsecond delay, simply return. the overhead + // of the function call takes 14 (16) cycles, which is 1.5us + if us <= 1 { + return; + } // = 3 cycles, (4 when true) + + // 4 cycles per busy_loop iteration = 0.4 us per busy loop, so 2.5 times to get 1 us + us = ((us << 2) + us) >> 1; // x2.5 + + busy_loop(us); + } +} + impl delay_v0::DelayUs for Delay { fn delay_us(&mut self, mut us: u16) { // for the 8 crate::clock::MHz internal clock diff --git a/avr-hal-generic/src/eeprom.rs b/avr-hal-generic/src/eeprom.rs index fcfd2fc47e..6eb7a947b3 100644 --- a/avr-hal-generic/src/eeprom.rs +++ b/avr-hal-generic/src/eeprom.rs @@ -63,7 +63,9 @@ where #[inline] pub fn erase_byte(&mut self, offset: u16) { assert!(offset < Self::CAPACITY); - self.p.raw_erase_byte(offset) + // Write 0xff here because the erase function is borked. + // See also: https://github.com/Rahix/avr-hal/issues/406 + self.p.raw_write_byte(offset, 0xff) } pub fn read(&self, offset: u16, buf: &mut [u8]) -> Result<(), OutOfBoundsError> { @@ -179,26 +181,12 @@ macro_rules! impl_eeprom_common { // Check if any bits are changed to '1' in the new value. if (diff_mask & data) != 0 { - // Now we know that _some_ bits need to be erased to '1'. - - // Check if any bits in the new value are '0'. - if data != 0xff { - // Now we know that some bits need to be programmed to '0' also. - self.eedr.write(|w| w.bits(data)); // Set EEPROM data register. - - { - let $periph_ewmode_var = &self; - $set_erasewrite_mode - } - self.eecr.write(|w| w.eepe().set_bit()); // Start Erase+Write operation. - } else { - // Now we know that all bits should be erased. - { - let $periph_emode_var = &self; - $set_erase_mode - } - self.eecr.write(|w| w.eepe().set_bit()); // Start Erase-only operation. + self.eedr.write(|w| w.bits(data)); // Set EEPROM data register. + { + let $periph_ewmode_var = &self; + $set_erasewrite_mode } + self.eecr.write(|w| w.eepe().set_bit()); // Start Erase+Write operation. } //Now we know that _no_ bits need to be erased to '1'. else { diff --git a/avr-specs/avr-atmega164pa.json b/avr-specs/avr-atmega164pa.json new file mode 100644 index 0000000000..a9b2da9c09 --- /dev/null +++ b/avr-specs/avr-atmega164pa.json @@ -0,0 +1,25 @@ +{ + "arch": "avr", + "atomic-cas": false, + "cpu": "atmega164pa", + "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", + "eh-frame-header": false, + "exe-suffix": ".elf", + "late-link-args": { + "gcc": [ + "-lgcc" + ] + }, + "linker": "avr-gcc", + "llvm-target": "avr-unknown-unknown", + "max-atomic-width": 8, + "no-default-libraries": false, + "pre-link-args": { + "gcc": [ + "-mmcu=atmega164pa" + ] + }, + "relocation-model": "static", + "target-c-int-width": "16", + "target-pointer-width": "16" +} diff --git a/avr-specs/sync-from-upstream.py b/avr-specs/sync-from-upstream.py index c2ece1eb0f..3d04e7471f 100755 --- a/avr-specs/sync-from-upstream.py +++ b/avr-specs/sync-from-upstream.py @@ -10,6 +10,9 @@ "atmega48p": { "cpu": "atmega48p", }, + "atmega164pa": { + "cpu": "atmega164pa", + }, "atmega168": { "cpu": "atmega168", }, diff --git a/mcu/atmega-hal/Cargo.toml b/mcu/atmega-hal/Cargo.toml index 4a08261b40..9841308047 100644 --- a/mcu/atmega-hal/Cargo.toml +++ b/mcu/atmega-hal/Cargo.toml @@ -9,6 +9,7 @@ rt = ["avr-device/rt"] device-selected = [] enable-extra-adc = [] atmega48p = ["avr-device/atmega48p", "device-selected"] +atmega164pa = ["avr-device/atmega164pa", "device-selected"] atmega168 = ["avr-device/atmega168", "device-selected"] atmega328p = ["avr-device/atmega328p", "device-selected"] atmega328pb = ["avr-device/atmega328pb", "device-selected"] diff --git a/mcu/atmega-hal/src/adc.rs b/mcu/atmega-hal/src/adc.rs index f7aa1a929f..fde390b828 100644 --- a/mcu/atmega-hal/src/adc.rs +++ b/mcu/atmega-hal/src/adc.rs @@ -110,6 +110,7 @@ pub mod channel { feature = "atmega128a", feature = "atmega1284p", feature = "atmega8", + feature = "atmega164pa", ))] pub struct Vbg; #[cfg(any( @@ -124,6 +125,7 @@ pub mod channel { feature = "atmega128a", feature = "atmega1284p", feature = "atmega8", + feature = "atmega164pa", ))] pub struct Gnd; #[cfg(any( @@ -345,3 +347,29 @@ avr_hal_generic::impl_adc! { channel::Gnd: crate::pac::adc::admux::MUX_A::ADC_GND, }, } + +#[cfg(any(feature = "atmega164pa"))] +avr_hal_generic::impl_adc! { + hal: crate::Atmega, + peripheral: crate::pac::ADC, + settings: AdcSettings, + apply_settings: |peripheral, settings| { apply_settings(peripheral, settings) }, + channel_id: crate::pac::adc::admux::MUX_A, + set_channel: |peripheral, id| { + peripheral.admux.modify(|_, w| w.mux().variant(id)); + }, + pins: { + port::PA0: (crate::pac::adc::admux::MUX_A::ADC0, didr0::adc0d), + port::PA1: (crate::pac::adc::admux::MUX_A::ADC1, didr0::adc1d), + port::PA2: (crate::pac::adc::admux::MUX_A::ADC2, didr0::adc2d), + port::PA3: (crate::pac::adc::admux::MUX_A::ADC3, didr0::adc3d), + port::PA4: (crate::pac::adc::admux::MUX_A::ADC4, didr0::adc4d), + port::PA5: (crate::pac::adc::admux::MUX_A::ADC5, didr0::adc5d), + port::PA6: (crate::pac::adc::admux::MUX_A::ADC6, didr0::adc6d), + port::PA7: (crate::pac::adc::admux::MUX_A::ADC7, didr0::adc7d), + }, + channels: { + channel::Vbg: crate::pac::adc::admux::MUX_A::ADC_VBG, + channel::Gnd: crate::pac::adc::admux::MUX_A::ADC_GND, + }, +} diff --git a/mcu/atmega-hal/src/eeprom.rs b/mcu/atmega-hal/src/eeprom.rs index c167fa5c1c..2da4cd12ab 100644 --- a/mcu/atmega-hal/src/eeprom.rs +++ b/mcu/atmega-hal/src/eeprom.rs @@ -14,7 +14,7 @@ avr_hal_generic::impl_eeprom_atmega! { }, } -#[cfg(feature = "atmega168")] +#[cfg(any(feature = "atmega168", feature = "atmega164pa"))] avr_hal_generic::impl_eeprom_atmega! { hal: crate::Atmega, peripheral: crate::pac::EEPROM, diff --git a/mcu/atmega-hal/src/i2c.rs b/mcu/atmega-hal/src/i2c.rs index 456ebfe9b5..6b81e0fc99 100644 --- a/mcu/atmega-hal/src/i2c.rs +++ b/mcu/atmega-hal/src/i2c.rs @@ -18,6 +18,22 @@ avr_hal_generic::impl_i2c_twi! { scl: port::PD0, } +#[cfg(any(feature = "atmega164pa"))] +pub type I2c = avr_hal_generic::i2c::I2c< + crate::Atmega, + crate::pac::TWI, + port::Pin, + port::Pin, + CLOCK, +>; +#[cfg(any(feature = "atmega164pa"))] +avr_hal_generic::impl_i2c_twi! { + hal: crate::Atmega, + peripheral: crate::pac::TWI, + sda: port::PC1, + scl: port::PC0, +} + #[cfg(any(feature = "atmega328p", feature = "atmega168", feature = "atmega48p", feature = "atmega8"))] pub type I2c = avr_hal_generic::i2c::I2c< crate::Atmega, diff --git a/mcu/atmega-hal/src/lib.rs b/mcu/atmega-hal/src/lib.rs index 49fab63741..ddfb6ca474 100644 --- a/mcu/atmega-hal/src/lib.rs +++ b/mcu/atmega-hal/src/lib.rs @@ -6,6 +6,7 @@ //! //! **Note**: This version of the documentation was built for #![cfg_attr(feature = "atmega48p", doc = "**ATmega48P**.")] +#![cfg_attr(feature = "atmega164pa", doc = "**ATmega164PA**.")] #![cfg_attr(feature = "atmega168", doc = "**ATmega168**.")] #![cfg_attr(feature = "atmega328p", doc = "**ATmega328P**.")] #![cfg_attr(feature = "atmega328pb", doc = "**ATmega328PB**.")] @@ -33,6 +34,7 @@ compile_error!( Please select one of the following * atmega48p + * atmega164pa * atmega168 * atmega328p * atmega328pb @@ -48,6 +50,9 @@ compile_error!( /// Reexport of `atmega1280` from `avr-device` #[cfg(feature = "atmega1280")] pub use avr_device::atmega1280 as pac; +/// Reexport of `atmega164pa` from `avr-device` +#[cfg(feature = "atmega164pa")] +pub use avr_device::atmega164pa as pac; /// Reexport of `atmega168` from `avr-device` #[cfg(feature = "atmega168")] pub use avr_device::atmega168 as pac; @@ -138,6 +143,13 @@ macro_rules! pins { $crate::Pins::new($p.PORTB, $p.PORTC, $p.PORTD) }; } +#[cfg(any(feature = "atmega164pa"))] +#[macro_export] +macro_rules! pins { + ($p:expr) => { + $crate::Pins::new($p.PORTA, $p.PORTB, $p.PORTC, $p.PORTD) + }; +} #[cfg(feature = "atmega328pb")] #[macro_export] macro_rules! pins { diff --git a/mcu/atmega-hal/src/port.rs b/mcu/atmega-hal/src/port.rs index d639c23abb..80a4746e15 100644 --- a/mcu/atmega-hal/src/port.rs +++ b/mcu/atmega-hal/src/port.rs @@ -9,6 +9,16 @@ avr_hal_generic::impl_port_traditional! { } } +#[cfg(any(feature = "atmega164pa"))] +avr_hal_generic::impl_port_traditional! { + enum Ports { + A: crate::pac::PORTA = [0, 1, 2, 3, 4, 5, 6 ,7], + B: crate::pac::PORTB = [0, 1, 2, 3, 4, 5, 6 ,7], + C: crate::pac::PORTC = [0, 1, 2, 3, 4, 5, 6 ,7], + D: crate::pac::PORTD = [0, 1, 2, 3, 4, 5, 6 ,7], + } +} + #[cfg(feature = "atmega328pb")] avr_hal_generic::impl_port_traditional! { enum Ports { diff --git a/mcu/atmega-hal/src/simple_pwm.rs b/mcu/atmega-hal/src/simple_pwm.rs index 5adb959ad4..33c77cc7d4 100644 --- a/mcu/atmega-hal/src/simple_pwm.rs +++ b/mcu/atmega-hal/src/simple_pwm.rs @@ -1,4 +1,4 @@ -pub use avr_hal_generic::simple_pwm::{PwmPinOps, Prescaler}; +pub use avr_hal_generic::simple_pwm::{IntoPwmPin, PwmPinOps, Prescaler}; #[allow(unused_imports)] use crate::port::*; @@ -1088,3 +1088,94 @@ avr_hal_generic::impl_simple_pwm! { }, } } + +#[cfg(any(feature = "atmega164pa"))] +avr_hal_generic::impl_simple_pwm! { + /// Use `TC0` for PWM (pins `PB3`) + /// + /// # Example + /// ``` + /// let mut timer0 = Timer0Pwm::new(dp.TC0, Prescaler::Prescale64); + /// + /// let mut b3 = pins.pb3.into_output().into_pwm(&mut timer0); + /// + /// b3.set_duty(128); + /// b3.enable(); + /// ``` + pub struct Timer0Pwm { + timer: crate::pac::TC0, + init: |tim, prescaler| { + tim.tccr0a.modify(|_r, w| w.wgm0().bits(0b11)); + tim.tccr0a.modify(|_r, w| w.com0a().bits(0b00)); + + tim.tccr0b.modify(|_r, w| match prescaler { + Prescaler::Direct => w.cs0().running_no_prescaling(), + Prescaler::Prescale8 => w.cs0().running_clk_8(), + Prescaler::Prescale64 => w.cs0().running_clk_64(), + Prescaler::Prescale256 => w.cs0().running_clk_256(), + Prescaler::Prescale1024 => w.cs0().running_clk_1024(), + }); + }, + pins: { + PB3: { + ocr: ocr0a, + into_pwm: |tim| if enable { + tim.tccr0a.modify(|_r, w| w.com0a().bits(0b11)); + } else { + tim.tccr0a.modify(|_r, w| w.com0a().bits(0b00)); + }, + }, + }, + } +} + +#[cfg(any(feature = "atmega164pa"))] +avr_hal_generic::impl_simple_pwm! { + /// Use `TC1` for PWM (pins `PD4`, `PD5`) + /// + /// # Example + /// ``` + /// let mut timer1 = Timer1Pwm::new(dp.TC1, Prescaler::Prescale64); + /// + /// let mut d4 = pins.pd4.into_output().into_pwm(&mut timer1); + /// let mut d5 = pins.pd5.into_output().into_pwm(&mut timer1); + /// + /// d4.set_duty(128); + /// d4.enable(); + /// d5.set_duty(64); + /// d5.enable(); + /// ``` + pub struct Timer1Pwm { + timer: crate::pac::TC1, + init: |tim, prescaler| { + tim.tccr1a.modify(|_r, w| w.wgm1().bits(0b01)); + tim.tccr1a.modify(|_r, w| w.com1a().bits(0b00)); + tim.tccr1a.modify(|_r, w| w.com1b().bits(0b00)); + tim.tccr1b.modify(|_r, w| match prescaler { + Prescaler::Direct => w.cs1().running_no_prescaling(), + Prescaler::Prescale8 => w.cs1().running_clk_8(), + Prescaler::Prescale64 => w.cs1().running_clk_64(), + Prescaler::Prescale256 => w.cs1().running_clk_256(), + Prescaler::Prescale1024 => w.cs1().running_clk_1024(), + }); + }, + pins: { + PD4: { + ocr: ocr1a, + into_pwm: |tim| if enable { + tim.tccr1a.modify(|_r, w| w.com1a().bits(0b11)); + } else { + tim.tccr1a.modify(|_r, w| w.com1a().bits(0b00)); + }, + }, + PD5: { + ocr: ocr1b, + into_pwm: |tim| if enable { + tim.tccr1a.modify(|_r, w| w.com1b().bits(0b11)); + } else { + tim.tccr1a.modify(|_r, w| w.com1b().bits(0b00)); + }, + }, + }, + } +} diff --git a/mcu/atmega-hal/src/usart.rs b/mcu/atmega-hal/src/usart.rs index 02f6a6565e..037cdd31ab 100644 --- a/mcu/atmega-hal/src/usart.rs +++ b/mcu/atmega-hal/src/usart.rs @@ -9,14 +9,14 @@ pub type UsartWriter = pub type UsartReader = avr_hal_generic::usart::UsartReader; -#[cfg(any(feature = "atmega168", feature = "atmega328p", feature = "atmega328pb", feature = "atmega1284p"))] +#[cfg(any(feature = "atmega168", feature = "atmega328p", feature = "atmega328pb", feature = "atmega1284p", feature = "atmega164pa"))] pub type Usart0 = Usart< crate::pac::USART0, port::Pin, port::Pin, CLOCK, >; -#[cfg(any(feature = "atmega168", feature = "atmega328p", feature = "atmega328pb", feature = "atmega1284p"))] +#[cfg(any(feature = "atmega168", feature = "atmega328p", feature = "atmega328pb", feature = "atmega1284p", feature = "atmega164pa"))] avr_hal_generic::impl_usart_traditional! { hal: crate::Atmega, peripheral: crate::pac::USART0, @@ -41,14 +41,14 @@ avr_hal_generic::impl_usart_traditional! { tx: port::PB3, } -#[cfg(any(feature = "atmega32u4",feature = "atmega128a", feature = "atmega1280", feature = "atmega2560", feature = "atmega1284p"))] +#[cfg(any(feature = "atmega32u4",feature = "atmega128a", feature = "atmega1280", feature = "atmega2560", feature = "atmega1284p", feature = "atmega164pa"))] pub type Usart1 = Usart< crate::pac::USART1, port::Pin, port::Pin, CLOCK, >; -#[cfg(any(feature = "atmega32u4", feature = "atmega1280", feature = "atmega2560", feature = "atmega1284p"))] +#[cfg(any(feature = "atmega32u4", feature = "atmega1280", feature = "atmega2560", feature = "atmega1284p", feature = "atmega164pa"))] avr_hal_generic::impl_usart_traditional! { hal: crate::Atmega, peripheral: crate::pac::USART1,