Skip to content

Commit fe6e9e4

Browse files
committed
DMA impl improvements
1. Wrap the TX transfer struct in a helper type which also checks whether the TC IRQ flag was set. This is strongly recommended in the datasheet 2. Basic refactoring to prepare allowing checks of events for both the split Tx and Rx handle
1 parent 3a0c163 commit fe6e9e4

File tree

3 files changed

+217
-28
lines changed

3 files changed

+217
-28
lines changed

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616

1717
No changes.
1818

19+
### Changed
20+
21+
- serial: The DMA functions `write_all` and `read_exact` now returns
22+
the wrapper structs `SerialDmaTx` and `SerialDmaRx` instead of the direct
23+
DMA transfer struct. These allow checking the USART ISR events
24+
with `is_event_triggered` as well.
25+
26+
### Fixed
27+
28+
- serial: The previous DMA `write_all` implementation did use the DMA transfer completion
29+
event to check for transfer completion, but MCU datasheet specifies that the TC
30+
flag of the USART peripheral should be checked for transfer completion to avoid
31+
corruption of the last transfer. This is now done by the new `SerialDmaTx` wrapper.
32+
33+
## Added
34+
35+
- serial: Public `is_event_triggered` method which allows to check for events
36+
given an event and a USART reference.
37+
1938
## [v0.9.1] - 2022-09-07
2039

2140
### Added

src/dma.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ impl<B, C: Channel, T: Target> Transfer<B, C, T> {
153153

154154
self.stop()
155155
}
156+
157+
pub(crate) fn target(&self) -> &T {
158+
let inner = crate::unwrap!(self.inner.as_ref());
159+
&inner.target
160+
}
156161
}
157162

158163
impl<B, C: Channel, T: Target> Drop for Transfer<B, C, T> {

src/serial.rs

Lines changed: 193 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,28 @@ pub enum Event {
140140
// WakeupFromStopMode,
141141
}
142142

143+
/// Check if an interrupt event happend.
144+
#[inline]
145+
pub fn is_event_triggered(uart: &impl Instance, event: Event) -> bool {
146+
let isr = uart.isr.read();
147+
match event {
148+
Event::TransmitDataRegisterEmtpy => isr.txe().bit(),
149+
Event::CtsInterrupt => isr.ctsif().bit(),
150+
Event::TransmissionComplete => isr.tc().bit(),
151+
Event::ReceiveDataRegisterNotEmpty => isr.rxne().bit(),
152+
Event::OverrunError => isr.ore().bit(),
153+
Event::Idle => isr.idle().bit(),
154+
Event::ParityError => isr.pe().bit(),
155+
Event::LinBreak => isr.lbdf().bit(),
156+
Event::NoiseError => isr.nf().bit(),
157+
Event::FramingError => isr.fe().bit(),
158+
Event::CharacterMatch => isr.cmf().bit(),
159+
Event::ReceiverTimeout => isr.rtof().bit(),
160+
// Event::EndOfBlock => isr.eobf().bit(),
161+
// Event::WakeupFromStopMode => isr.wuf().bit(),
162+
}
163+
}
164+
143165
/// Serial error
144166
///
145167
/// As these are status events, they can be converted to [`Event`]s, via [`Into`].
@@ -332,7 +354,7 @@ pub struct Serial<Usart, Pins> {
332354
}
333355

334356
mod split {
335-
use super::Instance;
357+
use super::{is_event_triggered, Event, Instance};
336358
/// Serial receiver
337359
#[derive(Debug)]
338360
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -396,6 +418,14 @@ mod split {
396418
pub(crate) unsafe fn usart_mut(&mut self) -> &mut Usart {
397419
&mut self.usart
398420
}
421+
422+
/// Check if an interrupt event happend.
423+
#[inline]
424+
pub fn is_event_triggered(&self, event: Event) -> bool {
425+
// Safety: We are only reading the ISR register here, which
426+
// should not affect the RX half
427+
is_event_triggered(unsafe { self.usart() }, event)
428+
}
399429
}
400430

401431
impl<Usart, Pin> Rx<Usart, Pin>
@@ -448,6 +478,14 @@ mod split {
448478
pub(crate) unsafe fn usart_mut(&mut self) -> &mut Usart {
449479
&mut self.usart
450480
}
481+
482+
/// Check if an interrupt event happend.
483+
#[inline]
484+
pub fn is_event_triggered(&self, event: Event) -> bool {
485+
// Safety: We are only reading the ISR register here, which
486+
// should not affect the TX half
487+
is_event_triggered(unsafe { self.usart() }, event)
488+
}
451489
}
452490
}
453491

@@ -579,7 +617,7 @@ where
579617
///
580618
/// ## Embedded HAL
581619
///
582-
/// To have a more managed way to read from the serial use the [`embeded_hal::serial::Read`]
620+
/// To have a more managed way to read from the serial use the [`embedded_hal::serial::Read`]
583621
/// trait implementation.
584622
#[doc(alias = "RDR")]
585623
pub fn read_data_register(&self) -> Option<u8> {
@@ -714,23 +752,7 @@ where
714752
/// Check if an interrupt event happend.
715753
#[inline]
716754
pub fn is_event_triggered(&self, event: Event) -> bool {
717-
let isr = self.usart.isr.read();
718-
match event {
719-
Event::TransmitDataRegisterEmtpy => isr.txe().bit(),
720-
Event::CtsInterrupt => isr.ctsif().bit(),
721-
Event::TransmissionComplete => isr.tc().bit(),
722-
Event::ReceiveDataRegisterNotEmpty => isr.rxne().bit(),
723-
Event::OverrunError => isr.ore().bit(),
724-
Event::Idle => isr.idle().bit(),
725-
Event::ParityError => isr.pe().bit(),
726-
Event::LinBreak => isr.lbdf().bit(),
727-
Event::NoiseError => isr.nf().bit(),
728-
Event::FramingError => isr.fe().bit(),
729-
Event::CharacterMatch => isr.cmf().bit(),
730-
Event::ReceiverTimeout => isr.rtof().bit(),
731-
// Event::EndOfBlock => isr.eobf().bit(),
732-
// Event::WakeupFromStopMode => isr.wuf().bit(),
733-
}
755+
is_event_triggered(&self.usart, event)
734756
}
735757

736758
/// Get an [`EnumSet`] of all fired interrupt events.
@@ -947,7 +969,7 @@ where
947969
/// up to the interrupt handler.
948970
///
949971
/// To read out the content of the read register without internal error handling, use
950-
/// [`Serial::read()`].
972+
/// [`Serial::read_data_register`].
951973
/// ...
952974
// -> According to this API it should be skipped.
953975
fn read(&mut self) -> nb::Result<u8, Error> {
@@ -1063,7 +1085,7 @@ where
10631085
Usart: Instance + Dma,
10641086
{
10651087
/// Fill the buffer with received data using DMA.
1066-
pub fn read_exact<B, C>(self, buffer: B, mut channel: C) -> dma::Transfer<B, C, Self>
1088+
pub fn read_exact<B, C>(self, buffer: B, mut channel: C) -> SerialDmaRx<B, C, Self>
10671089
where
10681090
Self: dma::OnChannel<C>,
10691091
B: dma::WriteBuffer<Word = u8> + 'static,
@@ -1077,7 +1099,9 @@ where
10771099
)
10781100
};
10791101

1080-
dma::Transfer::start_write(buffer, channel, self)
1102+
SerialDmaRx {
1103+
transfer: dma::Transfer::start_write(buffer, channel, self),
1104+
}
10811105
}
10821106
}
10831107

@@ -1088,13 +1112,148 @@ where
10881112
{
10891113
}
10901114

1115+
/// Thin wrapper struct over the DMA transfer struct.
1116+
///
1117+
/// This wrapper mostly exposes the [`dma::Transfer`] API but also also exposes
1118+
/// an API to check for other USART ISR events during on-going transfers.
1119+
pub struct SerialDmaRx<B: dma::WriteBuffer<Word = u8> + 'static, C: dma::Channel, T: dma::Target> {
1120+
transfer: dma::Transfer<B, C, T>,
1121+
}
1122+
1123+
impl<B, C, T> SerialDmaRx<B, C, T>
1124+
where
1125+
B: dma::WriteBuffer<Word = u8>,
1126+
C: dma::Channel,
1127+
T: dma::Target,
1128+
{
1129+
/// Call [`dma::Transfer::stop`].
1130+
pub fn stop(self) -> (B, C, T) {
1131+
self.transfer.stop()
1132+
}
1133+
1134+
/// Call [`dma::Transfer::is_complete`].
1135+
pub fn is_complete(&self) -> bool {
1136+
self.transfer.is_complete()
1137+
}
1138+
1139+
/// Call [`dma::Transfer::wait`].
1140+
pub fn wait(self) -> (B, C, T) {
1141+
self.transfer.wait()
1142+
}
1143+
}
1144+
1145+
impl<B, C, Usart, Pin> SerialDmaRx<B, C, Rx<Usart, Pin>>
1146+
where
1147+
B: dma::WriteBuffer<Word = u8>,
1148+
C: dma::Channel,
1149+
Usart: Instance + Dma,
1150+
Pin: RxPin<Usart>,
1151+
{
1152+
/// Check if an interrupt event happened.
1153+
pub fn is_event_triggered(&self, event: Event) -> bool {
1154+
self.transfer.target().is_event_triggered(event)
1155+
}
1156+
}
1157+
1158+
impl<B, C, Usart, Pins> SerialDmaRx<B, C, Serial<Usart, Pins>>
1159+
where
1160+
B: dma::WriteBuffer<Word = u8>,
1161+
C: dma::Channel,
1162+
Usart: Instance + Dma,
1163+
{
1164+
/// Check if an interrupt event happened.
1165+
pub fn is_event_triggered(&self, event: Event) -> bool {
1166+
self.transfer.target().is_event_triggered(event)
1167+
}
1168+
}
1169+
1170+
/// Thin wrapper struct over the DMA transfer struct.
1171+
///
1172+
/// This wrapper mostly exposes the [`dma::Transfer`] API but also implements
1173+
/// some additional checks because the conditions for DMA transfer completion
1174+
/// require that the USART TC ISR flag is set as well. It also exposes an API
1175+
/// to check for other USART ISR events during ongoing transfers.
1176+
pub struct SerialDmaTx<B: dma::ReadBuffer<Word = u8> + 'static, C: dma::Channel, T: dma::Target> {
1177+
transfer: dma::Transfer<B, C, T>,
1178+
}
1179+
1180+
impl<B, C, T> SerialDmaTx<B, C, T>
1181+
where
1182+
B: dma::ReadBuffer<Word = u8>,
1183+
C: dma::Channel,
1184+
T: dma::Target,
1185+
{
1186+
/// Calls [`dma::Transfer::stop`].
1187+
pub fn stop(self) -> (B, C, T) {
1188+
self.transfer.stop()
1189+
}
1190+
}
1191+
1192+
impl<B, C, Usart, Pin> SerialDmaTx<B, C, Tx<Usart, Pin>>
1193+
where
1194+
Usart: Instance + Dma,
1195+
C: dma::Channel,
1196+
B: dma::ReadBuffer<Word = u8>,
1197+
Pin: TxPin<Usart>,
1198+
{
1199+
/// Wrapper function which can be used to check transfer completion.
1200+
///
1201+
/// In addition to checking the transfer completion of the DMA, it also checks that the
1202+
/// USART Transmission Complete flag was set by the hardware. According to RM0316 29.5.15, this
1203+
/// is required to avoid corrupting the last transmission before disabling the USART or entering
1204+
/// stop mode.
1205+
pub fn is_complete(&self) -> bool {
1206+
let target = self.transfer.target();
1207+
self.transfer.is_complete() && target.is_event_triggered(Event::TransmissionComplete)
1208+
}
1209+
1210+
/// Block until the transfer is complete. This function also uses
1211+
/// [`SerialDmaTx::is_complete`] to check that the USART TC flag was set by
1212+
/// the hardware.
1213+
pub fn wait(self) -> (B, C, Tx<Usart, Pin>) {
1214+
while !self.is_complete() {}
1215+
self.stop()
1216+
}
1217+
1218+
/// Check if an interrupt event happened.
1219+
pub fn is_event_triggered(&self, event: Event) -> bool {
1220+
self.transfer.target().is_event_triggered(event)
1221+
}
1222+
}
1223+
1224+
impl<B, C, Usart, Pins> SerialDmaTx<B, C, Serial<Usart, Pins>>
1225+
where
1226+
Usart: Instance + Dma,
1227+
C: dma::Channel,
1228+
B: dma::ReadBuffer<Word = u8>,
1229+
{
1230+
/// Wrapper function which can be used to check transfer completion.
1231+
///
1232+
/// In addition to checking the transfer completion of the DMA, it also checks that the
1233+
/// USART Transmission Complete flag was set by the hardware. According to RM0316 29.5.15, this
1234+
/// is required to avoid corrupting the last transmission before disabling the USART or entering
1235+
/// stop mode.
1236+
pub fn is_complete(&self) -> bool {
1237+
let target = self.transfer.target();
1238+
self.transfer.is_complete() && target.is_event_triggered(Event::TransmissionComplete)
1239+
}
1240+
1241+
/// Block until the transfer is complete. This function also uses
1242+
/// [`SerialDmaTx::is_complete`] to check that the USART TC flag was set by
1243+
/// the hardware.
1244+
pub fn wait(self) -> (B, C, Serial<Usart, Pins>) {
1245+
while !self.is_complete() {}
1246+
self.stop()
1247+
}
1248+
}
1249+
10911250
impl<Usart, Pin> Tx<Usart, Pin>
10921251
where
10931252
Usart: Instance + Dma,
10941253
Pin: TxPin<Usart>,
10951254
{
10961255
/// Transmit all data in the buffer using DMA.
1097-
pub fn write_all<B, C>(self, buffer: B, mut channel: C) -> dma::Transfer<B, C, Self>
1256+
pub fn write_all<B, C>(self, buffer: B, mut channel: C) -> SerialDmaTx<B, C, Self>
10981257
where
10991258
Self: dma::OnChannel<C>,
11001259
B: dma::ReadBuffer<Word = u8> + 'static,
@@ -1108,7 +1267,9 @@ where
11081267
)
11091268
};
11101269

1111-
dma::Transfer::start_read(buffer, channel, self)
1270+
SerialDmaTx {
1271+
transfer: dma::Transfer::start_read(buffer, channel, self),
1272+
}
11121273
}
11131274
}
11141275

@@ -1156,7 +1317,7 @@ where
11561317
Usart: Instance + Dma,
11571318
{
11581319
/// Fill the buffer with received data using DMA.
1159-
pub fn read_exact<B, C>(self, buffer: B, mut channel: C) -> dma::Transfer<B, C, Self>
1320+
pub fn read_exact<B, C>(self, buffer: B, mut channel: C) -> SerialDmaRx<B, C, Self>
11601321
where
11611322
Self: dma::OnChannel<C>,
11621323
B: dma::WriteBuffer<Word = u8> + 'static,
@@ -1168,11 +1329,13 @@ where
11681329
.set_peripheral_address(&self.usart.rdr as *const _ as u32, dma::Increment::Disable)
11691330
};
11701331

1171-
dma::Transfer::start_write(buffer, channel, self)
1332+
SerialDmaRx {
1333+
transfer: dma::Transfer::start_write(buffer, channel, self),
1334+
}
11721335
}
11731336

11741337
/// Transmit all data in the buffer using DMA.
1175-
pub fn write_all<B, C>(self, buffer: B, mut channel: C) -> dma::Transfer<B, C, Self>
1338+
pub fn write_all<B, C>(self, buffer: B, mut channel: C) -> SerialDmaTx<B, C, Self>
11761339
where
11771340
Self: dma::OnChannel<C>,
11781341
B: dma::ReadBuffer<Word = u8> + 'static,
@@ -1184,7 +1347,9 @@ where
11841347
.set_peripheral_address(&self.usart.tdr as *const _ as u32, dma::Increment::Disable)
11851348
};
11861349

1187-
dma::Transfer::start_read(buffer, channel, self)
1350+
SerialDmaTx {
1351+
transfer: dma::Transfer::start_read(buffer, channel, self),
1352+
}
11881353
}
11891354
}
11901355

0 commit comments

Comments
 (0)