|
2 | 2 |
|
3 | 3 | use core::marker::PhantomData;
|
4 | 4 |
|
| 5 | +use embedded_hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin}; |
| 6 | + |
5 | 7 | use crate::pac::EXTI;
|
6 | 8 | use crate::syscfg::SysCfg;
|
7 | 9 |
|
@@ -745,6 +747,14 @@ macro_rules! gpio {
|
745 | 747 | _mode: self._mode,
|
746 | 748 | }
|
747 | 749 | }
|
| 750 | + |
| 751 | + /// Erases the pin number and the port from the type |
| 752 | + /// |
| 753 | + /// This is useful when you want to collect the pins into an array where you |
| 754 | + /// need all the elements to have the same type |
| 755 | + pub fn downgrade2(self) -> super::Pin<MODE>{ |
| 756 | + super::Pin::new($port_id, $i) |
| 757 | + } |
748 | 758 | }
|
749 | 759 |
|
750 | 760 | impl<MODE> OutputPin for $PXi<Output<MODE>> {
|
@@ -1037,3 +1047,109 @@ gpio!(GPIOK, gpiok, PK, 10, [
|
1037 | 1047 | PK6: (pk6, 6, Input<Floating>),
|
1038 | 1048 | PK7: (pk7, 7, Input<Floating>),
|
1039 | 1049 | ]);
|
| 1050 | + |
| 1051 | +/// Fully erased pin |
| 1052 | +pub struct Pin<MODE> { |
| 1053 | + // Bits 0-3: Pin, Bits 4-7: Port |
| 1054 | + pin_port: u8, |
| 1055 | + _mode: PhantomData<MODE>, |
| 1056 | +} |
| 1057 | + |
| 1058 | +impl<MODE> PinExt for Pin<MODE> { |
| 1059 | + type Mode = MODE; |
| 1060 | + |
| 1061 | + #[inline(always)] |
| 1062 | + fn pin_id(&self) -> u8 { |
| 1063 | + self.pin_port & 0x0f |
| 1064 | + } |
| 1065 | + #[inline(always)] |
| 1066 | + fn port_id(&self) -> u8 { |
| 1067 | + self.pin_port >> 4 |
| 1068 | + } |
| 1069 | +} |
| 1070 | + |
| 1071 | +impl<MODE> Pin<MODE> { |
| 1072 | + fn new(port: u8, pin: u8) -> Self { |
| 1073 | + Self { |
| 1074 | + pin_port: port << 4 | pin, |
| 1075 | + _mode: PhantomData, |
| 1076 | + } |
| 1077 | + } |
| 1078 | + |
| 1079 | + #[inline] |
| 1080 | + fn block(&self) -> &crate::pac::gpioa::RegisterBlock { |
| 1081 | + // This function uses pointer arithmetic instead of branching to be more efficient |
| 1082 | + |
| 1083 | + // The logic relies on the following assumptions: |
| 1084 | + // - GPIOA register is available on all chips |
| 1085 | + // - all gpio register blocks have the same layout |
| 1086 | + // - consecutive gpio register blocks have the same offset between them, namely 0x0400 |
| 1087 | + // - Pin::new was called with a valid port |
| 1088 | + |
| 1089 | + // FIXME could be calculated after const_raw_ptr_to_usize_cast stabilization #51910 |
| 1090 | + const GPIO_REGISTER_OFFSET: usize = 0x0400; |
| 1091 | + |
| 1092 | + let offset = GPIO_REGISTER_OFFSET * self.port_id() as usize; |
| 1093 | + let block_ptr = |
| 1094 | + (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock; |
| 1095 | + |
| 1096 | + unsafe { &*block_ptr } |
| 1097 | + } |
| 1098 | +} |
| 1099 | + |
| 1100 | +impl<MODE> OutputPin for Pin<Output<MODE>> { |
| 1101 | + type Error = core::convert::Infallible; |
| 1102 | + |
| 1103 | + fn set_low(&mut self) -> Result<(), Self::Error> { |
| 1104 | + // NOTE(unsafe) atomic write to a stateless register |
| 1105 | + unsafe { |
| 1106 | + self.block() |
| 1107 | + .bsrr |
| 1108 | + .write(|w| w.bits(1 << (self.pin_id() + 16))) |
| 1109 | + }; |
| 1110 | + Ok(()) |
| 1111 | + } |
| 1112 | + |
| 1113 | + fn set_high(&mut self) -> Result<(), Self::Error> { |
| 1114 | + // NOTE(unsafe) atomic write to a stateless register |
| 1115 | + unsafe { self.block().bsrr.write(|w| w.bits(1 << self.pin_id())) }; |
| 1116 | + Ok(()) |
| 1117 | + } |
| 1118 | +} |
| 1119 | + |
| 1120 | +impl<MODE> StatefulOutputPin for Pin<Output<MODE>> { |
| 1121 | + fn is_set_high(&self) -> Result<bool, Self::Error> { |
| 1122 | + self.is_set_low().map(|v| !v) |
| 1123 | + } |
| 1124 | + |
| 1125 | + fn is_set_low(&self) -> Result<bool, Self::Error> { |
| 1126 | + // NOTE(unsafe) atomic read with no side effects |
| 1127 | + Ok(self.block().odr.read().bits() & (1 << self.pin_id()) == 0) |
| 1128 | + } |
| 1129 | +} |
| 1130 | + |
| 1131 | +impl<MODE> toggleable::Default for Pin<Output<MODE>> {} |
| 1132 | + |
| 1133 | +impl<MODE> InputPin for Pin<Output<MODE>> { |
| 1134 | + type Error = core::convert::Infallible; |
| 1135 | + |
| 1136 | + fn is_high(&self) -> Result<bool, Self::Error> { |
| 1137 | + self.is_low().map(|v| !v) |
| 1138 | + } |
| 1139 | + |
| 1140 | + fn is_low(&self) -> Result<bool, Self::Error> { |
| 1141 | + Ok(self.block().idr.read().bits() & (1 << self.pin_id()) == 0) |
| 1142 | + } |
| 1143 | +} |
| 1144 | + |
| 1145 | +impl<MODE> InputPin for Pin<Input<MODE>> { |
| 1146 | + type Error = core::convert::Infallible; |
| 1147 | + |
| 1148 | + fn is_high(&self) -> Result<bool, Self::Error> { |
| 1149 | + self.is_low().map(|v| !v) |
| 1150 | + } |
| 1151 | + |
| 1152 | + fn is_low(&self) -> Result<bool, Self::Error> { |
| 1153 | + Ok(self.block().idr.read().bits() & (1 << self.pin_id()) == 0) |
| 1154 | + } |
| 1155 | +} |
0 commit comments