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
//! Demonstrates using DMA and UART with RTIC.
//!
//! The driver waits for a serial character, then sends the same
//! character back 32 times. All UART sends and receives are performed
//! with DMA.
#![no_std]
#![no_main]
use imxrt_hal as hal;
#[rtic::app(device = board, peripherals = false)]
mod app {
use super::{dma_receive, dma_transfer, hal};
/// What's our UART state?
pub enum State {
/// Waiting for the first character.
Receiving,
/// Sending back the results.
Transfering,
}
#[shared]
struct Shared {}
#[local]
struct Local {
led: board::Led,
console: board::Console,
channel: hal::dma::channel::Channel,
buffer: &'static mut [u8],
state: State,
}
#[init(local = [buf: [u8; 32] = [0; 32]])]
fn init(cx: init::Context) -> (Shared, Local) {
let (
board::Common { mut dma, .. },
board::Specifics {
led, mut console, ..
},
) = board::new();
let mut channel = dma[board::BOARD_DMA_A_INDEX].take().unwrap();
channel.set_interrupt_on_completion(true);
channel.set_disable_on_completion(true);
unsafe {
// Safety: buffer is static.
dma_receive(&mut channel, &mut console, &mut cx.local.buf[..1]);
}
(
Shared {},
Local {
led,
console,
channel,
buffer: cx.local.buf,
state: State::Receiving,
},
)
}
#[task(binds = BOARD_DMA_A, local = [led, console, channel, buffer, state])]
fn dma_complete(cx: dma_complete::Context) {
let dma_complete::LocalResources {
channel,
led,
state,
buffer,
console,
..
} = cx.local;
while channel.is_interrupt() {
channel.clear_interrupt();
}
if !channel.is_complete() || channel.is_error() {
led.set();
return;
}
channel.clear_complete();
match state {
State::Receiving => {
// Completed receive operation.
let recv = buffer[0];
buffer.fill(recv);
unsafe {
// Safety: buffer is static
dma_transfer(channel, console, buffer);
}
*state = State::Transfering;
}
State::Transfering => {
unsafe {
// Safety: buffer is static.
dma_receive(channel, console, &mut buffer[..1]);
}
*state = State::Receiving;
led.toggle();
}
}
}
}
use hal::dma::{
channel,
peripheral::{Destination, Source},
};
/// Prepares and activates the DMA receive operation.
///
/// When this call completes, the DMA peripheral
/// is waiting for UART signals to indicate new data.
///
/// # Safety
///
/// `buffer` must have static lifetime and cannot be observed
/// while the transfer is active.
#[allow(unused_unsafe)]
unsafe fn dma_receive(
channel: &mut channel::Channel,
source: &mut board::Console,
buffer: &mut [u8],
) {
channel.disable();
channel.clear_complete();
channel.clear_error();
channel.set_channel_configuration(channel::Configuration::enable(source.source_signal()));
unsafe {
// Safety: hardware address is valid.
channel::set_source_hardware(channel, source.source_address());
// Safety: buffer is static, so always valid.
channel::set_destination_linear_buffer(channel, buffer);
// Safety: combination of minor loop and transfer iterations prevent buffer overrun.
channel.set_minor_loop_bytes(core::mem::size_of::<u8>() as u32);
channel.set_transfer_iterations(buffer.len() as u16);
}
source.enable_source();
unsafe {
// Safety: channel is ready to go, and there's no stale configuration.
channel.enable();
}
}
/// Prepares and activates the DMA transfer operation.
///
/// When this call completes, the DMA peripheral is writing data from the buffer to
/// the UART peripheral.
///
/// # Safety
///
/// `buffer` must have static lifetime.
#[allow(unused_unsafe)]
unsafe fn dma_transfer(
channel: &mut channel::Channel,
destination: &mut board::Console,
buffer: &[u8],
) {
channel.disable();
channel.clear_complete();
channel.clear_error();
channel.set_channel_configuration(channel::Configuration::enable(
destination.destination_signal(),
));
unsafe {
// Safety: hardware address is valid.
channel::set_destination_hardware(channel, destination.destination_address());
// Safety: buffer is static, so always valid.
channel::set_source_linear_buffer(channel, buffer);
// Safety: combination of minor loop and transfer iterations prevent buffer overrun.
channel.set_minor_loop_bytes(core::mem::size_of::<u8>() as u32);
channel.set_transfer_iterations(buffer.len() as u16);
}
destination.enable_destination();
unsafe {
// Safety: channel is ready to go, and there's no stale configuration.
channel.enable();
}
}