1pub use embedded_hal::digital::PinState;
2
3use crate::ioconfig::FilterClockSelect;
4use crate::ioconfig::FilterType;
5#[cfg(feature = "vor1x")]
6use crate::{PeripheralSelect, sysconfig::enable_peripheral_clock};
7
8pub use crate::InvalidOffsetError;
9pub use crate::Port;
10pub use crate::ioconfig::regs::Pull;
11use crate::ioconfig::regs::{FunctionSelect, IoConfig, MmioIoConfig};
12use crate::pins::PinId;
13
14use super::Pin;
15
16#[derive(Debug, PartialEq, Eq)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub enum InterruptEdge {
19 HighToLow,
20 LowToHigh,
21 BothEdges,
22}
23
24#[derive(Debug, PartialEq, Eq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum InterruptLevel {
27 Low = 0,
28 High = 1,
29}
30
31#[derive(Debug, PartialEq, Eq, Clone, Copy)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub struct DynPinId {
35 port: Port,
36 offset: u8,
38}
39
40#[derive(Debug, thiserror::Error)]
41#[cfg(feature = "vor4x")]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43#[error("port G does not support interrupts")]
44pub struct PortDoesNotSupportInterrupts;
45
46impl DynPinId {
47 pub const fn new_unchecked(port: Port, offset: usize) -> Self {
49 if offset >= port.max_offset() {
50 panic!("Pin ID construction: offset is out of range");
51 }
52 DynPinId {
53 port,
54 offset: offset as u8,
55 }
56 }
57
58 pub const fn new(port: Port, offset: usize) -> Result<Self, InvalidOffsetError> {
59 if offset >= port.max_offset() {
60 return Err(InvalidOffsetError { offset, port });
61 }
62 Ok(DynPinId {
63 port,
64 offset: offset as u8,
65 })
66 }
67
68 pub const fn port(&self) -> Port {
69 self.port
70 }
71
72 pub const fn offset(&self) -> usize {
73 self.offset as usize
74 }
75
76 #[cfg(feature = "vor4x")]
78 pub fn irq(&self) -> Result<va416xx::Interrupt, PortDoesNotSupportInterrupts> {
79 if self.port() == Port::G {
80 return Err(PortDoesNotSupportInterrupts);
81 }
82 Ok(self.irq_unchecked())
83 }
84
85 #[cfg(feature = "vor4x")]
87 pub const fn irq_unchecked(&self) -> va416xx::Interrupt {
88 match self.port() {
89 Port::A => match self.offset() {
90 0 => va416xx::Interrupt::PORTA0,
91 1 => va416xx::Interrupt::PORTA1,
92 2 => va416xx::Interrupt::PORTA2,
93 3 => va416xx::Interrupt::PORTA3,
94 4 => va416xx::Interrupt::PORTA4,
95 5 => va416xx::Interrupt::PORTA5,
96 6 => va416xx::Interrupt::PORTA6,
97 7 => va416xx::Interrupt::PORTA7,
98 8 => va416xx::Interrupt::PORTA8,
99 9 => va416xx::Interrupt::PORTA9,
100 10 => va416xx::Interrupt::PORTA10,
101 11 => va416xx::Interrupt::PORTA11,
102 12 => va416xx::Interrupt::PORTA12,
103 13 => va416xx::Interrupt::PORTA13,
104 14 => va416xx::Interrupt::PORTA14,
105 15 => va416xx::Interrupt::PORTA15,
106 _ => unreachable!(),
107 },
108 Port::B => match self.offset() {
109 0 => va416xx::Interrupt::PORTB0,
110 1 => va416xx::Interrupt::PORTB1,
111 2 => va416xx::Interrupt::PORTB2,
112 3 => va416xx::Interrupt::PORTB3,
113 4 => va416xx::Interrupt::PORTB4,
114 5 => va416xx::Interrupt::PORTB5,
115 6 => va416xx::Interrupt::PORTB6,
116 7 => va416xx::Interrupt::PORTB7,
117 8 => va416xx::Interrupt::PORTB8,
118 9 => va416xx::Interrupt::PORTB9,
119 10 => va416xx::Interrupt::PORTB10,
120 11 => va416xx::Interrupt::PORTB11,
121 12 => va416xx::Interrupt::PORTB12,
122 13 => va416xx::Interrupt::PORTB13,
123 14 => va416xx::Interrupt::PORTB14,
124 15 => va416xx::Interrupt::PORTB15,
125 _ => unreachable!(),
126 },
127 Port::C => match self.offset() {
128 0 => va416xx::Interrupt::PORTC0,
129 1 => va416xx::Interrupt::PORTC1,
130 2 => va416xx::Interrupt::PORTC2,
131 3 => va416xx::Interrupt::PORTC3,
132 4 => va416xx::Interrupt::PORTC4,
133 5 => va416xx::Interrupt::PORTC5,
134 6 => va416xx::Interrupt::PORTC6,
135 7 => va416xx::Interrupt::PORTC7,
136 8 => va416xx::Interrupt::PORTC8,
137 9 => va416xx::Interrupt::PORTC9,
138 10 => va416xx::Interrupt::PORTC10,
139 11 => va416xx::Interrupt::PORTC11,
140 12 => va416xx::Interrupt::PORTC12,
141 13 => va416xx::Interrupt::PORTC13,
142 14 => va416xx::Interrupt::PORTC14,
143 15 => va416xx::Interrupt::PORTC15,
144 _ => unreachable!(),
145 },
146 Port::D => match self.offset() {
147 0 => va416xx::Interrupt::PORTD0,
148 1 => va416xx::Interrupt::PORTD1,
149 2 => va416xx::Interrupt::PORTD2,
150 3 => va416xx::Interrupt::PORTD3,
151 4 => va416xx::Interrupt::PORTD4,
152 5 => va416xx::Interrupt::PORTD5,
153 6 => va416xx::Interrupt::PORTD6,
154 7 => va416xx::Interrupt::PORTD7,
155 8 => va416xx::Interrupt::PORTD8,
156 9 => va416xx::Interrupt::PORTD9,
157 10 => va416xx::Interrupt::PORTD10,
158 11 => va416xx::Interrupt::PORTD11,
159 12 => va416xx::Interrupt::PORTD12,
160 13 => va416xx::Interrupt::PORTD13,
161 14 => va416xx::Interrupt::PORTD14,
162 15 => va416xx::Interrupt::PORTD15,
163 _ => unreachable!(),
164 },
165 Port::E => match self.offset() {
166 0 => va416xx::Interrupt::PORTE0,
167 1 => va416xx::Interrupt::PORTE1,
168 2 => va416xx::Interrupt::PORTE2,
169 3 => va416xx::Interrupt::PORTE3,
170 4 => va416xx::Interrupt::PORTE4,
171 5 => va416xx::Interrupt::PORTE5,
172 6 => va416xx::Interrupt::PORTE6,
173 7 => va416xx::Interrupt::PORTE7,
174 8 => va416xx::Interrupt::PORTE8,
175 9 => va416xx::Interrupt::PORTE9,
176 10 => va416xx::Interrupt::PORTE10,
177 11 => va416xx::Interrupt::PORTE11,
178 12 => va416xx::Interrupt::PORTE12,
179 13 => va416xx::Interrupt::PORTE13,
180 14 => va416xx::Interrupt::PORTE14,
181 15 => va416xx::Interrupt::PORTE15,
182 _ => unreachable!(),
183 },
184 Port::F => match self.offset() {
185 0 => va416xx::Interrupt::PORTF0,
186 1 => va416xx::Interrupt::PORTF1,
187 2 => va416xx::Interrupt::PORTF2,
188 3 => va416xx::Interrupt::PORTF3,
189 4 => va416xx::Interrupt::PORTF4,
190 5 => va416xx::Interrupt::PORTF5,
191 6 => va416xx::Interrupt::PORTF6,
192 7 => va416xx::Interrupt::PORTF7,
193 8 => va416xx::Interrupt::PORTF8,
194 9 => va416xx::Interrupt::PORTF9,
195 10 => va416xx::Interrupt::PORTF10,
196 11 => va416xx::Interrupt::PORTF11,
197 12 => va416xx::Interrupt::PORTF12,
198 13 => va416xx::Interrupt::PORTF13,
199 14 => va416xx::Interrupt::PORTF14,
200 15 => va416xx::Interrupt::PORTF15,
201 _ => unreachable!(),
202 },
203 Port::G => panic!("port G does not have interrupts"),
204 }
205 }
206}
207
208pub struct LowLevelGpio {
210 gpio: super::regs::MmioGpio<'static>,
211 ioconfig: MmioIoConfig<'static>,
212 id: DynPinId,
213}
214
215impl core::fmt::Debug for LowLevelGpio {
216 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
217 f.debug_struct("LowLevelGpio")
218 .field("gpio", &self.gpio.port())
219 .field("id", &self.id)
220 .finish()
221 }
222}
223
224impl LowLevelGpio {
225 pub fn new_with_pin<I: PinId>(_pin: Pin<I>) -> Self {
229 Self::new(I::ID)
230 }
231
232 pub fn new(id: DynPinId) -> Self {
234 LowLevelGpio {
235 gpio: super::regs::Gpio::new_mmio(id.port),
236 ioconfig: IoConfig::new_mmio(),
237 id,
238 }
239 }
240
241 #[inline]
242 pub fn id(&self) -> DynPinId {
243 self.id
244 }
245
246 #[inline]
247 pub fn port(&self) -> Port {
248 self.id.port()
249 }
250
251 #[inline]
252 pub fn offset(&self) -> usize {
253 self.id.offset()
254 }
255
256 pub fn configure_as_input_floating(&mut self) {
257 self.ioconfig.modify_pin_config(self.id, |mut config| {
258 config.set_funsel(FunctionSelect::Sel0);
259 config.set_io_disable(false);
260 config.set_invert_input(false);
261 config.set_open_drain(false);
262 config.set_pull_enable(false);
263 config.set_pull_when_output_active(false);
264 config.set_invert_output(false);
265 config.set_input_enable_when_output(false);
266 config
267 });
268 self.gpio.modify_dir(|mut dir| {
269 dir &= !(1 << self.id.offset());
270 dir
271 });
272 }
273
274 pub fn configure_as_input_with_pull(&mut self, pull: Pull) {
275 self.ioconfig.modify_pin_config(self.id, |mut config| {
276 config.set_funsel(FunctionSelect::Sel0);
277 config.set_io_disable(false);
278 config.set_invert_input(false);
279 config.set_open_drain(false);
280 config.set_pull_enable(true);
281 config.set_pull_dir(pull);
282 config.set_pull_when_output_active(false);
283 config.set_invert_output(false);
284 config.set_input_enable_when_output(false);
285 config
286 });
287 self.gpio.modify_dir(|mut dir| {
288 dir &= !(1 << self.id.offset());
289 dir
290 });
291 }
292
293 pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
294 self.ioconfig.modify_pin_config(self.id, |mut config| {
295 config.set_funsel(FunctionSelect::Sel0);
296 config.set_io_disable(false);
297 config.set_invert_input(false);
298 config.set_open_drain(false);
299 config.set_pull_enable(false);
300 config.set_pull_when_output_active(false);
301 config.set_invert_output(false);
302 config.set_input_enable_when_output(true);
303 config
304 });
305 match init_level {
306 PinState::Low => self.gpio.write_clr_out(self.mask_32()),
307 PinState::High => self.gpio.write_set_out(self.mask_32()),
308 }
309 self.gpio.modify_dir(|mut dir| {
310 dir |= 1 << self.id.offset();
311 dir
312 });
313 }
314
315 pub fn configure_as_output_open_drain(&mut self, init_level: PinState) {
316 self.ioconfig.modify_pin_config(self.id, |mut config| {
317 config.set_funsel(FunctionSelect::Sel0);
318 config.set_io_disable(false);
319 config.set_invert_input(false);
320 config.set_open_drain(true);
321 config.set_pull_enable(true);
322 config.set_pull_dir(Pull::Up);
323 config.set_pull_when_output_active(false);
324 config.set_invert_output(false);
325 config.set_input_enable_when_output(true);
326 config
327 });
328 let mask32 = self.mask_32();
329 match init_level {
330 PinState::Low => self.gpio.write_clr_out(mask32),
331 PinState::High => self.gpio.write_set_out(mask32),
332 }
333 self.gpio.modify_dir(|mut dir| {
334 dir |= mask32;
335 dir
336 });
337 }
338
339 pub fn configure_as_peripheral_pin(&mut self, fun_sel: FunctionSelect, pull: Option<Pull>) {
340 self.ioconfig.modify_pin_config(self.id, |mut config| {
341 config.set_funsel(fun_sel);
342 config.set_io_disable(false);
343 config.set_invert_input(false);
344 config.set_open_drain(false);
345 config.set_pull_enable(pull.is_some());
346 config.set_pull_dir(pull.unwrap_or(Pull::Up));
347 config.set_invert_output(false);
348 config
349 });
350 }
351
352 #[inline]
353 pub fn is_high(&self) -> bool {
354 (self.gpio.read_data_in() >> self.offset()) & 1 == 1
355 }
356
357 #[inline]
358 pub fn is_low(&self) -> bool {
359 !self.is_high()
360 }
361
362 #[inline]
363 pub fn set_high(&mut self) {
364 self.gpio.write_set_out(self.mask_32());
365 }
366
367 #[inline]
368 pub fn set_low(&mut self) {
369 self.gpio.write_clr_out(self.mask_32());
370 }
371
372 #[inline]
373 pub fn is_set_high(&self) -> bool {
374 (self.gpio.read_data_out() >> self.offset()) & 1 == 1
375 }
376
377 #[inline]
378 pub fn is_set_low(&self) -> bool {
379 !self.is_set_high()
380 }
381
382 #[inline]
383 pub fn toggle(&mut self) {
384 self.gpio.write_tog_out(self.mask_32());
385 }
386
387 #[cfg(feature = "vor1x")]
388 pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
389 if irq_cfg.route {
390 self.configure_irqsel(irq_cfg.id);
391 }
392 if irq_cfg.enable_in_nvic {
393 unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
394 }
395 self.gpio.modify_irq_enable(|mut value| {
396 value |= 1 << self.id.offset;
397 value
398 });
399 }
400
401 #[cfg(feature = "vor4x")]
402 pub fn enable_interrupt(
403 &mut self,
404 enable_in_nvic: bool,
405 ) -> Result<(), PortDoesNotSupportInterrupts> {
406 if enable_in_nvic {
407 unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
408 }
409 self.gpio.modify_irq_enable(|mut value| {
410 value |= 1 << self.id.offset;
411 value
412 });
413 Ok(())
414 }
415
416 #[cfg(feature = "vor1x")]
417 pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
418 if reset_irqsel {
419 self.reset_irqsel();
420 }
421 self.gpio.modify_irq_enable(|mut value| {
423 value &= !(1 << self.id.offset);
424 value
425 });
426 }
427
428 #[cfg(feature = "vor4x")]
429 pub fn disable_interrupt(&mut self) {
430 self.gpio.modify_irq_enable(|mut value| {
431 value &= !(1 << self.id.offset);
432 value
433 });
434 }
435
436 #[inline]
439 pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
440 let mask32 = self.mask_32();
441 self.gpio.modify_irq_sen(|mut value| {
442 value &= !mask32;
443 value
444 });
445 match edge_type {
446 InterruptEdge::HighToLow => {
447 self.gpio.modify_irq_evt(|mut value| {
448 value &= !mask32;
449 value
450 });
451 }
452 InterruptEdge::LowToHigh => {
453 self.gpio.modify_irq_evt(|mut value| {
454 value |= mask32;
455 value
456 });
457 }
458 InterruptEdge::BothEdges => {
459 self.gpio.modify_irq_edge(|mut value| {
460 value |= mask32;
461 value
462 });
463 }
464 }
465 }
466
467 #[inline]
469 pub fn configure_level_interrupt(&mut self, level: InterruptLevel) {
470 let mask32 = self.mask_32();
471 self.gpio.modify_irq_sen(|mut value| {
472 value |= mask32;
473 value
474 });
475 if level == InterruptLevel::Low {
476 self.gpio.modify_irq_evt(|mut value| {
477 value &= !mask32;
478 value
479 });
480 } else {
481 self.gpio.modify_irq_evt(|mut value| {
482 value |= mask32;
483 value
484 });
485 }
486 }
487
488 #[inline]
490 pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClockSelect) {
491 self.ioconfig.modify_pin_config(self.id, |mut config| {
492 config.set_filter_type(filter);
493 config.set_filter_clk_sel(clksel);
494 config
495 });
496 }
497
498 #[inline]
500 pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
501 self.gpio.modify_pulse(|mut value| {
502 if enable {
503 value |= 1 << self.id.offset;
504 } else {
505 value &= !(1 << self.id.offset);
506 }
507 value
508 });
509 self.gpio.modify_pulsebase(|mut value| {
510 if default_state == PinState::High {
511 value |= 1 << self.id.offset;
512 } else {
513 value &= !(1 << self.id.offset);
514 }
515 value
516 });
517 }
518
519 #[inline]
521 pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
522 self.gpio.modify_delay1(|mut value| {
523 if delay_1 {
524 value |= 1 << self.id.offset;
525 } else {
526 value &= !(1 << self.id.offset);
527 }
528 value
529 });
530 self.gpio.modify_delay2(|mut value| {
531 if delay_2 {
532 value |= 1 << self.id.offset;
533 } else {
534 value &= !(1 << self.id.offset);
535 }
536 value
537 });
538 }
539
540 #[cfg(feature = "vor1x")]
541 pub fn configure_irqsel(&mut self, id: va108xx::Interrupt) {
543 let irqsel = unsafe { va108xx::Irqsel::steal() };
544 enable_peripheral_clock(PeripheralSelect::Irqsel);
545 match self.id().port() {
546 super::Port::A => {
548 irqsel
549 .porta(self.id().offset())
550 .write(|w| unsafe { w.bits(id as u32) });
551 }
552 super::Port::B => {
553 irqsel
554 .portb(self.id().offset())
555 .write(|w| unsafe { w.bits(id as u32) });
556 }
557 }
558 }
559
560 #[cfg(feature = "vor1x")]
561 pub fn reset_irqsel(&mut self) {
563 let irqsel = unsafe { va108xx::Irqsel::steal() };
564 enable_peripheral_clock(PeripheralSelect::Irqsel);
565 match self.id().port() {
566 super::Port::A => {
568 irqsel
569 .porta(self.id().offset())
570 .write(|w| unsafe { w.bits(u32::MAX) });
571 }
572 super::Port::B => {
573 irqsel
574 .portb(self.id().offset())
575 .write(|w| unsafe { w.bits(u32::MAX) });
576 }
577 }
578 }
579
580 #[inline(always)]
581 pub const fn mask_32(&self) -> u32 {
582 1 << self.id.offset()
583 }
584}