vorago_shared_hal/gpio/
ll.rs

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/// Pin identifier for all physical pins exposed by Vorago MCUs.
32#[derive(Debug, PartialEq, Eq, Clone, Copy)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub struct DynPinId {
35    port: Port,
36    /// Offset within the port.
37    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    /// Unchecked constructor which panics on invalid offsets.
48    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    /// This function panics if the port is [Port::G].
77    #[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    /// This function panics if the port is [Port::G].
86    #[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
208/// Low-level driver structure for GPIO pins.
209pub 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    /// Create a new low-level GPIO pin instance from a given [Pin].
226    ///
227    /// Can be used for performing resource management of the [Pin]s.
228    pub fn new_with_pin<I: PinId>(_pin: Pin<I>) -> Self {
229        Self::new(I::ID)
230    }
231
232    /// Create a new low-level GPIO pin instance using only the [PinId].
233    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        // We only manipulate our own bit.
422        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    /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
437    /// When using edge mode, it is possible to generate interrupts on both edges as well
438    #[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    /// Configure which edge or level type triggers an interrupt
468    #[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    /// Only useful for input pins
489    #[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    /// Only useful for output pins.
499    #[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    /// Only useful for output pins
520    #[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    /// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
542    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            // Set the correct interrupt number in the IRQSEL register
547            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    /// Reset the IRQSEL peripheral value for this particular pin.
562    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            // Set the correct interrupt number in the IRQSEL register
567            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}