sifli_hal/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt;
6
7#[cfg(feature = "set-msplim")]
8use core::arch::global_asm;
9
10mod macros;
11
12mod utils;
13
14pub mod rcc;
15pub mod gpio;
16pub mod timer;
17pub mod time;
18pub mod pmu;
19pub mod usart;
20pub mod adc;
21pub mod lcdc;
22pub mod dma;
23#[cfg(feature = "usb")]
24pub mod usb;
25#[cfg(feature = "_time-driver")]
26pub mod time_driver;
27
28// Reexports
29pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
30#[cfg(feature = "unstable-pac")]
31pub use sifli_pac as pac;
32#[cfg(not(feature = "unstable-pac"))]
33pub(crate) use sifli_pac as pac;
34
35/// Operating modes for peripherals.
36pub mod mode {
37    trait SealedMode {}
38
39    /// Operating mode for a peripheral.
40    #[allow(private_bounds)]
41    pub trait Mode: SealedMode {}
42
43    macro_rules! impl_mode {
44        ($name:ident) => {
45            impl SealedMode for $name {}
46            impl Mode for $name {}
47        };
48    }
49
50    /// Blocking mode.
51    pub struct Blocking;
52    /// Async mode.
53    pub struct Async;
54
55    impl_mode!(Blocking);
56    impl_mode!(Async);
57}
58
59/// HAL configuration for SiFli
60pub mod config {
61    use crate::rcc;
62    use crate::interrupt;
63
64    /// HAL configuration passed when initializing.
65    #[non_exhaustive]
66    pub struct Config {
67        pub rcc: rcc::Config,
68        pub gpio1_it_priority: interrupt::Priority,
69    }
70
71    impl Default for Config {
72        fn default() -> Self {
73            Self {
74                rcc: rcc::Config::new_keep(),
75                gpio1_it_priority: interrupt::Priority::P3,
76            }
77        }
78    }
79}
80pub use config::Config;
81
82/// Initialize the `sifli-hal` with the provided configuration.
83///
84/// This returns the peripheral singletons that can be used for creating drivers.
85///
86/// This should only be called once at startup, otherwise it panics.
87pub fn init(config: Config) -> Peripherals {
88    system_init();
89
90    // Do this first, so that it panics if user is calling `init` a second time
91    // before doing anything important.
92    let p = Peripherals::take();
93
94    unsafe {
95        config.rcc.apply();
96
97        #[cfg(feature = "_time-driver")]
98        time_driver::init();
99        
100        gpio::init(config.gpio1_it_priority);
101        critical_section::with(|cs| {
102            dma::init(cs);
103        });
104        
105    }
106    p
107}
108
109fn system_init() {
110    unsafe {
111        let mut cp = cortex_m::Peripherals::steal();
112
113        // enable CP0/CP1/CP2 Full Access
114        cp.SCB.cpacr.modify(|r| {
115            r | (0b111111)
116        });
117
118        // Enable Cache
119        cp.SCB.enable_icache();
120        cp.SCB.enable_dcache(&mut cp.CPUID);
121    }
122}
123
124pub(crate) mod _generated {
125    #![allow(dead_code)]
126    #![allow(unused_imports)]
127    #![allow(non_snake_case)]
128    #![allow(missing_docs)]
129
130    include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
131}
132
133pub use _generated::interrupt;
134pub use _generated::{peripherals, Peripherals};
135
136/// Performs a busy-wait delay for a specified number of microseconds, using the `cortex-m::asm::delay` function.
137pub fn cortex_m_blocking_delay_us(us: u32) {
138    let freq = rcc::get_hclk_freq().unwrap().0 as u64;
139    let cycles = freq * us as u64 / 1_000_000;
140    cortex_m::asm::delay(cycles as u32);
141}
142
143/// Performs a busy-wait delay for a specified number of microseconds.
144pub fn blocking_delay_us(us: u32) {
145    #[cfg(feature = "time")]
146    embassy_time::block_for(embassy_time::Duration::from_micros(us as u64));
147    #[cfg(not(feature = "time"))]
148    cortex_m_blocking_delay_us(us);
149}
150
151/// Macro to bind interrupts to handlers.
152///
153/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
154/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
155/// prove at compile-time that the right interrupts have been bound.
156///
157/// Example of how to bind one interrupt:
158///
159/// ```rust,ignore
160/// use sifli_hal::{bind_interrupts, usb, peripherals};
161///
162/// bind_interrupts!(struct Irqs {
163///     USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;
164/// });
165/// ```
166///
167// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
168#[macro_export]
169macro_rules! bind_interrupts {
170    ($vis:vis struct $name:ident {
171        $(
172            $(#[cfg($cond_irq:meta)])?
173            $irq:ident => $(
174                $(#[cfg($cond_handler:meta)])?
175                $handler:ty
176            ),*;
177        )*
178    }) => {
179        #[derive(Copy, Clone)]
180        $vis struct $name;
181
182        $(
183            #[allow(non_snake_case)]
184            #[no_mangle]
185            $(#[cfg($cond_irq)])?
186            unsafe extern "C" fn $irq() {
187                $(
188                    $(#[cfg($cond_handler)])?
189                    <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
190
191                )*
192            }
193
194            $(#[cfg($cond_irq)])?
195            $crate::bind_interrupts!(@inner
196                $(
197                    $(#[cfg($cond_handler)])?
198                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
199                )*
200            );
201        )*
202    };
203    (@inner $($t:tt)*) => {
204        $($t)*
205    }
206}
207
208// Check README.md for details
209#[cfg(feature = "set-msplim")]
210global_asm!(
211    ".section .text._pre_init",
212    ".global __pre_init",
213    ".type __pre_init, %function",
214    ".thumb_func",
215    "__pre_init:",
216    "    ldr r0, =_stack_end",
217    "    msr MSPLIM, r0",
218    "    bx lr",
219    ".size __pre_init, . - __pre_init"
220);