From 2f4759cbafa2e1a5703f7326a984eaad32cd501d Mon Sep 17 00:00:00 2001 From: Rahix Date: Sun, 14 Feb 2021 14:20:36 +0100 Subject: [PATCH 1/8] generic: port: Add support for new-style IO cells Ref: #114 --- avr-hal-generic/src/port.rs | 193 ++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/avr-hal-generic/src/port.rs b/avr-hal-generic/src/port.rs index bfcdbad8d5..dcf9d47237 100644 --- a/avr-hal-generic/src/port.rs +++ b/avr-hal-generic/src/port.rs @@ -768,6 +768,199 @@ macro_rules! impl_port_traditional { }; } +#[macro_export] +macro_rules! impl_port_new { + ( + enum Ports { + $($PortName:ident: $Port:ty,)+ + } + + $(#[$pins_attr:meta])* + pub struct Pins { + $($pin:ident: $Pin:ident = ($PinPort:ty, $PinPortName:ident, $pin_num:expr),)+ + } + ) => { + pub use $crate::port::mode; + pub type Pin = $crate::port::Pin; + + $(#[$pins_attr])* + pub struct Pins { + $(pub $pin: Pin< + mode::Input, + $Pin, + >,)+ + } + + impl Pins { + pub fn new( + $(_: $Port,)+ + ) -> Self { + Self { + $($pin: $crate::port::Pin::new( + $Pin { _private: (), } + ),)+ + } + } + } + + #[repr(u8)] + pub enum DynamicPort { + $($PortName,)+ + } + + 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, pin_num: u8) -> Self { + Self { + port, + mask: 1 << pin_num, + } + } + } + + 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::$PortName => (*<$Port>::ptr()).outset.write(|w| w.bits(self.mask)),)+ + } + } + + #[inline] + unsafe fn out_clear(&mut self) { + match self.port { + $(DynamicPort::$PortName => (*<$Port>::ptr()).outclr.write(|w| w.bits(self.mask)),)+ + } + } + + #[inline] + unsafe fn out_toggle(&mut self) { + match self.port { + $(DynamicPort::$PortName => (*<$Port>::ptr()).outtgl.write(|w| w.bits(self.mask)),)+ + } + } + + #[inline] + unsafe fn out_get(&self) -> bool { + match self.port { + $(DynamicPort::$PortName => (*<$Port>::ptr()).out.read().bits() & self.mask != 0,)+ + } + } + + #[inline] + unsafe fn in_get(&self) -> bool { + match self.port { + $(DynamicPort::$PortName => (*<$Port>::ptr()).in_.read().bits() & self.mask != 0,)+ + } + } + + #[inline] + unsafe fn make_output(&mut self) { + match self.port { + $(DynamicPort::$PortName => (*<$Port>::ptr()).dirset.write(|w| w.bits(self.mask)),)+ + } + } + + #[inline] + unsafe fn make_input(&mut self, pull_up: bool) { + match self.port { + $(DynamicPort::$PortName => { + let regs = &*<$Port>::ptr(); + regs.dirclr.write(|w| w.bits(self.mask)); + match self.mask { + 0x01 => regs.pin0ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x02 => regs.pin1ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x04 => regs.pin2ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x08 => regs.pin3ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x10 => regs.pin4ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x20 => regs.pin5ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x40 => regs.pin6ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 0x80 => regs.pin7ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + _ => unreachable!(), + } + })+ + } + } + } + + $( + pub struct $Pin { + _private: () + } + + impl $crate::port::PinOps for $Pin { + type Dynamic = Dynamic; + + #[inline] + fn into_dynamic(self) -> Self::Dynamic { + Dynamic::new(DynamicPort::$PinPortName, $pin_num) + } + + #[inline] + unsafe fn out_set(&mut self) { + (*<$PinPort>::ptr()).outset.write(|w| w.bits(1 << $pin_num)) + } + + #[inline] + unsafe fn out_clear(&mut self) { + (*<$PinPort>::ptr()).outclr.write(|w| w.bits(1 << $pin_num)) + } + + #[inline] + unsafe fn out_toggle(&mut self) { + (*<$PinPort>::ptr()).outtgl.write(|w| w.bits(1 << $pin_num)) + } + + #[inline] + unsafe fn out_get(&self) -> bool { + (*<$PinPort>::ptr()).out.read().bits() & (1 << $pin_num) != 0 + } + + #[inline] + unsafe fn in_get(&self) -> bool { + (*<$PinPort>::ptr()).in_.read().bits() & (1 << $pin_num) != 0 + } + + #[inline] + unsafe fn make_output(&mut self) { + (*<$PinPort>::ptr()).dirset.write(|w| w.bits(1 << $pin_num)) + } + + #[inline] + unsafe fn make_input(&mut self, pull_up: bool) { + let regs = &*<$PinPort>::ptr(); + regs.dirclr.write(|w| w.bits(1 << $pin_num)); + match $pin_num { + 0 => regs.pin0ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 1 => regs.pin1ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 2 => regs.pin2ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 3 => regs.pin3ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 4 => regs.pin4ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 5 => regs.pin5ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 6 => regs.pin6ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + 7 => regs.pin7ctrl.modify(|_, w| w.pullupen().bit(pull_up)), + _ => unreachable!(), + } + } + } + )+ + }; +} + #[macro_export] macro_rules! renamed_pins { ( From 3a651df41bcd0c9bac6995213669e0f1b6023cab Mon Sep 17 00:00:00 2001 From: Rahix Date: Sun, 14 Feb 2021 14:18:24 +0100 Subject: [PATCH 2/8] [WIP] specs: Add hacked spec for ATmega4809 Uses avrxmega2 and atxmega32a4 because atmega4809 support isn't shipped with avr-libc for some reason... --- avr-specs/avr-atmega4809.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 avr-specs/avr-atmega4809.json diff --git a/avr-specs/avr-atmega4809.json b/avr-specs/avr-atmega4809.json new file mode 100644 index 0000000000..0b2b27e797 --- /dev/null +++ b/avr-specs/avr-atmega4809.json @@ -0,0 +1,27 @@ +{ + "arch": "avr", + "atomic-cas": false, + "cpu": "avrxmega2", + "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", + "executables": true, + "late-link-args": { + "gcc": [ + "-lgcc" + ] + }, + "linker": "avr-gcc", + "linker-is-gnu": true, + "llvm-target": "avr-unknown-unknown", + "max-atomic-width": 8, + "no-default-libraries": false, + "pre-link-args": { + "gcc": [ + "-mmcu=atxmega32a4", + "-Wl,--as-needed" + ] + }, + "target-c-int-width": "16", + "target-pointer-width": "16" +} From 917489433993ebec72e7643a0a0957e7f7410fbb Mon Sep 17 00:00:00 2001 From: Rahix Date: Sun, 14 Feb 2021 14:19:53 +0100 Subject: [PATCH 3/8] [WIP] atxmega-hal: Add barebones HAL with ATmega4809 support --- Cargo.toml | 1 + mcu/atxmega-hal/Cargo.toml | 27 ++++++++++++++++++ mcu/atxmega-hal/src/lib.rs | 52 +++++++++++++++++++++++++++++++++++ mcu/atxmega-hal/src/port.rs | 55 +++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 mcu/atxmega-hal/Cargo.toml create mode 100644 mcu/atxmega-hal/src/lib.rs create mode 100644 mcu/atxmega-hal/src/port.rs diff --git a/Cargo.toml b/Cargo.toml index ca159ee2b2..bef1dac181 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ # MCU HAL crates "mcu/atmega-hal", + "mcu/atxmega-hal", "mcu/attiny-hal", # Higher level crates diff --git a/mcu/atxmega-hal/Cargo.toml b/mcu/atxmega-hal/Cargo.toml new file mode 100644 index 0000000000..9b67d46b6b --- /dev/null +++ b/mcu/atxmega-hal/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "atxmega-hal" +version = "0.1.0" +authors = ["Rahix "] +edition = "2018" + +[features] +rt = ["avr-device/rt"] +device-selected = [] +atmega4809 = ["avr-device/atmega4809", "device-selected"] + +# Allow certain downstream crates to overwrite the device selection error by themselves. +disable-device-selection-error = [] + +[dependencies] +avr-hal-generic = { path = "../../avr-hal-generic/" } + +[dependencies.avr-device] +version = "0.3" + +# Because this crate has its own check that at least one device is selected, we +# can safely "circumvent" the check in `avr-device`. +# +# Why would we want that? Otherwise, as `avr-device` is compiled first, its +# error will be shown and ours won't which leads to a degraded user experience +# as the displayed error message does not really tell what needs to be done... +features = ["device-selected"] diff --git a/mcu/atxmega-hal/src/lib.rs b/mcu/atxmega-hal/src/lib.rs new file mode 100644 index 0000000000..ffc20879b6 --- /dev/null +++ b/mcu/atxmega-hal/src/lib.rs @@ -0,0 +1,52 @@ +#![no_std] + +#[cfg(all( + not(feature = "device-selected"), + not(feature = "disable-device-selection-error") +))] +compile_error!( + "This crate requires you to specify your target chip as a feature. + + Please select one of the following + + * atmega4809 + " +); + +/// 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")] +pub use avr_device::entry; + +pub use avr_hal_generic::clock; +pub use avr_hal_generic::delay; + +#[cfg(feature = "device-selected")] +pub mod port; +#[cfg(feature = "device-selected")] +pub use port::Pins; + +pub struct RawPeripheral

(pub(crate) P); + +#[allow(non_snake_case)] +#[cfg(feature = "device-selected")] +pub struct Peripherals { + pub pins: Pins, +} + +#[cfg(feature = "device-selected")] +impl Peripherals { + fn new(dp: pac::Peripherals) -> Self { + Self { + #[cfg(feature = "atmega4809")] + pins: Pins::new(dp.PORTA, dp.PORTB, dp.PORTC, dp.PORTD, dp.PORTE, dp.PORTF), + } + } + + pub fn take() -> Option { + pac::Peripherals::take().map(Self::new) + } +} diff --git a/mcu/atxmega-hal/src/port.rs b/mcu/atxmega-hal/src/port.rs new file mode 100644 index 0000000000..7924b986e1 --- /dev/null +++ b/mcu/atxmega-hal/src/port.rs @@ -0,0 +1,55 @@ +#[cfg(feature = "atmega4809")] +avr_hal_generic::impl_port_new! { + enum Ports { + PORTA: crate::pac::PORTA, + PORTB: crate::pac::PORTB, + PORTC: crate::pac::PORTC, + PORTD: crate::pac::PORTD, + PORTE: crate::pac::PORTE, + PORTF: crate::pac::PORTF, + } + + pub struct Pins { + pa0: PA0 = (crate::pac::PORTA, PORTA, 0), + pa1: PA1 = (crate::pac::PORTA, PORTA, 1), + pa2: PA2 = (crate::pac::PORTA, PORTA, 2), + pa3: PA3 = (crate::pac::PORTA, PORTA, 3), + pa4: PA4 = (crate::pac::PORTA, PORTA, 4), + pa5: PA5 = (crate::pac::PORTA, PORTA, 5), + pa6: PA6 = (crate::pac::PORTA, PORTA, 6), + pa7: PA7 = (crate::pac::PORTA, PORTA, 7), + pb0: PB0 = (crate::pac::PORTB, PORTB, 0), + pb1: PB1 = (crate::pac::PORTB, PORTB, 1), + pb2: PB2 = (crate::pac::PORTB, PORTB, 2), + pb3: PB3 = (crate::pac::PORTB, PORTB, 3), + pb4: PB4 = (crate::pac::PORTB, PORTB, 4), + pb5: PB5 = (crate::pac::PORTB, PORTB, 5), + pc0: PC0 = (crate::pac::PORTC, PORTC, 0), + pc1: PC1 = (crate::pac::PORTC, PORTC, 1), + pc2: PC2 = (crate::pac::PORTC, PORTC, 2), + pc3: PC3 = (crate::pac::PORTC, PORTC, 3), + pc4: PC4 = (crate::pac::PORTC, PORTC, 4), + pc5: PC5 = (crate::pac::PORTC, PORTC, 5), + pc6: PC6 = (crate::pac::PORTC, PORTC, 6), + pc7: PC7 = (crate::pac::PORTC, PORTC, 7), + pd0: PD0 = (crate::pac::PORTD, PORTD, 0), + pd1: PD1 = (crate::pac::PORTD, PORTD, 1), + pd2: PD2 = (crate::pac::PORTD, PORTD, 2), + pd3: PD3 = (crate::pac::PORTD, PORTD, 3), + pd4: PD4 = (crate::pac::PORTD, PORTD, 4), + pd5: PD5 = (crate::pac::PORTD, PORTD, 5), + pd6: PD6 = (crate::pac::PORTD, PORTD, 6), + pd7: PD7 = (crate::pac::PORTD, PORTD, 7), + pe0: PE0 = (crate::pac::PORTE, PORTE, 0), + pe1: PE1 = (crate::pac::PORTE, PORTE, 1), + pe2: PE2 = (crate::pac::PORTE, PORTE, 2), + pe3: PE3 = (crate::pac::PORTE, PORTE, 3), + pf0: PF0 = (crate::pac::PORTF, PORTF, 0), + pf1: PF1 = (crate::pac::PORTF, PORTF, 1), + pf2: PF2 = (crate::pac::PORTF, PORTF, 2), + pf3: PF3 = (crate::pac::PORTF, PORTF, 3), + pf4: PF4 = (crate::pac::PORTF, PORTF, 4), + pf5: PF5 = (crate::pac::PORTF, PORTF, 5), + pf6: PF6 = (crate::pac::PORTF, PORTF, 6), + } +} From 27d0e6a8de221c685c34591a3f3698f13161dee7 Mon Sep 17 00:00:00 2001 From: Rahix Date: Sun, 14 Feb 2021 14:24:40 +0100 Subject: [PATCH 4/8] [WIP] Add super simple blinky example for Arduino Nano Every This is all a big jumbled mess and in no way "production ready". Clock/delay seems to be wrong, at least the blinky blinks way too slow... --- Cargo.toml | 1 + .../arduino-nano-every/.cargo/config.toml | 5 +++ examples/arduino-nano-every/Cargo.toml | 16 +++++++++ .../arduino-nano-every/src/bin/every-blink.rs | 34 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 examples/arduino-nano-every/.cargo/config.toml create mode 100644 examples/arduino-nano-every/Cargo.toml create mode 100644 examples/arduino-nano-every/src/bin/every-blink.rs diff --git a/Cargo.toml b/Cargo.toml index bef1dac181..1411579585 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ members = [ "examples/arduino-mega2560", "examples/arduino-mega1280", "examples/arduino-nano", + "examples/arduino-nano-every", "examples/arduino-uno", "examples/atmega2560", "examples/nano168", diff --git a/examples/arduino-nano-every/.cargo/config.toml b/examples/arduino-nano-every/.cargo/config.toml new file mode 100644 index 0000000000..d023e0633d --- /dev/null +++ b/examples/arduino-nano-every/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "../../avr-specs/avr-atmega4809.json" + +[unstable] +build-std = ["core"] diff --git a/examples/arduino-nano-every/Cargo.toml b/examples/arduino-nano-every/Cargo.toml new file mode 100644 index 0000000000..ba4e639d17 --- /dev/null +++ b/examples/arduino-nano-every/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "arduino-nano-every-examples" +version = "0.0.0" +authors = ["Rahix "] +edition = "2018" +publish = false + +[dependencies] +panic-halt = "0.2.0" +ufmt = "0.1.0" +nb = "0.1.2" +embedded-hal = "0.2.3" + +[dependencies.atxmega-hal] +path = "../../mcu/atxmega-hal/" +features = ["atmega4809", "rt"] diff --git a/examples/arduino-nano-every/src/bin/every-blink.rs b/examples/arduino-nano-every/src/bin/every-blink.rs new file mode 100644 index 0000000000..c841d45c45 --- /dev/null +++ b/examples/arduino-nano-every/src/bin/every-blink.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +// Proof of concept, all hacked together right now... +// +// Compile with the dirty avr-atmega4809.json target (which actually uses atxmega32a4 ...). +// +// Flashing works using the following command, based on the Arduino stuff (not verified anything +// else yet): +// +// stty -F /dev/ttyACM0 hup 1200 && \ +// echo 1>/dev/ttyACM0 && \ +// ~/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/bin/avrdude \ +// -C${HOME}/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf \ +// -v -patmega4809 -cjtag2updi -P/dev/ttyACM0 -b115200 -e -D \ +// -Uflash:w:../../target/avr-atmega4809/release/every-blink.elf:e + +use panic_halt as _; + +use embedded_hal::blocking::delay::DelayMs; + +#[atxmega_hal::entry] +fn main() -> ! { + let dp = atxmega_hal::Peripherals::take().unwrap(); + + let mut led = dp.pins.pe2.into_output(); + + let mut delay = atxmega_hal::delay::Delay::::new(); + + loop { + led.toggle(); + delay.delay_ms(100u16); + } +} From f689a9fa829a543f8a6b955d738c2b37ae47538c Mon Sep 17 00:00:00 2001 From: Rahix Date: Sun, 14 Feb 2021 14:31:13 +0100 Subject: [PATCH 5/8] ci: Also test-build Arduino Nano Every examples --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cdba5321d3..2086227495 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,9 @@ jobs: - type: board name: arduino-nano examples: true + - type: board + name: arduino-nano-every + examples: true - type: board name: nano168 examples: true From 6f597f23547185bbd99043e02c3310fbb54a1790 Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Sun, 14 Feb 2021 14:31:13 +0100 Subject: [PATCH 6/8] feat: add nano every support --- arduino-hal/Cargo.toml | 5 + arduino-hal/src/clock.rs | 1 + arduino-hal/src/lib.rs | 36 ++++- arduino-hal/src/port/every.rs | 126 ++++++++++++++++ arduino-hal/src/port/mod.rs | 6 + avr-hal-generic/src/adc.rs | 104 +++++++++++++ avr-hal-generic/src/port.rs | 8 + avr-hal-generic/src/usart.rs | 115 +++++++++++++++ avr-specs/avr-atmega4809.json | 4 +- .../arduino-nano-every/.cargo/config.toml | 3 + examples/arduino-nano-every/Cargo.toml | 12 +- .../arduino-nano-every/src/bin/every-adc.rs | 58 ++++++++ .../arduino-nano-every/src/bin/every-blink.rs | 35 ++--- .../arduino-nano-every/src/bin/every-panic.rs | 69 +++++++++ mcu/atmega-hal/Cargo.toml | 1 + mcu/atxmega-hal/Cargo.toml | 3 +- mcu/atxmega-hal/src/adc.rs | 139 ++++++++++++++++++ mcu/atxmega-hal/src/lib.rs | 38 +++-- mcu/atxmega-hal/src/port.rs | 15 ++ mcu/atxmega-hal/src/usart.rs | 40 +++++ 20 files changed, 768 insertions(+), 50 deletions(-) create mode 100644 arduino-hal/src/port/every.rs create mode 100644 examples/arduino-nano-every/src/bin/every-adc.rs create mode 100644 examples/arduino-nano-every/src/bin/every-panic.rs create mode 100644 mcu/atxmega-hal/src/adc.rs create mode 100644 mcu/atxmega-hal/src/usart.rs diff --git a/arduino-hal/Cargo.toml b/arduino-hal/Cargo.toml index d14e5e4249..75c0be086b 100644 --- a/arduino-hal/Cargo.toml +++ b/arduino-hal/Cargo.toml @@ -19,12 +19,14 @@ critical-section-impl = ["avr-device/critical-section-impl"] board-selected = [] mcu-atmega = [] mcu-attiny = [] +mcu-atxmega = [] arduino-diecimila = ["mcu-atmega", "atmega-hal/atmega168", "board-selected"] arduino-leonardo = ["mcu-atmega", "atmega-hal/atmega32u4", "board-selected"] arduino-mega2560 = ["mcu-atmega", "atmega-hal/atmega2560", "board-selected"] arduino-mega1280 = ["mcu-atmega", "atmega-hal/atmega1280", "board-selected"] arduino-nano = ["mcu-atmega", "atmega-hal/atmega328p", "atmega-hal/enable-extra-adc", "board-selected"] arduino-uno = ["mcu-atmega", "atmega-hal/atmega328p", "board-selected"] +nano-every = ["mcu-atxmega", "atxmega-hal/atmega4809", "atxmega-hal/enable-extra-adc","board-selected"] trinket-pro = ["mcu-atmega", "atmega-hal/atmega328p", "board-selected"] sparkfun-promicro = ["mcu-atmega", "atmega-hal/atmega32u4", "board-selected"] sparkfun-promini-3v3 = ["mcu-atmega", "atmega-hal/atmega328p", "atmega-hal/enable-extra-adc", "board-selected"] @@ -51,6 +53,9 @@ path = "../avr-hal-generic/" path = "../mcu/atmega-hal/" optional = true +[dependencies.atxmega-hal] +path = "../mcu/atxmega-hal/" +optional = true # Because this crate has its own check that at least one device is selected, we # can safely "circumvent" the check in `atmega-hal`. Due to compile order, # this allows us to show our error instead of the one from `atmega-hal` (which diff --git a/arduino-hal/src/clock.rs b/arduino-hal/src/clock.rs index 7c53088015..35d504e38a 100644 --- a/arduino-hal/src/clock.rs +++ b/arduino-hal/src/clock.rs @@ -21,6 +21,7 @@ pub(crate) mod default { feature = "arduino-mega1280", feature = "arduino-nano", feature = "arduino-uno", + feature = "nano-every", feature = "sparkfun-promicro", feature = "sparkfun-promini-5v", feature = "trinket-pro", diff --git a/arduino-hal/src/lib.rs b/arduino-hal/src/lib.rs index 5e78dc3318..da37f47f66 100644 --- a/arduino-hal/src/lib.rs +++ b/arduino-hal/src/lib.rs @@ -12,6 +12,7 @@ #![cfg_attr(feature = "arduino-mega1280", doc = "**Arduino Mega 1280**.")] #![cfg_attr(feature = "arduino-nano", doc = "**Arduino Nano**.")] #![cfg_attr(feature = "arduino-uno", doc = "**Arduino Uno**.")] +#![cfg_attr(feature = "nano-every", doc = "**Nano Every**.")] #![cfg_attr(feature = "sparkfun-promicro", doc = "**SparkFun ProMicro**.")] #![cfg_attr( feature = "sparkfun-promini-3v3", @@ -66,6 +67,7 @@ compile_error!( * arduino-mega1280 * arduino-nano * arduino-uno + * nano-every * sparkfun-promicro * sparkfun-promini-3v3 * sparkfun-promini-5v @@ -101,6 +103,13 @@ pub use atmega_hal as hal; #[cfg(feature = "mcu-atmega")] pub use atmega_hal::pac; +#[doc(no_inline)] +#[cfg(feature = "mcu-atxmega")] +pub use atxmega_hal as hal; +#[doc(no_inline)] +#[cfg(feature = "mcu-atxmega")] +pub use atxmega_hal::pac; + #[doc(no_inline)] #[cfg(feature = "mcu-attiny")] pub use attiny_hal as hal; @@ -130,7 +139,7 @@ pub mod port; pub use port::Pins; /// Analog to Digital converter. -#[cfg(feature = "mcu-atmega")] +#[cfg(any(feature = "mcu-atmega", feature = "mcu-atxmega"))] pub mod adc { pub use crate::hal::adc::{ channel, AdcChannel, AdcOps, AdcSettings, Channel, ClockDivider, ReferenceVoltage, @@ -140,7 +149,7 @@ pub mod adc { pub type Adc = crate::hal::Adc; } #[doc(no_inline)] -#[cfg(feature = "mcu-atmega")] +#[cfg(any(feature = "mcu-atmega", feature = "mcu-atxmega"))] pub use adc::Adc; /// I2C bus controller. @@ -165,7 +174,7 @@ pub mod spi { #[cfg(feature = "mcu-atmega")] pub use spi::Spi; -#[cfg(feature = "mcu-atmega")] +#[cfg(any(feature = "mcu-atmega", feature = "mcu-atxmega"))] pub mod usart { pub use crate::hal::usart::{Baudrate, UsartOps}; @@ -177,15 +186,15 @@ pub mod usart { } #[doc(no_inline)] -#[cfg(feature = "mcu-atmega")] +#[cfg(any(feature = "mcu-atmega", feature = "mcu-atxmega"))] pub use usart::Usart; -#[cfg(feature = "board-selected")] +#[cfg(all(feature = "board-selected", not(feature = "nano-every")))] pub mod eeprom { pub use crate::hal::eeprom::{Eeprom, EepromOps, OutOfBoundsError}; } #[doc(no_inline)] -#[cfg(feature = "board-selected")] +#[cfg(all(feature = "board-selected", not(feature = "nano-every")))] pub use eeprom::Eeprom; #[cfg(feature = "board-selected")] @@ -197,7 +206,7 @@ pub mod simple_pwm { pub use attiny_hal::simple_pwm::*; } -#[cfg(feature = "mcu-atmega")] +#[cfg(any(feature = "mcu-atmega", feature = "mcu-atxmega"))] pub mod prelude { pub use crate::hal::prelude::*; @@ -251,6 +260,19 @@ macro_rules! default_serial { }; } +#[cfg(any(feature = "nano-every"))] +#[macro_export] +macro_rules! default_serial { + ($p:expr, $pins:expr, $baud:expr) => { + $crate::Usart::new( + $p.USART3, + $pins.rx, + $pins.tx.into_output(), + $crate::hal::usart::BaudrateAtxExt::into_baudrate($baud), + ) + }; +} + /// Convenience macro to instantiate the [`Usart`] driver for this board. /// /// # Example diff --git a/arduino-hal/src/port/every.rs b/arduino-hal/src/port/every.rs new file mode 100644 index 0000000000..6b2caa0669 --- /dev/null +++ b/arduino-hal/src/port/every.rs @@ -0,0 +1,126 @@ +pub use atxmega_hal::port::{mode, Pin}; + +avr_hal_generic::renamed_pins! { + /// Pins of the **Arduino Nano Every**. + /// + /// This struct is best initialized via the [`arduino_hal::pins!()`][crate::pins] macro. + pub struct Pins { + /// `A0` + /// + /// * AIN3 (ADC input pin 3) + /// * INT27: External Interrupt + pub a0: atxmega_hal::port::PD3 = pd3, + /// `A1` + /// + /// * AIN2 (ADC input pin 2) + /// * INT26: External Interrupt + pub a1: atxmega_hal::port::PD2 = pd2, + /// `A2` + /// + /// * AIN1 (ADC input channel 2) + /// * INT25: External Interrupt + pub a2: atxmega_hal::port::PD1 = pd1, + /// `A3` + /// + /// * AIN0 (ADC input channel 0) + /// * INT24: External Interrupt + pub a3: atxmega_hal::port::PD0 = pd0, + /// `A4` + /// + /// * AIN12 (ADC input channel 12) + /// * SDA (2-wire serial bus data input/output line) + /// * INT2: External Interrupt + pub a4: atxmega_hal::port::PF2 = pf2, + /// `A5` + /// + /// * AIN13 (ADC input channel 13) + /// * SCL (2-wire serial bus clock line) + /// * INT3: External Interrupt + pub a5: atxmega_hal::port::PF3 = pf3, + /// `A6` + /// + /// * AIN4 (ADC input channel 5) + /// * INT28: External Interrupt + pub a6: atxmega_hal::port::PD4 = pd4, + /// `A7` + /// + /// * AIN5 (ADC input channel 5) + /// * INT29: External Interrupt + pub a7: atxmega_hal::port::PD5 = pd5, + /// `D0` / `RX` + /// + /// * RXD (USART input pin) + /// * INT20: External Interrupt + pub d0: atxmega_hal::port::PC5 = pc5, + /// `D1` / `TX` + /// + /// * TXD (USART output pin) + /// * INT21: External Interrupt + pub d1: atxmega_hal::port::PC4 = pc4, + /// `D2` + /// + /// * INT0: External Interrupt + pub d2: atxmega_hal::port::PA0 = pa0, + /// `D3` + /// + /// * AIN15 (analog comparator positive input) + /// * INT45: External Interrupt + pub d3: atxmega_hal::port::PF5 = pf5, + /// `D4` + /// + /// * INT22: External Interrupt + pub d4: atxmega_hal::port::PC6 = pc6, + /// `D5` + /// + /// * **PWM**: + /// * INT22: External Interrupt + pub d5: atxmega_hal::port::PB2 = pb2, + /// `D6` + /// + /// * **PWM** + /// * AIN14 (analog comparator positive input) + /// * INT44: External Interrupt + pub d6: atxmega_hal::port::PF4 = pf4, + /// `D7` + /// + /// * INT1: External Interrupt + pub d7: atxmega_hal::port::PA1 = pa1, + /// `D8` + /// + /// * INT35: External Interrupt + pub d8: atxmega_hal::port::PE3 = pe3, + /// `D9` + /// + /// * **PWM** + /// * INT9: External Interrupt + pub d9: atxmega_hal::port::PB0 = pb0, + /// `D10` + /// + /// * **PWM** + /// * INT10: External Interrupt + pub d10: atxmega_hal::port::PB1 = pb1, + /// `D11` + /// + /// * **PWM** + /// * INT32: External Interrupt + pub d11: atxmega_hal::port::PE0 = pe0, + /// `D12` + /// + /// * INT33: External Interrupt + pub d12: atxmega_hal::port::PE1 = pe1, + /// `D13` + /// + /// * SCK (SPI bus master clock input) + /// * INT34: External Interrupt + /// * L LED on Arduino Uno + pub d13: atxmega_hal::port::PE2 = pe2, + + pub rx : atxmega_hal::port::PB5= pb5, + pub tx : atxmega_hal::port::PB4 = pb4, + } + + impl Pins { + type Pin = Pin; + type McuPins = atxmega_hal::Pins; + } +} diff --git a/arduino-hal/src/port/mod.rs b/arduino-hal/src/port/mod.rs index 160d3f9811..1af1374b6f 100644 --- a/arduino-hal/src/port/mod.rs +++ b/arduino-hal/src/port/mod.rs @@ -43,6 +43,12 @@ mod uno; feature = "sparkfun-promini-5v" ))] pub use uno::*; +#[cfg(feature = "nano-every")] +mod every; + +#[cfg(feature = "nano-every")] +pub use every::*; + #[cfg(feature = "sparkfun-promicro")] mod promicro; #[cfg(feature = "sparkfun-promicro")] diff --git a/avr-hal-generic/src/adc.rs b/avr-hal-generic/src/adc.rs index 0d6904c2d4..ab059f5250 100644 --- a/avr-hal-generic/src/adc.rs +++ b/avr-hal-generic/src/adc.rs @@ -16,6 +16,7 @@ pub enum ClockDivider { Factor64, /// (default) Factor128, + Factor256, } impl Default for ClockDivider { @@ -329,3 +330,106 @@ macro_rules! impl_adc { )*)? }; } + +#[macro_export] +macro_rules! impl_adc_new { + ( + hal: $HAL:ty, + peripheral: $ADC:ty, + settings: $Settings:ty, + apply_settings: |$settings_periph_var:ident, $settings_var:ident| $apply_settings:block, + channel_id: $Channel:ty, + set_channel: |$periph_var:ident, $chan_var:ident| $set_channel:block, + pins: { + $( + $(#[$pin_attr:meta])* + $pin:ty: ($pin_channel:expr), + )+ + }, + $(channels: { + $( + $(#[$channel_attr:meta])* + $channel_ty:ty: $channel:expr, + )* + },)? + ) => { + impl $crate::adc::AdcOps<$HAL> for $ADC { + type Channel = $Channel; + type Settings = $Settings; + + #[inline] + fn raw_init(&mut self, settings: Self::Settings) { + let $settings_periph_var = self; + let $settings_var = settings; + + $apply_settings + } + + #[inline] + fn raw_read_adc(&self) -> u16 { + // todo wait for RESRDY + self.res.read().bits() + } + + #[inline] + fn raw_is_converting(&self) -> bool { + self.command.read().stconv().bit_is_set() + } + + #[inline] + fn raw_start_conversion(&mut self) { + self.command.modify(|_, w| w.stconv().set_bit()); + } + + #[inline] + fn raw_set_channel(&mut self, channel: Self::Channel) { + let $periph_var = self; + let $chan_var = channel; + + $set_channel + } + + #[inline] + fn raw_enable_channel(&mut self, channel: Self::Channel) { + + } + + #[inline] + fn raw_disable_channel(&mut self, channel: Self::Channel) { + + } + } + + $( + $(#[$pin_attr])* + impl $crate::adc::AdcChannel<$HAL, $ADC> for $crate::port::Pin<$crate::port::mode::Analog, $pin> { + #[inline] + fn channel(&self) -> $Channel { + $pin_channel + } + } + )+ + + $($( + $(#[$channel_attr])* + impl $crate::adc::AdcChannel<$HAL, $ADC> for $channel_ty { + #[inline] + fn channel(&self) -> $Channel { + $channel + } + } + + /// Convert this channel into a generic "[`Channel`][adc-channel]" type. + /// + /// The generic channel type can be used to store multiple channels in an array. + /// + /// [adc-channel]: crate::adc::Channel + $(#[$channel_attr])* + impl $channel_ty { + pub fn into_channel(self) -> $crate::adc::Channel<$HAL, $ADC> { + $crate::adc::Channel::new(self) + } + } + )*)? + }; +} diff --git a/avr-hal-generic/src/port.rs b/avr-hal-generic/src/port.rs index dcf9d47237..eb561397cd 100644 --- a/avr-hal-generic/src/port.rs +++ b/avr-hal-generic/src/port.rs @@ -771,6 +771,7 @@ macro_rules! impl_port_traditional { #[macro_export] macro_rules! impl_port_new { ( + init: |$portmux_var:ident, $clkctrl_var:ident,$cpu_var:ident| $init:block, enum Ports { $($PortName:ident: $Port:ty,)+ } @@ -793,8 +794,15 @@ macro_rules! impl_port_new { impl Pins { pub fn new( + portmux: &crate::pac::PORTMUX, + clkctrl: &crate::pac::CLKCTRL, + cpu: &crate::pac::CPU, $(_: $Port,)+ ) -> Self { + let $portmux_var = portmux; + let $clkctrl_var = clkctrl; + let $cpu_var = cpu; + $init; Self { $($pin: $crate::port::Pin::new( $Pin { _private: (), } diff --git a/avr-hal-generic/src/usart.rs b/avr-hal-generic/src/usart.rs index d929cf553b..d014d1ca6f 100644 --- a/avr-hal-generic/src/usart.rs +++ b/avr-hal-generic/src/usart.rs @@ -141,6 +141,20 @@ impl BaudrateArduinoExt for u32 { } } +pub trait BaudrateAtxExt { + /// Calculate baudrate parameters for atxmega. + fn into_baudrate(self) -> Baudrate; +} + +impl crate::usart::BaudrateAtxExt for u32 { + fn into_baudrate(self) -> Baudrate { + //https://github.com/arduino/ArduinoCore-megaavr/blob/5e639ee40afa693354d3d056ba7fb795a8948c11/cores/arduino/UART.cpp#L157 + let ubrr = ((8 * CLOCK::FREQ / self) + 1) / 2; + debug_assert!(ubrr <= u16::MAX as u32); + Baudrate::with_exact(false, ubrr as u16) + } +} + /// Events/Interrupts for USART peripherals #[repr(u8)] pub enum Event { @@ -547,3 +561,104 @@ macro_rules! impl_usart_traditional { } }; } + +#[macro_export] +macro_rules! impl_usart_new { + ( + hal: $HAL:ty, + peripheral: $USART:ty, + 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) { + + + self.ctrlb.modify(|_, w| w.rxen().clear_bit().txen().clear_bit()); + self.ctrla.reset(); // disable interrupt during init + + self.baud.write(|w|w.bits(baudrate.ubrr)); + + + // 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_chsize()._8bit() + .normal_sbmode()._1bit() + .normal_pmode().disabled() + ); + + + // Enable receiver and transmitter but leave interrupts disabled. + self.ctrlb.write(|w| w + .txen().set_bit() + .rxen().set_bit() + .rxmode().normal() + .sfden() + .clear_bit() // Disable start-of-frame detection + .odme() + .clear_bit() // Disable open-drain mode + .mpcm() + .clear_bit() + ); + + self.ctrla.write(|w| { + w.rxcie() + .set_bit() // Receive Complete Interrupt Enable + .dreie() + .set_bit() // Data Register Empty Interrupt Enable + }); + } + + 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().txcif().bit_is_set() { + Ok(()) + } else { + Err($crate::nb::Error::WouldBlock) + } + } + + fn raw_write(&mut self, byte: u8) -> $crate::nb::Result<(), core::convert::Infallible> { + if self.status.read().dreif().bit_is_set() { + self.txdatal.write(|w| w.bits(byte)); + Ok(()) + } else { + Err($crate::nb::Error::WouldBlock) + } + } + + 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/avr-specs/avr-atmega4809.json b/avr-specs/avr-atmega4809.json index 0b2b27e797..ffe32f8c46 100644 --- a/avr-specs/avr-atmega4809.json +++ b/avr-specs/avr-atmega4809.json @@ -1,7 +1,7 @@ { "arch": "avr", "atomic-cas": false, - "cpu": "avrxmega2", + "cpu": "avrxmega3", "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", @@ -18,7 +18,7 @@ "no-default-libraries": false, "pre-link-args": { "gcc": [ - "-mmcu=atxmega32a4", + "-mmcu=atmega4809", "-Wl,--as-needed" ] }, diff --git a/examples/arduino-nano-every/.cargo/config.toml b/examples/arduino-nano-every/.cargo/config.toml index d023e0633d..b34b2a69c0 100644 --- a/examples/arduino-nano-every/.cargo/config.toml +++ b/examples/arduino-nano-every/.cargo/config.toml @@ -1,5 +1,8 @@ [build] target = "../../avr-specs/avr-atmega4809.json" +[target.'cfg(target_arch = "avr")'] +runner = "ravedude nano-every -cb 57600 " + [unstable] build-std = ["core"] diff --git a/examples/arduino-nano-every/Cargo.toml b/examples/arduino-nano-every/Cargo.toml index ba4e639d17..1495c8fe06 100644 --- a/examples/arduino-nano-every/Cargo.toml +++ b/examples/arduino-nano-every/Cargo.toml @@ -11,6 +11,14 @@ ufmt = "0.1.0" nb = "0.1.2" embedded-hal = "0.2.3" + +[dependencies.arduino-hal] +path = "../../arduino-hal/" +features = ["nano-every"] + [dependencies.atxmega-hal] -path = "../../mcu/atxmega-hal/" -features = ["atmega4809", "rt"] +path = "../../mcu/atxmega-hal" + + +[dependencies.avr-device] +path = "../../../avr-device" diff --git a/examples/arduino-nano-every/src/bin/every-adc.rs b/examples/arduino-nano-every/src/bin/every-adc.rs new file mode 100644 index 0000000000..acad8f02bf --- /dev/null +++ b/examples/arduino-nano-every/src/bin/every-adc.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] + +use arduino_hal::prelude::*; +use panic_halt as _; + +use arduino_hal::adc; + +#[arduino_hal::entry] +fn main() -> ! { + let dp = arduino_hal::Peripherals::take().unwrap(); + let pins = arduino_hal::pins!(dp); + let mut serial = arduino_hal::default_serial!(dp, pins, 57600); + + let mut adc = arduino_hal::Adc::new(dp.ADC0, Default::default()); + + let (vbg, gnd, tmp) = ( + adc.read_blocking(&adc::channel::Vbg), + adc.read_blocking(&adc::channel::Gnd), + adc.read_blocking(&adc::channel::Temperature), + ); + ufmt::uwriteln!(&mut serial, "Vbandgap: {}", vbg).unwrap_infallible(); + ufmt::uwriteln!(&mut serial, "Ground: {}", gnd).unwrap_infallible(); + ufmt::uwriteln!(&mut serial, "Temperature: {}", tmp).unwrap_infallible(); + + let a0 = pins.a0.into_analog_input(&mut adc); + let a1 = pins.a1.into_analog_input(&mut adc); + let a2 = pins.a2.into_analog_input(&mut adc); + let a3 = pins.a3.into_analog_input(&mut adc); + let a4 = pins.a4.into_analog_input(&mut adc); + let a5 = pins.a5.into_analog_input(&mut adc); + + loop { + let values = [ + a0.analog_read(&mut adc), + a1.analog_read(&mut adc), + a2.analog_read(&mut adc), + a3.analog_read(&mut adc), + a4.analog_read(&mut adc), + a5.analog_read(&mut adc), + ]; + + for (i, v) in values.iter().enumerate() { + ufmt::uwrite!(&mut serial, "A{}: {} ", i, v).unwrap_infallible(); + } + + // Arduino Nano has two more ADC pins A6 and A7. Accessing them works a bit different from + // the other pins as they are not normal IO pins. The code below shows how it works. + let (a6, a7) = ( + adc.read_blocking(&adc::channel::ADC6), + adc.read_blocking(&adc::channel::ADC7), + ); + ufmt::uwrite!(&mut serial, "A6: {} A7: {}", a6, a7).unwrap_infallible(); + + ufmt::uwriteln!(&mut serial, "").unwrap_infallible(); + arduino_hal::delay_ms(1000); + } +} diff --git a/examples/arduino-nano-every/src/bin/every-blink.rs b/examples/arduino-nano-every/src/bin/every-blink.rs index c841d45c45..8650f1274d 100644 --- a/examples/arduino-nano-every/src/bin/every-blink.rs +++ b/examples/arduino-nano-every/src/bin/every-blink.rs @@ -1,34 +1,25 @@ #![no_std] #![no_main] -// Proof of concept, all hacked together right now... -// -// Compile with the dirty avr-atmega4809.json target (which actually uses atxmega32a4 ...). -// -// Flashing works using the following command, based on the Arduino stuff (not verified anything -// else yet): -// -// stty -F /dev/ttyACM0 hup 1200 && \ -// echo 1>/dev/ttyACM0 && \ -// ~/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/bin/avrdude \ -// -C${HOME}/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf \ -// -v -patmega4809 -cjtag2updi -P/dev/ttyACM0 -b115200 -e -D \ -// -Uflash:w:../../target/avr-atmega4809/release/every-blink.elf:e - use panic_halt as _; -use embedded_hal::blocking::delay::DelayMs; - -#[atxmega_hal::entry] +#[arduino_hal::entry] fn main() -> ! { - let dp = atxmega_hal::Peripherals::take().unwrap(); - - let mut led = dp.pins.pe2.into_output(); + let dp = arduino_hal::Peripherals::take().unwrap(); + let pins = arduino_hal::pins!(dp); - let mut delay = atxmega_hal::delay::Delay::::new(); + // Digital pin 13 is also connected to an onboard LED marked "L" + let mut led = pins.d13.into_output(); + led.set_high(); loop { led.toggle(); - delay.delay_ms(100u16); + arduino_hal::delay_ms(100); + led.toggle(); + arduino_hal::delay_ms(100); + led.toggle(); + arduino_hal::delay_ms(100); + led.toggle(); + arduino_hal::delay_ms(800); } } diff --git a/examples/arduino-nano-every/src/bin/every-panic.rs b/examples/arduino-nano-every/src/bin/every-panic.rs new file mode 100644 index 0000000000..a2047094cf --- /dev/null +++ b/examples/arduino-nano-every/src/bin/every-panic.rs @@ -0,0 +1,69 @@ +/*! + * Example of a custom panic handler. + * + * The panic handler will print out where the panic occurred and then blink the oboard LED rapidly + * to make the user aware of the problem. + */ +#![no_std] +#![no_main] + +use arduino_hal::prelude::*; + +// Documentation build does not like this and fails with the following error, even though +// everything is fine when compiling the binary. +// +// error[E0152]: found duplicate lang item `panic_impl` +// +// Ignore the panic handler in documentation builds. This is not needed in downstream code, it is +// an artifact of the avr-hal examples structure. +#[cfg(not(doc))] +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + // disable interrupts - firmware has panicked so no ISRs should continue running + avr_device::interrupt::disable(); + + // get the peripherals so we can access serial and the LED. + // + // SAFETY: Because main() already has references to the peripherals this is an unsafe + // operation - but because no other code can run after the panic handler was called, + // we know it is okay. + let dp = unsafe { arduino_hal::Peripherals::steal() }; + let pins = arduino_hal::pins!(dp); + let mut serial = arduino_hal::default_serial!(dp, pins, 57600); + + // Print out panic location + ufmt::uwriteln!(&mut serial, "Firmware panic!\r").unwrap_infallible(); + if let Some(loc) = info.location() { + ufmt::uwriteln!( + &mut serial, + " At {}:{}:{}\r", + loc.file(), + loc.line(), + loc.column(), + ) + .unwrap_infallible(); + } + + // Blink LED rapidly + let mut led = pins.d13.into_output(); + loop { + led.toggle(); + arduino_hal::delay_ms(100); + } +} + +#[arduino_hal::entry] +fn main() -> ! { + let dp = arduino_hal::Peripherals::take().unwrap(); + let pins = arduino_hal::pins!(dp); + let mut serial = arduino_hal::default_serial!(dp, pins, 57600); + + ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").unwrap_infallible(); + ufmt::uwriteln!(&mut serial, "Panic in 5 seconds!\r").unwrap_infallible(); + + arduino_hal::delay_ms(5000); + + // Panic messages cannot yet be captured because they rely on core::fmt + // which is way too big for AVR + panic!(); +} diff --git a/mcu/atmega-hal/Cargo.toml b/mcu/atmega-hal/Cargo.toml index 7f8c5b6ce4..288b86705d 100644 --- a/mcu/atmega-hal/Cargo.toml +++ b/mcu/atmega-hal/Cargo.toml @@ -19,6 +19,7 @@ atmega164pa = ["avr-device/atmega164pa", "device-selected"] atmega168 = ["avr-device/atmega168", "device-selected"] atmega328p = ["avr-device/atmega328p", "device-selected"] atmega328pb = ["avr-device/atmega328pb", "device-selected"] +atmega4809 = ["avr-device/atmega4809", "device-selected"] atmega32a = ["avr-device/atmega32a", "device-selected"] atmega32u4 = ["avr-device/atmega32u4", "device-selected"] atmega2560 = ["avr-device/atmega2560", "device-selected"] diff --git a/mcu/atxmega-hal/Cargo.toml b/mcu/atxmega-hal/Cargo.toml index 9b67d46b6b..84ee631e02 100644 --- a/mcu/atxmega-hal/Cargo.toml +++ b/mcu/atxmega-hal/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [features] rt = ["avr-device/rt"] device-selected = [] +enable-extra-adc = [] atmega4809 = ["avr-device/atmega4809", "device-selected"] # Allow certain downstream crates to overwrite the device selection error by themselves. @@ -16,7 +17,7 @@ disable-device-selection-error = [] avr-hal-generic = { path = "../../avr-hal-generic/" } [dependencies.avr-device] -version = "0.3" +version = "0.7.0" # Because this crate has its own check that at least one device is selected, we # can safely "circumvent" the check in `avr-device`. diff --git a/mcu/atxmega-hal/src/adc.rs b/mcu/atxmega-hal/src/adc.rs new file mode 100644 index 0000000000..738d91aa19 --- /dev/null +++ b/mcu/atxmega-hal/src/adc.rs @@ -0,0 +1,139 @@ +//! Analog-to-Digital Converter +//! +//! # Example +//! +//! Complete example source code can be found in the repository: +//! [`atmega2560-adc.rs`](https://github.com/Rahix/avr-hal/blob/main/examples/atmega2560/src/bin/atmega2560-adc.rs) +//! +//! ``` +//! let dp = atmega_hal::Peripherals::take().unwrap(); +//! let pins = atmega_hal::pins!(dp); +//! +//! let mut adc = Adc::new(dp.ADC, Default::default()); +//! +//! let channels: [atmega_hal::adc::Channel; 4] = [ +//! pins.pf0.into_analog_input(&mut adc).into_channel(), +//! pins.pf1.into_analog_input(&mut adc).into_channel(), +//! pins.pf2.into_analog_input(&mut adc).into_channel(), +//! pins.pf3.into_analog_input(&mut adc).into_channel(), +//! ]; +//! +//! for (index, channel) in channels.iter().enumerate() { +//! let value = adc.read_blocking(channel); +//! ufmt::uwrite!(&mut serial, "CH{}: {} ", index, value).unwrap(); +//! } +//! ``` + +use crate::port; +pub use avr_hal_generic::adc::{AdcChannel, AdcOps, ClockDivider}; + +/// Select the voltage reference for the ADC peripheral +/// +/// The internal voltage reference options may not be used if an external reference voltage is +/// being applied to the AREF pin. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum ReferenceVoltage { + /// Voltage applied to AREF pin. + Aref, + /// Default reference voltage (default). + AVcc, + /// Internal reference voltage. + Internal, +} + +impl Default for ReferenceVoltage { + fn default() -> Self { + Self::AVcc + } +} + +/// Configuration for the ADC peripheral. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub struct AdcSettings { + pub clock_divider: ClockDivider, + pub ref_voltage: ReferenceVoltage, +} + +fn apply_settings(peripheral: &crate::pac::ADC0, settings: AdcSettings) { + peripheral.ctrlc.write(|w| { + match settings.ref_voltage { + ReferenceVoltage::Aref => w.refsel().vrefa(), + ReferenceVoltage::AVcc => w.refsel().vddref(), + ReferenceVoltage::Internal => w.refsel().intref(), + }; + match settings.clock_divider { + ClockDivider::Factor2 => w.presc().div2(), + ClockDivider::Factor4 => w.presc().div4(), + ClockDivider::Factor8 => w.presc().div8(), + ClockDivider::Factor16 => w.presc().div16(), + ClockDivider::Factor32 => w.presc().div32(), + ClockDivider::Factor64 => w.presc().div64(), + ClockDivider::Factor128 => w.presc().div128(), + ClockDivider::Factor256 => w.presc().div256(), + } + }); + peripheral.ctrla.write(|w| w.enable().set_bit()); +} + +/// Check the [`avr_hal_generic::adc::Adc`] documentation. +pub type Adc = avr_hal_generic::adc::Adc; + +/// Check the [`avr_hal_generic::adc::Channel`] documentation. +pub type Channel = avr_hal_generic::adc::Channel; + +/// Additional channels +/// +/// Some channels are not directly connected to pins. This module provides types which can be used +/// to access them. +/// +/// # Example +/// ``` +/// let dp = atxmega_hal::Peripherals::take().unwrap(); +/// let mut adc = atxmega_hal::Adc::new(dp.ADC, Default::default()); +/// +/// let value = adc.read_blocking(&channel::Vbg); +/// ``` +pub mod channel { + #[cfg(all(any(feature = "atmega4809",), feature = "enable-extra-adc",))] + pub struct ADC6; + #[cfg(all(any(feature = "atmega4809",), feature = "enable-extra-adc",))] + pub struct ADC7; + #[cfg(any(feature = "atmega4809",))] + pub struct Vbg; + #[cfg(any(feature = "atmega4809",))] + pub struct Gnd; + #[cfg(any(feature = "atmega4809",))] + pub struct Temperature; +} + +#[cfg(any(feature = "atmega4809"))] +avr_hal_generic::impl_adc_new! { + hal: crate::Atxmega, + peripheral: crate::pac::ADC0, + settings: AdcSettings, + apply_settings: |peripheral, settings| { apply_settings(peripheral, settings) }, + channel_id: crate::pac::adc0::muxpos::MUXPOS_A, + set_channel: |peripheral, id| { + peripheral.muxpos.modify(|_, w| w.muxpos().variant(id)); + }, + pins: { + port::PD0: (crate::pac::adc0::muxpos::MUXPOS_A::AIN0), + port::PD1: (crate::pac::adc0::muxpos::MUXPOS_A::AIN1), + port::PD2: (crate::pac::adc0::muxpos::MUXPOS_A::AIN2), + port::PD3: (crate::pac::adc0::muxpos::MUXPOS_A::AIN3), + port::PD4: (crate::pac::adc0::muxpos::MUXPOS_A::AIN4), + port::PD5: (crate::pac::adc0::muxpos::MUXPOS_A::AIN5), + port::PF2: (crate::pac::adc0::muxpos::MUXPOS_A::AIN12), + port::PF3: (crate::pac::adc0::muxpos::MUXPOS_A::AIN13), + }, + channels: { + #[cfg(feature = "enable-extra-adc")] + channel::ADC6: crate::pac::adc0::muxpos::MUXPOS_A::AIN6, + #[cfg(feature = "enable-extra-adc")] + channel::ADC7: crate::pac::adc0::muxpos::MUXPOS_A::AIN7, + channel::Vbg: crate::pac::adc0::muxpos::MUXPOS_A::DACREF, + channel::Gnd: crate::pac::adc0::muxpos::MUXPOS_A::GND, + channel::Temperature: crate::pac::adc0::muxpos::MUXPOS_A::TEMPSENSE, + }, +} diff --git a/mcu/atxmega-hal/src/lib.rs b/mcu/atxmega-hal/src/lib.rs index ffc20879b6..4ed8cb740e 100644 --- a/mcu/atxmega-hal/src/lib.rs +++ b/mcu/atxmega-hal/src/lib.rs @@ -17,36 +17,42 @@ compile_error!( #[cfg(feature = "atmega4809")] pub use avr_device::atmega4809 as pac; +#[cfg(feature = "device-selected")] +pub use pac::Peripherals; + /// See [`avr_device::entry`](https://docs.rs/avr-device/latest/avr_device/attr.entry.html). #[cfg(feature = "rt")] pub use avr_device::entry; pub use avr_hal_generic::clock; pub use avr_hal_generic::delay; +pub use avr_hal_generic::prelude; #[cfg(feature = "device-selected")] pub mod port; #[cfg(feature = "device-selected")] +pub mod usart; +#[cfg(feature = "device-selected")] pub use port::Pins; +#[cfg(feature = "device-selected")] +pub use usart::Usart; -pub struct RawPeripheral

(pub(crate) P); +pub struct Atxmega; -#[allow(non_snake_case)] #[cfg(feature = "device-selected")] -pub struct Peripherals { - pub pins: Pins, -} - +pub mod adc; #[cfg(feature = "device-selected")] -impl Peripherals { - fn new(dp: pac::Peripherals) -> Self { - Self { - #[cfg(feature = "atmega4809")] - pins: Pins::new(dp.PORTA, dp.PORTB, dp.PORTC, dp.PORTD, dp.PORTE, dp.PORTF), - } - } +pub use adc::Adc; - pub fn take() -> Option { - pac::Peripherals::take().map(Self::new) - } +#[cfg(feature = "device-selected")] +#[macro_export] +macro_rules! pins { + ($p:expr) => { + $crate::Pins::new( + &$p.PORTMUX, &$p.CLKCTRL, &$p.CPU, $p.PORTA, $p.PORTB, $p.PORTC, $p.PORTD, $p.PORTE, $p.PORTF, + ) + }; } + +#[cfg(feature = "device-selected")] +pub mod simple_pwm; diff --git a/mcu/atxmega-hal/src/port.rs b/mcu/atxmega-hal/src/port.rs index 7924b986e1..647ffbb079 100644 --- a/mcu/atxmega-hal/src/port.rs +++ b/mcu/atxmega-hal/src/port.rs @@ -1,5 +1,20 @@ +use avr_device::atmega4809::clkctrl; +use avr_hal_generic::port; + +fn init(portmux: &crate::pac::PORTMUX, clkctrl: &crate::pac::CLKCTRL, cpu: &crate::pac::CPU) { + portmux.usartroutea.write(|w| w.usart1().alt1().usart3().alt1()); + // PORTMUX setting for TCA -> all outputs [0:2] point to PORTB pins [0:2] timer + portmux.tcaroutea.write(|w|w.tca0().portb()); + + portmux.tcbroutea.write(|w|w.tcb0().set_bit().tcb1().set_bit()); + + cpu.ccp.write(|w|w.ccp().ioreg()); + clkctrl.mclkctrlb.write(|w|w.pen().clear_bit()); +} + #[cfg(feature = "atmega4809")] avr_hal_generic::impl_port_new! { + init: |portmux,clkctrl,cpu|{init(portmux,clkctrl, cpu)}, enum Ports { PORTA: crate::pac::PORTA, PORTB: crate::pac::PORTB, diff --git a/mcu/atxmega-hal/src/usart.rs b/mcu/atxmega-hal/src/usart.rs new file mode 100644 index 0000000000..f12fac090e --- /dev/null +++ b/mcu/atxmega-hal/src/usart.rs @@ -0,0 +1,40 @@ +#[allow(unused_imports)] +use crate::port; +pub use avr_hal_generic::usart::*; + +pub type Usart = + avr_hal_generic::usart::Usart; +pub type UsartWriter = + avr_hal_generic::usart::UsartWriter; +pub type UsartReader = + avr_hal_generic::usart::UsartReader; + +#[cfg(feature = "atmega4809")] +pub type Usart1 = Usart< + crate::pac::USART1, + port::Pin, + port::Pin, + CLOCK, +>; +#[cfg(feature = "atmega4809")] +avr_hal_generic::impl_usart_new! { + hal: crate::Atxmega, + peripheral: crate::pac::USART1, + rx: port::PC5, + tx: port::PC4, +} + +#[cfg(feature = "atmega4809")] +pub type Usart3 = Usart< + crate::pac::USART3, + port::Pin, + port::Pin, + CLOCK, +>; +#[cfg(feature = "atmega4809")] +avr_hal_generic::impl_usart_new! { + hal: crate::Atxmega, + peripheral: crate::pac::USART3, + rx: port::PB5, + tx: port::PB4, +} From 5cbc0f519ba65ca4bfdfe60577c6907c7f5341ea Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Mon, 28 Oct 2024 12:41:58 +0100 Subject: [PATCH 7/8] feat: add nano every to ravedude --- ravedude/src/avrdude/mod.rs | 8 ++++++++ ravedude/src/boards.toml | 17 +++++++++++++++++ ravedude/src/config.rs | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/ravedude/src/avrdude/mod.rs b/ravedude/src/avrdude/mod.rs index 65224e4ba8..dfbd84d839 100644 --- a/ravedude/src/avrdude/mod.rs +++ b/ravedude/src/avrdude/mod.rs @@ -95,6 +95,14 @@ impl Avrdude { command = command.arg("-b").arg(baudrate.to_string()); } + for option in options.extra_options.iter().flatten() { + command = command.arg("-U").arg(option) + } + + if let Some(touch_rate) = options.touch_baudrate { + command = command.arg("-r").arg(touch_rate.to_string()); + } + // TODO: Check that `bin` does not contain : let mut flash_instruction: std::ffi::OsString = "flash:w:".into(); flash_instruction.push(bin); diff --git a/ravedude/src/boards.toml b/ravedude/src/boards.toml index 52a5cba2db..2dd281df46 100644 --- a/ravedude/src/boards.toml +++ b/ravedude/src/boards.toml @@ -48,6 +48,23 @@ [nano-new.usb-info] error = "Not able to guess port" +[nano-every] + name = "Arduino Every" + + [nano-every.reset] + automatic = true + + [nano-every.avrdude] + programmer = "jtag2updi" + partno = "atmega4809" + baudrate = 115200 + do-chip-erase = true + extra-options = ["fuse2:w:0x01:m", "fuse5:w:0xC9:m", "fuse8:w:0x00:m"] + touch-baudrate = 1200 + + [nano-every.usb-info] + port-ids = [{ vid = 0x2341, pid = 0x0058 }] + [leonardo] name = "Arduino Leonardo" diff --git a/ravedude/src/config.rs b/ravedude/src/config.rs index 8e688f22ff..da1636baf1 100644 --- a/ravedude/src/config.rs +++ b/ravedude/src/config.rs @@ -131,6 +131,8 @@ pub struct BoardAvrdudeOptions { // Option pub baudrate: Option>, pub do_chip_erase: Option, + pub extra_options: Option>, + pub touch_baudrate: Option, } impl BoardAvrdudeOptions { pub fn merge(self, base: Self) -> Self { @@ -139,6 +141,8 @@ impl BoardAvrdudeOptions { partno: self.partno.or(base.partno), baudrate: self.baudrate.or(base.baudrate), do_chip_erase: self.do_chip_erase.or(base.do_chip_erase), + extra_options: self.extra_options.or(base.extra_options), + touch_baudrate: self.touch_baudrate.or(base.touch_baudrate), } } } From ed6a144a1858ddb84e51a66c61cca86c2d61b7be Mon Sep 17 00:00:00 2001 From: Florian Bezannier Date: Tue, 7 Jan 2025 20:20:16 +0100 Subject: [PATCH 8/8] fixup! feat: add nano every support --- examples/arduino-nano-every/Cargo.toml | 2 +- mcu/atxmega-hal/src/port.rs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/arduino-nano-every/Cargo.toml b/examples/arduino-nano-every/Cargo.toml index 1495c8fe06..d087d8984a 100644 --- a/examples/arduino-nano-every/Cargo.toml +++ b/examples/arduino-nano-every/Cargo.toml @@ -21,4 +21,4 @@ path = "../../mcu/atxmega-hal" [dependencies.avr-device] -path = "../../../avr-device" +version = "0.7" diff --git a/mcu/atxmega-hal/src/port.rs b/mcu/atxmega-hal/src/port.rs index 647ffbb079..d7b1214365 100644 --- a/mcu/atxmega-hal/src/port.rs +++ b/mcu/atxmega-hal/src/port.rs @@ -1,6 +1,3 @@ -use avr_device::atmega4809::clkctrl; -use avr_hal_generic::port; - fn init(portmux: &crate::pac::PORTMUX, clkctrl: &crate::pac::CLKCTRL, cpu: &crate::pac::CPU) { portmux.usartroutea.write(|w| w.usart1().alt1().usart3().alt1()); // PORTMUX setting for TCA -> all outputs [0:2] point to PORTB pins [0:2] timer