Skip to main content

teensy4_bsp/
board.rs

1//! Pre-configured board resources.
2//!
3//! The `board` module conveniently exposes drivers and hardware resources.
4//! It configures peripheral clocks and power so that you can simply use the
5//! board's peripherals.
6//!
7//! # Getting started
8//!
9//! Create board resources depending on your board:
10//!
11//! - Teensy 4.0 boards should use [`t40`].
12//! - Teensy 4.1 boards should use [`t41`].
13//! - Teensy MicroMod boards should use [`tmm`].
14//!
15//! The produced resources are nearly the same, except for the pins.
16//!
17//! ```no_run
18//! use teensy4_bsp as bsp;
19//! use bsp::board;
20//!
21//! let resources = board::t40(board::instances());
22//! ```
23//!
24//! Resources take ownership of all peripheral instances `Instances`. Use [`instances()`] to
25//! acquire these once.
26//!
27//! > Note that if you're using RTIC, you can let the RTIC framework allocate
28//! > the instances. The examples included in this package demonstrate this pattern.
29//!
30//! # Resource categories
31//!
32//! The objects in [`Resources`] have two main categories:
33//!
34//! 1. ready-to-go drivers, like `gpt1` and `dma`, which are unlikely to change no matter your
35//!    hardware setup.
36//! 2. lower-level resources, like `pins` and `lpuart3`, which you can compose to create drivers
37//!    specific for your hardware setup.
38//!
39//! Given the Teensy 4's I/O flexibility, it would be difficult to expose all features as ready-to-go
40//! drivers. Instead, `board` tries to help you initialize drivers for your hardware
41//! setup. For example,
42//!
43//! - use [`led`] to prepare the on-board LED.
44//! - use [`lpspi`] to prepare SPI peripherals.
45//! - use [`lpuart`] to prepare serial peripherals.
46//! - use [`lpi2c`] to prepare I2C peripherals.
47//!
48//! For the complete list of helper functions, study the rest of this API documentation and their
49//! examples. And for the complete driver documentation, see [the `hal` module](crate::hal) and
50//! its submodules.
51//!
52//! These helper functions assume that you're following the `board`'s clock (and power) policy.
53//!
54//! # Clock policy
55//!
56//! Once [`t40`] and [`t41`] return, the clocks of all returned resources are running, and clock gates are
57//! enabled. Essentially, `board` handles CCM configurations so that you can use peripherals with reasonable
58//! settings. `board` exposes constants describing these clock frequencies.
59//!
60//! Clock frequency constants, and all APIs depending on those constants, assume that you do not change the clock policy.
61//! But for those who want extra control, `Resources` exposes the clock management peripherals. If you take this
62//! route, you're responsible for ensuring your peripheral's clock configurations, and you should ignore the constants
63//! exposed by this module.
64//!
65//! ## Clock details
66//!
67//! `board` makes a few guarantees about the clock policy. This section describes those guarantees.
68//!
69//! It's considered an API-breaking change to vary the frequencies and derivations of these clocks:
70//!
71//! - The ARM core frequency is documented in [`ARM_FREQUENCY`].
72//! - PERCLK, the root clock for GPT and PIT timers, is documented in [`PERCLK_FREQUENCY`].
73//! - The crystal oscillator is documented in [`XTAL_OSCILLATOR_FREQUENCY`].
74//!
75//! `board` does not touch the following root clock components. You should configure these components yourself.
76//!
77//! - FlexIO1 and FlexIO2 dividers and multiplexers.
78//!
79//! Other clock frequencies are exempt from this policy; they may change value or derivation without
80//! notice. Nevertheless, if these clock frequencies / derivations change, `board` still guarantees that
81//! the functions to set peripheral baud and clock rates will work as expected.
82//!
83//! By the time your code has access to [`Resources`], the clock gates for _all_ peripherals provided by
84//! `Resources` is set to "on." If you're not using specific resources, you may configure their clock
85//! gates to "off." `board` code that borrows or converts peripherals may assume that clock gates are
86//! set to "on."
87//!
88//! If you're not using the BSP to prepare board `Resources`, consider using [`prepare_clocks_and_power`]
89//! to realize the BSP's clock policy.
90//!
91//! # Naming conventions
92//!
93//! This module identifies peripherals by their hardware names. It does not use identifiers
94//! from the Teensy 4 SDK and documentation. For instance, to name the serial peripheral
95//! that uses pins 14 and 15,
96//!
97//! - this module uses the name "LPUART**2**".
98//! - the Teensy 4 SDK / documentation uses the name "UART**3**".
99//!
100//! There's a few reasons for these differences:
101//!
102//! - It would be unintuitive to register a LPUART**2** *interrupt* for the peripheral named
103//!   UART**3**. Since the BSP typically does not manage interrupt registration, this naming
104//!   decision makes it easier for you to register and implement interrupts.
105//! - It makes clear the peripheral implementation. You know that the serial peripheral
106//!   supports low-power (LP) operation, and that it's not a FlexIO peripheral emulating a UART
107//!   device.
108
109use crate::{hal, pins, ral};
110use core::sync::atomic::{AtomicBool, Ordering};
111pub use hal::lpi2c::Lpi2c;
112pub use hal::lpspi::Lpspi;
113pub use hal::lpspi::Pins as LpspiPins;
114pub use hal::lpuart::Lpuart;
115
116/// Use [`instances()`] to safely acquire.
117pub use ral::Instances;
118
119/// Acquire peripheral instances.
120///
121/// These are the resources supplied to [a resource constructor](crate::board#getting-started).
122/// They can also be used to initialize your own drivers.
123///
124/// ```
125/// use teensy4_bsp as bsp;
126/// use bsp::board;
127///
128/// let instances = board::instances();
129/// ```
130///
131/// # Panics
132///
133/// Panics if the instances have already been taken from this function.
134/// If you're using RTIC, know that RTIC may take these instances before
135/// calling your `init` function.
136///
137/// ```should_panic
138/// # use teensy4_bsp as bsp;
139/// # use bsp::board;
140/// let instances = board::instances();
141/// // Sometime later...
142/// board::instances(); // Panics! Instances already taken.
143/// ```
144///
145/// # Safety
146///
147/// This function provides a safe, convenient way to acquire one [`Instances`] object.
148/// It's considered safe for the use-cases that `teensy4-bsp` intends to support;
149/// specifically, you're building one binary that represents the entire firmware
150/// image, and that there's no other software which is acquiring these resources.
151pub fn instances() -> Instances {
152    static TAKEN: AtomicBool = AtomicBool::new(false);
153    assert!(!TAKEN.swap(true, Ordering::SeqCst));
154    unsafe { Instances::instances() }
155}
156
157pub use crate::clock_power::*;
158
159/// Resources for a Teensy 4.0.
160///
161/// Use [`t40`] to construct this. The pins are specific to the Teensy 4.0.
162pub type T40Resources = Resources<pins::t40::Pins>;
163/// Resources for a Teensy 4.1.
164///
165/// Use [`t41`] to construct this. The pins are specific to the Teensy 4.1.
166pub type T41Resources = Resources<pins::t41::Pins>;
167/// Resources for a Teensy MicroMod.
168///
169/// Use [`tmm`] to construct this. The pins are specific to the Teensy MicroMod.
170pub type TMMResources = Resources<pins::tmm::Pins>;
171
172/// Resources constructed by the board.
173///
174/// The concrete `Pins` type depends on how this is constructed.
175/// See the various `*Resources` aliases for more information.
176#[non_exhaustive]
177pub struct Resources<Pins> {
178    /// Periodic interrupt timer.
179    pub pit: hal::pit::Pit,
180    /// General purpose timer 1.
181    pub gpt1: hal::gpt::Gpt,
182    /// General purpose timer 2.
183    pub gpt2: hal::gpt::Gpt,
184    /// GPIO1 port.
185    pub gpio1: hal::gpio::Port,
186    /// GPIO2 port.
187    pub gpio2: hal::gpio::Port,
188    /// GPIO3 port.
189    pub gpio3: hal::gpio::Port,
190    /// GPIO4 port.
191    pub gpio4: hal::gpio::Port,
192    /// USB1 instances.
193    ///
194    /// Use this to construct higher-level USB drivers, or to initialize the USB logger.
195    pub usb: crate::usbd::Instances<1>,
196    /// DMA channels.
197    pub dma: [Option<hal::dma::channel::Channel>; hal::dma::CHANNEL_COUNT],
198    /// The secure real-time counter.
199    ///
200    /// It's initially disabled, and you may enable it in your firmware.
201    pub srtc: hal::snvs::srtc::Disabled,
202    /// Core registers for the SNVS low-power domain.
203    ///
204    /// Use this with the SRTC and other SNVS LP components.
205    pub snvs_lp_core: hal::snvs::LpCore,
206    /// Clock control module.
207    pub ccm: ral::ccm::CCM,
208    /// Analog clock control module.
209    pub ccm_analog: ral::ccm_analog::CCM_ANALOG,
210    /// DCDC converter
211    pub dcdc: ral::dcdc::DCDC,
212    /// All available pins.
213    pub pins: Pins,
214    /// The register block for LPI2C1.
215    pub lpi2c1: ral::lpi2c::LPI2C1,
216    /// The register block for LPI2C3.
217    pub lpi2c3: ral::lpi2c::LPI2C3,
218    /// The register block for LPSPI1.
219    pub lpspi1: ral::lpspi::LPSPI1,
220    /// The register block for LPSPI2.
221    pub lpspi2: ral::lpspi::LPSPI2,
222    /// The register block for LPSPI3.
223    pub lpspi3: ral::lpspi::LPSPI3,
224    /// The register block for LPSPI4.
225    pub lpspi4: ral::lpspi::LPSPI4,
226    /// The register block for LPUART6.
227    pub lpuart6: ral::lpuart::LPUART6,
228    /// The register block for LPUART4.
229    pub lpuart4: ral::lpuart::LPUART4,
230    /// The register block for LPUART2.
231    pub lpuart2: ral::lpuart::LPUART2,
232    /// The register block for LPUART3.
233    pub lpuart3: ral::lpuart::LPUART3,
234    /// The register block for LPUART8.
235    pub lpuart8: ral::lpuart::LPUART8,
236    /// The register block for LPUART1.
237    pub lpuart1: ral::lpuart::LPUART1,
238    /// The register block for LPUART5.
239    pub lpuart5: ral::lpuart::LPUART5,
240    /// The register block for LPUART7.
241    pub lpuart7: ral::lpuart::LPUART7,
242    /// FlexPWM1.
243    pub flexpwm1: hal::flexpwm::Pwm,
244    /// FlexPWM2.
245    pub flexpwm2: hal::flexpwm::Pwm,
246    /// FlexPWM3.
247    pub flexpwm3: hal::flexpwm::Pwm,
248    /// FlexPWM4.
249    pub flexpwm4: hal::flexpwm::Pwm,
250    /// The FlexIO1 register block.
251    pub flexio1: ral::flexio::FLEXIO1,
252    /// The FlexIO2 register block.
253    pub flexio2: ral::flexio::FLEXIO2,
254    /// The FlexIO3 register block.
255    pub flexio3: ral::flexio::FLEXIO3,
256    /// The register block for ADC1.
257    ///
258    /// ADC drivers constructed by `board` use a pre-configured clock and divisor. To change
259    /// this configuration, call `release()` to acquire the register block, then re-construct
260    /// the driver.
261    pub adc1: hal::adc::Adc,
262    /// The register block for ADC2.
263    pub adc2: hal::adc::Adc,
264    /// True random number generator.
265    pub trng: hal::trng::Trng,
266    /// Temperature monitor of the core.
267    pub tempmon: hal::tempmon::TempMon,
268    /// The register block for SAI1 (I2S audio).
269    ///
270    /// SAI1 is the primary audio interface used by the Teensy Audio Shield.
271    pub sai1: ral::sai::SAI1,
272    /// The register block for SAI2.
273    pub sai2: ral::sai::SAI2,
274    /// The register block for SAI3.
275    pub sai3: ral::sai::SAI3,
276    /// The IOMUXC general purpose register block.
277    pub iomuxc_gpr: ral::iomuxc_gpr::IOMUXC_GPR,
278    /// The USDHC1 peripheral instance.
279    pub usdhc1: ral::usdhc::USDHC1,
280    /// The FlexSPI2 peripheral instance.
281    pub flexspi2: ral::flexspi::FLEXSPI2,
282}
283
284/// The board's dedicated LED.
285pub type Led = hal::gpio::Output;
286
287/// Create the board's LED.
288///
289/// ```no_run
290/// use teensy4_bsp as bsp;
291/// use bsp::board;
292///
293/// let board::Resources { mut gpio2, pins, .. }
294///     = board::t40(board::instances());
295///
296/// let led = board::led(&mut gpio2, pins.p13);
297/// ```
298pub fn led(gpio2: &mut hal::gpio::Port, p13: pins::common::P13) -> Led {
299    gpio2.output(p13).expect("P13 is a GPIO2 pin")
300}
301
302/// Create a LPI2C peripheral.
303///
304/// Consider using explicit type annotations, as demonstrated below,
305/// to simplify any errors from the compiler.
306///
307/// This helper assumes that the actual LPI2C clock frequency equals [`LPI2C_FREQUENCY`].
308///
309/// ```no_run
310/// use teensy4_bsp as bsp;
311/// use bsp::board;
312///
313/// let board::Resources { lpi2c3, pins, ..}
314///     = board::t40(board::instances());
315///
316/// let mut lpi2c: board::Lpi2c = board::lpi2c(
317///     lpi2c3,
318///     pins.p16,
319///     pins.p17,
320///     board::Lpi2cClockSpeed::KHz400,
321/// );
322/// ```
323pub fn lpi2c<Scl, Sda, const N: u8>(
324    instance: ral::lpi2c::Instance<N>,
325    scl: Scl,
326    sda: Sda,
327    clock_speed: Lpi2cClockSpeed,
328) -> hal::lpi2c::Lpi2c
329where
330    Scl: hal::iomuxc::lpi2c::Pin<
331            Signal = hal::iomuxc::lpi2c::Scl,
332            Module = hal::iomuxc::consts::Const<N>,
333        >,
334    Sda: hal::iomuxc::lpi2c::Pin<
335            Signal = hal::iomuxc::lpi2c::Sda,
336            Module = hal::iomuxc::consts::Const<N>,
337        >,
338{
339    hal::lpi2c::Lpi2c::with_pins(
340        instance,
341        hal::lpi2c::Pins { scl, sda },
342        &lpi2c_baud(clock_speed),
343    )
344}
345
346/// Create a LPSPI peripheral.
347///
348/// `baud` is the SCK frequency, in Hz. Consider using explicit type annotations, as demonstrated
349/// below, to simplify any errors from the compiler.
350///
351/// This helper assumes that the LPSPI clock frequency equals [`LPSPI_FREQUENCY`].
352///
353/// ```no_run
354/// use teensy4_bsp as bsp;
355/// use bsp::board;
356///
357/// let board::T40Resources { lpspi4, pins, .. }
358///     = board::t40(board::instances());
359///
360/// let mut lpspi4: board::Lpspi = board::lpspi(
361///     lpspi4,
362///     board::LpspiPins {
363///         sdo: pins.p11,
364///         sdi: pins.p12,
365///         sck: pins.p13,
366///     },
367///     1_000_000,
368/// );
369/// ```
370pub fn lpspi<Sdo, Sdi, Sck, const N: u8>(
371    instance: ral::lpspi::Instance<N>,
372    pins: LpspiPins<Sdo, Sdi, Sck>,
373    baud: u32,
374) -> hal::lpspi::Lpspi
375where
376    Sdo: hal::iomuxc::lpspi::Pin<
377            Signal = hal::iomuxc::lpspi::Sdo,
378            Module = hal::iomuxc::consts::Const<N>,
379        >,
380    Sdi: hal::iomuxc::lpspi::Pin<
381            Signal = hal::iomuxc::lpspi::Sdi,
382            Module = hal::iomuxc::consts::Const<N>,
383        >,
384    Sck: hal::iomuxc::lpspi::Pin<
385            Signal = hal::iomuxc::lpspi::Sck,
386            Module = hal::iomuxc::consts::Const<N>,
387        >,
388{
389    let mut spi = hal::lpspi::Lpspi::with_pins(instance, pins);
390    spi.disabled(|spi| spi.set_clock_hz(LPSPI_FREQUENCY, baud));
391    spi
392}
393
394/// Create a LPUART peripheral.
395///
396/// The specific peripheral you're creating depends on
397///
398/// - the type of `instance`.
399/// - the TX and RX pins.
400/// - the return type, which can be explicitly annotated.
401///
402/// Consider using an explicit type annotation to help catch any
403/// programming errors.
404///
405/// This helper assumes that the UART clock frequency equals [`UART_FREQUENCY`].
406///
407/// ```no_run
408/// use teensy4_bsp as bsp;
409/// use bsp::board;
410///
411/// let board::T40Resources { lpuart6, lpuart2, pins, .. }
412///     = board::t40(board::instances());
413///
414/// // Explicit type:
415/// let mut lpuart6: board::Lpuart = board::lpuart(
416///     lpuart6,
417///     pins.p1,
418///     pins.p0,
419///     115200,
420/// );
421///
422/// // Implicit type:
423/// let mut lpuart2 = board::lpuart(lpuart2, pins.p14, pins.p15, 9600);
424/// ```
425pub fn lpuart<Tx, Rx, const N: u8>(
426    instance: ral::lpuart::Instance<N>,
427    tx: Tx,
428    rx: Rx,
429    baud: u32,
430) -> hal::lpuart::Lpuart
431where
432    Tx: hal::iomuxc::lpuart::Pin<
433            Direction = hal::iomuxc::lpuart::Tx,
434            Module = hal::iomuxc::consts::Const<N>,
435        >,
436    Rx: hal::iomuxc::lpuart::Pin<
437            Direction = hal::iomuxc::lpuart::Rx,
438            Module = hal::iomuxc::consts::Const<N>,
439        >,
440{
441    let mut uart = hal::lpuart::Lpuart::with_pins(instance, hal::lpuart::Pins { tx, rx });
442    uart.disable(|uart| uart.set_baud(&lpuart_baud(baud)));
443    uart
444}
445
446fn prepare_resources<Pins>(
447    mut instances: Instances,
448    from_pads: impl FnOnce(hal::iomuxc::pads::Pads) -> Pins,
449) -> Resources<Pins> {
450    prepare_clocks_and_power(
451        &mut instances.CCM,
452        &mut instances.CCM_ANALOG,
453        &mut instances.DCDC,
454    );
455    let iomuxc = hal::iomuxc::into_pads(instances.IOMUXC);
456    let pins = from_pads(iomuxc);
457
458    // Stop timers in debug mode.
459    ral::modify_reg!(ral::pit, instances.PIT, MCR, FRZ: FRZ_1);
460    let pit = hal::pit::Pit::new(instances.PIT);
461
462    let mut gpt1 = hal::gpt::Gpt::new(instances.GPT1);
463    gpt1.disable();
464
465    let mut gpt2 = hal::gpt::Gpt::new(instances.GPT2);
466    gpt2.disable();
467
468    let hal::snvs::Snvs {
469        low_power:
470            hal::snvs::LowPower {
471                core: snvs_lp_core,
472                srtc,
473                ..
474            },
475        ..
476    } = hal::snvs::new(instances.SNVS);
477
478    let adc1 = hal::adc::Adc::new(
479        instances.ADC1,
480        hal::adc::ClockSelect::ADACK,
481        hal::adc::ClockDivision::Div8,
482    );
483    let adc2 = hal::adc::Adc::new(
484        instances.ADC2,
485        hal::adc::ClockSelect::ADACK,
486        hal::adc::ClockDivision::Div8,
487    );
488
489    let trng = hal::trng::Trng::new(instances.TRNG, Default::default(), Default::default());
490    let tempmon = hal::tempmon::TempMon::with_measure_freq(instances.TEMPMON, 0x1000);
491
492    Resources {
493        pit,
494        gpt1,
495        gpt2,
496        gpio1: hal::gpio::Port::new(instances.GPIO1),
497        gpio2: hal::gpio::Port::new(instances.GPIO2),
498        gpio3: hal::gpio::Port::new(instances.GPIO3),
499        gpio4: hal::gpio::Port::new(instances.GPIO4),
500        usb: crate::usbd::Instances {
501            usb: instances.USB1,
502            usbphy: instances.USBPHY1,
503            usbnc: instances.USBNC1,
504        },
505        dma: hal::dma::channels(instances.DMA, instances.DMAMUX),
506        srtc,
507        snvs_lp_core,
508        ccm: instances.CCM,
509        ccm_analog: instances.CCM_ANALOG,
510        dcdc: instances.DCDC,
511        pins,
512        lpi2c1: instances.LPI2C1,
513        lpi2c3: instances.LPI2C3,
514        lpspi1: instances.LPSPI1,
515        lpspi2: instances.LPSPI2,
516        lpspi3: instances.LPSPI3,
517        lpspi4: instances.LPSPI4,
518        lpuart6: instances.LPUART6,
519        lpuart4: instances.LPUART4,
520        lpuart2: instances.LPUART2,
521        lpuart3: instances.LPUART3,
522        lpuart8: instances.LPUART8,
523        lpuart1: instances.LPUART1,
524        lpuart7: instances.LPUART7,
525        lpuart5: instances.LPUART5,
526        flexio1: instances.FLEXIO1,
527        flexio2: instances.FLEXIO2,
528        flexio3: instances.FLEXIO3,
529        flexpwm1: hal::flexpwm::Pwm::new(instances.PWM1),
530        flexpwm2: hal::flexpwm::Pwm::new(instances.PWM2),
531        flexpwm3: hal::flexpwm::Pwm::new(instances.PWM3),
532        flexpwm4: hal::flexpwm::Pwm::new(instances.PWM4),
533        adc1,
534        adc2,
535        trng,
536        tempmon,
537        sai1: instances.SAI1,
538        sai2: instances.SAI2,
539        sai3: instances.SAI3,
540        iomuxc_gpr: instances.IOMUXC_GPR,
541        usdhc1: instances.USDHC1,
542        flexspi2: instances.FLEXSPI2,
543    }
544}
545
546/// Create resources for the Teensy 4.0 board.
547///
548/// Note that the peripheral instances acquired by RTIC -- named `device` in the
549/// `init::Context` object -- can be used as the argument to this function.
550pub fn t40(instances: impl Into<Instances>) -> T40Resources {
551    prepare_resources(instances.into(), pins::t40::from_pads)
552}
553
554/// Create resources for the Teensy 4.1 board.
555///
556/// Note that the peripheral instances acquired by RTIC -- named `device` in the
557/// `init::Context` object -- can be used as the argument to this function.
558pub fn t41(instances: impl Into<Instances>) -> T41Resources {
559    prepare_resources(instances.into(), pins::t41::from_pads)
560}
561
562/// Create resources for the Teensy MicroMod board.
563///
564/// Note that the peripheral instances acquired by RTIC -- named `device` in the
565/// `init::Context` object -- can be used as the argument to this function.
566pub fn tmm(instances: impl Into<Instances>) -> TMMResources {
567    prepare_resources(instances.into(), pins::tmm::from_pads)
568}