Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add port implementations for AVR XMEGA / ATtiny404 #357

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ jobs:
name: attiny2313
spec: attiny2313
crate: attiny-hal
- type: mcu
name: attiny404
spec: attiny404
crate: avrxmega-hal
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
# MCU HAL crates
"mcu/atmega-hal",
"mcu/attiny-hal",
"mcu/avrxmega-hal",

# Higher level crates
"arduino-hal",
Expand Down
231 changes: 231 additions & 0 deletions avr-hal-generic/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,237 @@ macro_rules! impl_port_traditional {
};
}

#[macro_export]
macro_rules! impl_port_xmega {
(
enum Ports {
$($PortName:ident: ($Port:ty),)+
}

$(#[$pins_attr:meta])*
pub struct Pins {
$($pin:ident: $Pin:ident = ($PinPort:ty, $PinPortName:ident, $pin_num:expr, $pin_pinctrl_reg:ident),)+
}
) => {
/// 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<MODE, PIN = Dynamic> = $crate::port::Pin<MODE, PIN>;

$(#[$pins_attr])*
pub struct Pins {
$(pub $pin: Pin<
mode::Input<mode::Floating>,
$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.
//
// (TODO verify this statement quantitatively? This implementation detail was
// copied from impl_port_traditional, but it makes some things more complicated,
// like accessing PINxCTRL to configure pull-up.
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 => {
if pull_up {
match self.mask {
0x01 => (*<$Port>::ptr()).pin0ctrl.modify(|_, w| w.pullupen().set_bit()),
0x02 => (*<$Port>::ptr()).pin1ctrl.modify(|_, w| w.pullupen().set_bit()),
0x04 => (*<$Port>::ptr()).pin2ctrl.modify(|_, w| w.pullupen().set_bit()),
0x08 => (*<$Port>::ptr()).pin3ctrl.modify(|_, w| w.pullupen().set_bit()),
0x10 => (*<$Port>::ptr()).pin4ctrl.modify(|_, w| w.pullupen().set_bit()),
0x20 => (*<$Port>::ptr()).pin5ctrl.modify(|_, w| w.pullupen().set_bit()),
0x40 => (*<$Port>::ptr()).pin6ctrl.modify(|_, w| w.pullupen().set_bit()),
0x80 => (*<$Port>::ptr()).pin7ctrl.modify(|_, w| w.pullupen().set_bit()),
// TODO exhaustive match with an enum?
//_ => unreachable!()
_ => {}
}
} else {
match self.mask {
0x01 => (*<$Port>::ptr()).pin0ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x02 => (*<$Port>::ptr()).pin1ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x04 => (*<$Port>::ptr()).pin2ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x08 => (*<$Port>::ptr()).pin3ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x10 => (*<$Port>::ptr()).pin4ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x20 => (*<$Port>::ptr()).pin5ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x40 => (*<$Port>::ptr()).pin6ctrl.modify(|_, w| w.pullupen().clear_bit()),
0x80 => (*<$Port>::ptr()).pin7ctrl.modify(|_, w| w.pullupen().clear_bit()),
// TODO exhaustive match with an enum?
//_ => unreachable!()
_ => {}
}
}
(*<$Port>::ptr()).dirclr.write(|w| {
w.bits(self.mask)
});
})+
}
}
}

$(
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) {
if pull_up {
(*<$PinPort>::ptr()).$pin_pinctrl_reg.modify(|_, w| w.pullupen().set_bit());
} else {
(*<$PinPort>::ptr()).$pin_pinctrl_reg.modify(|_, w| w.pullupen().clear_bit());
}
(*<$PinPort>::ptr()).dirclr.write(|w| {
w.bits(1 << $pin_num)
});
}
}
)+
};
}

#[macro_export]
macro_rules! renamed_pins {
(
Expand Down
25 changes: 25 additions & 0 deletions avr-specs/avr-attiny404.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"arch": "avr",
"atomic-cas": false,
"cpu": "attiny404",
"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",
"llvm-target": "avr-unknown-unknown",
"max-atomic-width": 8,
"no-default-libraries": false,
"pre-link-args": {
"gcc": [
"-mmcu=attiny404"
]
},
"target-c-int-width": "16",
"target-pointer-width": "16"
}
3 changes: 3 additions & 0 deletions avr-specs/sync-from-upstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"attiny167": {
"cpu": "attiny167",
},
"attiny404": {
"cpu": "attiny404",
},
"attiny2313": {
"cpu": "attiny2313",
},
Expand Down
28 changes: 28 additions & 0 deletions mcu/avrxmega-hal/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "avrxmega-hal"
version = "0.1.0"
edition = "2018"

[features]
rt = ["avr-device/rt"]
device-selected = []
attiny404 = ["avr-device/attiny404", "device-selected"]

critical-section-impl = ["avr-device/critical-section-impl"]

# 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.5"

# 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"]
55 changes: 55 additions & 0 deletions mcu/avrxmega-hal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![no_std]

//! `avrxmega-hal`
//! =============
//! Common HAL (hardware abstraction layer) for AVR XMEGA microcontrollers.
//!
//! **Note**: This version of the documentation was built for
#![cfg_attr(feature = "attiny404", doc = "**ATtiny404**.")]
//! 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:
//!
//! ```text
//! cargo doc --features <your-mcu> --open
//! ```

#[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

* attiny404
"
);

/// Reexport of `attiny404` from `avr-device`
#[cfg(feature = "attiny404")]
pub use avr_device::attiny404 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;

#[cfg(feature = "device-selected")]
pub use pac::Peripherals;

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 use port::Pins;

#[cfg(feature = "attiny404")]
#[macro_export]
macro_rules! pins {
($p:expr) => {
$crate::Pins::new($p.PORTA, $p.PORTB)
};
}
Loading