99///! # Licence
1010///! This source code as a whole is licensed under the GPL v3. Third-party crates are covered by their respective licences.
1111use cortex_m:: interrupt:: free as disable_interrupts;
12+ use heapless:: {
13+ consts:: * ,
14+ i,
15+ spsc:: { Consumer , Producer , Queue } ,
16+ } ;
1217use rtic:: app;
1318use stm32f0xx_hal:: {
1419 gpio:: gpioa:: { PA10 , PA11 , PA12 , PA15 , PA2 , PA3 , PA9 } ,
@@ -21,7 +26,7 @@ use stm32f0xx_hal::{
2126} ;
2227
2328use neotron_bmc as _;
24- use neotron_bmc:: monotonic:: { Instant , Tim3Monotonic , U16Ext } ;
29+ use neotron_bmc:: monotonic:: { Tim3Monotonic , U16Ext } ;
2530
2631/// Version string auto-generated by git.
2732static VERSION : & ' static str = include_str ! ( concat!( env!( "OUT_DIR" ) , "/version.txt" ) ) ;
@@ -44,6 +49,26 @@ pub enum DcPowerState {
4449 Off = 0 ,
4550}
4651
52+ /// Handles decoding incoming PS/2 packets
53+ ///
54+ /// Each packet has 11 bits:
55+ ///
56+ /// * Start Bit
57+ /// * 8 Data Bits (LSB first)
58+ /// * Parity Bit
59+ /// * Stop Bit
60+ #[ derive( Debug ) ]
61+ pub struct Ps2Decoder {
62+ bit_count : u8 ,
63+ collector : u16 ,
64+ }
65+
66+ /// This is our system state, as accessible via SPI reads and writes.
67+ #[ derive( Debug ) ]
68+ pub struct RegisterState {
69+ firmware_version : & ' static str ,
70+ }
71+
4772#[ app( device = crate :: pac, peripherals = true , monotonic = crate :: Tim3Monotonic ) ]
4873const APP : ( ) = {
4974 struct Resources {
@@ -82,8 +107,16 @@ const APP: () = {
82107 ps2_dat1 : PB5 < Input < Floating > > ,
83108 /// The external interrupt peripheral
84109 exti : pac:: EXTI ,
85- /// Our PS/2 keyboard decoder
86- kb : pc_keyboard:: Keyboard < pc_keyboard:: layouts:: Uk105Key , pc_keyboard:: ScancodeSet2 > ,
110+ /// Our register state
111+ register_state : RegisterState ,
112+ /// Keyboard PS/2 decoder
113+ kb_decoder : Ps2Decoder ,
114+ /// Mouse PS/2 decoder
115+ ms_decoder : Ps2Decoder ,
116+ /// Keyboard bytes sink
117+ kb_c : Consumer < ' static , u16 , U8 > ,
118+ /// Keyboard bytes source
119+ kb_p : Producer < ' static , u16 , U8 > ,
87120 }
88121
89122 /// The entry point to our application.
@@ -94,6 +127,8 @@ const APP: () = {
94127 /// * Task `button_poll` - checks the power and reset buttons
95128 #[ init( spawn = [ led_power_blink, button_poll] ) ]
96129 fn init ( ctx : init:: Context ) -> init:: LateResources {
130+ static mut Q : Queue < u16 , U8 > = Queue ( i:: Queue :: new ( ) ) ;
131+
97132 defmt:: info!( "Neotron BMC version {:?} booting" , VERSION ) ;
98133
99134 let dp: pac:: Peripherals = ctx. device ;
@@ -174,6 +209,8 @@ const APP: () = {
174209
175210 defmt:: info!( "Init complete!" ) ;
176211
212+ let ( kb_p, kb_c) = Q . split ( ) ;
213+
177214 init:: LateResources {
178215 serial,
179216 pin_uart_cts,
@@ -192,48 +229,49 @@ const APP: () = {
192229 ps2_dat0,
193230 ps2_dat1,
194231 exti : dp. EXTI ,
195- kb : pc_keyboard:: Keyboard :: new (
196- pc_keyboard:: layouts:: Uk105Key ,
197- pc_keyboard:: ScancodeSet2 ,
198- pc_keyboard:: HandleControl :: MapLettersToUnicode ,
199- ) ,
232+ register_state : RegisterState {
233+ firmware_version : "Neotron BMC v0.0.0" ,
234+ } ,
235+ kb_p,
236+ kb_c,
237+ kb_decoder : Ps2Decoder :: new ( ) ,
238+ ms_decoder : Ps2Decoder :: new ( ) ,
200239 }
201240 }
202241
203242 /// Our idle task.
204243 ///
205244 /// This task is called when there is nothing else to do. We
206245 /// do a little logging, then put the CPU to sleep waiting for an interrupt.
207- #[ idle( resources = [ ] ) ]
208- fn idle ( _ctx : idle:: Context ) -> ! {
246+ #[ idle( resources = [ kb_c ] ) ]
247+ fn idle ( ctx : idle:: Context ) -> ! {
209248 defmt:: info!( "Idle is running..." ) ;
210249 loop {
211- cortex_m:: asm:: wfi ( ) ;
212- defmt:: trace!( "It is now {}" , crate :: Instant :: now( ) . counts( ) ) ;
250+ if let Some ( word) = ctx. resources . kb_c . dequeue ( ) {
251+ if let Some ( byte) = Ps2Decoder :: check_word ( word) {
252+ defmt:: info!( "< KB {:x}" , byte) ;
253+ } else {
254+ defmt:: info!( "< Bad KB {:x}" , word) ;
255+ }
256+ }
213257 }
214258 }
215259
216260 /// This is the PS/2 Keyboard task.
217261 ///
262+ /// It is very high priority, as we can't afford to miss a clock edge.
263+ ///
218264 /// It fires when there is a falling edge on the PS/2 Keyboard clock pin.
219- #[ task( binds = EXTI4_15 , resources=[ ps2_clk0, ps2_dat0, exti, kb] ) ]
265+ #[ task(
266+ binds = EXTI4_15 ,
267+ priority = 15 ,
268+ resources=[ ps2_clk0, ps2_dat0, exti, kb_decoder, kb_p] ) ]
220269 fn exti4_15_interrupt ( ctx : exti4_15_interrupt:: Context ) {
221- match ctx
222- . resources
223- . kb
224- . add_bit ( ctx. resources . ps2_dat0 . is_high ( ) . unwrap ( ) )
225- {
226- Err ( e) => {
227- defmt:: warn!( "Error decoding kb: {}" , e as usize ) ;
228- }
229- Ok ( None ) => { }
230- Ok ( Some ( evt) ) => {
231- defmt:: info!(
232- "Got event core={}, state={}" ,
233- evt. code as usize ,
234- evt. state as usize
235- ) ;
236- }
270+ let data_bit = ctx. resources . ps2_dat0 . is_high ( ) . unwrap ( ) ;
271+ // Do we have a complete word (and if so, is the parity OK)?
272+ if let Some ( data) = ctx. resources . kb_decoder . add_bit ( data_bit) {
273+ // Don't dump in the ISR - we're busy. Add it to this nice lockless queue instead.
274+ ctx. resources . kb_p . enqueue ( data) . unwrap ( ) ;
237275 }
238276 // Clear the pending flag
239277 ctx. resources . exti . pr . write ( |w| w. pr15 ( ) . set_bit ( ) ) ;
@@ -347,6 +385,59 @@ const APP: () = {
347385 }
348386} ;
349387
388+ impl Ps2Decoder {
389+ fn new ( ) -> Ps2Decoder {
390+ Ps2Decoder {
391+ bit_count : 0 ,
392+ collector : 0 ,
393+ }
394+ }
395+
396+ fn reset ( & mut self ) {
397+ self . bit_count = 0 ;
398+ self . collector = 0 ;
399+ }
400+
401+ fn add_bit ( & mut self , bit : bool ) -> Option < u16 > {
402+ if bit {
403+ self . collector |= 1 << self . bit_count ;
404+ }
405+ self . bit_count += 1 ;
406+ if self . bit_count == 11 {
407+ let result = self . collector ;
408+ self . reset ( ) ;
409+ Some ( result)
410+ } else {
411+ None
412+ }
413+ }
414+
415+ /// Check 11-bit word has 1 start bit, 1 stop bit and an odd parity bit.
416+ fn check_word ( word : u16 ) -> Option < u8 > {
417+ let start_bit = ( word & 0x0001 ) != 0 ;
418+ let parity_bit = ( word & 0x0200 ) != 0 ;
419+ let stop_bit = ( word & 0x0400 ) != 0 ;
420+ let data = ( ( word >> 1 ) & 0xFF ) as u8 ;
421+
422+ if start_bit {
423+ return None ;
424+ }
425+
426+ if !stop_bit {
427+ return None ;
428+ }
429+
430+ let need_parity = ( data. count_ones ( ) % 2 ) == 0 ;
431+
432+ // Odd parity, so these must not match
433+ if need_parity != parity_bit {
434+ return None ;
435+ }
436+
437+ Some ( data)
438+ }
439+ }
440+
350441// TODO: Pins we haven't used yet
351442// SPI pins
352443// spi_clk: gpioa.pa5.into_alternate_af0(cs),
0 commit comments