va416xx_embassy/
lib.rs

1//! # Embassy-rs support for the Vorago VA416xx MCU family
2//!
3//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
4//! VA416xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
5//! peripherals provided by the VA416xx family for this purpose.
6//!
7//! ## Usage
8//!
9//! This library only exposes the [embassy::init] method which sets up the time driver. This
10//! function must be called once at the start of the application.
11//!
12//! This implementation requires two TIM peripherals provided by the VA108xx device.
13//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
14//! into the [init] method. If the interrupt handlers are provided by the library, the ID of the
15//! used TIM peripherals has to match the ID of the passed timer peripherals. Currently, this
16//! can only be checked at run-time, and a run-time assertion will panic on the embassy
17//! initialization in case of a missmatch.
18//!
19//! The application also requires two interrupt handlers to handle the timekeeper and alarm
20//! interrupts. By default, this library will define the interrupt handler inside the library
21//! itself by using the `irq-tim14-tim15` feature flag. This library exposes three combinations:
22//!
23//! - `irq-tim14-tim15`: Uses [pac::Interrupt::TIM14] for alarm and [pac::Interrupt::TIM15]
24//!   for timekeeper
25//! - `irq-tim13-tim14`: Uses [pac::Interrupt::TIM13] for alarm and [pac::Interrupt::TIM14]
26//!   for timekeeper
27//! - `irq-tim22-tim23`: Uses [pac::Interrupt::TIM22] for alarm and [pac::Interrupt::TIM23]
28//!   for timekeeper
29//!
30//! You can disable the default features and then specify one of the features above to use the
31//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
32//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
33//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
34//! method to pass the IRQ numbers to the library.
35//!
36//! ## Examples
37//!
38//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
39#![no_std]
40#![cfg_attr(docsrs, feature(doc_auto_cfg))]
41use va416xx_hal::{
42    clock::Clocks,
43    irq_router::enable_and_init_irq_router,
44    pac::{self, interrupt},
45    timer::{TimInstance, TIM_IRQ_OFFSET},
46};
47use vorago_shared_hal::embassy::time_driver;
48
49/// Macro to define the IRQ handlers for the time driver.
50///
51/// By default, the code generated by this macro will be defined inside the library depending on
52/// the feature flags specified. However, the macro is exported to allow users to specify the
53/// interrupt handlers themselves.
54///
55/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
56/// macro in the application code in case this macro is used there.
57#[macro_export]
58macro_rules! embassy_time_driver_irqs {
59    (
60        timekeeper_irq = $timekeeper_irq:ident,
61        alarm_irq = $alarm_irq:ident
62    ) => {
63        const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::$timekeeper_irq;
64
65        #[interrupt]
66        #[allow(non_snake_case)]
67        fn $timekeeper_irq() {
68            // Safety: We call it once here.
69            unsafe { $crate::time_driver().on_interrupt_timekeeping() }
70        }
71
72        const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
73
74        #[interrupt]
75        #[allow(non_snake_case)]
76        fn $alarm_irq() {
77            // Safety: We call it once here.
78            unsafe { $crate::time_driver().on_interrupt_alarm() }
79        }
80    };
81}
82
83// Provide three combinations of IRQs for the time driver by default.
84
85#[cfg(feature = "irq-tim14-tim15")]
86embassy_time_driver_irqs!(timekeeper_irq = TIM15, alarm_irq = TIM14);
87#[cfg(feature = "irq-tim13-tim14")]
88embassy_time_driver_irqs!(timekeeper_irq = TIM14, alarm_irq = TIM13);
89#[cfg(feature = "irq-tim22-tim23")]
90embassy_time_driver_irqs!(timekeeper_irq = TIM23, alarm_irq = TIM22);
91
92/// Initialization method for embassy
93///
94/// If the interrupt handlers are provided by the library, the ID of the
95/// used TIM peripherals has to match the ID of the passed timer peripherals. Currently, this
96/// can only be checked at run-time, and a run-time assertion will panic on the embassy
97/// initialization in case of a missmatch.
98pub fn init<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
99    timekeeper: TimekeeperTim,
100    alarm: AlarmTim,
101    clocks: &Clocks,
102) {
103    #[cfg(feature = "_irqs-in-lib")]
104    assert_eq!(
105        TimekeeperTim::ID.value(),
106        TIMEKEEPER_IRQ as u8 - TIM_IRQ_OFFSET as u8,
107        "Timekeeper TIM and IRQ missmatch"
108    );
109    #[cfg(feature = "_irqs-in-lib")]
110    assert_eq!(
111        AlarmTim::ID.value(),
112        ALARM_IRQ as u8 - TIM_IRQ_OFFSET as u8,
113        "Alarm TIM and IRQ missmatch"
114    );
115    enable_and_init_irq_router();
116    time_driver().__init(timekeeper, alarm, clocks)
117}