Skip to content

First Release #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 162 commits into from
Aug 5, 2025
Merged
Show file tree
Hide file tree
Changes from 140 commits
Commits
Show all changes
162 commits
Select commit Hold shift + click to select a range
998f8bf
Add conversion from mp_obj_t to cv:Size
sfe-SparkFro May 21, 2025
c538c3d
Add morphology functions
sfe-SparkFro May 21, 2025
8560bcf
Add conversion from mp_obj_t to cv::Point and cv::Scalar
sfe-SparkFro May 21, 2025
164ef13
Ensure morphology functions have all optional arguments
sfe-SparkFro May 21, 2025
2f9b0f7
Add simple drawing functions
sfe-SparkFro May 21, 2025
6590714
Downgrade to OpenCV 4.11
sfe-SparkFro May 22, 2025
4cf886d
Remove min() and max()
sfe-SparkFro May 22, 2025
f4f776d
Add Canny and Hough functions
sfe-SparkFro May 28, 2025
c918dd5
Add blurring/filtering functions
sfe-SparkFro May 30, 2025
1948925
Add gradient filter functions
sfe-SparkFro May 30, 2025
da3452c
Add thresholding functions
sfe-SparkFro May 30, 2025
60cd368
Add template matching
sfe-SparkFro May 30, 2025
ec05574
Add connectedComponents
sfe-SparkFro May 30, 2025
48fcb8f
Initial imshow() implementation
sfe-SparkFro Jun 2, 2025
e1800f9
Add waitKey()
sfe-SparkFro Jun 3, 2025
4d0627e
Improve st7789_spi.py imshow()
sfe-SparkFro Jun 4, 2025
361fbbe
Add convertScaleAbs()
sfe-SparkFro Jun 4, 2025
7e30426
Improve/simplify/modularize ST7789 SPI driver
sfe-SparkFro Jun 4, 2025
56903a4
Make waitKey() get input from REPL
sfe-SparkFro Jun 4, 2025
be186c2
Update waitKey() to return integer instead of string character
sfe-SparkFro Jun 4, 2025
74c1832
Clean up Hello OpenCV example
sfe-SparkFro Jun 4, 2025
4202cb9
Rename Hello OpenCV example with `ex01_` prefix
sfe-SparkFro Jun 4, 2025
a2bf9f2
Fix for #13
sfe-SparkFro Jun 5, 2025
0830116
Merge pull request #7 from sparkfun/imshow
sfe-SparkFro Jun 5, 2025
1d27888
Fix RP2350 atomics not working with PSRAM
sfe-SparkFro Jun 6, 2025
98edf13
Update OpenCV submodule with generic embedded platform cmake files
sfe-SparkFro Jun 6, 2025
4354a73
Update ulab
sfe-SparkFro Jun 6, 2025
e2f0fc6
Update ST7789 SPI driver to use ndarray.ndim
sfe-SparkFro Jun 6, 2025
e98482a
Add imread()
sfe-SparkFro Jun 9, 2025
da558f4
Disable OPENJPEG in OpenCV
sfe-SparkFro Jun 9, 2025
2d9605c
Add imwrite()
sfe-SparkFro Jun 9, 2025
e2defe4
Move module globals into module headers
sfe-SparkFro Jun 9, 2025
a3f6cd5
Set Mat allocator in imread()
sfe-SparkFro Jun 9, 2025
50f29e5
Ensure image codecs are allocated in C heap
sfe-SparkFro Jun 10, 2025
52a290e
Set default Mat allocator to NumpyAllocator
sfe-SparkFro Jun 10, 2025
01931af
Move global initializers to new boot function
sfe-SparkFro Jun 10, 2025
37756bb
Don't set NumPy allocator as default
sfe-SparkFro Jun 10, 2025
918cb58
Change ST7789 SPI driver to require SPI bus instead of initializing it
sfe-SparkFro Jun 11, 2025
660668f
Add boot.py example to initialize display and mount SD card
sfe-SparkFro Jun 11, 2025
18e938d
Add Example 2 - imread() and _imwrite()
sfe-SparkFro Jun 11, 2025
3cca6b0
ST7789 SPI driver - Rename saveDcPin() to savePinModeAlt()
sfe-SparkFro Jun 11, 2025
7f7590b
Add prints to end of Example 1
sfe-SparkFro Jun 11, 2025
9599677
Comment typo and cleanup
sfe-SparkFro Jun 11, 2025
f3d8c11
Add note to Example 2 about large/small images
sfe-SparkFro Jun 11, 2025
8696ea7
Remove main.py
sfe-SparkFro Jun 11, 2025
8b98132
Add splash screen support to boot.py
sfe-SparkFro Jun 11, 2025
79fd3a1
Initial HM01B0 PIO implementation
sfe-SparkFro Jun 12, 2025
8c1abbd
Improve HM01B0 PIO sync
sfe-SparkFro Jun 13, 2025
2944b43
Add Example 3 - Camera
sfe-SparkFro Jun 13, 2025
ec2e3de
Merge pull request #18 from sparkfun/main
sfe-SparkFro Jun 16, 2025
dda0e32
Update build.yml
sfe-SparkFro Jun 16, 2025
8ea6bc4
Update build.yml
sfe-SparkFro Jun 16, 2025
4d38135
Update build.yml
sfe-SparkFro Jun 16, 2025
c190764
Update build.yml
sfe-SparkFro Jun 16, 2025
39629a2
Update build.yml
sfe-SparkFro Jun 16, 2025
d5346a3
Update build.yml
sfe-SparkFro Jun 16, 2025
7e04ea8
Update OpenCV Makefile to use -C instead of cd
sfe-SparkFro Jun 16, 2025
3471137
Update build.yml
sfe-SparkFro Jun 16, 2025
72e08e5
Update build.yml
sfe-SparkFro Jun 16, 2025
279b2d5
Update build.yml
sfe-SparkFro Jun 16, 2025
ab3e5a7
Update OpenCV with fixes for updated compiler
sfe-SparkFro Jun 16, 2025
1ba836f
Fix OpenCV uint typedef
sfe-SparkFro Jun 16, 2025
c7b47b6
Update build.yml
sfe-SparkFro Jun 16, 2025
175b13e
Update build.yml
sfe-SparkFro Jun 16, 2025
1941bce
Add OpenCV Makefile and embedded platform definitions
sfe-SparkFro Jun 17, 2025
ce8c444
Set workflow to build on push to development and PR to main
sfe-SparkFro Jun 17, 2025
7bdeb99
Fix typo in workflow
sfe-SparkFro Jun 17, 2025
c02bd1f
HM01B0 PIO driver: restart state machine in vsync handler
sfe-SparkFro Jun 17, 2025
f99b1c8
Change HM01B0 PIO driver to use 32-bit DMA transfers
sfe-SparkFro Jun 17, 2025
270f2ae
Update comment in savePinModeAlt()
sfe-SparkFro Jun 18, 2025
3b782a1
Restructure drivers
sfe-SparkFro Jun 19, 2025
fc54ace
HM01B0 PIO driver: shift data 24 bits in sm.put()
sfe-SparkFro Jun 19, 2025
199796d
Clean up display drivers
sfe-SparkFro Jun 19, 2025
e9e992e
Optimize savePinModeAlt()
sfe-SparkFro Jun 19, 2025
6150a9e
Change HM01B0 PIO driver to use DMA transfers
sfe-SparkFro Jun 20, 2025
e1dd199
Change HM01B0 PIO driver to use 32-bit transfers after initialization
sfe-SparkFro Jun 20, 2025
0e9857b
Add optional image argument to camera read()
sfe-SparkFro Jun 20, 2025
d4a137b
Fix ST7789 PIO driver swapping pairs of pixels
sfe-SparkFro Jun 20, 2025
da81330
Update waitKey() prompts in examples 2 and 3
sfe-SparkFro Jun 20, 2025
8144fe8
Fix ST7789 PIO driver pin modes
sfe-SparkFro Jun 20, 2025
09aa23c
Add helpful prints to example 2
sfe-SparkFro Jun 20, 2025
dfff6f2
Add more helpful error messages to boot.py SD card initialization
sfe-SparkFro Jun 20, 2025
9e3ec32
Tweak print in example 2
sfe-SparkFro Jun 20, 2025
e959eb6
Add ST7789 PIO driver to example boot.py
sfe-SparkFro Jun 20, 2025
1a4ee29
Fix build on Ubuntu 24.02 (missing return)
sfe-SparkFro Jun 26, 2025
a34c4e9
Remove duplicate installs from GitHub build workflow
sfe-SparkFro Jun 26, 2025
9bc2b20
Refactor display driver folder
sfe-SparkFro Jun 26, 2025
07fa663
Add initial OV5640 PIO driver (broken)
sfe-SparkFro Jun 26, 2025
bdb9fee
Remove base classes from display driver __init__.py
sfe-SparkFro Jun 26, 2025
184df97
Update boot.py with camera driver refactor
sfe-SparkFro Jun 26, 2025
ade4731
Fix RP2 PIO DVP interface
sfe-SparkFro Jun 26, 2025
81f7cb1
Clean up camera drivers a bit
sfe-SparkFro Jul 2, 2025
be7d810
Add comment about copying buffers being slow
sfe-SparkFro Jul 2, 2025
83376cf
Add findContours(), drawContours(), and moments()
sfe-SparkFro Jul 3, 2025
527a8a5
Add more contour functions
sfe-SparkFro Jul 8, 2025
3acc441
Implement HoughLinesP()
sfe-SparkFro Jul 8, 2025
2486762
Add connectedComponentsWithStats()
sfe-SparkFro Jul 8, 2025
3e57d35
Fix HM01B0 reset
sfe-SparkFro Jul 9, 2025
307ea7c
RP2 DVP PIO driver: Disable DMA in active()
sfe-SparkFro Jul 9, 2025
ca68e8c
Initial CST816 touch screen driver
sfe-SparkFro Jul 9, 2025
3ac97b8
Move manifest.py out of cv2_drivers
sfe-SparkFro Jul 10, 2025
90f87dc
Remove TODO comment for ULAB_MAX_DIMS
sfe-SparkFro Jul 10, 2025
a70c047
Remove dependency on OpenCV fork
sfe-SparkFro Jul 10, 2025
4f1acc6
Remove commented lines from cmake file
sfe-SparkFro Jul 10, 2025
d9501a7
Update to use large binary variant
sfe-SparkFro Jul 10, 2025
9edd995
Move hardware initialization out of boot.py
sfe-SparkFro Jul 10, 2025
808dd8d
Update comments in touch screen init example
sfe-SparkFro Jul 10, 2025
8cad950
Add initial touch screen example
sfe-SparkFro Jul 10, 2025
604874b
Renumber examples 2-4
sfe-SparkFro Jul 10, 2025
908ad71
Add SFE logo detection example
sfe-SparkFro Jul 11, 2025
8502bf8
Fix alphabetical order of imgproc module
sfe-SparkFro Jul 14, 2025
a548e37
Remove references to boot.py from examples
sfe-SparkFro Jul 14, 2025
2067566
Fix typo in comment
sfe-SparkFro Jul 14, 2025
0bcb62d
Change examples to import cv2 as cv
sfe-SparkFro Jul 14, 2025
17b0d53
Initial readme
sfe-SparkFro Jul 14, 2025
aa49d59
Remove tabs from Functions section in readme
sfe-SparkFro Jul 14, 2025
258e018
Fix hyperlinks in readme
sfe-SparkFro Jul 14, 2025
53d53ab
Update readme Quick Start info about hardware drivers
sfe-SparkFro Jul 14, 2025
8f16324
Add missing imgcodecs header in readme
sfe-SparkFro Jul 14, 2025
3ca9229
Fix threshrold to return tuple
sfe-SparkFro Jul 15, 2025
9e4649f
Add waitKeyEx()
sfe-SparkFro Jul 15, 2025
6a81d86
Fix ndarray_to_mat to copy if needed
sfe-SparkFro Jul 15, 2025
f37d860
Fix example 5
sfe-SparkFro Jul 15, 2025
3285909
Add header comments to examples
sfe-SparkFro Jul 15, 2025
ba60958
Improve logo detection example
sfe-SparkFro Jul 16, 2025
9a034f8
Add center and size text to logo detection example
sfe-SparkFro Jul 16, 2025
15ffc29
Add performance example
sfe-SparkFro Jul 17, 2025
08a7dbc
Update performance example with all changes this time
sfe-SparkFro Jul 17, 2025
e2872c2
Add XRP touch drive example
sfe-SparkFro Jul 18, 2025
471115f
Change default sm_id values of PIO drivers
sfe-SparkFro Jul 18, 2025
a2f461f
Move display splash screen code to base display class
sfe-SparkFro Jul 18, 2025
0df676a
Add optional splash image filename
sfe-SparkFro Jul 18, 2025
ad298cf
Update logo detection example to draw marker instead of circle for ce…
sfe-SparkFro Jul 21, 2025
dc112ba
Add XRP orange ring example
sfe-SparkFro Jul 21, 2025
9c875a0
Clean up touch screen example
sfe-SparkFro Jul 21, 2025
5728bc4
Add automatic builds that contain examples on release
malcolm-sparkfun Jul 22, 2025
5de809f
Add unmount of immutable directory and persistent tracking so expandi…
malcolm-sparkfun Jul 22, 2025
3439047
Fix XRP touch drive example
sfe-SparkFro Jul 22, 2025
3d65c02
Clean up examples and drivers
sfe-SparkFro Jul 22, 2025
10eed92
Add header comments to files in src
sfe-SparkFro Jul 24, 2025
72887c7
Add header comments to files in cv2_drivers
sfe-SparkFro Jul 24, 2025
94beeac
Add missing docstring comments in drivers
sfe-SparkFro Jul 24, 2025
0885da7
update names for frozen examples
malcolm-sparkfun Jul 24, 2025
fa2d97a
pedantic fixes
malcolm-sparkfun Jul 24, 2025
fe4cc0d
Use built in extraction of freezefs to simplify build script
malcolm-sparkfun Jul 24, 2025
95c72eb
Update content of PERSISTENT_FILE_FOR_UNPACK
sfe-SparkFro Jul 30, 2025
ae33125
Nitpicky fixes in examples
sfe-SparkFro Jul 30, 2025
68584d7
Add splash image and update image paths
sfe-SparkFro Jul 30, 2025
5423f37
Remove sparkfun_logo.png
sfe-SparkFro Jul 30, 2025
b03ca23
Nitpick print tweaks to SD card initialization
sfe-SparkFro Jul 30, 2025
66f61fd
Tweak XRP grab ring example to wait on user button pushes
sfe-SparkFro Jul 30, 2025
18c4039
Rename examaples folder to opencv-examples
sfe-SparkFro Jul 30, 2025
4fc3bbe
Update build.sh
sfe-SparkFro Jul 30, 2025
65cb21a
Merge branch 'features_for_launch' into feature/build_release
sfe-SparkFro Jul 30, 2025
c37d7b3
Change release workflow to rename firmware file
sfe-SparkFro Jul 30, 2025
b6b5958
build.sh: only copy frozen directory if different
sfe-SparkFro Jul 30, 2025
27bbff9
Add animation example
sfe-SparkFro Jul 30, 2025
d327ecd
Merge pull request #38 from sparkfun/feature/build_release
sfe-SparkFro Jul 30, 2025
8637030
Update README.md
sfe-SparkFro Jul 31, 2025
81f3be7
Fix XRP touch screen drive example
sfe-SparkFro Jul 31, 2025
afdf23b
Update performance section of README.md
sfe-SparkFro Jul 31, 2025
e502357
Remove non-OpenCV features
sfe-SparkFro Aug 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
name: Build firmware

on:
pull_request:
branches:
- main
push:
branches:
- features_for_launch
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
- name: make
- name: Install packages
run: |
sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi
make -C micropython/mpy-cross
make -C micropython/ports/rp2 BOARD=SPARKFUN_XRP_CONTROLLER submodules
make BOARD=SPARKFUN_XRP_CONTROLLER
sudo apt install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
- name: Build MPY Cross
run: make -C micropython/mpy-cross
- name: MicroPython submodules
run: make -C micropython/ports/rp2 BOARD=SPARKFUN_XRP_CONTROLLER submodules
- name: Set Pico SDK path
run: echo "PICO_SDK_PATH=$GITHUB_WORKSPACE/micropython/lib/pico-sdk" >> "$GITHUB_ENV"
- name: Build OpenCV
run: make -C src/opencv PLATFORM=rp2350 --no-print-directory -j4
- name: Build firmware
run: make BOARD=SPARKFUN_XRP_CONTROLLER -j4
- name: Upload UF2
uses: actions/upload-artifact@v4
with:
name: firmware.uf2
path: micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/firmware.uf2
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "src/opencv"]
path = src/opencv
url = https://github.com/sfe-SparkFro/opencv.git
[submodule "src/opencv/opencv"]
path = src/opencv/opencv
url = https://github.com/opencv/opencv.git
[submodule "src/ulab"]
path = src/ulab
url = https://github.com/v923z/micropython-ulab.git
Expand Down
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ CURRENT_DIR = $(shell pwd)
# Set the MicroPython user C module path to the OpenCV module
MAKE_ARGS = USER_C_MODULES="$(CURRENT_DIR)/src/opencv_upy.cmake"

# Ensure we're building the OpenCV board variant
MAKE_ARGS += BOARD_VARIANT=LARGE_BINARY

# Use the OpenCV driver manifest
MAKE_ARGS += FROZEN_MANIFEST="$(CURRENT_DIR)/manifest.py"

# Build MicroPython with the OpenCV module
all:
@cd micropython/ports/rp2 && export CMAKE_ARGS="$(CMAKE_ARGS)" && make -f Makefile $(MAKEFLAGS) $(MAKE_ARGS)

# Clean the MicroPython build
clean:
@cd micropython/ports/rp2 && make -f Makefile $(MAKEFLAGS) clean
@cd micropython/ports/rp2 && make -f Makefile $(MAKEFLAGS) $(MAKE_ARGS) clean

# Load the MicroPython submodules
submodules:
@cd micropython/ports/rp2 && make -f Makefile $(MAKEFLAGS) submodules
@cd micropython/ports/rp2 && make -f Makefile $(MAKEFLAGS) $(MAKE_ARGS) submodules
337 changes: 336 additions & 1 deletion README.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions cv2_drivers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#-------------------------------------------------------------------------------
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 SparkFun Electronics
#-------------------------------------------------------------------------------
# cv2_drivers/touch_screens/__init__.py
#
# Imports all available drivers for MicroPython OpenCV.
#-------------------------------------------------------------------------------

from . import displays
from . import cameras
from . import touch_screens
17 changes: 17 additions & 0 deletions cv2_drivers/cameras/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#-------------------------------------------------------------------------------
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 SparkFun Electronics
#-------------------------------------------------------------------------------
# cv2_drivers/cameras/__init__.py
#
# Imports all available camera drivers for MicroPython OpenCV.
#-------------------------------------------------------------------------------

# Import sys module to check platform
import sys

# Import RP2 drivers
if 'rp2' in sys.platform:
from . import hm01b0_pio
from . import ov5640_pio
45 changes: 45 additions & 0 deletions cv2_drivers/cameras/cv2_camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#-------------------------------------------------------------------------------
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 SparkFun Electronics
#-------------------------------------------------------------------------------
# cv2_camera.py
#
# Base class for OpenCV camera drivers.
#-------------------------------------------------------------------------------

class CV2_Camera():
"""
Base class for OpenCV camera drivers.
"""
def __init__(self):
"""
Initializes the camera.
"""
pass

def open(self):
"""
Opens the camera and prepares it for capturing images.
"""
raise NotImplementedError("open() must be implemented by driver")

def release(self):
"""
Releases the camera and frees any resources.
"""
raise NotImplementedError("release() must be implemented by driver")

def read(self, image=None):
"""
Reads an image from the camera.

Args:
image (ndarray, optional): Image to read into

Returns:
tuple: (success, image)
- success (bool): True if the image was read, otherwise False
- image (ndarray): The captured image, or None if reading failed
"""
raise NotImplementedError("read() must be implemented by driver")
60 changes: 60 additions & 0 deletions cv2_drivers/cameras/dvp_camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#-------------------------------------------------------------------------------
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 SparkFun Electronics
#-------------------------------------------------------------------------------
# dvp_camera.py
#
# Base class for OpenCV DVP (Digital Video Port) camera drivers.
#-------------------------------------------------------------------------------

from .cv2_camera import CV2_Camera

class DVP_Camera(CV2_Camera):
"""
Base class for OpenCV DVP (Digital Video Port) camera drivers.
"""
def __init__(
self,
i2c,
i2c_address
):
"""
Initializes the DVP camera with I2C communication.

Args:
i2c (I2C): I2C object for communication
i2c_address (int): I2C address of the camera
"""
super().__init__()

self._i2c = i2c
self._i2c_address = i2c_address

def _read_register(self, reg, nbytes=1):
"""
Reads a register from the camera over I2C.

Args:
reg (int): Register address to read
nbytes (int): Number of bytes to read from the register

Returns:
bytes: Data read from the register
"""
self._i2c.writeto(self._i2c_address, bytes([reg >> 8, reg & 0xFF]))
return self._i2c.readfrom(self._i2c_address, nbytes)

def _write_register(self, reg, data):
"""
Writes data to a register on the camera over I2C.

Args:
reg (int): Register address to write
data (bytes, int, list, tuple): Data to write to the register
"""
if isinstance(data, int):
data = bytes([data])
elif isinstance(data, (list, tuple)):
data = bytes(data)
self._i2c.writeto(self._i2c_address, bytes([reg >> 8, reg & 0xFF]) + data)
182 changes: 182 additions & 0 deletions cv2_drivers/cameras/dvp_rp2_pio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#-------------------------------------------------------------------------------
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 SparkFun Electronics
#-------------------------------------------------------------------------------
# dvp_rp2_pio.py
#
# This class implements a DVP (Digital Video Port) interface using the RP2 PIO
# (Programmable Input/Output) interface. This is only available on Raspberry Pi
# RP2 processors.
#
# This class is derived from:
# https://github.com/adafruit/Adafruit_ImageCapture/blob/main/src/arch/rp2040.cpp
# Released under the MIT license.
# Copyright (c) 2021 Adafruit Industries
#-------------------------------------------------------------------------------

import rp2
from machine import Pin, PWM

class DVP_RP2_PIO():
"""
This class implements a DVP (Digital Video Port) interface using the RP2 PIO
(Programmable Input/Output) interface. This is only available on Raspberry
Pi RP2 processors.
"""
def __init__(
self,
pin_d0,
pin_vsync,
pin_hsync,
pin_pclk,
pin_xclk,
xclk_freq,
sm_id,
num_data_pins,
bytes_per_frame,
byte_swap
):
"""
Initializes the DVP interface with the specified parameters.

Args:
pin_d0 (int): Data 0 pin number for DVP interface
pin_vsync (int): Vertical sync pin number
pin_hsync (int): Horizontal sync pin number
pin_pclk (int): Pixel clock pin number
pin_xclk (int): External clock pin number
xclk_freq (int): Frequency in Hz for the external clock
sm_id (int): PIO state machine ID
num_data_pins (int): Number of data pins used in DVP interface
bytes_per_frame (int): Number of bytes per frame to capture
byte_swap (bool): Whether to swap bytes in the captured data
"""
self._pin_d0 = pin_d0
self._pin_vsync = pin_vsync
self._pin_hsync = pin_hsync
self._pin_pclk = pin_pclk
self._pin_xclk = pin_xclk
self._sm_id = sm_id

# Initialize DVP pins as inputs
for i in range(num_data_pins):
Pin(pin_d0+i, Pin.IN)
Pin(pin_vsync, Pin.IN)
Pin(pin_hsync, Pin.IN)
Pin(pin_pclk, Pin.IN)

# Set up XCLK pin if provided
if self._pin_xclk is not None:
self._xclk = PWM(Pin(pin_xclk))
self._xclk.freq(xclk_freq)
self._xclk.duty_u16(32768) # 50% duty cycle

# Copy the PIO program
program = self._pio_read_dvp

# Mask in the GPIO pins
program[0][0] |= self._pin_hsync & 0x1F
program[0][1] |= self._pin_pclk & 0x1F
program[0][3] |= self._pin_pclk & 0x1F

# Mask in the number of data pins
program[0][2] &= 0xFFFFFFE0
program[0][2] |= num_data_pins

# Create PIO state machine to capture DVP data
self._sm = rp2.StateMachine(
self._sm_id,
program,
in_base = pin_d0
)

# Create DMA controller to transfer data from PIO to buffer
self._dma = rp2.DMA()
req_num = ((self._sm_id // 4) << 3) + (self._sm_id % 4) + 4
bytes_per_transfer = 4
dma_ctrl = self._dma.pack_ctrl(
# 0 = 1 byte, 1 = 2 bytes, 2 = 4 bytes
size = {1:0, 2:1, 4:2}[bytes_per_transfer],
inc_read = False,
treq_sel = req_num,
bswap = byte_swap
)
self._dma.config(
read = self._sm,
count = bytes_per_frame // bytes_per_transfer,
ctrl = dma_ctrl
)

def _active(self, active=None):
"""
Sets or gets the active state of the DVP interface.

Args:
active (bool, optional):
- True: Activate the DVP interface
- False: Deactivate the DVP interface
- None: Get the current active state

Returns:
bool: Current active state if no argument is provided
"""
# If no argument is provided, return the current active state
if active == None:
return self._sm.active()

# Disable the DMA, the VSYNC handler will re-enable it when needed
self._dma.active(False)

# Set the active state of the state machine
self._sm.active(active)

# If active, set up the VSYNC interrupt handler
if active:
Pin(self._pin_vsync).irq(
trigger = Pin.IRQ_FALLING,
handler = lambda pin: self._vsync_handler()
)
# If not active, disable the VSYNC interrupt handler
else:
Pin(self._pin_vsync).irq(
handler = None
)

def _vsync_handler(self):
"""
Handles the VSYNC interrupt to capture a frame of data.
"""
# Disable DMA before reconfiguring it
self._dma.active(False)

# Reset state machine to ensure ISR is cleared
self._sm.restart()

# Ensure PIO RX FIFO is empty (it's not emptied by `sm.restart()`)
while self._sm.rx_fifo() > 0:
self._sm.get()

# Reset the DMA write address
self._dma.write = self._buffer

# Start the DMA
self._dma.active(True)

# Here is the PIO program, which is configurable to mask in the GPIO pins
# and the number of data pins. It must be configured before the state
# machine is created
@rp2.asm_pio(
in_shiftdir = rp2.PIO.SHIFT_LEFT,
push_thresh = 32,
autopush = True,
fifo_join = rp2.PIO.JOIN_RX
)
def _pio_read_dvp():
"""
PIO program to read DVP data from the GPIO pins.
"""
wait(1, gpio, 0) # Mask in HSYNC pin
wait(1, gpio, 0) # Mask in PCLK pin
in_(pins, 1) # Mask in number of pins
wait(0, gpio, 0) # Mask in PCLK pin
Loading