Place to put your Basic demos and examples

Moderator: Mmiscool

User avatar
By Ecoli-557
#54744 OK folks, here is a demo on a menu system using 5 keys from a SPI port expander for UP - DOWN - LEFT - RIGHT - SELECT. Using ESP Basic 3.0.Alpha 46
There is a demo set of menu items (12) and the selection is noted by a square radio button (see pix).
As I found out when using multiple SPI devices on a solderless breadboard - you MUST not use the common tie-points - the capacitance is too high at the higher speeds (see pix). Trust me, I wasted a fair amount of time chasing that down!

I like SPI for its speed, full-duplex, and that more peripheral chips are available using this scheme and the fact that we can now change the speed on the fly (Thanks Mike) using spi.setfrequency= whatever speed you need, things are really getting exciting for the ESP.

Code and pictures below:
Code: Select all'Demo showing how to add several SPI modules for a project.
'It is IMPORTANT that you DO NOT USE THE SOLDERLESS BREADBOARD for SPI buses!
'You will need to INVERT the 4 pins for proper use and sSOLDER wires to/from
'these SPI devices in order for it to work reliably.
'Buttons are PORTA of the MCP23S17 SPI port expander with pullups enabled on-chip.
'LEDs are of PORTB as diagnostics and code was removed after working.
'It is also IMPORTANT that you do not try to drive ALL of this demo circuitry from
'the weak current regulator on the NodeMCU, it causes unreliable operations.
'Another note, in order for this demo to work, the number of variable was
'increased from 50 to 100 --->  Hat tip to Mike!
'as well as his other guidance.....

memclear
'Setup TFT touch-screen----------------------------
tft.setup(15, 2, 2)      'CS on GPIO15 w/10k pull-down to GND, D/C on GPIO4,Landscape mode.  USE A SEPARATE PS!
SPIf = 10000000   'Using new SPI directive which 'sets' the SPI clock signal - here is 10meg

'SPI init for the 23S17 expander
spi.setup(10000000) 'clk on gpio14(d5) mosi on gpio13(d7)   'Reaffirm pins and legacy clock freq

'Setup MCP23S17 here
CS = 5 ' CS on gpio4(d2) for MCP23S17
S17Int = 4 'Interrupt from MCP23S17
'equate registers to vars for later use - this is brute-force but does work
'except the interrupt function - couldn't make it work reliably - basic needs to mature
'a bit more perhaps in order to get edge-level detection
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
'-------------------------------------------------------------------------------------
'Setup Activity Messages now - no more than 19 chars!
dim Act(13) as string   'Arrays are more effecient for memory
Act(1) = "This is Activity 1"
Act(2) = "This is Activity 2"
Act(3) = "This is Activity 3"
Act(4) = "This is Activity 4"
Act(5) = "This is Activity 5"
Act(6) = "This is Activity 6"
Act(7) = "This is Activity 7"
Act(8) = "This is Activity 8"
Act(9) = "This is Activity 9"
Act(10) = "This is Activity10"
Act(11) = "This is Activity11"
Act(12) = "This is Activity12"


dim option(13)
OldKey = 0   'Var that is used the check for 'difference' in last key and the current key


Selected = 0   'This is the var that gets us around in the menu
spi.setfrequency(SPIf)
'Now setup the 23S17 for use in this demo
io(po,CS,0)
spi.hex(Wrt & IOCON & "20",3) 'No sequential writes/reads, INT pin active driver, active LOW
io(po,CS,1) 

io(po,CS,0)
spi.hex(Wrt & IODIRB & "00",3) 'All PORTB are OUTPUTS
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & IODIRA & "FF",3) 'All PORTA are INPUTS
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & IPOLA & "FF",3) 'INVERT all PORTA inputs
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & GPPUA & "FF",3) 'All PORTA are PULLED HIGH internally
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & GPINTENA & "FF",3) 'All PORTA are INTERRUPT ENABLED
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & DEFVALA & "00",3) 'PORTA interupts occur when HIGH
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & INTCONA & "00",3) 'was FF -PORTA interrupts based on DEFVAL register
io(po,CS,1)

'Reset any left over interrupt data
io(po,CS,0)
buttons = spi.hex(Rd & INTCAPA & "00" ,3) 'READ PORTA for keypresses, note '00' as a pad char
io(po,CS,1)
buttons = 0

'Flush the PORTB buffer by writing 0xFF
io(po,CS,0)
spi.hex(Wrt & OutB & "FF",3)
io(po,CS,1)
'interrupt S17Int, [Check.Buttons] 'Interrupt from 23S17 so we can see which button was pressed - doesn't work reliably

'-----------------------------------------------------

[Top]
timer 100, [Check.Buttons]   'So now the keys are on a 'polled' scheme - every 100ms
tft.text.color(tft.rgb(255,0,255))
tft.text.cursor(5,0)
tft.text.size(3)
tft.print(" ACTIVITIES")

'tft.obj.radio(label, x, y, height, checked, text_size, fore_color, back_color)
for x = 1 to 12
    option(x) = tft.obj.checkbox(Act(x),0,10+x*20,18,0,2, tft.rgb(0,255,0), tft.rgb(0,0,0))   'Prints the menu to TFT
next x

tft.text.color(tft.rgb(0,255,255))
tft.text.cursor(5,280)
tft.print("  USE CURSOR KEYS,")   'Prints helpfulo message at bottom of display
tft.text.cursor(5,300)
tft.print("   THEN 'SELECT'")      

wait


[Check.Buttons]    'Get here every 100ms
spi.setfrequency(SPIf)
io(po,CS,0)
hexkey = spi.hex(Rd & InPrtA & "00" ,3) 'Reads entire PORTA, note '00' as a pad char for button data
io(po,CS,1)
pressed = (hextoint(hexkey) and 255 )   'Strips off 4 high bytes, leaving low 2 byte result
if pressed = OldKey then wait
OldKey = pressed                      'Equate a temp var so we can loop as long as user has finger on button
'Menu action takes place here
if pressed = 1 then
'   Action = "up"
    goto [MenuUp]
endif
if pressed = 2 then
'    Action = "down"
    goto [MenuDown]
endif
if pressed = 4 then
'    Action = "left"
    goto [MenuLeft]
endif
if pressed = 8 then
'    Action = "right"
    goto [MenuRight]
endif
if pressed = 10 then
'    Action = "select"
    goto [MenuSelect]
endif

wait

[MenuUp]
    Selected = Selected + 1
    If Selected = 13 then Selected = 1

for x = 1 to 12
    if Selected = x then
      tft.obj.setchecked(option(x),1)   'Menmu item was the one of interest so 'check it'
    else
      tft.obj.setchecked(option(x),0)   'Menu item was not the one we are looking for so 'un-select it'
    end if
next x
    wait
   
[MenuDown]
   Selected = Selected - 1
    If Selected = 0 then Selected = 12

for x = 1 to 12
    if Selected = x then
      tft.obj.setchecked(option(x),1)   'Same as in MenuUp
    else
      tft.obj.setchecked(option(x),0)   'Same as in MenuUp
    end if
next x
    wait
[MenuLeft]   'Leave it up to you to code
    wait
[MenuRight]   'Leave it up to you to code
    wait
[MenuSelect]   'Leave it up to you to code
    wait

'------------------------------------------------------------------------------------
end

You do not have the required permissions to view the files attached to this post.
User avatar
By picstart
#54816 I quick observation the TFT code requires a separate CS from the SPI extender. Two CS pins in all. The issue can be that the TFT code can and does assert de-assert its CS deep within the code. This makes having to synchronize the tft with the SPI extender to avoid conflicts or alternatively to digout the low level tft CS asserts. It is a tension. Electrical engineers see software as low level since they see electrons as king. Coders see code as king and see hardware as low level. Fortunately most code written for hardware devices such as the esp8266 do allow direct read and writes to pins at high level.