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 #[inline]
390 pub fn enable_interrupt_gpio_only(&mut self) {
391 self.gpio.modify_irq_enable(|mut value| {
392 value |= 1 << self.id.offset;
393 value
394 });
395 }
396
397 #[cfg(feature = "vor1x")]
403 pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
404 if irq_cfg.route {
405 self.configure_irqsel(irq_cfg.id);
406 }
407 if irq_cfg.enable_in_nvic {
408 unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
409 }
410 if gpio {
411 self.enable_interrupt_gpio_only();
412 }
413 }
414
415 #[cfg(feature = "vor4x")]
420 pub fn enable_interrupt(
421 &mut self,
422 enable_in_nvic: bool,
423 gpio: bool,
424 ) -> Result<(), PortDoesNotSupportInterrupts> {
425 if self.id().port() == Port::G {
426 return Err(PortDoesNotSupportInterrupts);
427 }
428 if enable_in_nvic {
429 unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
430 }
431 if gpio {
432 self.enable_interrupt_gpio_only();
433 }
434 Ok(())
435 }
436
437 #[cfg(feature = "vor1x")]
438 pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
439 if reset_irqsel {
440 self.reset_irqsel();
441 }
442 self.gpio.modify_irq_enable(|mut value| {
444 value &= !(1 << self.id.offset);
445 value
446 });
447 }
448
449 #[cfg(feature = "vor4x")]
450 pub fn disable_interrupt(&mut self) {
451 self.gpio.modify_irq_enable(|mut value| {
452 value &= !(1 << self.id.offset);
453 value
454 });
455 }
456
457 #[inline]
460 pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
461 let mask32 = self.mask_32();
462 self.gpio.modify_irq_sen(|mut value| {
463 value &= !mask32;
464 value
465 });
466 match edge_type {
467 InterruptEdge::HighToLow => {
468 self.gpio.modify_irq_evt(|mut value| {
469 value &= !mask32;
470 value
471 });
472 }
473 InterruptEdge::LowToHigh => {
474 self.gpio.modify_irq_evt(|mut value| {
475 value |= mask32;
476 value
477 });
478 }
479 InterruptEdge::BothEdges => {
480 self.gpio.modify_irq_edge(|mut value| {
481 value |= mask32;
482 value
483 });
484 }
485 }
486 }
487
488 #[inline]
490 pub fn configure_level_interrupt(&mut self, level: InterruptLevel) {
491 let mask32 = self.mask_32();
492 self.gpio.modify_irq_sen(|mut value| {
493 value |= mask32;
494 value
495 });
496 if level == InterruptLevel::Low {
497 self.gpio.modify_irq_evt(|mut value| {
498 value &= !mask32;
499 value
500 });
501 } else {
502 self.gpio.modify_irq_evt(|mut value| {
503 value |= mask32;
504 value
505 });
506 }
507 }
508
509 #[inline]
511 pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClockSelect) {
512 self.ioconfig.modify_pin_config(self.id, |mut config| {
513 config.set_filter_type(filter);
514 config.set_filter_clk_sel(clksel);
515 config
516 });
517 }
518
519 #[inline]
521 pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
522 self.gpio.modify_pulse(|mut value| {
523 if enable {
524 value |= 1 << self.id.offset;
525 } else {
526 value &= !(1 << self.id.offset);
527 }
528 value
529 });
530 self.gpio.modify_pulsebase(|mut value| {
531 if default_state == PinState::High {
532 value |= 1 << self.id.offset;
533 } else {
534 value &= !(1 << self.id.offset);
535 }
536 value
537 });
538 }
539
540 #[inline]
542 pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
543 self.gpio.modify_delay1(|mut value| {
544 if delay_1 {
545 value |= 1 << self.id.offset;
546 } else {
547 value &= !(1 << self.id.offset);
548 }
549 value
550 });
551 self.gpio.modify_delay2(|mut value| {
552 if delay_2 {
553 value |= 1 << self.id.offset;
554 } else {
555 value &= !(1 << self.id.offset);
556 }
557 value
558 });
559 }
560
561 #[cfg(feature = "vor1x")]
562 pub fn configure_irqsel(&mut self, id: va108xx::Interrupt) {
564 let irqsel = unsafe { va108xx::Irqsel::steal() };
565 enable_peripheral_clock(PeripheralSelect::Irqsel);
566 match self.id().port() {
567 super::Port::A => {
569 irqsel
570 .porta(self.id().offset())
571 .write(|w| unsafe { w.bits(id as u32) });
572 }
573 super::Port::B => {
574 irqsel
575 .portb(self.id().offset())
576 .write(|w| unsafe { w.bits(id as u32) });
577 }
578 }
579 }
580
581 #[cfg(feature = "vor1x")]
582 pub fn reset_irqsel(&mut self) {
584 let irqsel = unsafe { va108xx::Irqsel::steal() };
585 enable_peripheral_clock(PeripheralSelect::Irqsel);
586 match self.id().port() {
587 super::Port::A => {
589 irqsel
590 .porta(self.id().offset())
591 .write(|w| unsafe { w.bits(u32::MAX) });
592 }
593 super::Port::B => {
594 irqsel
595 .portb(self.id().offset())
596 .write(|w| unsafe { w.bits(u32::MAX) });
597 }
598 }
599 }
600
601 #[inline(always)]
602 pub const fn mask_32(&self) -> u32 {
603 1 << self.id.offset()
604 }
605}