1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Demonstrates imxrt-log text and defmt logging using RTIC.
//!
//! Modify the `FRONTEND` and `BACKEND` configurations to combine log / defmt
//! with a LPUART or USB CDC logger. When LPUART is selected, the log messages
//! are written over the board's console; see your board's `Console` documentation
//! for information on the settings. When a USB device is selected, the log messages
//! are written over a USB serial interface.
//!
//! See the `imxrt-log` documentation for more information on each logging backend.
#![no_std]
#![no_main]
#[rtic::app(device = board, peripherals = false, dispatchers = [BOARD_SWTASK0])]
mod app {
//
// Configure the demo below.
//
/// Change me to change how log messages are serialized
/// and transported.
const FRONTEND: board::logging::Frontend = board::logging::Frontend::Log;
/// This is a function of your board. Want to change it? Change it right
/// here to explore different example code paths.
const BACKEND: board::logging::Backend = board::logging::BACKEND;
/// When using the LPUART backend, you want to occasionally poll
/// for new log message. This interval (milliseconds) describes how
/// long you should wait in between polls.
///
/// This constant does nothing when the USBD backend is used, since
/// the USBD backend uses its own timer for this purpose.
const LPUART_POLL_INTERVAL_MS: u32 = board::PIT_FREQUENCY / 1_000 * 4;
/// How frequently (milliseconds) should we make a log message?
///
/// Decrease this constant to log more frequently.
const MAKE_LOG_INTERVAL_MS: u32 = board::PIT_FREQUENCY / 1_000 * 250;
//
// End configurations.
//
use imxrt_hal as hal;
#[local]
struct Local {
/// Toggle when we make a log.
led: board::Led,
/// This timer tells us how frequently to poll
/// for logs. It's only used with the LPUART
/// logging backend.
poll_log: hal::pit::Pit<1>,
/// This timer tells us how frequently to generate
/// logs. It's always used.
make_log: hal::pit::Pit<2>,
}
#[shared]
struct Shared {
/// The poller drives the logging backend.
poller: board::logging::Poller,
}
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
let mut cortex_m = cx.core;
let (
board::Common {
pit: (_, mut poll_log, mut make_log, _),
usb1,
usbnc1,
usbphy1,
mut dma,
..
},
board::Specifics { led, console, .. },
) = board::new();
cortex_m.DCB.enable_trace();
cortex_m::peripheral::DWT::unlock();
cortex_m.DWT.enable_cycle_counter();
// We only need the extra timer when the LPUART backend is used.
// The USBD backend uses the USB peripheral's internal timer to
// track time for us.
if BACKEND == board::logging::Backend::Lpuart {
poll_log.set_load_timer_value(LPUART_POLL_INTERVAL_MS);
poll_log.set_interrupt_enable(true);
poll_log.enable();
} else {
poll_log.disable();
}
make_log.set_load_timer_value(MAKE_LOG_INTERVAL_MS);
make_log.set_interrupt_enable(true);
make_log.enable();
let usbd = hal::usbd::Instances {
usb: usb1,
usbnc: usbnc1,
usbphy: usbphy1,
};
let dma_a = dma[board::BOARD_DMA_A_INDEX].take().unwrap();
let poller = board::logging::init(FRONTEND, BACKEND, console, dma_a, usbd);
(
Shared { poller },
Local {
led,
poll_log,
make_log,
},
)
}
/// This interrupt fires
///
/// - when log messages have been written (to the USB host).
/// - every few milliseconds; check the imxrt-log docs for the
/// specific number.
#[task(binds = BOARD_USB1, priority = 1)]
fn usb_interrupt(_: usb_interrupt::Context) {
poll_logger::spawn().unwrap();
}
/// This interrupt fires
///
/// - when log messages have been written.
///
/// Notice how there's no "periodic" or "time" component here.
/// When using the LPUART backend, you should use another time
/// source, or a polling loop, to make sure poll periodically
/// happens. Without this, you won't see your log messages.
#[task(binds = BOARD_DMA_A, priority = 1)]
fn dma_interrupt(_: dma_interrupt::Context) {
poll_logger::spawn().unwrap();
}
/// Actually performs the poll call.
#[task(shared = [poller], priority = 2)]
async fn poll_logger(mut cx: poll_logger::Context) {
cx.shared.poller.lock(|poller| poller.poll());
}
#[task(binds = BOARD_PIT, local = [led, poll_log, make_log, counter: u32 = 0], priority = 1)]
fn pit_interrupt(cx: pit_interrupt::Context) {
let pit_interrupt::LocalResources {
poll_log,
make_log,
led,
counter,
..
} = cx.local;
// Is it time for us to poll the logger?
// This only happens for the LPUART backend.
if poll_log.is_elapsed() {
while poll_log.is_elapsed() {
poll_log.clear_elapsed();
}
poll_logger::spawn().unwrap();
}
// Is it time for us to send a new log message?
if make_log.is_elapsed() {
led.toggle();
while make_log.is_elapsed() {
make_log.clear_elapsed();
}
let count = cycles(|| {
log::info!("Hello from the log framework over {BACKEND:?}! The count is {counter}")
});
log::info!(
"That last message took {count} cycles to be copied into the logging buffer"
);
let count = cycles(|| {
defmt::println!(
"Hello from defmt over {}! The count is {=u32}",
BACKEND,
counter
)
});
defmt::println!(
"That last message took {=u32} cycles to be copied into the logging buffer",
count
);
*counter += 1;
}
}
/// Count the clock cycles required to execute `f`
fn cycles<F: FnOnce()>(f: F) -> u32 {
let start = cortex_m::peripheral::DWT::cycle_count();
f();
let end = cortex_m::peripheral::DWT::cycle_count();
end - start
}
}