11import rp2
22from machine import Pin , PWM
33
4+ # This class is derived from:
5+ # https://github.com/adafruit/Adafruit_ImageCapture/blob/main/src/arch/rp2040.cpp
6+ # Released under the MIT license.
7+ # Copyright (c) 2021 Adafruit Industries
48class DVP_RP2_PIO ():
9+ """
10+ This class implements a DVP (Digital Video Port) interface using the RP2 PIO
11+ (Programmable Input/Output) interface. This is only available on Raspberry
12+ Pi RP2 processors.
13+ """
514 def __init__ (
615 self ,
716 pin_d0 ,
@@ -15,12 +24,27 @@ def __init__(
1524 bytes_per_frame ,
1625 byte_swap
1726 ):
18- self .pin_d0 = pin_d0
19- self .pin_vsync = pin_vsync
20- self .pin_hsync = pin_hsync
21- self .pin_pclk = pin_pclk
22- self .pin_xclk = pin_xclk
23- self .sm_id = sm_id
27+ """
28+ Initializes the DVP interface with the specified parameters.
29+
30+ Args:
31+ pin_d0 (int): Data 0 pin number for DVP interface
32+ pin_vsync (int): Vertical sync pin number
33+ pin_hsync (int): Horizontal sync pin number
34+ pin_pclk (int): Pixel clock pin number
35+ pin_xclk (int): External clock pin number
36+ xclk_freq (int): Frequency in Hz for the external clock
37+ sm_id (int): PIO state machine ID
38+ num_data_pins (int): Number of data pins used in DVP interface
39+ bytes_per_frame (int): Number of bytes per frame to capture
40+ byte_swap (bool): Whether to swap bytes in the captured data
41+ """
42+ self ._pin_d0 = pin_d0
43+ self ._pin_vsync = pin_vsync
44+ self ._pin_hsync = pin_hsync
45+ self ._pin_pclk = pin_pclk
46+ self ._pin_xclk = pin_xclk
47+ self ._sm_id = sm_id
2448
2549 # Initialize DVP pins as inputs
2650 for i in range (num_data_pins ):
@@ -30,86 +54,98 @@ def __init__(
3054 Pin (pin_pclk , Pin .IN )
3155
3256 # Set up XCLK pin if provided
33- if self .pin_xclk is not None :
34- self .xclk = PWM (Pin (pin_xclk ))
35- self .xclk .freq (xclk_freq )
36- self .xclk .duty_u16 (32768 ) # 50% duty cycle
57+ if self ._pin_xclk is not None :
58+ self ._xclk = PWM (Pin (pin_xclk ))
59+ self ._xclk .freq (xclk_freq )
60+ self ._xclk .duty_u16 (32768 ) # 50% duty cycle
3761
3862 # Copy the PIO program
3963 program = self ._pio_read_dvp
4064
4165 # Mask in the GPIO pins
42- program [0 ][0 ] |= self .pin_hsync & 0x1F
43- program [0 ][1 ] |= self .pin_pclk & 0x1F
44- program [0 ][3 ] |= self .pin_pclk & 0x1F
66+ program [0 ][0 ] |= self ._pin_hsync & 0x1F
67+ program [0 ][1 ] |= self ._pin_pclk & 0x1F
68+ program [0 ][3 ] |= self ._pin_pclk & 0x1F
4569
4670 # Mask in the number of data pins
4771 program [0 ][2 ] &= 0xFFFFFFE0
4872 program [0 ][2 ] |= num_data_pins
4973
5074 # Create PIO state machine to capture DVP data
51- self .sm = rp2 .StateMachine (
52- self .sm_id ,
75+ self ._sm = rp2 .StateMachine (
76+ self ._sm_id ,
5377 program ,
5478 in_base = pin_d0
5579 )
5680
5781 # Create DMA controller to transfer data from PIO to buffer
58- self .dma = rp2 .DMA ()
59- req_num = ((self .sm_id // 4 ) << 3 ) + (self .sm_id % 4 ) + 4
82+ self ._dma = rp2 .DMA ()
83+ req_num = ((self ._sm_id // 4 ) << 3 ) + (self ._sm_id % 4 ) + 4
6084 bytes_per_transfer = 4
61- dma_ctrl = self .dma .pack_ctrl (
85+ dma_ctrl = self ._dma .pack_ctrl (
6286 # 0 = 1 byte, 1 = 2 bytes, 2 = 4 bytes
6387 size = {1 :0 , 2 :1 , 4 :2 }[bytes_per_transfer ],
6488 inc_read = False ,
6589 treq_sel = req_num ,
6690 bswap = byte_swap
6791 )
68- self .dma .config (
69- read = self .sm ,
92+ self ._dma .config (
93+ read = self ._sm ,
7094 count = bytes_per_frame // bytes_per_transfer ,
7195 ctrl = dma_ctrl
7296 )
7397
74- def active (self , active = None ):
98+ def _active (self , active = None ):
99+ """
100+ Sets or gets the active state of the DVP interface.
101+
102+ Args:
103+ active (bool, optional):
104+ - True: Activate the DVP interface
105+ - False: Deactivate the DVP interface
106+ - None: Get the current active state
107+ """
75108 # If no argument is provided, return the current active state
76109 if active == None :
77- return self .sm .active ()
110+ return self ._sm .active ()
78111
79112 # Disable the DMA, the VSYNC handler will re-enable it when needed
80- self .dma .active (False )
113+ self ._dma .active (False )
81114
82115 # Set the active state of the state machine
83- self .sm .active (active )
116+ self ._sm .active (active )
84117
85118 # If active, set up the VSYNC interrupt handler
86119 if active :
87- Pin (self .pin_vsync ).irq (
120+ Pin (self ._pin_vsync ).irq (
88121 trigger = Pin .IRQ_FALLING ,
89122 handler = lambda pin : self ._vsync_handler ()
90123 )
91124 # If not active, disable the VSYNC interrupt handler
92125 else :
93- Pin (self .pin_vsync ).irq (
126+ Pin (self ._pin_vsync ).irq (
94127 handler = None
95128 )
96129
97130 def _vsync_handler (self ):
131+ """
132+ Handles the VSYNC interrupt to capture a frame of data.
133+ """
98134 # Disable DMA before reconfiguring it
99- self .dma .active (False )
135+ self ._dma .active (False )
100136
101137 # Reset state machine to ensure ISR is cleared
102- self .sm .restart ()
138+ self ._sm .restart ()
103139
104140 # Ensure PIO RX FIFO is empty (it's not emptied by `sm.restart()`)
105- while self .sm .rx_fifo () > 0 :
106- self .sm .get ()
141+ while self ._sm .rx_fifo () > 0 :
142+ self ._sm .get ()
107143
108144 # Reset the DMA write address
109- self .dma .write = self .buffer
145+ self ._dma .write = self ._buffer
110146
111147 # Start the DMA
112- self .dma .active (True )
148+ self ._dma .active (True )
113149
114150 # Here is the PIO program, which is configurable to mask in the GPIO pins
115151 # and the number of data pins. It must be configured before the state
@@ -121,6 +157,9 @@ def _vsync_handler(self):
121157 fifo_join = rp2 .PIO .JOIN_RX
122158 )
123159 def _pio_read_dvp ():
160+ """
161+ PIO program to read DVP data from the GPIO pins.
162+ """
124163 wait (1 , gpio , 0 ) # Mask in HSYNC pin
125164 wait (1 , gpio , 0 ) # Mask in PCLK pin
126165 in_ (pins , 1 ) # Mask in number of pins
0 commit comments