Skip to content

Commit 8c9d5fa

Browse files
committed
Add avrxmega crate; add port implementations for xmega / attiny404
1 parent b3a4c5d commit 8c9d5fa

File tree

5 files changed

+338
-0
lines changed

5 files changed

+338
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ members = [
1818
# MCU HAL crates
1919
"mcu/atmega-hal",
2020
"mcu/attiny-hal",
21+
"mcu/avrxmega-hal",
2122

2223
# Higher level crates
2324
"arduino-hal",

avr-hal-generic/src/port.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,237 @@ macro_rules! impl_port_traditional {
663663
};
664664
}
665665

666+
#[macro_export]
667+
macro_rules! impl_port_xmega {
668+
(
669+
enum Ports {
670+
$($PortName:ident: ($Port:ty),)+
671+
}
672+
673+
$(#[$pins_attr:meta])*
674+
pub struct Pins {
675+
$($pin:ident: $Pin:ident = ($PinPort:ty, $PinPortName:ident, $pin_num:expr, $pin_pinctrl_reg:ident),)+
676+
}
677+
) => {
678+
/// Type-alias for a pin type which can represent any concrete pin.
679+
///
680+
/// Sometimes it is easier to handle pins if they are all of the same type. By default,
681+
/// each pin gets its own distinct type in `avr-hal`, but by
682+
/// [downgrading][avr_hal_generic::port::Pin#downgrading], you can cast them into this
683+
/// "dynamic" type. Do note, however, that using this dynamic type has a runtime cost.
684+
pub type Pin<MODE, PIN = Dynamic> = $crate::port::Pin<MODE, PIN>;
685+
686+
$(#[$pins_attr])*
687+
pub struct Pins {
688+
$(pub $pin: Pin<
689+
mode::Input<mode::Floating>,
690+
$Pin,
691+
>,)+
692+
}
693+
694+
impl Pins {
695+
pub fn new(
696+
$(_: $Port,)+
697+
) -> Self {
698+
Self {
699+
$($pin: $crate::port::Pin::new(
700+
$Pin { _private: (), }
701+
),)+
702+
}
703+
}
704+
}
705+
706+
#[repr(u8)]
707+
pub enum DynamicPort {
708+
$($PortName,)+
709+
}
710+
711+
pub struct Dynamic {
712+
port: DynamicPort,
713+
// We'll store the mask instead of the pin number because this allows much less code to
714+
// be generated for the trait method implementations.
715+
//
716+
// (TODO verify this statement quantitatively? This implementation detail was
717+
// copied from impl_port_traditional, but it makes some things more complicated,
718+
// like accessing PINxCTRL to configure pull-up.
719+
mask: u8,
720+
}
721+
722+
impl Dynamic {
723+
fn new(port: DynamicPort, pin_num: u8) -> Self {
724+
Self {
725+
port,
726+
mask: 1 << pin_num,
727+
}
728+
}
729+
}
730+
731+
impl $crate::port::PinOps for Dynamic {
732+
type Dynamic = Self;
733+
734+
#[inline]
735+
fn into_dynamic(self) -> Self::Dynamic {
736+
self
737+
}
738+
739+
#[inline]
740+
unsafe fn out_set(&mut self) {
741+
match self.port {
742+
$(DynamicPort::$PortName => (*<$Port>::ptr()).outset.write(|w| {
743+
w.bits(self.mask)
744+
}),)+
745+
}
746+
}
747+
748+
#[inline]
749+
unsafe fn out_clear(&mut self) {
750+
match self.port {
751+
$(DynamicPort::$PortName => (*<$Port>::ptr()).outclr.write(|w| {
752+
w.bits(self.mask)
753+
}),)+
754+
}
755+
}
756+
757+
#[inline]
758+
unsafe fn out_toggle(&mut self) {
759+
match self.port {
760+
$(DynamicPort::$PortName => (*<$Port>::ptr()).outtgl.write(|w| {
761+
w.bits(self.mask)
762+
}),)+
763+
}
764+
}
765+
766+
#[inline]
767+
unsafe fn out_get(&self) -> bool {
768+
match self.port {
769+
$(DynamicPort::$PortName => (*<$Port>::ptr()).out.read().bits()
770+
& self.mask != 0,)+
771+
}
772+
}
773+
774+
#[inline]
775+
unsafe fn in_get(&self) -> bool {
776+
match self.port {
777+
$(DynamicPort::$PortName => (*<$Port>::ptr()).in_.read().bits()
778+
& self.mask != 0,)+
779+
}
780+
}
781+
782+
#[inline]
783+
unsafe fn make_output(&mut self) {
784+
match self.port {
785+
$(DynamicPort::$PortName => (*<$Port>::ptr()).dirset.write(|w| {
786+
w.bits(self.mask)
787+
}),)+
788+
}
789+
}
790+
791+
#[inline]
792+
unsafe fn make_input(&mut self, pull_up: bool) {
793+
match self.port {
794+
$(DynamicPort::$PortName => {
795+
if pull_up {
796+
match self.mask {
797+
0x01 => (*<$Port>::ptr()).pin0ctrl.modify(|_, w| w.pullupen().set_bit()),
798+
0x02 => (*<$Port>::ptr()).pin1ctrl.modify(|_, w| w.pullupen().set_bit()),
799+
0x04 => (*<$Port>::ptr()).pin2ctrl.modify(|_, w| w.pullupen().set_bit()),
800+
0x08 => (*<$Port>::ptr()).pin3ctrl.modify(|_, w| w.pullupen().set_bit()),
801+
0x10 => (*<$Port>::ptr()).pin4ctrl.modify(|_, w| w.pullupen().set_bit()),
802+
0x20 => (*<$Port>::ptr()).pin5ctrl.modify(|_, w| w.pullupen().set_bit()),
803+
0x40 => (*<$Port>::ptr()).pin6ctrl.modify(|_, w| w.pullupen().set_bit()),
804+
0x80 => (*<$Port>::ptr()).pin7ctrl.modify(|_, w| w.pullupen().set_bit()),
805+
// TODO exhaustive match with an enum?
806+
//_ => unreachable!()
807+
_ => {}
808+
}
809+
} else {
810+
match self.mask {
811+
0x01 => (*<$Port>::ptr()).pin0ctrl.modify(|_, w| w.pullupen().clear_bit()),
812+
0x02 => (*<$Port>::ptr()).pin1ctrl.modify(|_, w| w.pullupen().clear_bit()),
813+
0x04 => (*<$Port>::ptr()).pin2ctrl.modify(|_, w| w.pullupen().clear_bit()),
814+
0x08 => (*<$Port>::ptr()).pin3ctrl.modify(|_, w| w.pullupen().clear_bit()),
815+
0x10 => (*<$Port>::ptr()).pin4ctrl.modify(|_, w| w.pullupen().clear_bit()),
816+
0x20 => (*<$Port>::ptr()).pin5ctrl.modify(|_, w| w.pullupen().clear_bit()),
817+
0x40 => (*<$Port>::ptr()).pin6ctrl.modify(|_, w| w.pullupen().clear_bit()),
818+
0x80 => (*<$Port>::ptr()).pin7ctrl.modify(|_, w| w.pullupen().clear_bit()),
819+
// TODO exhaustive match with an enum?
820+
//_ => unreachable!()
821+
_ => {}
822+
}
823+
}
824+
(*<$Port>::ptr()).dirclr.write(|w| {
825+
w.bits(self.mask)
826+
});
827+
})+
828+
}
829+
}
830+
}
831+
832+
$(
833+
pub struct $Pin {
834+
_private: ()
835+
}
836+
837+
impl $crate::port::PinOps for $Pin {
838+
type Dynamic = Dynamic;
839+
840+
#[inline]
841+
fn into_dynamic(self) -> Self::Dynamic {
842+
Dynamic::new(DynamicPort::$PinPortName, $pin_num)
843+
}
844+
845+
#[inline]
846+
unsafe fn out_set(&mut self) {
847+
(*<$PinPort>::ptr()).outset.write(|w| {
848+
w.bits(1 << $pin_num)
849+
});
850+
}
851+
852+
#[inline]
853+
unsafe fn out_clear(&mut self) {
854+
(*<$PinPort>::ptr()).outclr.write(|w| {
855+
w.bits(1 << $pin_num)
856+
});
857+
}
858+
859+
#[inline]
860+
unsafe fn out_toggle(&mut self) {
861+
(*<$PinPort>::ptr()).outtgl.write(|w| w.bits(1 << $pin_num));
862+
}
863+
864+
#[inline]
865+
unsafe fn out_get(&self) -> bool {
866+
(*<$PinPort>::ptr()).out.read().bits() & (1 << $pin_num) != 0
867+
}
868+
869+
#[inline]
870+
unsafe fn in_get(&self) -> bool {
871+
(*<$PinPort>::ptr()).in_.read().bits() & (1 << $pin_num) != 0
872+
}
873+
874+
#[inline]
875+
unsafe fn make_output(&mut self) {
876+
(*<$PinPort>::ptr()).dirset.write(|w| {
877+
w.bits(1 << $pin_num)
878+
});
879+
}
880+
881+
#[inline]
882+
unsafe fn make_input(&mut self, pull_up: bool) {
883+
if pull_up {
884+
(*<$PinPort>::ptr()).$pin_pinctrl_reg.modify(|_, w| w.pullupen().set_bit());
885+
} else {
886+
(*<$PinPort>::ptr()).$pin_pinctrl_reg.modify(|_, w| w.pullupen().clear_bit());
887+
}
888+
(*<$PinPort>::ptr()).dirclr.write(|w| {
889+
w.bits(1 << $pin_num)
890+
});
891+
}
892+
}
893+
)+
894+
};
895+
}
896+
666897
#[macro_export]
667898
macro_rules! renamed_pins {
668899
(

mcu/avrxmega-hal/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "avrxmega-hal"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
[features]
7+
rt = ["avr-device/rt"]
8+
device-selected = []
9+
attiny404 = ["avr-device/attiny404", "device-selected"]
10+
11+
# Allow certain downstream crates to overwrite the device selection error by themselves.
12+
disable-device-selection-error = []
13+
14+
[dependencies]
15+
avr-hal-generic = { path = "../../avr-hal-generic/" }
16+
17+
[dependencies.avr-device]
18+
version = "0.4"
19+
20+
# Because this crate has its own check that at least one device is selected, we
21+
# can safely "circumvent" the check in `avr-device`.
22+
#
23+
# Why would we want that? Otherwise, as `avr-device` is compiled first, its
24+
# error will be shown and ours won't which leads to a degraded user experience
25+
# as the displayed error message does not really tell what needs to be done...
26+
features = ["device-selected"]

mcu/avrxmega-hal/src/lib.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#![no_std]
2+
3+
//! `avrxmega-hal`
4+
//! =============
5+
//! Common HAL (hardware abstraction layer) for AVR XMEGA microcontrollers.
6+
//!
7+
//! **Note**: This version of the documentation was built for
8+
#![cfg_attr(feature = "attiny404", doc = "**ATtiny404**.")]
9+
//! This means that only items which are available for this MCU are visible. If you are using
10+
//! a different chip, try building the documentation locally with:
11+
//!
12+
//! ```text
13+
//! cargo doc --features <your-mcu> --open
14+
//! ```
15+
16+
#[cfg(all(
17+
not(feature = "device-selected"),
18+
not(feature = "disable-device-selection-error")
19+
))]
20+
compile_error!(
21+
"This crate requires you to specify your target chip as a feature.
22+
23+
Please select one of the following
24+
25+
* attiny404
26+
"
27+
);
28+
29+
/// Reexport of `attiny404` from `avr-device`
30+
#[cfg(feature = "attiny404")]
31+
pub use avr_device::attiny404 as pac;
32+
33+
/// See [`avr_device::entry`](https://docs.rs/avr-device/latest/avr_device/attr.entry.html).
34+
#[cfg(feature = "rt")]
35+
pub use avr_device::entry;
36+
37+
#[cfg(feature = "device-selected")]
38+
pub use pac::Peripherals;
39+
40+
pub use avr_hal_generic::clock;
41+
pub use avr_hal_generic::delay;
42+
pub use avr_hal_generic::prelude;
43+
44+
#[cfg(feature = "device-selected")]
45+
pub mod port;
46+
#[cfg(feature = "device-selected")]
47+
pub use port::Pins;
48+
49+
#[cfg(feature = "attiny404")]
50+
#[macro_export]
51+
macro_rules! pins {
52+
($p:expr) => {
53+
$crate::Pins::new($p.PORTA, $p.PORTB)
54+
};
55+
}

mcu/avrxmega-hal/src/port.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pub use avr_hal_generic::port::{mode, PinOps, PinMode};
2+
3+
#[cfg(feature = "attiny404")]
4+
avr_hal_generic::impl_port_xmega! {
5+
enum Ports {
6+
PORTA: (crate::pac::PORTA),
7+
PORTB: (crate::pac::PORTB),
8+
}
9+
10+
pub struct Pins {
11+
pa0: PA0 = (crate::pac::PORTA, PORTA, 0, pin0ctrl),
12+
pa1: PA1 = (crate::pac::PORTA, PORTA, 1, pin1ctrl),
13+
pa2: PA2 = (crate::pac::PORTA, PORTA, 2, pin2ctrl),
14+
pa3: PA3 = (crate::pac::PORTA, PORTA, 3, pin3ctrl),
15+
pa4: PA4 = (crate::pac::PORTA, PORTA, 4, pin4ctrl),
16+
pa5: PA5 = (crate::pac::PORTA, PORTA, 5, pin5ctrl),
17+
pa6: PA6 = (crate::pac::PORTA, PORTA, 6, pin6ctrl),
18+
pa7: PA7 = (crate::pac::PORTA, PORTA, 7, pin7ctrl),
19+
20+
pb0: PB0 = (crate::pac::PORTB, PORTB, 0, pin0ctrl),
21+
pb1: PB1 = (crate::pac::PORTB, PORTB, 1, pin1ctrl),
22+
pb2: PB2 = (crate::pac::PORTB, PORTB, 2, pin2ctrl),
23+
pb3: PB3 = (crate::pac::PORTB, PORTB, 3, pin3ctrl),
24+
}
25+
}

0 commit comments

Comments
 (0)