@@ -45,6 +45,7 @@ pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
4545// Sub-modules
4646// -----------------------------------------------------------------------------
4747
48+ pub mod mcp23s17;
4849pub mod rtc;
4950pub mod vga;
5051
@@ -108,12 +109,14 @@ type I2cPins = (
108109 Pin < bank0:: Gpio15 , Function < hal:: gpio:: I2C > > ,
109110) ;
110111
112+ type SpiBus = hal:: Spi < hal:: spi:: Enabled , pac:: SPI0 , 8 > ;
113+
111114/// All the hardware we use on the Pico
112115struct Hardware {
113116 /// All the pins we use on the Raspberry Pi Pico
114117 pins : Pins ,
115118 /// The SPI bus connected to all the slots
116- spi_bus : hal :: Spi < hal :: spi :: Enabled , pac :: SPI0 , 8 > ,
119+ spi_bus : SpiBus ,
117120 /// Something to perform small delays with. Uses SysTICK.
118121 delay : cortex_m:: delay:: Delay ,
119122 /// Current 5-bit value shown on the LEDs (including the HDD in bit 0).
@@ -215,11 +218,11 @@ static HARDWARE: Mutex<core::cell::RefCell<Option<Hardware>>> =
215218pub static OS_IMAGE : [ u8 ; include_bytes ! ( "thumbv6m-none-eabi-flash1002-libneotron_os.bin" ) . len ( ) ] =
216219 * include_bytes ! ( "thumbv6m-none-eabi-flash1002-libneotron_os.bin" ) ;
217220
218- // Tracks if we have had an IO interrupt.
219- //
220- // Set by the GPIO interrupt routine to reflect the level state of the IRQ input
221- // from the IO chip. This this value is `true` if any of `IRQ0` through `IRQ7`
222- // is currently active (low).
221+ /// Tracks if we have had an IO interrupt.
222+ ///
223+ /// Set by the GPIO interrupt routine to reflect the level state of the IRQ input
224+ /// from the IO chip. This this value is `true` if any of `IRQ0` through `IRQ7`
225+ /// is currently active (low).
223226static INTERRUPT_PENDING : AtomicBool = AtomicBool :: new ( false ) ;
224227
225228/// The table of API calls we provide the OS
@@ -474,27 +477,6 @@ impl Hardware {
474477 /// Give the BMC 6us to calculate its response
475478 const BMC_REQUEST_RESPONSE_DELAY_CLOCKS : u32 = 6_000 / Self :: NS_PER_CLOCK_CYCLE ;
476479
477- /// Data Direction Register A on the MCP23S17
478- const MCP23S17_DDRA : u8 = 0x00 ;
479-
480- /// Interrupt-on-Change Control Register B on the MCP23S17
481- const MCP23S17_GPINTENB : u8 = 0x05 ;
482-
483- /// Interrupt Control Register B on the MCP23S17
484- const MCP23S17_DEFVALB : u8 = 0x07 ;
485-
486- /// Interrupt Control Register B on the MCP23S17
487- const MCP23S17_INTCONB : u8 = 0x09 ;
488-
489- /// GPIO Pull-Up Register B on the MCP23S17
490- const MCP23S17_GPPUB : u8 = 0x0D ;
491-
492- /// GPIO Data Register A on the MCP23S17
493- const MCP23S17_GPIOA : u8 = 0x12 ;
494-
495- /// GPIO Data Register B on the MCP23S17
496- const MCP23S17_GPIOB : u8 = 0x13 ;
497-
498480 /// Build all our hardware drivers.
499481 ///
500482 /// Puts the pins into the right modes, builds the SPI driver, etc.
@@ -729,43 +711,45 @@ impl Hardware {
729711 /// You are required to have called `self.release_cs_lines()` previously,
730712 /// otherwise the I/O chip and your selected bus device will both see a
731713 /// chip-select signal.
732- fn with_io_cs < F > ( & mut self , func : F )
714+ fn with_io_cs < F , T > ( & mut self , func : F ) -> T
733715 where
734- F : FnOnce ( & mut hal:: Spi < hal:: spi:: Enabled , pac:: SPI0 , 8_u8 > ) ,
716+ F : FnOnce ( & mut hal:: Spi < hal:: spi:: Enabled , pac:: SPI0 , 8_u8 > ) -> T ,
735717 {
736718 // Select MCP23S17
737719 self . pins . nspi_cs_io . set_low ( ) . unwrap ( ) ;
738720 // Setup time
739721 cortex_m:: asm:: delay ( Self :: CS_IO_SETUP_CPU_CLOCKS ) ;
740722 // Do the SPI thing
741- func ( & mut self . spi_bus ) ;
723+ let result = func ( & mut self . spi_bus ) ;
742724 // Hold the CS pin a bit longer
743725 cortex_m:: asm:: delay ( Self :: CS_IO_HOLD_CPU_CLOCKS ) ;
744726 // Release the CS pin
745727 self . pins . nspi_cs_io . set_high ( ) . unwrap ( ) ;
728+ // Return the result from the closure
729+ result
746730 }
747731
748732 /// Write to a register on the MCP23S17 I/O chip.
749733 ///
750734 /// * `register` - the address of the register to write to
751- /// * `data ` - the value to write
752- fn io_chip_write ( & mut self , register : u8 , data : u8 ) {
735+ /// * `value ` - the value to write
736+ fn io_chip_write ( & mut self , register : mcp23s17 :: Register , value : u8 ) {
753737 // Inter-packet delay
754738 cortex_m:: asm:: delay ( Self :: CS_IO_DISABLE_CPU_CLOCKS ) ;
755739
756740 // Do the operation with CS pin active
757741 self . with_io_cs ( |spi| {
758- spi . write ( & [ 0x40 , register, data ] ) . unwrap ( ) ;
742+ mcp23s17 :: write_register ( spi , register, value ) ;
759743 } ) ;
760744
761745 // Inter-packet delay
762746 cortex_m:: asm:: delay ( Self :: CS_IO_DISABLE_CPU_CLOCKS ) ;
763747
764748 let read_back = self . io_chip_read ( register) ;
765- if read_back != data {
749+ if read_back != value {
766750 defmt:: panic!(
767- "Wrote 0x{:02x} to IO chip register {}, got 0x{:02x}" ,
768- data ,
751+ "Wrote 0x{:02x} to IO chip register {:? }, got 0x{:02x}" ,
752+ value ,
769753 register,
770754 read_back
771755 ) ;
@@ -775,20 +759,12 @@ impl Hardware {
775759 /// Read from a register on the MCP23S17 I/O chip.
776760 ///
777761 /// * `register` - the address of the register to read from
778- fn io_chip_read ( & mut self , register : u8 ) -> u8 {
762+ fn io_chip_read ( & mut self , register : mcp23s17 :: Register ) -> u8 {
779763 // Inter-packet delay
780764 cortex_m:: asm:: delay ( Self :: CS_IO_DISABLE_CPU_CLOCKS ) ;
781765
782- // Starts with outbound, is replaced with inbound
783- let mut buffer = [ 0x41 , register, 0x00 ] ;
784-
785766 // Do the operation with CS pin active
786- self . with_io_cs ( |spi| {
787- spi. transfer ( & mut buffer) . unwrap ( ) ;
788- } ) ;
789-
790- // Last byte has the value we read
791- buffer[ 2 ]
767+ self . with_io_cs ( |spi| mcp23s17:: read_register ( spi, register) )
792768 }
793769
794770 /// Set the four debug LEDs on the PCB.
@@ -798,7 +774,10 @@ impl Hardware {
798774 // LEDs are active-low.
799775 let leds = ( leds ^ 0xFF ) & 0xF ;
800776 self . led_state = leds << 1 | ( self . led_state & 1 ) ;
801- self . io_chip_write ( 0x12 , self . led_state << 3 | self . last_cs ) ;
777+ self . io_chip_write (
778+ mcp23s17:: Register :: GPIOA ,
779+ self . led_state << 3 | self . last_cs ,
780+ ) ;
802781 }
803782
804783 /// Set the HDD LED on the PCB.
@@ -807,7 +786,10 @@ impl Hardware {
807786 fn set_hdd_led ( & mut self , enabled : bool ) {
808787 // LEDs are active-low.
809788 self . led_state = ( self . led_state & 0x1e ) | u8:: from ( !enabled) ;
810- self . io_chip_write ( 0x12 , self . led_state << 3 | self . last_cs ) ;
789+ self . io_chip_write (
790+ mcp23s17:: Register :: GPIOA ,
791+ self . led_state << 3 | self . last_cs ,
792+ ) ;
811793 }
812794
813795 /// Perform some SPI transaction with a specific bus chip-select pin active.
@@ -823,7 +805,7 @@ impl Hardware {
823805
824806 if cs != self . last_cs {
825807 // Set CS Outputs into decoder/buffer
826- self . io_chip_write ( 0x12 , self . led_state << 3 | cs) ;
808+ self . io_chip_write ( mcp23s17 :: Register :: GPIOA , self . led_state << 3 | cs) ;
827809 self . last_cs = cs;
828810 }
829811
@@ -863,20 +845,16 @@ impl Hardware {
863845 // Undrive CS lines from decoder/buffer
864846 self . release_cs_lines ( ) ;
865847
866- // Inter-packet delay
867848 cortex_m:: asm:: delay ( Self :: CS_IO_DISABLE_CPU_CLOCKS ) ;
868849
869850 // Set IODIRA = 0x00 => GPIOA is all outputs
870- self . io_chip_write ( Self :: MCP23S17_DDRA , 0x00 ) ;
871-
872- // Inter-packet delay
873- cortex_m:: asm:: delay ( Self :: CS_IO_DISABLE_CPU_CLOCKS ) ;
851+ self . io_chip_write ( mcp23s17:: Register :: DDRA , 0x00 ) ;
874852
875853 // Set GPIOA = 0x00 => GPIOA is all low
876- self . io_chip_write ( Self :: MCP23S17_GPIOA , 0x00 ) ;
854+ self . io_chip_write ( mcp23s17 :: Register :: GPIOA , 0x00 ) ;
877855
878856 // Set GPPUB to = 0xFF => GPIOB is pulled-up
879- self . io_chip_write ( Self :: MCP23S17_GPPUB , 0xFF ) ;
857+ self . io_chip_write ( mcp23s17 :: Register :: GPPUB , 0xFF ) ;
880858
881859 // Set up interrupts. We want the line to go low if anything on GPIOB is
882860 // low.
@@ -885,18 +863,18 @@ impl Hardware {
885863 // for the interrupt-on-change feature. All the bits are set, so the
886864 // corresponding I/O pin is compared against the associated bit in the
887865 // DEFVAL register.
888- self . io_chip_write ( Self :: MCP23S17_INTCONB , 0xFF ) ;
866+ self . io_chip_write ( mcp23s17 :: Register :: INTCONB , 0xFF ) ;
889867
890868 // All the bits are set, so the corresponding pins are enabled for
891869 // interrupt-on-change. The DEFVAL and INTCON registers must also be
892870 // configured if any pins are enabled for interrupt-on-change.
893- self . io_chip_write ( Self :: MCP23S17_GPINTENB , 0xFF ) ;
871+ self . io_chip_write ( mcp23s17 :: Register :: GPINTENB , 0xFF ) ;
894872
895873 // The default comparison value is configured in the DEFVAL register. If
896874 // enabled (via GPINTEN and INTCON) to compare against the DEFVAL
897875 // register, an opposite value on the associated pin will cause an
898876 // interrupt to occur.
899- self . io_chip_write ( Self :: MCP23S17_DEFVALB , 0xFF ) ;
877+ self . io_chip_write ( mcp23s17 :: Register :: DEFVALB , 0x00 ) ;
900878 }
901879
902880 /// If the interrupt flag was set by the IRQ handler, read the interrupt
@@ -912,9 +890,11 @@ impl Hardware {
912890 }
913891 }
914892
915- /// Returns the contents of the GPIOB register, i.e. eight bits, where if bit N is low, IRQN is active.
893+ /// Returns the contents of the GPIOB register, i.e. eight bits, where if
894+ /// bit N is low, IRQN is active.
916895 fn io_read_interrupts ( & mut self ) -> u8 {
917- self . io_chip_read ( Self :: MCP23S17_GPIOB )
896+ self . io_chip_read ( mcp23s17:: Register :: GPIOB )
897+ }
918898 }
919899
920900 /// Send a request to the BMC (a register read or a register write) and get
0 commit comments