-
Notifications
You must be signed in to change notification settings - Fork 1
Driver encapsulation #10
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
base: develop
Are you sure you want to change the base?
Changes from 4 commits
e2717b2
8ea03ee
6d66765
a6a06b9
9752efb
217b473
12bf2b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,10 @@ | |
| # | ||
| # Copyright (c) 2025 SparkFun Electronics | ||
| #------------------------------------------------------------------------------- | ||
| # dvp_rp2_pio.py | ||
| # red_vision/cameras/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. | ||
| # Red Vision DVP (Digital Video Port) camera interface using the RP2 PIO | ||
| # (Programmable Input/Output). Only available on Raspberry Pi RP2 processors. | ||
| # | ||
| # This class is derived from: | ||
| # https://github.com/adafruit/Adafruit_ImageCapture/blob/main/src/arch/rp2040.cpp | ||
|
|
@@ -19,26 +18,21 @@ | |
| import array | ||
| from machine import Pin, PWM | ||
| from uctypes import addressof | ||
| from ..utils import memory | ||
|
|
||
| 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. | ||
| Red Vision DVP (Digital Video Port) camera interface using the RP2 PIO | ||
| (Programmable Input/Output). Only available on Raspberry Pi RP2 processors. | ||
| """ | ||
| def __init__( | ||
| self, | ||
| sm_id, | ||
| pin_d0, | ||
| pin_vsync, | ||
| pin_hsync, | ||
| pin_pclk, | ||
| pin_xclk, | ||
| xclk_freq, | ||
| sm_id, | ||
| num_data_pins, | ||
| bytes_per_pixel, | ||
| byte_swap, | ||
| continuous = False | ||
| pin_xclk = None, | ||
| ): | ||
| """ | ||
| Initializes the DVP interface with the specified parameters. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the "Args:" comment to reflect the new changed arguments. |
||
|
|
@@ -62,35 +56,65 @@ def __init__( | |
| self._pin_hsync = pin_hsync | ||
| self._pin_pclk = pin_pclk | ||
| self._pin_xclk = pin_xclk | ||
| self._sm_id = sm_id | ||
|
|
||
| def begin( | ||
| self, | ||
| buffer, | ||
| xclk_freq, | ||
| num_data_pins, | ||
| byte_swap, | ||
| continuous = False, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a comment saying what these args are similar to what you have for Might want to add a short/simple block function comment describing what this function does as well |
||
| ): | ||
| self._buffer = buffer | ||
| self._height, self._width, self._bytes_per_pixel = buffer.shape | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should buffer be type checked before the assignment statements are performed? |
||
|
|
||
| # Initialize DVP pins as inputs | ||
| self._num_data_pins = num_data_pins | ||
| 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) | ||
| Pin(self._pin_d0+i, Pin.IN) | ||
| Pin(self._pin_vsync, Pin.IN) | ||
| Pin(self._pin_hsync, Pin.IN) | ||
| Pin(self._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 = PWM(Pin(self._pin_xclk)) | ||
| self._xclk.freq(xclk_freq) | ||
| self._xclk.duty_u16(32768) # 50% duty cycle | ||
|
|
||
| # If there's only 1 byte per pixel, we can safely transfer multiple | ||
| # pixels at a time without worrying about byte alignment. So we use the | ||
| # maximum of 4 pixels per transfer to improve DMA efficiency. | ||
| if self._bytes_per_pixel == 1: | ||
| self._bytes_per_transfer = 4 | ||
| # The PIO left shifts the pixel data in the FIFO buffer, so we need | ||
| # to swap the bytes to get the correct order. | ||
| byte_swap = True | ||
| else: | ||
| self._bytes_per_transfer = self._bytes_per_pixel | ||
|
|
||
| # Store transfer parameters | ||
| self._bytes_per_pixel = bytes_per_pixel | ||
| self._byte_swap = byte_swap | ||
|
|
||
| # Whether to continuously capture frames | ||
| self._continuous = continuous | ||
|
|
||
| # Set up the PIO state machine | ||
| self._sm_id = sm_id | ||
| self._setup_pio() | ||
|
|
||
| # Set up the DMA controllers | ||
| self._setup_dmas() | ||
|
|
||
| def buffer(self): | ||
| """ | ||
| Returns the current frame buffer from the camera. | ||
|
|
||
| Returns: | ||
| ndarray: Frame buffer | ||
| """ | ||
| return self._buffer | ||
|
|
||
| def _setup_pio(self): | ||
| # Copy the PIO program | ||
| program = self._pio_read_dvp | ||
|
|
@@ -108,7 +132,7 @@ def _setup_pio(self): | |
| self._sm_id, | ||
| program, | ||
| in_base = self._pin_d0, | ||
| push_thresh = self._bytes_per_pixel * 8 | ||
| push_thresh = self._bytes_per_transfer * 8 | ||
| ) | ||
|
|
||
| # Here is the PIO program, which is configurable to mask in the GPIO pins | ||
|
|
@@ -128,22 +152,6 @@ def _pio_read_dvp(): | |
| in_(pins, 32) # Mask in number of pins | ||
| wait(0, gpio, 0) # Mask in PCLK pin | ||
|
|
||
| def _is_in_sram(self, data_addr): | ||
| """ | ||
| Checks whether a given memory address is in SRAM. | ||
|
|
||
| Args: | ||
| data_addr (int): Memory address to check | ||
| Returns: | ||
| bool: True if address is in SRAM, False otherwise | ||
| """ | ||
| # SRAM address range. | ||
| SRAM_BASE = 0x20000000 | ||
| total_sram_size = 520*1024 # 520 KB | ||
|
|
||
| # Return whether address is in SRAM. | ||
| return data_addr >= SRAM_BASE and data_addr < SRAM_BASE + total_sram_size | ||
|
|
||
| def _setup_dmas(self): | ||
| """ | ||
| Sets up the DMA controllers for the DVP interface. | ||
|
|
@@ -239,7 +247,7 @@ def _setup_dmas(self): | |
| self._dma_executer = rp2.DMA() | ||
|
|
||
| # Check if the display buffer is in PSRAM. | ||
| self._buffer_is_in_psram = not self._is_in_sram(addressof(self._buffer)) | ||
| self._buffer_is_in_psram = memory.is_in_external_ram(self._buffer) | ||
|
|
||
| # If the buffer is in PSRAM, create the streamer DMA channel and row | ||
| # buffer in SRAM. | ||
|
|
@@ -253,7 +261,7 @@ def _setup_dmas(self): | |
|
|
||
| # Verify row buffer is in SRAM. If not, we'll still have the same | ||
| # latency problem. | ||
| if not self._is_in_sram(addressof(self._row_buffer)): | ||
| if memory.is_in_external_ram(self._row_buffer): | ||
| raise MemoryError("not enough space in SRAM for row buffer") | ||
|
|
||
| # Create DMA control register values. | ||
|
|
@@ -304,7 +312,7 @@ def _create_dma_ctrl_registers(self): | |
| # needed. Once done, it chains back to the dispatcher to get the next | ||
| # control block. | ||
| self._dma_ctrl_pio_repeat = self._dma_executer.pack_ctrl( | ||
| size = {1:0, 2:1, 4:2}[self._bytes_per_pixel], | ||
| size = {1:0, 2:1, 4:2}[self._bytes_per_transfer], | ||
| inc_read = False, | ||
| inc_write = True, | ||
| # ring_size = 0, | ||
|
|
@@ -430,7 +438,7 @@ def _create_control_blocks(self): | |
| self._cb_pio_repeat = array.array('I', [ | ||
| pio_rx_fifo_addr, # READ_ADDR | ||
| addressof(self._row_buffer), # WRITE_ADDR | ||
| self._bytes_per_row // self._bytes_per_pixel, # TRANS_COUNT | ||
| self._bytes_per_row // self._bytes_per_transfer, # TRANS_COUNT | ||
| self._dma_ctrl_pio_repeat, # CTRL_TRIG | ||
| ]) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a better name for this module.
memory- especially later in the code - appears to be a build in, native module which is confusing ... Maybe rv_memory, or something ...Or if this is just internal, so it's accessed using the package name
rv, probably not an issue at all