Skip to content

Commit 17f59f3

Browse files
committed
test: Add trigger interrupt event test
1 parent 804d20c commit 17f59f3

File tree

2 files changed

+125
-6
lines changed

2 files changed

+125
-6
lines changed

testsuite/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ harness = false
4242
[dependencies]
4343
cfg-if = "1.0"
4444
cortex-m = "0.7.0"
45+
cortex-m-rt = "0.6.15"
4546
defmt = "0.2.0"
4647
defmt-rtt = "0.2.0"
4748
defmt-test = "0.2.0"
49+
enumset = { version = "1.0.6" }
4850
# TODO: Set stm32f303xc as default, but make it overwritable
4951
stm32f3xx-hal = { path = "..", features = ["rt", "defmt-trace", "can", "ld", "enumset"]}
5052
panic-probe = { version = "0.2.0", features = ["print-defmt"] }

testsuite/tests/uart.rs

Lines changed: 123 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33

44
use testsuite as _;
55

6+
use enumset::EnumSet;
7+
68
use stm32f3xx_hal as hal;
79

810
use hal::gpio::{OpenDrain, PushPull, AF7};
911
use hal::pac;
1012
use hal::prelude::*;
11-
use hal::serial::Serial;
1213
use hal::serial::{
1314
config::{Config, Parity, StopBits},
14-
Error, Event,
15+
Error, Event, Instance, Serial, TxPin, RxPin,
1516
};
1617
use hal::time::rate::Baud;
1718
use hal::{
@@ -22,8 +23,14 @@ use hal::{
2223
rcc::{Clocks, APB1, APB2},
2324
};
2425

26+
use hal::interrupt;
27+
2528
use core::array::IntoIter;
26-
use defmt::{assert_eq, unwrap};
29+
use defmt::{assert, assert_eq, unwrap};
30+
31+
use core::sync::atomic::{AtomicBool, Ordering};
32+
33+
static INTERRUPT_FIRED: AtomicBool = AtomicBool::new(false);
2734

2835
struct State {
2936
serial1: Option<Serial<pac::USART1, (PA9<AF7<PushPull>>, PA10<AF7<PushPull>>)>>,
@@ -51,6 +58,47 @@ fn test_test_msg_loopback(state: &mut State, config: impl Into<Config>) {
5158
state.serial1 = Some(serial);
5259
}
5360

61+
fn trigger_event<Usart, Tx, Rx>(
62+
event: Event,
63+
serial: &mut Serial<Usart, (Tx, Rx)>,
64+
mut trigger: impl FnMut(&mut Serial<Usart, (Tx, Rx)>),
65+
) where
66+
Usart: Instance,
67+
Tx: TxPin<Usart>,
68+
Rx: RxPin<Usart>,
69+
{
70+
// Create an enumset of events with only one
71+
// event. Applying it disabled all other interrupts.
72+
let mut events = EnumSet::new();
73+
events.insert(event);
74+
// Clear events, so that previously triggered events do not fire
75+
// the now configured interupt imediatly.
76+
serial.clear_events();
77+
serial.configure_interrupts(events);
78+
// Check that the interrupt has not been run, since the configuration
79+
// and before the trigger.
80+
assert!(!INTERRUPT_FIRED.load(Ordering::SeqCst));
81+
trigger(serial);
82+
while !INTERRUPT_FIRED.load(Ordering::SeqCst) {}
83+
// Disable all configured interrupts.
84+
serial.configure_interrupts(EnumSet::new());
85+
// Only clear the particular event, which fired the interrupt
86+
assert!(serial.triggered_events().contains(event));
87+
serial.clear_event(event);
88+
assert!(!serial.triggered_events().contains(event));
89+
// TODO: Is that true?: Unpend any pending interrupts - more than one could be pending,
90+
// because of the late clearing of the interrupt
91+
cortex_m::peripheral::NVIC::unpend(<pac::USART1 as Instance>::INTERRUPT);
92+
// Now unmask all interrupts again, which where masks in the iterrupt rountine,
93+
// as a measurement to disable all interrupts.
94+
unsafe { cortex_m::peripheral::NVIC::unmask(<pac::USART1 as Instance>::INTERRUPT) }
95+
// Clear the interrupt flag again. And make double sure, that no interrupt
96+
// fired again.
97+
INTERRUPT_FIRED.store(false, Ordering::SeqCst);
98+
cortex_m::asm::delay(10);
99+
assert!(!INTERRUPT_FIRED.load(Ordering::SeqCst));
100+
}
101+
54102
#[defmt_test::tests]
55103
mod tests {
56104
use super::*;
@@ -92,6 +140,8 @@ mod tests {
92140
.into_af7_open_drain(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrl),
93141
};
94142

143+
unsafe { cortex_m::peripheral::NVIC::unmask(<pac::USART1 as Instance>::INTERRUPT) }
144+
95145
super::State {
96146
serial1: Some(Serial::new(
97147
dp.USART1,
@@ -310,7 +360,74 @@ mod tests {
310360
state.serial_fast = Some(Serial::join(tx_fast, rx_fast));
311361
}
312362

313-
// TODO: Test interrupts
314-
// #[test]
315-
// fn enable_interrupt_and_wait_for_fire(state: &mut super::State) {}
363+
// TODO: Currently, this is a limited test, just to see, that
364+
// an interrupt has fired. It does **not** test, if the correct
365+
// event caused the interrupt.
366+
//
367+
// This increases the implemetation effort, because
368+
#[test]
369+
fn trigger_events(state: &mut super::State) {
370+
let mut serial = state.serial1.take().unwrap();
371+
// let mut events = EnumSet::new();
372+
373+
trigger_event(Event::ReceiveDataRegisterNotEmpty, &mut serial, |serial| {
374+
unwrap!(serial.write(b'A').ok());
375+
});
376+
377+
trigger_event(Event::TransmissionComplete, &mut serial, |serial| {
378+
unwrap!(serial.write(b'A').ok());
379+
});
380+
381+
// TODO: This is difficult to test, as the data reigster is
382+
// empty imidiatly, when the interrupt is configured.
383+
// trigger_event(Event::TransmitDataRegisterEmtpy, &mut serial, |serial| {
384+
// unwrap!(serial.write(b'A').ok());
385+
// });
386+
387+
trigger_event(Event::OverrunError, &mut serial, |serial| {
388+
// Imidiatly overrun, because we do not read out the received register.
389+
unwrap!(nb::block!(serial.write(b'A')).ok());
390+
});
391+
392+
trigger_event(Event::Idle, &mut serial, |serial| {
393+
// Note: The IDLE bit will not be set again until the RXNE bit has been set (i.e. a new
394+
// idle line occurs).
395+
// To provoke IDLE, send something again so that RXNE is set.
396+
unwrap!(nb::block!(serial.write(b'A')).ok());
397+
});
398+
399+
serial.set_match_character(b'A');
400+
assert!(serial.match_character() == b'A');
401+
trigger_event(Event::CharacterMatch, &mut serial, |serial| {
402+
unwrap!(nb::block!(serial.write(b'A')).ok());
403+
});
404+
405+
serial.set_receiver_timeout(Some(100));
406+
assert!(serial.receiver_timeout() == Some(100));
407+
trigger_event(Event::ReceiverTimeout, &mut serial, |serial| {
408+
unwrap!(nb::block!(serial.write(b'A')).ok());
409+
unwrap!(nb::block!(serial.read()));
410+
});
411+
412+
state.serial1 = Some(serial);
413+
}
414+
}
415+
416+
// TODO: This maybe can be moved into a function inside the
417+
// mod tests, if defmt_test does allow free functions, which do not
418+
// correspond to tests.
419+
#[interrupt]
420+
fn USART1_EXTI25() {
421+
INTERRUPT_FIRED.store(true, Ordering::SeqCst);
422+
423+
// Make it easy on ourselfs and just disable all interrupts.
424+
// This way, the interrupt rountine dooes not have to have access to the serial peripheral,
425+
// which would mean to access the internally managed state of the test module,
426+
// which can't be accessed right now.
427+
//
428+
// This is all needed, to clear the fired interrupt.
429+
assert!(cortex_m::peripheral::NVIC::is_active(
430+
<pac::USART1 as Instance>::INTERRUPT
431+
));
432+
cortex_m::peripheral::NVIC::mask(<pac::USART1 as Instance>::INTERRUPT);
316433
}

0 commit comments

Comments
 (0)