-
Notifications
You must be signed in to change notification settings - Fork 211
/
Copy pathrtic-serial-dma-rx-idle.rs
153 lines (122 loc) · 4.86 KB
/
rtic-serial-dma-rx-idle.rs
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
// This example implement simple and safe method receiving data with unknown length by UART.
// The data received by using DMA, and IDLE event denote end of data packet.
// See https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx for details.
// If you use big buffers, it is recommended to add memory pools (allocators) and use
// lock-free queues to send buffer without memcpy.
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [TIM2])]
mod app {
use hal::{
dma::{config::DmaConfig, DmaFlag, PeripheralToMemory, Stream2, StreamsTuple, Transfer},
pac::{DMA2, USART1},
prelude::*,
rcc::RccExt,
serial,
};
use panic_semihosting as _;
use systick_monotonic::*;
use stm32f4xx_hal as hal;
const BUFFER_SIZE: usize = 100;
type RxTransfer = Transfer<
Stream2<DMA2>,
4,
serial::Rx<USART1>,
PeripheralToMemory,
&'static mut [u8; BUFFER_SIZE],
>;
#[shared]
struct Shared {
#[lock_free]
rx_transfer: RxTransfer,
}
#[local]
struct Local {
rx_buffer: Option<&'static mut [u8; BUFFER_SIZE]>,
}
#[monotonic(binds = SysTick, default = true)]
type MyMono = Systick<1000>; // 1000 Hz / 1 ms granularity
#[init(local = [
rx_pool_memory: [u8; 400] = [0; 400],
])]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let core = cx.core;
let dp: hal::pac::Peripherals = cx.device;
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze();
let mono = Systick::new(core.SYST, clocks.sysclk().to_Hz());
let gpioa = dp.GPIOA.split();
// Initialize UART with DMA events
let rx_pin = gpioa.pa10;
let mut rx = dp
.USART1
.rx(
rx_pin,
serial::Config::default()
.baudrate(9600.bps())
.dma(serial::config::DmaConfig::Rx),
&clocks,
)
.unwrap();
// Listen UART IDLE event, which will be call USART1 interrupt
rx.listen_idle();
let dma2 = StreamsTuple::new(dp.DMA2);
// Note! It is better to use memory pools, such as heapless::pool::Pool. But it not work with embedded_dma yet.
// See CHANGELOG of unreleased main branch and issue https://github.com/japaric/heapless/pull/362 for details.
let rx_buffer1 = cortex_m::singleton!(: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
let rx_buffer2 = cortex_m::singleton!(: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
// Initialize and start DMA stream
let mut rx_transfer = Transfer::init_peripheral_to_memory(
dma2.2,
rx,
rx_buffer1,
None,
DmaConfig::default()
.memory_increment(true)
.fifo_enable(true)
.fifo_error_interrupt(true)
.transfer_complete_interrupt(true),
);
rx_transfer.start(|_rx| {});
(
Shared { rx_transfer },
Local {
rx_buffer: Some(rx_buffer2),
},
init::Monotonics(mono),
)
}
// Important! USART1 and DMA2_STREAM2 should the same interrupt priority!
#[task(binds = USART1, priority=1, local = [rx_buffer],shared = [rx_transfer])]
fn usart1(mut cx: usart1::Context) {
let transfer = &mut cx.shared.rx_transfer;
if transfer.is_idle() {
// Calc received bytes count
let bytes_count = BUFFER_SIZE - transfer.number_of_transfers() as usize;
// Allocate new buffer
let new_buffer = cx.local.rx_buffer.take().unwrap();
// Replace buffer and restart DMA stream
let (buffer, _) = transfer.next_transfer(new_buffer).unwrap();
// Get slice for received bytes
let _bytes = &buffer[..bytes_count];
// Do something with received bytes
// For example, parse it or send (buffer, bytes_count) to lock-free queue.
// Free buffer
*cx.local.rx_buffer = Some(buffer);
}
}
#[task(binds = DMA2_STREAM2, priority=1,shared = [rx_transfer])]
fn dma2_stream2(mut cx: dma2_stream2::Context) {
let transfer = &mut cx.shared.rx_transfer;
let flags = transfer.flags();
transfer.clear_flags(DmaFlag::FifoError | DmaFlag::TransferComplete);
if flags.is_transfer_complete() {
// Buffer is full, but no IDLE received!
// You can process this data or discard data (ignore transfer complete interrupt and wait IDLE).
// Note! If you want process this data, it is recommended to use double buffering.
// See Transfer::init_peripheral_to_memory for details.
}
}
}