2
2
#![ feature( const_fn_trait_bound) ]
3
3
#![ feature( const_fn_fn_ptr_basics) ]
4
4
#![ feature( const_mut_refs) ]
5
+ #![ feature( let_else) ]
5
6
#![ deny( unsafe_op_in_unsafe_fn) ]
6
7
#![ deny( unsupported_naked_functions) ]
7
8
#![ no_std]
@@ -13,18 +14,25 @@ use r3_port_arm_m as port;
13
14
use wio_terminal as wio;
14
15
15
16
use core:: {
17
+ cell:: RefCell ,
16
18
fmt:: Write ,
17
19
panic:: PanicInfo ,
18
20
sync:: atomic:: { AtomicUsize , Ordering } ,
19
21
} ;
22
+ use cortex_m:: { interrupt:: Mutex as PrimaskMutex , singleton} ;
20
23
use eg:: { image:: Image , mono_font, pixelcolor:: Rgb565 , prelude:: * , primitives, text} ;
21
24
use r3:: {
22
- kernel:: { cfg:: CfgBuilder , Mutex , StartupHook , Task } ,
25
+ kernel:: { cfg:: CfgBuilder , InterruptLine , InterruptNum , Mutex , StartupHook , Task , Timer } ,
23
26
prelude:: * ,
24
27
} ;
25
28
use spin:: Mutex as SpinMutex ;
29
+ use usb_device:: {
30
+ bus:: UsbBusAllocator ,
31
+ device:: { UsbDevice , UsbDeviceBuilder , UsbVidPid } ,
32
+ } ;
33
+ use usbd_serial:: { SerialPort , USB_CLASS_CDC } ;
26
34
use wio:: {
27
- hal:: { clock:: GenericClockController , delay:: Delay , gpio} ,
35
+ hal:: { clock:: GenericClockController , delay:: Delay , gpio, usb :: UsbBus } ,
28
36
pac:: { CorePeripherals , Peripherals } ,
29
37
prelude:: * ,
30
38
Pins , Sets ,
@@ -40,6 +48,7 @@ impl port::ThreadingOptions for System {}
40
48
41
49
impl port:: SysTickOptions for System {
42
50
const FREQUENCY : u64 = 120_000_000 ; // ??
51
+ const TICK_PERIOD : u32 = Self :: FREQUENCY as u32 / 500 ; // 2ms
43
52
}
44
53
45
54
/// This part is `port::use_rt!` minus `__INTERRUPTS`. `wio_terminal`'s default
@@ -86,6 +95,9 @@ struct Objects {
86
95
console_pipe : queue:: Queue < System , u8 > ,
87
96
lcd_mutex : Mutex < System > ,
88
97
button_reporter_task : Task < System > ,
98
+ usb_in_task : Task < System > ,
99
+ usb_poll_timer : Timer < System > ,
100
+ usb_interrupt_lines : [ InterruptLine < System > ; 3 ] ,
89
101
}
90
102
91
103
const COTTAGE : Objects = r3:: build!( System , configure_app => Objects ) ;
@@ -121,6 +133,36 @@ const fn configure_app(b: &mut CfgBuilder<System>) -> Objects {
121
133
. active ( true )
122
134
. finish ( b) ;
123
135
136
+ // USB input handler
137
+ let usb_in_task = Task :: build ( )
138
+ . start ( usb_in_task_body)
139
+ . priority ( 2 )
140
+ . active ( true )
141
+ . finish ( b) ;
142
+ let usb_poll_timer = Timer :: build ( )
143
+ . start ( usb_poll_timer_handler)
144
+ . delay ( r3:: time:: Duration :: from_millis ( 0 ) )
145
+ // Should be < 10ms for USB compliance
146
+ . period ( r3:: time:: Duration :: from_millis ( 5 ) )
147
+ . finish ( b) ;
148
+ let usb_interrupt_lines = [
149
+ InterruptLine :: build ( )
150
+ . line ( interrupt:: USB_OTHER as InterruptNum + port:: INTERRUPT_EXTERNAL0 )
151
+ . priority ( 1 )
152
+ . enabled ( true )
153
+ . finish ( b) ,
154
+ InterruptLine :: build ( )
155
+ . line ( interrupt:: USB_TRCPT0 as InterruptNum + port:: INTERRUPT_EXTERNAL0 )
156
+ . priority ( 1 )
157
+ . enabled ( true )
158
+ . finish ( b) ,
159
+ InterruptLine :: build ( )
160
+ . line ( interrupt:: USB_TRCPT1 as InterruptNum + port:: INTERRUPT_EXTERNAL0 )
161
+ . priority ( 1 )
162
+ . enabled ( true )
163
+ . finish ( b) ,
164
+ ] ;
165
+
124
166
// Graphics-related tasks and objects
125
167
let _animation_task = Task :: build ( )
126
168
. start ( animation_task_body)
@@ -139,6 +181,9 @@ const fn configure_app(b: &mut CfgBuilder<System>) -> Objects {
139
181
console_pipe,
140
182
lcd_mutex,
141
183
button_reporter_task,
184
+ usb_in_task,
185
+ usb_poll_timer,
186
+ usb_interrupt_lines,
142
187
}
143
188
}
144
189
@@ -196,8 +241,53 @@ fn init_hardware() {
196
241
) ;
197
242
button_ctrlr. enable ( & mut core_peripherals. NVIC ) ;
198
243
unsafe { BUTTON_CTRLR = Some ( button_ctrlr) } ;
244
+
245
+ // Configure the USB serial device
246
+ let sets_usb = sets. usb ;
247
+ let peripherals_usb = peripherals. USB ;
248
+ let peripherals_mclk = & mut peripherals. MCLK ;
249
+ let usb_bus_allocator = singleton ! (
250
+ : UsbBusAllocator <UsbBus > =
251
+ sets_usb. usb_allocator(
252
+ peripherals_usb,
253
+ & mut clocks,
254
+ peripherals_mclk,
255
+ )
256
+ )
257
+ . unwrap ( ) ;
258
+ let serial = SerialPort :: new ( usb_bus_allocator) ;
259
+ let usb_device = UsbDeviceBuilder :: new ( usb_bus_allocator, UsbVidPid ( 0x16c0 , 0x27dd ) )
260
+ . product ( "R3 Example" )
261
+ . device_class ( USB_CLASS_CDC )
262
+ . max_packet_size_0 ( 64 )
263
+ . build ( ) ;
264
+ * USB_STDIO_GLOBAL . lock ( ) = Some ( UsbStdioGlobal { serial, usb_device } ) ;
199
265
}
200
266
267
+ // Message producer
268
+ // ----------------------------------------------------------------------------
269
+
270
+ /// The task responsible for outputting messages to the console.
271
+ fn noisy_task_body ( _: usize ) {
272
+ let _ = writeln ! ( Console , "////////////////////////////////" ) ;
273
+ let _ = writeln ! (
274
+ Console ,
275
+ "Hello! Send text to me over the USB serial port \
276
+ (e.g., `/dev/ttyACM0`), and I'll display it!"
277
+ ) ;
278
+ let _ = writeln ! ( Console , "////////////////////////////////" ) ;
279
+ loop {
280
+ // Print a message
281
+ let _ = write ! ( Console , "-- {:?} --" , System :: time( ) . unwrap( ) ) ;
282
+
283
+ System :: sleep ( r3:: time:: Duration :: from_secs ( 60 ) ) . unwrap ( ) ;
284
+ let _ = writeln ! ( Console ) ;
285
+ }
286
+ }
287
+
288
+ // Console and graphics
289
+ // ----------------------------------------------------------------------------
290
+
201
291
/// Acquire a lock on `wio::LCD`, yielding the CPU to lower-priority tasks as
202
292
/// necessary.
203
293
///
@@ -242,20 +332,6 @@ impl Write for Console {
242
332
}
243
333
}
244
334
245
- /// The task responsible for outputting messages to the console.
246
- fn noisy_task_body ( _: usize ) {
247
- loop {
248
- // Print a message
249
- let _ = write ! ( Console , "time = {:?}" , System :: time( ) . unwrap( ) ) ;
250
-
251
- for _ in 0 ..10 {
252
- let _ = write ! ( Console , "." ) ;
253
- System :: sleep ( r3:: time:: Duration :: from_millis ( 55 ) ) . unwrap ( ) ;
254
- }
255
- let _ = writeln ! ( Console ) ;
256
- }
257
- }
258
-
259
335
/// The task responsible for blinking the user LED.
260
336
fn blink_task_body ( _: usize ) {
261
337
let mut st = BLINK_ST . lock ( ) ;
@@ -363,6 +439,9 @@ fn animation_task_body(_: usize) {
363
439
}
364
440
}
365
441
442
+ // Button listener
443
+ // ----------------------------------------------------------------------------
444
+
366
445
static BUTTON_STATE : AtomicUsize = AtomicUsize :: new ( 0 ) ;
367
446
368
447
/// The task responsible for reporting button events.
@@ -434,6 +513,132 @@ wio::button_interrupt! {
434
513
}
435
514
}
436
515
516
+ // USB serial
517
+ // ----------------------------------------------------------------------------
518
+
519
+ struct UsbStdioGlobal {
520
+ usb_device : UsbDevice < ' static , UsbBus > ,
521
+ serial : SerialPort < ' static , UsbBus > ,
522
+ }
523
+
524
+ /// Stores [`UsbStdioGlobal`]. Only accessed by `poll_usb` (the USB interrupt
525
+ /// handler).
526
+ static USB_STDIO_GLOBAL : SpinMutex < Option < UsbStdioGlobal > > = SpinMutex :: new ( None ) ;
527
+
528
+ /// The USB input queue size
529
+ const USB_BUF_CAP : usize = 64 ;
530
+
531
+ /// The queue through which received data is passed from `poll_usb` to
532
+ /// `usb_in_task_body`
533
+ static USB_BUF_IN : PrimaskMutex < RefCell < ( [ u8 ; USB_BUF_CAP ] , usize ) > > =
534
+ PrimaskMutex :: new ( RefCell :: new ( ( [ 0 ; USB_BUF_CAP ] , 0 ) ) ) ;
535
+
536
+ /// USB interrupt handler
537
+ fn poll_usb ( ) {
538
+ let Some ( mut g) = USB_STDIO_GLOBAL . try_lock ( ) else { return } ;
539
+ let g = g. as_mut ( ) . unwrap ( ) ;
540
+
541
+ // It's important that we poll the USB device frequently enough
542
+ g. usb_device . poll ( & mut [ & mut g. serial ] ) ;
543
+
544
+ let mut should_unpark = false ;
545
+ let mut should_start_polling = false ;
546
+
547
+ disable_interrupts ( |cs| {
548
+ let mut usb_buf_in = USB_BUF_IN . borrow ( cs) . borrow_mut ( ) ;
549
+ let ( buf, buf_len) = & mut * usb_buf_in;
550
+ let remaining = & mut buf[ * buf_len..] ;
551
+ if remaining. is_empty ( ) {
552
+ // We can't process the data fast enough; apply back-pressure.
553
+ // Also, disable the USB interrupt lines because we would otherwise
554
+ // get an interrupt storm. (I'm surprised we have to do this. Is
555
+ // this really the proper way to apply back-pressure?)
556
+ should_start_polling = true ;
557
+ return ;
558
+ }
559
+
560
+ if let Ok ( len) = g. serial . read ( remaining) {
561
+ assert ! ( len <= remaining. len( ) ) ;
562
+ * buf_len += len;
563
+ should_unpark = len > 0 ;
564
+ }
565
+ } ) ;
566
+
567
+ // In this configuration `disable_interrupts` is equivalent to CPU Lock, so
568
+ // kernel functions cannot be called inside it
569
+ if should_unpark {
570
+ COTTAGE . usb_in_task . unpark ( ) . unwrap ( ) ;
571
+ }
572
+
573
+ if should_start_polling {
574
+ set_usb_polling ( true ) ;
575
+ }
576
+ }
577
+
578
+ #[ interrupt]
579
+ fn USB_OTHER ( ) {
580
+ poll_usb ( ) ;
581
+ }
582
+
583
+ #[ interrupt]
584
+ fn USB_TRCPT0 ( ) {
585
+ poll_usb ( ) ;
586
+ }
587
+
588
+ #[ interrupt]
589
+ fn USB_TRCPT1 ( ) {
590
+ poll_usb ( ) ;
591
+ }
592
+
593
+ fn usb_poll_timer_handler ( _: usize ) {
594
+ poll_usb ( ) ;
595
+ }
596
+
597
+ /// Change whether `poll_usb` is called in response to USB interrupts or in a
598
+ /// constant interval.
599
+ fn set_usb_polling ( b : bool ) {
600
+ if b {
601
+ COTTAGE . usb_poll_timer . start ( ) . unwrap ( ) ;
602
+ for line in COTTAGE . usb_interrupt_lines . iter ( ) {
603
+ line. disable ( ) . unwrap ( ) ;
604
+ }
605
+ } else {
606
+ COTTAGE . usb_poll_timer . stop ( ) . unwrap ( ) ;
607
+ for line in COTTAGE . usb_interrupt_lines . iter ( ) {
608
+ line. enable ( ) . unwrap ( ) ;
609
+ }
610
+ }
611
+ }
612
+
613
+ /// The task to print the data received by the USB serial endpoint
614
+ fn usb_in_task_body ( _: usize ) {
615
+ let mut data = arrayvec:: ArrayVec :: < u8 , USB_BUF_CAP > :: new ( ) ;
616
+ loop {
617
+ // Get next data to output
618
+ disable_interrupts ( |cs| {
619
+ let mut usb_buf_in = USB_BUF_IN . borrow ( cs) . borrow_mut ( ) ;
620
+ let ( buf, buf_len) = & mut * usb_buf_in;
621
+ data. clear ( ) ;
622
+ data. try_extend_from_slice ( & buf[ ..* buf_len] ) . unwrap ( ) ;
623
+ * buf_len = 0 ;
624
+ } ) ;
625
+
626
+ if data. is_empty ( ) {
627
+ // Got nothing; sleep until new data arrives
628
+ System :: park ( ) . unwrap ( ) ;
629
+ continue ;
630
+ }
631
+
632
+ if data. is_full ( ) {
633
+ set_usb_polling ( false ) ;
634
+ }
635
+
636
+ // Send it to the console
637
+ let data = core:: str:: from_utf8 ( & data) . unwrap_or ( "" ) ;
638
+ let _ = Console . write_str ( data) ;
639
+ }
640
+ }
641
+
437
642
// Utilities
438
643
// ----------------------------------------------------------------------------
439
644
0 commit comments