imxrt1050_evkb/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Reference Manual for the Imxrt-1052 development board
6//!
7//! - <https://www.nxp.com/webapp/Download?colCode=IMXRT1050RM>
8
9#![no_std]
10#![no_main]
11#![deny(missing_docs)]
12
13use core::ptr::addr_of_mut;
14
15use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
16use components::gpio::GpioComponent;
17use kernel::capabilities;
18use kernel::component::Component;
19use kernel::debug;
20use kernel::hil::gpio::Configure;
21use kernel::hil::led::LedLow;
22use kernel::platform::{KernelResources, SyscallDriverLookup};
23use kernel::process::ProcessArray;
24use kernel::scheduler::round_robin::RoundRobinSched;
25use kernel::{create_capability, static_init};
26
27// use components::fxos8700::Fxos8700Component;
28// use components::ninedof::NineDofComponent;
29use imxrt1050::iomuxc::DriveStrength;
30use imxrt1050::iomuxc::MuxMode;
31use imxrt1050::iomuxc::OpenDrainEn;
32use imxrt1050::iomuxc::PadId;
33use imxrt1050::iomuxc::PullKeepEn;
34use imxrt1050::iomuxc::PullUpDown;
35use imxrt1050::iomuxc::Sion;
36use imxrt1050::iomuxc::Speed;
37use imxrt10xx as imxrt1050;
38
39// Unit Tests for drivers.
40// #[allow(dead_code)]
41// mod virtual_uart_rx_test;
42
43/// Support routines for debugging I/O.
44pub mod io;
45
46/// Defines a vector which contains the boot section
47pub mod boot_header;
48
49// Number of concurrent processes this platform supports.
50const NUM_PROCS: usize = 4;
51
52type ChipHw = imxrt1050::chip::Imxrt10xx<imxrt1050::chip::Imxrt10xxDefaultPeripherals>;
53
54/// Static variables used by io.rs.
55static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
56static mut CHIP: Option<&'static ChipHw> = None;
57static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
58    None;
59
60// How should the kernel respond when a process faults.
61const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
62    capsules_system::process_policies::PanicFaultPolicy {};
63
64// Manually setting the boot header section that contains the FCB header
65#[used]
66#[link_section = ".boot_hdr"]
67static BOOT_HDR: [u8; 8192] = boot_header::BOOT_HDR;
68
69kernel::stack_size! {0x2000}
70
71// const NUM_LEDS: usize = 1;
72
73/// A structure representing this platform that holds references to all
74/// capsules for this platform.
75struct Imxrt1050EVKB {
76    alarm: &'static capsules_core::alarm::AlarmDriver<
77        'static,
78        VirtualMuxAlarm<'static, imxrt1050::gpt::Gpt1<'static>>,
79    >,
80    button: &'static capsules_core::button::Button<'static, imxrt1050::gpio::Pin<'static>>,
81    console: &'static capsules_core::console::Console<'static>,
82    gpio: &'static capsules_core::gpio::GPIO<'static, imxrt1050::gpio::Pin<'static>>,
83    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
84    led: &'static capsules_core::led::LedDriver<
85        'static,
86        LedLow<'static, imxrt1050::gpio::Pin<'static>>,
87        1,
88    >,
89    ninedof: &'static capsules_extra::ninedof::NineDof<'static>,
90
91    scheduler: &'static RoundRobinSched<'static>,
92    systick: cortexm7::systick::SysTick,
93}
94
95/// Mapping of integer syscalls to objects that implement syscalls.
96impl SyscallDriverLookup for Imxrt1050EVKB {
97    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
98    where
99        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
100    {
101        match driver_num {
102            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
103            capsules_core::button::DRIVER_NUM => f(Some(self.button)),
104            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
105            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
106            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
107            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
108            capsules_extra::ninedof::DRIVER_NUM => f(Some(self.ninedof)),
109            _ => f(None),
110        }
111    }
112}
113
114impl KernelResources<imxrt1050::chip::Imxrt10xx<imxrt1050::chip::Imxrt10xxDefaultPeripherals>>
115    for Imxrt1050EVKB
116{
117    type SyscallDriverLookup = Self;
118    type SyscallFilter = ();
119    type ProcessFault = ();
120    type Scheduler = RoundRobinSched<'static>;
121    type SchedulerTimer = cortexm7::systick::SysTick;
122    type WatchDog = ();
123    type ContextSwitchCallback = ();
124
125    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
126        self
127    }
128    fn syscall_filter(&self) -> &Self::SyscallFilter {
129        &()
130    }
131    fn process_fault(&self) -> &Self::ProcessFault {
132        &()
133    }
134    fn scheduler(&self) -> &Self::Scheduler {
135        self.scheduler
136    }
137    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
138        &self.systick
139    }
140    fn watchdog(&self) -> &Self::WatchDog {
141        &()
142    }
143    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
144        &()
145    }
146}
147
148/// Helper function called during bring-up that configures DMA.
149/// DMA for imxrt1050-evkb is not implemented yet.
150// unsafe fn setup_dma() {
151// }
152
153/// Helper function called during bring-up that configures multiplexed I/O.
154unsafe fn set_pin_primary_functions(
155    peripherals: &'static imxrt1050::chip::Imxrt10xxDefaultPeripherals,
156) {
157    use imxrt1050::gpio::PinId;
158
159    peripherals.ccm.enable_iomuxc_clock();
160    peripherals.ccm.enable_iomuxc_snvs_clock();
161
162    peripherals.ports.gpio1.enable_clock();
163
164    // User_LED is connected to GPIO_AD_B0_09.
165    // Values set accordingly to the evkbimxrt1050_iled_blinky SDK example
166
167    // First we configure the pin in GPIO mode and disable the Software Input
168    // on Field, so that the Input Path is determined by functionality.
169    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
170        PadId::AdB0,
171        MuxMode::ALT5, // ALT5 for AdB0_09: GPIO1_IO09 of instance: gpio1
172        Sion::Disabled,
173        9,
174    );
175
176    // Configure the pin resistance value, pull up or pull down and other
177    // physical aspects.
178    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
179        PadId::AdB0,
180        9,
181        PullUpDown::Pus0_100kOhmPullDown,   // 100K Ohm Pull Down
182        PullKeepEn::Pke1PullKeeperEnabled,  // Pull-down resistor or keep the previous value
183        OpenDrainEn::Ode0OpenDrainDisabled, // Output is CMOS, either 0 logic or 1 logic
184        Speed::Medium2,                     // Operating frequency: 100MHz - 150MHz
185        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
186    );
187
188    // Configuring the GPIO_AD_B0_09 as output
189    let pin = peripherals.ports.pin(PinId::AdB0_09);
190    pin.make_output();
191    kernel::debug::assign_gpios(Some(pin), None, None);
192
193    // User_Button is connected to IOMUXC_SNVS_WAKEUP.
194    peripherals.ports.gpio5.enable_clock();
195
196    // We configure the pin in GPIO mode and disable the Software Input
197    // on Field, so that the Input Path is determined by functionality.
198    peripherals.iomuxc_snvs.enable_sw_mux_ctl_pad_gpio(
199        MuxMode::ALT5, // ALT5 for AdB0_09: GPIO5_IO00 of instance: gpio5
200        Sion::Disabled,
201        0,
202    );
203
204    // Configuring the IOMUXC_SNVS_WAKEUP pin as input
205    peripherals.ports.pin(PinId::Wakeup).make_input();
206}
207
208/// Helper function for miscellaneous peripheral functions
209unsafe fn setup_peripherals(peripherals: &imxrt1050::chip::Imxrt10xxDefaultPeripherals) {
210    // LPUART1 IRQn is 20
211    cortexm7::nvic::Nvic::new(imxrt1050::nvic::LPUART1).enable();
212
213    // TIM2 IRQn is 28
214    peripherals.gpt1.enable_clock();
215    peripherals.gpt1.start(
216        peripherals.ccm.perclk_sel(),
217        peripherals.ccm.perclk_divider(),
218    );
219    cortexm7::nvic::Nvic::new(imxrt1050::nvic::GPT1).enable();
220}
221
222/// This is in a separate, inline(never) function so that its stack frame is
223/// removed when this function returns. Otherwise, the stack space used for
224/// these static_inits is wasted.
225#[inline(never)]
226unsafe fn start() -> (
227    &'static kernel::Kernel,
228    Imxrt1050EVKB,
229    &'static imxrt1050::chip::Imxrt10xx<imxrt1050::chip::Imxrt10xxDefaultPeripherals>,
230) {
231    imxrt1050::init();
232
233    let ccm = static_init!(imxrt1050::ccm::Ccm, imxrt1050::ccm::Ccm::new());
234    let peripherals = static_init!(
235        imxrt1050::chip::Imxrt10xxDefaultPeripherals,
236        imxrt1050::chip::Imxrt10xxDefaultPeripherals::new(ccm)
237    );
238    peripherals.ccm.set_low_power_mode();
239    peripherals.lpuart1.disable_clock();
240    peripherals.lpuart2.disable_clock();
241    peripherals
242        .ccm
243        .set_uart_clock_sel(imxrt1050::ccm::UartClockSelection::PLL3);
244    peripherals.ccm.set_uart_clock_podf(1);
245    peripherals.lpuart1.set_baud();
246
247    set_pin_primary_functions(peripherals);
248
249    setup_peripherals(peripherals);
250
251    // Create an array to hold process references.
252    let processes = components::process_array::ProcessArrayComponent::new()
253        .finalize(components::process_array_component_static!(NUM_PROCS));
254    PROCESSES = Some(processes);
255
256    // Setup space to store the core kernel data structure.
257    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
258
259    let chip = static_init!(ChipHw, ChipHw::new(peripherals));
260    CHIP = Some(chip);
261
262    // LPUART1
263
264    // Enable tx and rx from iomuxc
265    // TX is on pad GPIO_AD_B0_12
266    // RX is on pad GPIO_AD_B0_13
267    // Values set accordingly to the evkbimxrt1050_hello_world SDK example
268
269    // First we configure the pin in LPUART mode and disable the Software Input
270    // on Field, so that the Input Path is determined by functionality.
271    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
272        PadId::AdB0,
273        MuxMode::ALT2, // ALT2: LPUART1_TXD of instance: lpuart1
274        Sion::Disabled,
275        13,
276    );
277    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
278        PadId::AdB0,
279        MuxMode::ALT2, // ALT2: LPUART1_RXD of instance: lpuart1
280        Sion::Disabled,
281        14,
282    );
283
284    // Configure the pin resistance value, pull up or pull down and other
285    // physical aspects.
286    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
287        PadId::AdB0,
288        13,
289        PullUpDown::Pus0_100kOhmPullDown,   // 100K Ohm Pull Down
290        PullKeepEn::Pke1PullKeeperEnabled,  // Pull-down resistor or keep the previous value
291        OpenDrainEn::Ode0OpenDrainDisabled, // Output is CMOS, either 0 logic or 1 logic
292        Speed::Medium2,                     // Operating frequency: 100MHz - 150MHz
293        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
294    );
295    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
296        PadId::AdB0,
297        14,
298        PullUpDown::Pus0_100kOhmPullDown,   // 100K Ohm Pull Down
299        PullKeepEn::Pke1PullKeeperEnabled,  // Pull-down resistor or keep the previous value
300        OpenDrainEn::Ode0OpenDrainDisabled, // Output is CMOS, either 0 logic or 1 logic
301        Speed::Medium2,                     // Operating frequency: 100MHz - 150MHz
302        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
303    );
304
305    // Enable clock
306    peripherals.lpuart1.enable_clock();
307
308    let lpuart_mux = components::console::UartMuxComponent::new(&peripherals.lpuart1, 115200)
309        .finalize(components::uart_mux_component_static!());
310    (*addr_of_mut!(io::WRITER)).set_initialized();
311
312    // Create capabilities that the board needs to call certain protected kernel
313    // functions.
314    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
315    let process_management_capability =
316        create_capability!(capabilities::ProcessManagementCapability);
317
318    // Setup the console.
319    let console = components::console::ConsoleComponent::new(
320        board_kernel,
321        capsules_core::console::DRIVER_NUM,
322        lpuart_mux,
323    )
324    .finalize(components::console_component_static!());
325    // Create the debugger object that handles calls to `debug!()`.
326    components::debug_writer::DebugWriterComponent::new::<
327        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
328    >(
329        lpuart_mux,
330        create_capability!(capabilities::SetDebugWriterCapability),
331    )
332    .finalize(components::debug_writer_component_static!());
333
334    // LEDs
335
336    // Clock to Port A is enabled in `set_pin_primary_functions()
337    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
338        LedLow<'static, imxrt1050::gpio::Pin<'static>>,
339        LedLow::new(peripherals.ports.pin(imxrt1050::gpio::PinId::AdB0_09)),
340    ));
341
342    // BUTTONs
343    let button = components::button::ButtonComponent::new(
344        board_kernel,
345        capsules_core::button::DRIVER_NUM,
346        components::button_component_helper!(
347            imxrt1050::gpio::Pin,
348            (
349                peripherals.ports.pin(imxrt1050::gpio::PinId::Wakeup),
350                kernel::hil::gpio::ActivationMode::ActiveHigh,
351                kernel::hil::gpio::FloatingState::PullDown
352            )
353        ),
354    )
355    .finalize(components::button_component_static!(imxrt1050::gpio::Pin));
356
357    // ALARM
358    let gpt1 = &peripherals.gpt1;
359    let mux_alarm = components::alarm::AlarmMuxComponent::new(gpt1).finalize(
360        components::alarm_mux_component_static!(imxrt1050::gpt::Gpt1),
361    );
362
363    let alarm = components::alarm::AlarmDriverComponent::new(
364        board_kernel,
365        capsules_core::alarm::DRIVER_NUM,
366        mux_alarm,
367    )
368    .finalize(components::alarm_component_static!(imxrt1050::gpt::Gpt1));
369
370    // GPIO
371    // For now we expose only two pins
372    let gpio = GpioComponent::new(
373        board_kernel,
374        capsules_core::gpio::DRIVER_NUM,
375        components::gpio_component_helper!(
376            imxrt1050::gpio::Pin<'static>,
377            // The User Led
378            0 => peripherals.ports.pin(imxrt1050::gpio::PinId::AdB0_09)
379        ),
380    )
381    .finalize(components::gpio_component_static!(
382        imxrt1050::gpio::Pin<'static>
383    ));
384
385    // LPI2C
386    // AD_B1_00 is LPI2C1_SCL
387    // AD_B1_01 is LPI2C1_SDA
388    // Values set accordingly to the evkbimxrt1050_bubble_peripheral SDK example
389
390    // First we configure the pin in LPUART mode and enable the Software Input
391    // on Field, so that we force input path of the pad.
392    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
393        PadId::AdB1,
394        MuxMode::ALT3, // ALT3:  LPI2C1_SCL of instance: lpi2c1
395        Sion::Enabled,
396        0,
397    );
398    // Selecting AD_B1_00 for LPI2C1_SCL in the Daisy Chain.
399    peripherals.iomuxc.enable_lpi2c_scl_select_input();
400
401    peripherals.iomuxc.enable_sw_mux_ctl_pad_gpio(
402        PadId::AdB1,
403        MuxMode::ALT3, // ALT3:  LPI2C1_SDA of instance: lpi2c1
404        Sion::Enabled,
405        1,
406    );
407    // Selecting AD_B1_01 for LPI2C1_SDA in the Daisy Chain.
408    peripherals.iomuxc.enable_lpi2c_sda_select_input();
409
410    // Configure the pin resistance value, pull up or pull down and other
411    // physical aspects.
412    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
413        PadId::AdB1,
414        0,
415        PullUpDown::Pus3_22kOhmPullUp,     // 22K Ohm Pull Up
416        PullKeepEn::Pke1PullKeeperEnabled, // Pull-down resistor or keep the previous value
417        OpenDrainEn::Ode1OpenDrainEnabled, // Open Drain Enabled (Output is Open Drain)
418        Speed::Medium2,                    // Operating frequency: 100MHz - 150MHz
419        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
420    );
421
422    peripherals.iomuxc.configure_sw_pad_ctl_pad_gpio(
423        PadId::AdB1,
424        1,
425        PullUpDown::Pus3_22kOhmPullUp,     // 22K Ohm Pull Up
426        PullKeepEn::Pke1PullKeeperEnabled, // Pull-down resistor or keep the previous value
427        OpenDrainEn::Ode1OpenDrainEnabled, // Open Drain Enabled (Output is Open Drain)
428        Speed::Medium2,                    // Operating frequency: 100MHz - 150MHz
429        DriveStrength::DSE6, // Dual/Single voltage: 43/43 Ohm @ 1.8V, 40/26 Ohm @ 3.3V
430    );
431
432    // Enabling the lpi2c1 clock and setting the speed.
433    peripherals.lpi2c1.enable_clock();
434    peripherals
435        .lpi2c1
436        .set_speed(imxrt1050::lpi2c::Lpi2cSpeed::Speed100k, 8);
437
438    use imxrt1050::gpio::PinId;
439    let mux_i2c = components::i2c::I2CMuxComponent::new(&peripherals.lpi2c1, None).finalize(
440        components::i2c_mux_component_static!(imxrt1050::lpi2c::Lpi2c),
441    );
442
443    // Fxos8700 sensor
444    let fxos8700 = components::fxos8700::Fxos8700Component::new(
445        mux_i2c,
446        0x1f,
447        peripherals.ports.pin(PinId::AdB1_00),
448    )
449    .finalize(components::fxos8700_component_static!(
450        imxrt1050::lpi2c::Lpi2c
451    ));
452
453    // Ninedof
454    let ninedof = components::ninedof::NineDofComponent::new(
455        board_kernel,
456        capsules_extra::ninedof::DRIVER_NUM,
457    )
458    .finalize(components::ninedof_component_static!(fxos8700));
459
460    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
461        .finalize(components::round_robin_component_static!(NUM_PROCS));
462
463    let imxrt1050 = Imxrt1050EVKB {
464        console,
465        ipc: kernel::ipc::IPC::new(
466            board_kernel,
467            kernel::ipc::DRIVER_NUM,
468            &memory_allocation_capability,
469        ),
470        led,
471        button,
472        ninedof,
473        alarm,
474        gpio,
475
476        scheduler,
477        systick: cortexm7::systick::SysTick::new_with_calibration(792_000_000),
478    };
479
480    // Optional kernel tests
481    //
482    // See comment in `boards/imix/src/main.rs`
483    // virtual_uart_rx_test::run_virtual_uart_receive(mux_uart);
484
485    //--------------------------------------------------------------------------
486    // Process Console
487    //---------------------------------------------------------------------------
488    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
489        .finalize(components::process_printer_text_component_static!());
490    PROCESS_PRINTER = Some(process_printer);
491
492    let process_console = components::process_console::ProcessConsoleComponent::new(
493        board_kernel,
494        lpuart_mux,
495        mux_alarm,
496        process_printer,
497        None,
498    )
499    .finalize(components::process_console_component_static!(
500        imxrt1050::gpt::Gpt1
501    ));
502    let _ = process_console.start();
503
504    debug!("Tock OS initialization complete. Entering main loop");
505
506    extern "C" {
507        /// Beginning of the ROM region containing app images.
508        ///
509        /// This symbol is defined in the linker script.
510        static _sapps: u8;
511        /// End of the ROM region containing app images.
512        ///
513        /// This symbol is defined in the linker script.
514        static _eapps: u8;
515        /// Beginning of the RAM region for app memory.
516        static mut _sappmem: u8;
517        /// End of the RAM region for app memory.
518        static _eappmem: u8;
519    }
520
521    kernel::process::load_processes(
522        board_kernel,
523        chip,
524        core::slice::from_raw_parts(
525            core::ptr::addr_of!(_sapps),
526            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
527        ),
528        core::slice::from_raw_parts_mut(
529            core::ptr::addr_of_mut!(_sappmem),
530            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
531        ),
532        &FAULT_RESPONSE,
533        &process_management_capability,
534    )
535    .unwrap_or_else(|err| {
536        debug!("Error loading processes!");
537        debug!("{:?}", err);
538    });
539
540    (board_kernel, imxrt1050, chip)
541}
542
543/// Main function called after RAM initialized.
544#[no_mangle]
545pub unsafe fn main() {
546    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
547
548    let (board_kernel, board, chip) = start();
549    board_kernel.kernel_loop(&board, chip, Some(&board.ipc), &main_loop_capability);
550}