1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
use super::Register;
/// Represents whether a pin is an input or an output.
pub enum DataDirection {
/// The pin is exclusively used for reading signals.
Input,
/// The pin is exclusively used for sending signals.
Output,
}
/// An IO pin.
pub trait Pin {
/// The associated data direction register.
type DDR: Register<T = u8>;
/// The associated port register.
type PORT: Register<T = u8>;
/// Reads from the register will read input bits.
// FIXME: Writes to the register can be used to toggle bits.
type PIN: Register<T = u8>;
/// The numeric offset of the `Pin` in the register
const OFFSET: u8;
/// The mask of the pin used for accessing registers.
const MASK: u8;
/// Sets the data direction of the pin.
#[inline(always)]
fn set_direction(direction: DataDirection) {
match direction {
DataDirection::Input => Self::set_input(),
DataDirection::Output => Self::set_output(),
}
}
/// Sets the pin up as an input.
#[inline(always)]
fn set_input() {
Self::DDR::unset_mask_raw(Self::MASK);
}
/// Sets the pin up as an output.
#[inline(always)]
fn set_output() {
Self::DDR::set_mask_raw(Self::MASK);
}
/// Set the pin to high.
///
/// The pin must be configured as an output.
#[inline(always)]
fn set_high() {
Self::PORT::set_mask_raw(Self::MASK);
}
/// Set the pin to low.
///
/// The pin must be configured as an output.
#[inline(always)]
fn set_low() {
Self::PORT::unset_mask_raw(Self::MASK);
}
/// Toggles the pin.
///
/// The pin must be configured as an output.
#[inline(always)]
fn toggle() {
// FIXME: We can optimise this on post-2006 AVRs.
// http://www.avrfreaks.net/forum/toggle-state-output-pin
// set(Self::PIN, Self::MASK);
Self::PORT::toggle_raw(Self::MASK);
}
/// Check if the pin is currently high.
///
/// The pin must be configured as an input.
#[inline(always)]
fn is_high() -> bool {
Self::PIN::is_mask_set_raw(Self::MASK)
}
/// Checks if the pin is currently low.
///
/// The pin must be configured as an input.
#[inline(always)]
fn is_low() -> bool {
Self::PIN::is_clear_raw(Self::MASK)
}
}
/// Convenience macro to define a pin struct directly from the `DDR`, `PORT` and `PIN` `Register`s.
/// Requires you to `use Pin;` and `use register::*;` from this module.
///
/// Example: To define `pin::a0` from `DDRA`, `PORTA` and `PINA` registers, use `pin!(A, a0, 0);`.
macro_rules! pin {
($pin_group: ident, $mask_bit: expr) => {
paste::paste! {
// define new `pub struct` with the `Pin`'s name
pub struct [<$pin_group:lower $mask_bit>];
// impl `Pin` for the struct
impl Pin for [<$pin_group:lower $mask_bit>] {
/// Data Direction Register.
type DDR = [<DDR $pin_group>];
/// output PORT register.
type PORT = [<PORT $pin_group>];
/// input PIN register.
type PIN = [<PIN $pin_group>];
/// offset of the `Pin` in the register
const OFFSET: u8 = $mask_bit;
/// bit MASK for the corresponding pin
const MASK: u8 = 1 << $mask_bit;
}
}
};
}
// export macro to the crate
pub(crate) use pin;
/// Convenience macro to define all 8 pins grouped into a single PORT group.
/// Requires you to `use Pin;` and `use register::*;` from this module.
///
/// Example: To define `port::a0` through `port::a7` from `DDRA`, `PORTA` and `PINA` registers, use
/// `port!(A);`.
macro_rules! port {
($pin_group: ident) => {
pin!($pin_group, 0);
pin!($pin_group, 1);
pin!($pin_group, 2);
pin!($pin_group, 3);
pin!($pin_group, 4);
pin!($pin_group, 5);
pin!($pin_group, 6);
pin!($pin_group, 7);
};
}
// export macro to the crate
pub(crate) use port;
/// Convenience setter and getter macros to set multiple pins in the same register at once.
///
/// Example: To set `b0`, `b1` and `b7` to `0b110`, use `set_pins!(b0, b1, b7, 0b110);`.
macro_rules! set_pins {
([$base_pin: ident, $($pin: ident),*], $value: expr $(,)?) => {
// check that users have really used this macro only for pins in the same PORT group
let mut _typecheck = <$base_pin as Pin>::DDR::default();
$(_typecheck = <$pin as Pin>::DDR::default();)*
let mut _typecheck = <$base_pin as Pin>::PORT::default();
$(_typecheck = <$pin as Pin>::PORT::default();)*
let mut _typecheck = <$base_pin as Pin>::PIN::default();
$(_typecheck = <$pin as Pin>::PIN::default();)*
// set pins as outputs
let pin_mask = $base_pin::MASK $(| $pin::MASK)*;
<$base_pin as Pin>::DDR::set_mask_raw(pin_mask);
// set pins' values
<$base_pin as Pin>::PORT::write(
(<$base_pin as Pin>::PORT::read() & !pin_mask)
| set_pins!(@reverse_for_output_mask [$($pin, )*], [$base_pin], $value)
);
};
// base case: pass reversed array of pins to @output_mask
(@reverse_for_output_mask [], [$($pin_rev: ident),* $(,)?], $value: expr) => {
set_pins!(@output_mask 0, [$($pin_rev, )*], $value)
};
// otherwise: add front element to front of the reversed array of pins
(@reverse_for_output_mask [$first_pin: ident, $($pin: ident),* $(,)?], [$($pin_rev: ident),* $(,)?], $value: expr) => {
set_pins!(@reverse_for_output_mask [$($pin, )*], [$first_pin, $($pin_rev, )*], $value)
};
// add correct mask for the next pin
(@output_mask $position: expr, [$last_pin: ident, $($pin_rev: ident),* $(,)?], $value: expr) => {
(
// 1. `>>`-shift $value by $position bits to the right and extract the least significant bit
(($value >> $position) & 1)
// 2. `<<`-shift this extracted bit to the correct mask offset of the respective pin
<< <$last_pin as Pin>::OFFSET)
// 3. compute the logical `|` with the remaining pins' bitmask
| set_pins!(@output_mask $position, [$($pin_rev, )*], $value >> 1)
};
// end of recursion: all pins included in the mask
(@output_mask $position: expr, [], $value: expr) => {
0
};
}
// export macro to the crate
pub(crate) use set_pins;