3
3
4
4
use testsuite as _;
5
5
6
+ use enumset:: EnumSet ;
7
+
6
8
use stm32f3xx_hal as hal;
7
9
8
10
use hal:: gpio:: { OpenDrain , PushPull , AF7 } ;
9
11
use hal:: pac;
10
12
use hal:: prelude:: * ;
11
- use hal:: serial:: Serial ;
12
13
use hal:: serial:: {
13
14
config:: { Config , Parity , StopBits } ,
14
- Error , Event ,
15
+ Error , Event , Instance , Serial , TxPin , RxPin ,
15
16
} ;
16
17
use hal:: time:: rate:: Baud ;
17
18
use hal:: {
@@ -22,8 +23,14 @@ use hal::{
22
23
rcc:: { Clocks , APB1 , APB2 } ,
23
24
} ;
24
25
26
+ use hal:: interrupt;
27
+
25
28
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 ) ;
27
34
28
35
struct State {
29
36
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>) {
51
58
state. serial1 = Some ( serial) ;
52
59
}
53
60
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
+
54
102
#[ defmt_test:: tests]
55
103
mod tests {
56
104
use super :: * ;
@@ -92,6 +140,8 @@ mod tests {
92
140
. into_af7_open_drain ( & mut gpioa. moder , & mut gpioa. otyper , & mut gpioa. afrl ) ,
93
141
} ;
94
142
143
+ unsafe { cortex_m:: peripheral:: NVIC :: unmask ( <pac:: USART1 as Instance >:: INTERRUPT ) }
144
+
95
145
super :: State {
96
146
serial1 : Some ( Serial :: new (
97
147
dp. USART1 ,
@@ -310,7 +360,74 @@ mod tests {
310
360
state. serial_fast = Some ( Serial :: join ( tx_fast, rx_fast) ) ;
311
361
}
312
362
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 ) ;
316
433
}
0 commit comments