Adsense HTML/JavaScript

Monday, March 21, 2022

Arduino Nano RP2040 Connect/CircuitPython + ov7670 cam (incomplete) display on st7789 Display

It's just my exercise running on Arduino Nano RP2040 Connect/CircuitPython 7.2.3, to work with ov7670 cam module and display on ST7789 SPI display.


For the connection between Nano RP2040 Connect and ov7670/st7789, refer to the code listed below.

In my practice, pre-defined SPI MOSI/SCK are assigned to SDA/SCL for ST7789. I2C SCL/SDA are assigned to SCL/SDA for ov7670.

*ov7670 SCL/SDA are I2C-like control pins, pull-up resistors are needed. 2K ohm resistors are used in my exercise.

For the st7789 part, it's very straightforward - just install libraries and run the exercise code.
Libraries adafruit_st7789 and adafruit_display_text are needed.

For ov7670 part, libraries adafruit_st7789 and adafruit_ov7670 are needed. adafruit_ov7670 is a CircuitPython driver for OV7670 cameras.

The problem is in adafruit_ov7670, data_pins is a list of 8 data pins in sequential order.  But I can't find 8 continuous sequence GPIO on Nano RP2040 Connect. As an exercise, GPIO14~21 are assigned to ov7670 d0~d7, GPIO14 is internal connceted to SCK of the onboard ESP32 (U-blox Nina W102), so it's actually no use. So there are only 7 bits in parallel data bus, and ESP32 SCK is mis-used (you should cannot use ESP32 at the same time). That's why I call it "incomplete".

It seem functionally work, as shown in the video. But I don't know any side-effect. It's just my exercise and not suggested, or follow at your own risk.

To list pins name of all GPIOs (include internal), read last post.


cpyNrp2040_spiST7789_320.py
"""
Example of Arduino Nano RP2040 Connect/CircuitPython 7.2.3
to display on 2.0" IPS 240x320 (RGB) screen
with SPI ST7789 driver.

Connection between Nano RP2040 Connect and
the SPI ST7789 IPS screen.

BLK  - 3V3 (backlight, always on)
CS   - A0
DC   - A1
RES  - A2
SDA  - D11 (=MOSI)
SCL  - D13 (=SCK)
VCC  - 3V3
GND  - GND
"""

import os
import sys
import board
import microcontroller
import time
import terminalio
import displayio
import busio
from adafruit_display_text import label
#from adafruit_st7789 import ST7789
import adafruit_st7789

print("=====================================")
info = sys.implementation[0] + ' ' + \
       os.uname()[3] + '\n' + \
       'run on ' + os.uname()[4]
print(info)
print("board_id:", board.board_id)

print("with number of cpu: " +
      str(len(microcontroller.cpus)))
print("=====================================")
print(adafruit_st7789.__name__ + " version: "
      + adafruit_st7789.__version__)

print()

# Release any resources currently in use for the displays
displayio.release_displays()

tft_cs = board.A0
tft_dc = board.A1
tft_res = board.A2
spi_mosi = board.D11
spi_clk = board.D13

"""
classbusio.SPI(clock: microcontroller.Pin,
                MOSI: Optional[microcontroller.Pin] = None,
                MISO: Optional[microcontroller.Pin] = None)
"""
spi = busio.SPI(clock=spi_clk, MOSI=spi_mosi)

display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=tft_res
)

display = adafruit_st7789.ST7789(display_bus,
                    width=240, height=320)

# Make the display context
splash = displayio.Group()
display.show(splash)

color_bitmap = displayio.Bitmap(135, 240, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x00FF00

bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette,
                               x=0, y=0)
splash.append(bg_sprite)

# Draw a smaller inner rectangle
inner_bitmap = displayio.Bitmap(133, 238, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = 0x0000FF
inner_sprite = displayio.TileGrid(inner_bitmap,
                                  pixel_shader=inner_palette,
                                  x=1, y=1)
splash.append(inner_sprite)

# Draw a label
text_group1 = displayio.Group(scale=1, x=20, y=40)
text1 = os.uname()[4]
text_area1 = label.Label(terminalio.FONT, text=text1, color=0xFF0000)
text_group1.append(text_area1)  # Subgroup for text scaling
# Draw a label
text_group2 = displayio.Group(scale=1, x=20, y=60)
text2 = sys.implementation[0] + " " + os.uname()[3]
text_area2 = label.Label(terminalio.FONT, text=text2, color=0x00FF00)
text_group2.append(text_area2)  # Subgroup for text scaling

# Draw a label
text_group3 = displayio.Group(scale=2, x=20, y=100)
text3 = adafruit_st7789.__name__
text_area3 = label.Label(terminalio.FONT, text=text3, color=0xFFFFFF)
text_group3.append(text_area3)  # Subgroup for text scaling
# Draw a label
text_group4 = displayio.Group(scale=2, x=20, y=120)
text4 = adafruit_st7789.__version__
text_area4 = label.Label(terminalio.FONT, text=text4, color=0xFFFFFF)
text_group4.append(text_area4)  # Subgroup for text scaling

splash.append(text_group1)
splash.append(text_group2)
splash.append(text_group3)
splash.append(text_group4)

time.sleep(3.0)

rot = 0
while True:
    time.sleep(5.0)
    rot = rot + 90
    if (rot>=360):
        rot =0
    display.rotation = rot

cpyNrp2040_ov7670_displayio_pico_st7789.py
"""
Run on Arduino NANO RP2040 Connect/CircuitPython 7.2.3
+ ov7670 cam + st7789 LCD.

Capture images from the camera and display it on LCD.

Modified from ov7670_displayio_pico_st7789_2in.py example

ref:
Adafruit CircuitPython ov7670 Library
https://docs.circuitpython.org/projects/ov7670/
"""

import time
from displayio import (
    Bitmap,
    Group,
    TileGrid,
    FourWire,
    release_displays,
    ColorConverter,
    Colorspace,
)

import board
import microcontroller
import busio
import digitalio
import displayio
import adafruit_st7789
import adafruit_ov7670

"""
Connection between Nano RP2040 Connect and
the SPI ST7789 IPS screen.
==========================================
BLK  - 3V3 (backlight, always on)
CS   - A0
DC   - A1
RES  - A2
SDA  - D11 (MOSI)
SCL  - D13 (SCK)
VCC  - 3V3
GND  - GND
"""

tft_cs = board.A0
tft_dc = board.A1
tft_res = board.A2
spi_mosi = board.D11
spi_clk = board.D13

"""
Connection between Arduino Nano RP2040 Connect and
the ov7670 camera module.
==========================================
              ov7670
             +-----------+
    3V3      |3V3    GND | GND
    A5 (SCL) |SCL    SDA | A4 (SDA)
    TX       |VS     HS  | RX
    D12      |PLK    XLK | D10
    D9       |D7     D6  | D8
    D7       |D5     D4  | D6
    D5       |D3     D2  | D4
    D3       |D1     D0  | XXX
    A3       |RET    PWDN| -
             +-----------+
			 
*ov7670 SCL/SDA are I2C-like control pins, pull-up resistors are needed.
 I use 2K ohm resistor for it.
"""
cam_scl = board.A5
cam_sda = board.A4
cam_vs = board.TX
cam_hs = board.RX
cam_plk = board.D12
cam_xlk = board.D10
cam_ret = board.A3

# cam data pins must be sequential
# I can't find 8 continuous sequence GPIO on Nano RP2040 Connect
# As a exercise, I assign GPIO14~21 to d0~d7,
# GPIO14 is internal connceted to onboard ESP32 SCK,
# so it's actually no use.
cam_d0 = board.SCK1 # GPIO14
cam_d1 = board.D3   # GPIO15
cam_d2 = board.D4   # GPIO16
cam_d3 = board.D5   # GPIO17
cam_d4 = board.D6   # GPIO18
cam_d5 = board.D7   # GPIO19
cam_d6 = board.D8   # GPIO20
cam_d7 = board.D9   # GPIO21

release_displays()

# Set up the display
spi = busio.SPI(clock=spi_clk, MOSI=spi_mosi)
display_bus = displayio.FourWire(
    spi, command=tft_dc, chip_select=tft_cs, reset=tft_res)
display = adafruit_st7789.ST7789(display_bus,
                    width=240, height=320)

display.auto_refresh = False

# Ensure the camera is shut down,
# so that it releases the SDA/SCL lines,
# then create the configuration I2C bus

with digitalio.DigitalInOut(cam_ret) as reset:
    reset.switch_to_output(False)
    time.sleep(0.001)
    bus = busio.I2C(cam_scl, cam_sda)

# Set up the camera
cam = adafruit_ov7670.OV7670(
    bus,
    data_pins=[
        cam_d0,
        cam_d1,
        cam_d2,
        cam_d3,
        cam_d4,
        cam_d5,
        cam_d6,
        cam_d7,
    ],  #Pins must be sequential
    clock=cam_plk,
    vsync=cam_vs,
    href=cam_hs,
    mclk=cam_xlk,
    shutdown=None,
    reset=cam_ret,
)

#cam.test_pattern = adafruit_ov7670.OV7670_TEST_PATTERN_COLOR_BAR

print(adafruit_ov7670.__name__ + " version: "
      + adafruit_ov7670.__version__)
print(adafruit_st7789.__name__ + " version: "
      + adafruit_st7789.__version__)
pid = cam.product_id
ver = cam.product_version
print(f"Detected cam pid={pid:x} ver={ver:x}")

width = display.width
height = display.height

bitmap = None
# Select the biggest size for which we can allocate a bitmap successfully,
# and which is not bigger than the display
for size in range(adafruit_ov7670.OV7670_SIZE_DIV1,
                  adafruit_ov7670.OV7670_SIZE_DIV16 + 1):
    cam.size = size
    if cam.width > width:
        continue
    if cam.height > height:
        continue
    try:
        bitmap = Bitmap(cam.width, cam.height, 65536)
        break
    except MemoryError:
        continue

print(width, height, cam.width, cam.height)
if bitmap is None:
    raise SystemExit("Could not allocate a bitmap")

g = Group(scale=1,
          x=(width - cam.width) // 2,
          y=(height - cam.height) // 2)
tg = TileGrid(
    bitmap, pixel_shader=ColorConverter(
        input_colorspace=Colorspace.RGB565_SWAPPED)
)
g.append(tg)
display.show(g)

#t0 = time.monotonic_ns()
display.auto_refresh = False
while True:
    cam.capture(bitmap)
    bitmap.dirty()
    display.refresh(minimum_frames_per_second=0)
    #t1 = time.monotonic_ns()
    #print("fps", 1e9 / (t1 - t0))
    #t0 = t1


No comments:

Post a Comment