'Demo showing how to configure and use a MCP23S17 SPI port expander for the ESP8266
spi.setup(1000000) 'clk on gpio14(d5) mosi on gpio13(d7)
CS = 4 ' CS on gpio4(d2)
'equate registers to vars for later use
let Wrt = "40" 'Write hexcode
let Rd = "41" 'Read hexcode
'Registers for Bank=0 configuration as done with IOCON
let IODIRA = "00" 'Direction for PORTA; IN or OUT. In=1, Out=0
let IODIRB = "01" 'Direction for PORTB; IN or OUT. In=1, Out=0
let IPOLA = "02" 'Input polarity for PORTA
let IPOLB = "03" 'Input polarity for PORTB
let GPINTENA = "04" 'Interrupt on change for PORTA
let GPINTENB = "05" 'Interrupt on change for PORTA
let DEFVALA = "06" 'Default value for interrupt on change for PORTA
let DEFVALB = "07" 'Default value for interrupt on change for PORTB
let INTCONA = "08" 'Interrupt control for PORTA
let INTCONB = "09" 'Interrupt control for PORTB
let IOCON = "0A" 'Main configuration for the chip
let GPPUA = "0C" 'Pull-Up resisters for PORTA
let GPPUB = "0D" 'Pull-Up resisters for PORTB
let INTFA = "0E" 'Interrupt flags for PORTA
let INTFB = "0F" 'Interrupt flags for PORTB
let INTCAPA = "10" 'Interrupt capture for PORTA
let INTCAPB = "11" 'Interrupt capture for PORTB
let InPrtA = "12" 'GPIO for PORTA, used to READ, can be used to write
let InPrtB = "13" 'GPIO for PORTB, used to READ, can be used to write
let OutA = "14" 'Output latches for PORTA - I prefer this
let OutB = "15" 'Output latches for PORTB - I prefer this
'Now setup the 23S17 for use in this demo
io(po,CS,0)
spi.hex(Wrt & IOCON & "22",3) 'Sets up the IOCON register
io(po,CS,1)
io(po,CS,0)
spi.hex(Wrt & IODIRB & "FF",3) 'All PORTB are INPUTS
io(po,CS,1)
io(po,CS,0)
spi.hex(Wrt & GPPUB & "FF",3) 'All PORTB are PULLED HIGH internally
io(po,CS,1)
io(po,CS,0)
spi.hex(Wrt & IODIRA & "00",3) 'All PORTA are OUTPUTS
io(po,CS,1)
'------------------------------------------------------------------------------------
[Start]
'Performs a 'Cylon' LED movement, works but at a 'brute' force tactic. Need more elegance.
'Shift through PORT bits 1-8
for Pin = 1 to 8 'Cycle through the bit positions on the PORT
if Pin = 1 then ThisPin = 1 'Get the physical bit position to the PORT - not elegant
if Pin = 2 then ThisPin = 2 'Use decimal port equivilent due to no port/bit function
if Pin = 3 then ThisPin = 4
if Pin = 4 then ThisPin = 8
if Pin = 5 then ThisPin = 16
if Pin = 6 then ThisPin = 32
if Pin = 7 then ThisPin = 64
if Pin = 8 then ThisPin = 128
'LEDs are active LOW
' OutPin = hex(ThisPin xor 255) 'this should function the same as below, but doesn't
OutPin = ThisPin xor 255 'Need the INVERSE as the LEDs are active LOW
OutPin = hex(OutPin) 'Need to convert to Hex for the port expander
io(po,CS,0)
spi.hex(Wrt & OutA & OutPin,3) 'Write data
io(po,CS,1)
delay 50 'Wait a bit
next Pin
'Then for next going from 8 to 1
for Pin = 8 to 1 step -1 'Cycle through the bit positions on the PORT
if Pin = 1 then ThisPin = 1 'Get the physical bit position to the PORT - not elegant
if Pin = 2 then ThisPin = 2 'Use decimal port equivilent due to no port/bit function
if Pin = 3 then ThisPin = 4
if Pin = 4 then ThisPin = 8
if Pin = 5 then ThisPin = 16
if Pin = 6 then ThisPin = 32
if Pin = 7 then ThisPin = 64
if Pin = 8 then ThisPin = 128
'LEDs are active LOW
' OutPin = hex(ThisPin xor 255) 'this should function the same as below, but doesn't
OutPin = ThisPin xor 255 'Need the INVERSE as the LEDs are active LOW
OutPin = hex(OutPin) 'Need to convert to Hex for the port expander
io(po,CS,0)
spi.hex(Wrt & OutA & OutPin,3) 'Write data
io(po,CS,1)
delay 50 'Wait a bit
next Pin
goto [Start] 'Do it endlessly
'------------------------------------------------------------------------------------
'This will write a port at once. My LEDs are active LOW. ONLY the 4th bit counting from 1 is lit
'io(po,CS,0)
'spi.hex(Wrt & OutA & "F7",3)
'io(po,CS,1)
'------------------------------------------------------------------------------------
'read a pushbutton on PORTB and display on PORTA
'io(po,CS,0)
'buttons = spi.hex(Rd & InPrtB & "00",3) 'READ PORTB for keypresses, note '00' as a pad char
'io(po,CS,1)
'pressed = mid(buttons,5,2)
'io(po,CS,0)
'spi.hex(Wrt & OutA & pressed,3) 'writes whatever is pressed to PORTA
'io(po,CS,1)
'-----------------------------------------------------------------------------------
end
I strongly suggest you download the datasheet (48 pages) to get a more in depth feel on how I am controlling this chip in order to work it for your own use.
As is, it will turn on LEDs connected to PORTA through a 220 ohm resistor to 3.3v - they are active LOW. It will start with LED on GPA0 ON, then OFF, move to GPA1 ON, then OFF, and so on until GPA7 then it runs back down like the Cylons 'eye' did or Kit in Knight Rider.
There are 2 other programs which are commented and you can comment the Cylon portion and selectively use the other 2; one is for a byte-wide push to an entire port at once. The other illustrates how to read an input on PORTB (pulled HIGH) and display on PORTA which is active LOW.
The Cylon routine is brute force as I am still quite new to this and I don't know a more elegant or simple way to do what I needed to do as we do not have more granularity for SPI control - but it does work and it is illustrative. I tried to upload a MP4 of the Cylon demo portion, but MP4s are not allowed on this site.
Any comments to make it work more efficiently would be appreciated.
Enjoy and Regards to All.
- Albert Einstein