My code for LCD dispaly

I have had a little play with a 1.77" 160x128 TFT display. I made the PCB to be similar to the official OpenMV LCD shield but except to be in landscape instead of portrait.

For the moment the code is very light and just streams your frame buffer to the screen, there plenty of room to add features.

I might sit down and do the same for a 2.4" 320x240 TFT display as well.

See it in action here - YouTube

and this is the code that was running when the video was taken

import time
from pyb import Pin, SPI
from micropython import const
from ustruct import pack
import sensor, image, time

_SWRESET = const(0x01) # Software Reset
_SLPOUT = const(0x11) # Sleep Out
_COLMOD = const(0x3A) # Colour Mode
_DISPON = const(0x29) # Display On
_MADCTL = const(0x36) # Memory Data Access
_CASET = const(0x2A) # Column Address Set
_RASET = const(0x2B) # Row Address set
_RAMWR = const(0x2C) #write to screen memory

dc = Pin('P8', Pin.OUT_PP)  #setup the data/command pin
spi = SPI(2, SPI.MASTER, 54000000) #setup SPI to go as fast as possible


def send_spi(data, is_data):
    dc.value(is_data) #set the data/commond pin
    spi.send(data)

def init():
  send_spi(bytearray([_SWRESET]), False) #software reset
  time.sleep(200)
  send_spi(bytearray([_SLPOUT]), False)  #sleep out
  time.sleep(200)
  send_spi(bytearray([_COLMOD]), False)  #set 16 bit colour
  send_spi(bytearray([0x05]),True)
  send_spi(bytearray([_DISPON]), False)  #display on
  send_spi(bytearray([_MADCTL]), False)  #set mode for writing to screen
  send_spi(bytearray([0b10100000]),True) #this was the mode that I used for my screen

def set_window(x0, y0, width, height):
    x1=x0+width-1
    y1=y0+height-1
    send_spi(bytearray([_CASET]),False) #set width
    send_spi(pack(">HH", x0, x1), True) #
    send_spi(bytearray([_RASET]),False) #set hieght
    send_spi(pack(">HH", y0, y1), True) #

def dump_data(data):
  send_spi(bytearray([_RAMWR]), False) #command to write to screen RAM
  send_spi(data, True)



sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
clock = time.clock()

init()
set_window(0,0,160,120)

while True:
      clock.tick()
      img = sensor.snapshot()
      dump_data(img)
      print(clock.fps())

This is really awesome man.

Keep going! I just tweeted your video.

I have ordered some smd 8 pin headers and a smd on/off switch and am going to build a much better little screen to plug into the back of the OpenMV cam.

I may also play with adding touch to the screen. The touch is driven by a XPT2045 chip that I will mount on the back of the screen PCB. The XPT2045 is driven via SPI too so will need another CS line. Does OpenMV cam firmware allow to specify other CS lines besides P3 and multiply instances of SPI driver??

There’s only one SPI bus. But the CS lines are all controlled in software so you can have as many as you have pins for.

What’s the correct syntax for declaring different CS pin??

This is the code that I used for the screen driver

spi = SPI(2, SPI.MASTER, 54000000)

but be able to add something like this to create a second instances of the SPI driver but with different CS pins

spi_touch = SPI(2, SPI.MASTER, 54000000, cs='P4')

You just create a pin and make it go high and low. The SPI thing doesn’t drive the CS pin at all. It only does sclk, mosi, and miso.

The SPI thing doesn’t drive the CS pin at all.

This is true for the underlying C library but the pyb.SPI adds this in for the user see my program above for running the screen and nowhere is there any mention of the CS pin as it is auto by the pyb.SPI

see my 2 lines of code to send SPI data.

spi = SPI(2, SPI.MASTER, 54000000) #setup SPI to go as fast as possible
spi.send(data)

If I am going to manually drive ‘P4’ low to talk to the touch chip I also need to make sure that the pyb.SPI.send doesn’t send ‘P3’ low as well otherwise both the screen and the touch will both receive the data.

Best solution of all is what they did with ESP32 port of Micro-Python add 1 more arg to machine.SPI.write (pyb.SPI.send with this port) where you can specify the CS pin as this is very user friendly.

Then something like this can be used

spi_screen = SPI(2, SPI.MASTER, cs='P3', baudrate=54000000)
spi_touch = SPI(2, SPI.MASTER, cs='P4', baudrate=54000000)

see the wiki for the ESP32 port of MicroPython that I use spi · loboris/MicroPython_ESP32_psRAM_LoBo Wiki · GitHub

See this:

https://github.com/openmv/openmv/blob/master/scripts/examples/02-Board-Control/spi_control.py

At no point does SPI actually drive the CS pin. I don’t know write how why your LCD shield is hooked up. But, CS is controlled by software only. After write returns you can safely change which pin is selected.

At no point does SPI actually drive the CS pin.

If this is the case then I am surprised that my LCD driver worked as I didn’t do anything with the CS pin in software at all so it would have been floating. I was under the assumption that the pyb.SPI.send method took care of this.

Ok I will add driving the CS pin to all my SPI programs from now on :slight_smile:

Also I might look at shifting my DC pin from ‘P8’ to ‘P9’ so that it doesn’t use the I2C pin so that the LCD and I2C can be used at the same time.

If I hard wire the RST and BL to 3.3v and have a manual human operated switch to turn the screen and Black light on/off then I will only use MOSI, MISO, CS AND DC this means the LCD will be able to be used in combo with your other shields :slight_smile: