diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..36c6db4
Binary files /dev/null and b/.DS_Store differ
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..55d1bc1
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,483 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..0cb232d
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..a51d071
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "useNewFormat": true
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "associatedIndex": 5
+}
+
+
+
+
+
+ {
+ "keyToString": {
+ "RunOnceActivity.RadMigrateCodeStyle": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.cidr.known.project.marker": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "RunOnceActivity.readMode.enableVisualFormatting": "true",
+ "RunOnceActivity.west.config.association.type.startup.service": "true",
+ "cf.first.check.clang-format": "false",
+ "cidr.known.project.marker": "true",
+ "git-widget-placeholder": "main",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "clion.platformio.config",
+ "vue.rearranger.settings.migration": "true"
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1742580557791
+
+
+ 1742580557791
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index efdad1b..685b679 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,7 +44,7 @@ pico_set_program_version(PicoIris "0.1")
# Generate PIO header
pico_generate_pio_header(PicoIris ${CMAKE_CURRENT_LIST_DIR}/src/pio/i2c/i2c.pio)
-# Modify the below lines to enable/disable output over UART/USB
+#Disable debug messages on UART by redirecting them to USB
pico_enable_stdio_uart(PicoIris 0)
pico_enable_stdio_usb(PicoIris 1)
diff --git a/image_receiver.py b/image_receiver.py
new file mode 100644
index 0000000..4b07816
--- /dev/null
+++ b/image_receiver.py
@@ -0,0 +1,314 @@
+import serial
+import struct
+import numpy as np
+import os
+import binascii
+import time
+import cv2 # Added OpenCV for image processing
+
+# Configure serial port - this will connect to the USB serial port from the Pico
+PORT = '/dev/tty.usbmodem101' # Adjust for your system
+BAUD_RATE = 57600 # Reduced to match the Pico's baud rate for debugging
+TIMEOUT = 60 # Increased timeout for slower data transfer
+
+# Enable detailed debugging information
+DEBUG_MODE = True # Set to True for verbose output, False for normal operation
+
+# Frame constants - match the values in the C++ code
+FRAME_HEADER = b'\xDE\xAD\xBE\xEF' # Distinctive header
+FRAME_FOOTER = b'\xCA\xFE\xBA\xBE' # Distinctive footer
+BINARY_START_MARKER = "BINARY_START"
+BINARY_END_MARKER = "###BINARY_END###" # Updated to match the new format
+
+# Directory to save the frames
+output_dir = "frames"
+os.makedirs(output_dir, exist_ok=True)
+
+def debug_hex(data):
+ """Print data in hexadecimal format for debugging."""
+ return binascii.hexlify(data).decode('ascii')
+
+def save_as_image(frame_data, filename_base, frame_size=(160, 120)):
+ """Save the frame data as both PNG and raw file formats."""
+ try:
+ # Convert the raw data to a numpy array
+ img_array = np.frombuffer(frame_data, dtype=np.uint16)
+
+ # Reshape to the expected dimensions
+ width, height = frame_size
+ img_array = img_array.reshape(height, width)
+
+ # Convert from RGB565 to BGR format that OpenCV uses
+ # This conversion depends on your exact pixel format
+ # RGB565: 5 bits red, 6 bits green, 5 bits blue
+ img_rgb = np.zeros((height, width, 3), dtype=np.uint8)
+
+ # Extract R, G, B components from RGB565
+ r = ((img_array & 0xF800) >> 11) * 8 # 5 bits for red (0-31) scaled to 0-255
+ g = ((img_array & 0x07E0) >> 5) * 4 # 6 bits for green (0-63) scaled to 0-255
+ b = (img_array & 0x001F) * 8 # 5 bits for blue (0-31) scaled to 0-255
+
+ img_rgb[:,:,0] = b # OpenCV uses BGR order
+ img_rgb[:,:,1] = g
+ img_rgb[:,:,2] = r
+
+ # Save as PNG
+ png_filename = f"{filename_base}.png"
+ cv2.imwrite(png_filename, img_rgb)
+ print(f"Saved PNG image: {png_filename}")
+
+ # Save as JPG with 90% quality
+ jpg_filename = f"{filename_base}.jpg"
+ cv2.imwrite(jpg_filename, img_rgb, [cv2.IMWRITE_JPEG_QUALITY, 90])
+ print(f"Saved JPG image: {jpg_filename}")
+
+ # Also save the raw numpy array for further analysis if needed
+ np_filename = f"{filename_base}.npy"
+ np.save(np_filename, img_array)
+ print(f"Saved NumPy array: {np_filename}")
+
+ # Save raw binary data too
+ raw_filename = f"{filename_base}.raw"
+ with open(raw_filename, "wb") as f:
+ f.write(frame_data)
+ print(f"Saved raw data: {raw_filename}")
+
+ return True
+
+ except Exception as e:
+ print(f"Error saving image: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+def receive_frame():
+ """Receive a complete frame from the serial port."""
+ try:
+ with serial.Serial(PORT, BAUD_RATE, timeout=TIMEOUT) as ser:
+ print(f"Monitoring serial port: {ser.name}")
+
+ line = ""
+ in_binary_section = False
+ binary_data = bytearray()
+
+ # Safety check - maximum buffer size to prevent infinite collection
+ MAX_BUFFER_SIZE = 500000 # 500KB should be more than enough for a 160x120 frame
+
+ while True:
+ # Read data from serial port
+ if ser.in_waiting > 0:
+ # Try to read a line first to check for markers
+ c = ser.read(1)
+
+ # If in binary section, collect binary data
+ if in_binary_section:
+ binary_data.append(c[0])
+
+ # Safety check - if buffer gets too large, abort
+ if len(binary_data) > MAX_BUFFER_SIZE:
+ print(f"WARNING: Binary data exceeded maximum buffer size ({MAX_BUFFER_SIZE})")
+ print("Aborting binary collection to prevent memory issues")
+ return None
+
+ # Show progress for large binary sections
+ if len(binary_data) % 1000 == 0:
+ print(f"Collected {len(binary_data)} bytes of binary data")
+
+ # Check if we've accumulated enough data to check for the end marker
+ # Add the character to our text buffer as well for end marker detection
+ if c.isascii() and not c.isspace():
+ line += c.decode('ascii', errors='replace')
+ else:
+ # For whitespace characters, we need special handling
+ if c == b'\n':
+ # If the current line contains our end marker
+ if BINARY_END_MARKER in line:
+ print(f"Binary end marker detected in line: {line}")
+ in_binary_section = False
+ print(f"Total binary data collected: {len(binary_data)} bytes")
+
+ # Print binary data summary
+ print("First 32 bytes:", debug_hex(binary_data[:32]))
+ print("Last 32 bytes:", debug_hex(binary_data[-32:]))
+
+ # Find the last occurrence of FRAME_FOOTER
+ footer_pos = -1
+ for i in range(len(binary_data) - len(FRAME_FOOTER)):
+ if binary_data[i:i+len(FRAME_FOOTER)] == FRAME_FOOTER:
+ footer_pos = i
+
+ if footer_pos >= 0:
+ print(f"Frame footer found at position {footer_pos}: {debug_hex(FRAME_FOOTER)}")
+ # Trim the binary data to end at the footer + footer length
+ binary_data = binary_data[:footer_pos + len(FRAME_FOOTER)]
+ else:
+ print(f"WARNING: Frame footer {debug_hex(FRAME_FOOTER)} not found in binary data")
+
+ # Process the binary data
+ return process_binary_data(binary_data)
+ line = ""
+ elif c == b'\r':
+ pass # Ignore CR characters
+ else:
+ # Normal text processing
+ if c == b'\n':
+ # Check if this line is the start marker
+ if BINARY_START_MARKER in line:
+ print("Binary start marker detected")
+ in_binary_section = True
+ binary_data = bytearray()
+ line = ""
+ else:
+ print(f"Log: {line}")
+ line = ""
+ else:
+ line += c.decode('ascii', errors='replace')
+ else:
+ # No data available, sleep briefly
+ time.sleep(0.01)
+
+ except serial.SerialException as e:
+ print(f"Serial error: {e}")
+ return None
+ except Exception as e:
+ print(f"Unexpected error: {e}")
+ import traceback
+ traceback.print_exc()
+ return None
+
+def process_binary_data(data):
+ """Process the binary data to extract the frame."""
+ print(f"Processing {len(data)} bytes of binary data")
+
+ # Search for the frame header
+ header_pos = -1
+ for i in range(len(data) - len(FRAME_HEADER)):
+ if data[i:i+len(FRAME_HEADER)] == FRAME_HEADER:
+ header_pos = i
+ break
+
+ if header_pos == -1:
+ print("Error: Frame header not found")
+ return None
+
+ print(f"Frame header found at position {header_pos}")
+
+ # Extract the frame size
+ size_start = header_pos + len(FRAME_HEADER)
+ size_bytes = data[size_start:size_start+4]
+ if len(size_bytes) < 4:
+ print("Error: Incomplete size bytes")
+ return None
+
+ frame_size = struct.unpack('
#include "cstring"
-// PIO2 is reserved for the cam data retreive functions
+// Frame header and footer markers for synchronization - using distinctive values
+const uint8_t FRAME_HEADER[] = {0xDE, 0xAD, 0xBE, 0xEF}; // Distinctive header
+const uint8_t FRAME_FOOTER[] = {0xCA, 0xFE, 0xBA, 0xBE}; // Distinctive footer
+const int FRAME_MARKER_SIZE = 4;
+// Reduced baud rate for slower, more reliable debugging
+#define BAUD_RATE 57600 // Reduced for debugging (standard options: 9600, 19200, 38400, 57600, 115200)
+
+// PIO2 is reserved for the cam data retrieve functions
camera_config_t left_eye_config = {
- .pin_pwdn = -1, /*!< GPIO pin for camera power down line */ //? Also called PWDN, or set to -1 and tie to GND
- .pin_reset = -1, /*!< GPIO pin for camera reset line */ //? Cam reset, or set to -1 and tie to 3.3V
- .pin_xclk = 3, /*!< GPIO pin for camera XCLK line */ //? in theory could be shared or perhaps ommited?
+ .pin_pwdn = -1, /*!< GPIO pin for camera power down line */
+ .pin_reset = -1, /*!< GPIO pin for camera reset line */
+ .pin_xclk = 3, /*!< GPIO pin for camera XCLK line */
.pin_sccb_sda = 4, /*!< GPIO pin for camera SDA line */
.pin_sccb_scl = 5, /*!< GPIO pin for camera SCL line */
.pin_data_base = 6, /*!< this pin + 7 consecutive will be used D0-D7 PCLK HREF */
.pin_vsync = 16, /*!< GPIO pin for camera VSYNC line */
- .xclk_freq_hz = 0, /*!< Frequency of XCLK signal, in Hz. */ //! Figure out the highest it can go to
+ .xclk_freq_hz = 0, /*!< Frequency of XCLK signal, in Hz. */
.sccb_ctrl = 0, /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */
};
+// Send a properly framed image using printf for binary data
+void send_frame(const void* buffer, size_t size) {
+ // Ensure UART output buffer is flushed before sending binary data
+ printf("\n\nBINARY_START\n\n");
+ sleep_ms(100); // Small delay to ensure the text is transmitted separately
+
+ // Send binary data using fwrite directly to stdout
+ // This avoids any text processing that printf might do
+
+ // Send frame header
+ fwrite(FRAME_HEADER, 1, FRAME_MARKER_SIZE, stdout);
+
+ // Send frame size (4 bytes, little-endian)
+ uint8_t size_bytes[4];
+ size_bytes[0] = size & 0xFF;
+ size_bytes[1] = (size >> 8) & 0xFF;
+ size_bytes[2] = (size >> 16) & 0xFF;
+ size_bytes[3] = (size >> 24) & 0xFF;
+ fwrite(size_bytes, 1, 4, stdout);
+
+ // Calculate checksum
+ uint32_t checksum = 0;
+ const uint8_t* buf = (const uint8_t*)buffer;
+ for (size_t i = 0; i < size; i++) {
+ checksum += buf[i];
+ }
+
+ // Send frame data
+ fwrite(buf, 1, size, stdout);
+
+ // Send checksum (4 bytes)
+ uint8_t checksum_bytes[4];
+ checksum_bytes[0] = checksum & 0xFF;
+ checksum_bytes[1] = (checksum >> 8) & 0xFF;
+ checksum_bytes[2] = (checksum >> 16) & 0xFF;
+ checksum_bytes[3] = (checksum >> 24) & 0xFF;
+ fwrite(checksum_bytes, 1, 4, stdout);
+
+ // Send frame footer
+ fwrite(FRAME_FOOTER, 1, FRAME_MARKER_SIZE, stdout);
+
+ // Flush to ensure all binary data is sent
+ fflush(stdout);
+
+ // Make the end marker very distinctive with multiple newlines and unique pattern
+ sleep_ms(100);
+ printf("\n\n###BINARY_END###\n\n");
+}
+
+// Test function to verify binary data transmission
+void test_binary_transfer() {
+ printf("Testing binary transfer...\n");
+
+ // Create a small test buffer
+ uint8_t test_data[16];
+ for (int i = 0; i < 16; i++) {
+ test_data[i] = i;
+ }
+
+ // Send it as a frame
+ send_frame(test_data, sizeof(test_data));
+
+ printf("Binary test complete\n");
+}
+
int main()
{
stdio_init_all();
-
- sleep_ms(4000);
- //printf("Hello World!");
+
+ printf("\n\n=== PicoIris Starting ===\n");
+ sleep_ms(1000);
+
+ // Test binary transfer
+ test_binary_transfer();
+ sleep_ms(1000);
+
+ printf("Configuring Camera...\n");
OV2640 left_cam = OV2640(left_eye_config);
left_cam.begin(DMA_IRQ_0);
- //left_cam.capture_frame();
- //fwrite(left_cam.fb, 1, sizeof(left_cam.fb), stdout);
+ printf("Camera Configured!...\n");
+
+ // Image size (160 x 120 x 2 bytes per pixel for RGB565)
+ const size_t frame_size = 160 * 120 * 2;
+
while (true) {
- //printf("Hello, world!\n");
- //sleep_ms(1000);
+ // Capture a frame
+ printf("Attempting to capture frame...\n");
left_cam.capture_frame();
- //fwrite(&left_cam.fb, 1, sizeof(left_cam.fb), stdout);
- memset(&left_cam.fb, 0x00, sizeof(left_cam.fb));
- sleep_ms(1000);
- }
-}
+
+ // Once the frame is ready, send it over UART
+ printf("Checking if frame is ready...\n");
+ printf("Frame ready, sending...\n");
+ send_frame(left_cam.fb, frame_size);
+ left_cam.resetFrameReady();
+ printf("Frame sent...\n");
+
+
+ // Small delay to allow processing time
+ printf("Waiting before next attempt...\n");
+ sleep_ms(3000); // Increased delay for debugging
+ }
+}
\ No newline at end of file
diff --git a/src/ov2640/SCCB.cpp b/src/ov2640/SCCB.cpp
index b3f3a75..f4028f3 100644
--- a/src/ov2640/SCCB.cpp
+++ b/src/ov2640/SCCB.cpp
@@ -22,7 +22,7 @@ int SCCB::begin(uint8_t ctrl)
this->ctrl = ctrl;
if (ctrl < 2) {
this->i2cc = ctrl == 0 ? i2c0 : i2c1;
- i2c_init(this->i2cc, 100 * 1000);
+ i2c_init(this->i2cc, 5 * 1000);
gpio_set_function(this->sda, GPIO_FUNC_I2C);
gpio_set_function(this->scl, GPIO_FUNC_I2C);
gpio_pull_up(this->sda);
@@ -100,6 +100,6 @@ void SCCB::writeList(const register_val_t *cfg,
for (int i = 0; i < len; i++)
{
writeRegister(cfg[i].reg, cfg[i].value);
- sleep_ms(2); // Some cams require, else lockup on init
+ sleep_ms(10); // Some cams require, else lockup on init
}
}
\ No newline at end of file
diff --git a/src/ov2640/camera.cpp b/src/ov2640/camera.cpp
index cba2553..7199aef 100644
--- a/src/ov2640/camera.cpp
+++ b/src/ov2640/camera.cpp
@@ -14,29 +14,31 @@
#include "src/pio/image/image.pio.h"
-static volatile bool frameReady = false; // true at end-of-frame
-static volatile bool suspended = true;
+static volatile bool frameReady = false;
+static volatile bool suspended = true;
+static volatile bool dma_busy = false;
void* pointer;
-static void iCap_vsync_irq(uint gpio, uint32_t events) {
- if (!suspended) {
- // Clear PIO FIFOs and start DMA transfer
- pio_sm_clear_fifos(pio2, 0);
- dma_channel_start(0);
+// VSYNC interrupt handler
+void iCap_vsync_irq(uint gpio, uint32_t events) {
+ if (!suspended && !dma_busy) {
+ pio_sm_clear_fifos(pio2, 0); // Clear PIO FIFOs
+ dma_busy = true; // Mark DMA as busy
+ dma_channel_start(0); // Start DMA transfer
}
- }
+}
-static void iCap_dma_finish_irq() {
- frameReady = true;
- suspended = true;
- //printf("iCap_dma_finish_irq");
- dma_channel_set_write_addr(0, (uint8_t *)(pointer), false);
- printf("0x%\n", (uintptr_t)pointer);
- dma_hw->ints0 = 1u << 0;
- }
-
+// DMA completion interrupt handler
+void iCap_dma_finish_irq() {
+ frameReady = true; // Signal frame is ready
+ suspended = true; // Suspend further captures
+ dma_busy = false; // DMA is no longer busy
+ dma_channel_set_write_addr(0, pointer, false); // Reset write address
+ dma_hw->ints0 = 1u << 0; // Clear DMA interrupt flag
+}
// TODO: Stolen from Adafruit repo (Check if correct)
+
// PIO code in this table is modified at runtime so that PCLK is
// configurable (rather than fixed GP## or PIN offset). Data pins
// must be contiguous but are otherwise configurable.
@@ -605,14 +607,17 @@ register_val_t OV2640_qqvga[] = {
// };
+
OV2640::OV2640(camera_config_t config)
{
+ uint8_t fb[160 * 120 * 2];
this->config = config;
memset(&this->fb, 0x00, sizeof(this->fb));
pointer = &this->fb;
//this->fb = image_buf;
}
+
// Pin interrupt on VSYNC calls this to start DMA transfer (unless suspended).
// static void vsync_irq(uint gpio, uint32_t events)
// {
@@ -642,173 +647,106 @@ int OV2640::begin(int dma_irq)
pwm_set_gpio_level(this->config.pin_xclk, 4);
pwm_set_enabled(slice_num, true);
+ printf("PWM clock setup for XCLK complete (9.375 MHz)\n");
+
// Init SCCB
- //prtintf("Init SCCB\n");
+ printf("Initializing SCCB on SDA=%d, SCL=%d...\n",
+ this->config.pin_sccb_sda, this->config.pin_sccb_scl);
this->sccb = SCCB(this->config.pin_sccb_sda, this->config.pin_sccb_scl);
this->sccb.begin(this->config.sccb_ctrl);
- //prtintf("Inited SCCB\n");
+ printf("SCCB initialization complete\n");
// ENABLE AND/OR RESET CAMERA --------------------------------------------
// Unsure of camera startup time from beginning of input clock.
// Let's guess it's similar to tS:REG (300 ms) from datasheet.
+ printf("Waiting for camera to stabilize...\n");
sleep_ms(300);
- // Perform a soft reset (we dont wanna waste GPIO on wiring in reset pins)
+ // Perform a soft reset
+ printf("Sending camera reset command...\n");
this->sccb.writeRegister(OV2640_REG_RA_DLMT, OV2640_RA_DLMT_SENSOR); // Bank select 1
this->sccb.writeRegister(OV2640_REG1_COM7, OV2640_COM7_SRST); // System reset
- sleep_ms(300); // Datasheet: tS:RESET = 1 ms
+ sleep_ms(1000); // Datasheet: tS:RESET = 1 ms
+ printf("Camera reset complete\n");
- // Init main camera settings
- //printf("Stolemjpg init\n");
- //this->sccb.writeList(OV2640_stolem_jpeg_init, sizeof(OV2640_stolem_jpeg_init)/sizeof(OV2640_stolem_jpeg_init[0]));
- //printf("Stolemjpg init DONE\n");
+ printf("Initializing camera registers...\n");
this->sccb.writeList(OV2640_init, sizeof(OV2640_init)/sizeof(OV2640_init[0]));
this->sccb.writeList(OV2640_qqvga, sizeof(OV2640_qqvga)/sizeof(OV2640_qqvga[0]));
this->sccb.writeList(OV2640_rgb, sizeof(OV2640_rgb)/sizeof(OV2640_rgb[0]));
- //this->sccb.writeList(OV2640_jpeg, sizeof(OV2640_jpeg)/sizeof(OV2640_jpeg[0]));
- //this->sccb.writeRegister(0xd3, 0x0f);
+
+ // // Enable color bar test pattern for debugging
+ // printf("Enabling color bar test pattern...\n");
+ // this->sccb.writeRegister(OV2640_REG_RA_DLMT, OV2640_RA_DLMT_SENSOR);
+ // this->sccb.writeRegister(OV2640_REG1_COM7, OV2640_COM7_COLORBAR); // Output test pattern
+ // printf("Test pattern enabled\n");
-
- // this->set_framesize();
- // sleep_ms(10);
- // this->sccb.writeList(OV2640_jpeg, sizeof(OV2640_jpeg)/sizeof(OV2640_jpeg[0]));
-
- //this->set_gainceiling(GAINCEILING_2X);
- //sleep_ms(10);
- //this->set_bpc(false);
- //sleep_ms(10);
- //this->set_wpc(true);
- //sleep_ms(10);
- //this->set_lenc(true);
- //sleep_ms(10);
- // this->set_quality(5);
- // sleep_ms(10);
- // this->sccb.writeList(OV2640_jpeg, sizeof(OV2640_jpeg)/sizeof(OV2640_jpeg[0]));
-
- // this->set_quality(5);
-
- // //prtintf("Write list of init\n");
- // // sleep_ms(10);
- // // this->sccb.writeRegister(OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP);
- // sleep_ms(10);
- // //this->sccb.writeRegister(0xd3, 0x05);
- // sleep_ms(10);
-
-
- // // for (int i=0; i<5; i++) {
- // // this->sccb.writeRegister(0xff, 0x00);
- // // this->sccb.writeRegister(special_effects_regs[0][i], special_effects_regs[3][i]);
- // // }
-
- // this->sccb.writeRegister(0xff, 0x00);
- // this->sccb.writeRegister(0xd3, 0x05); // try 0x80 next
-
-
- //prtintf("Gpio init\n");
+ // GPIO pin setup
+ printf("Setting up GPIO pins for camera data...\n");
gpio_init(this->config.pin_vsync);
gpio_set_dir(this->config.pin_vsync, GPIO_IN);
gpio_pull_down(this->config.pin_vsync);
+
+ printf("Initializing data pins %d through %d...\n",
+ this->config.pin_data_base, this->config.pin_data_base + 9);
for (uint8_t i = 0; i < 10; i++)
{
gpio_init(this->config.pin_data_base + i);
gpio_set_dir(this->config.pin_data_base + i, GPIO_IN);
- //gpio_pull_down(this->config.pin_data_base + i);
}
- //prtintf("Gpio init done\n");
-
- // PIO periph to use is currently specified by the user in the arch struct,
- // but I suppose this could be written to use whatever PIO has resources.
-
- // Mask the GPIO pin used PCLK into the PIO opcodes -- see notes at top
- // TODO: check if this is correct (code made for rp2040 not rp2350 so it might differ)
- //prtintf("PIO init\n");
- //! Temporary replace with the image.pio from known working library rp2040_ov2640
- /*capture_pio_opcodes[0] |= (this->config.pin_href & 31);
- capture_pio_opcodes[1] |= (this->config.pin_pclk & 31);
- capture_pio_opcodes[3] |= (this->config.pin_pclk & 31);
-
- uint offset = pio_add_program(pio2, &cap_pio_program);
- this->sm = pio_claim_unused_sm(pio2, true); // 0-3
- sm = this->sm;
- pio_sm_set_consecutive_pindirs(pio2, this->sm, this->config.pin_data_base, 8, false);
-
- pio_sm_config c = pio_get_default_sm_config();
- c.pinctrl = 0; // SDK fails to set this
- sm_config_set_wrap(&c, offset, offset + cap_pio_program.length - 1);
-
- sm_config_set_in_pins(&c, this->config.pin_data_base);
- sm_config_set_in_shift(&c, false, true, 16); // 1 pixel (16b) ISR to FIFO
- sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
-
- pio_sm_init(pio2, this->sm, offset, &c);
- pio_sm_set_enabled(pio2, this->sm, true);
- //prtintf("PIO init done\n");
-
- // SET UP DMA ------------------------------------------------------------
- //prtintf("DMA init\n");
- this->dma_channel = dma_claim_unused_channel(false); // don't panic
- dma_channel = this->dma_channel;
- this->dma_config = dma_channel_get_default_config(this->dma_channel);
- channel_config_set_transfer_data_size(&this->dma_config, DMA_SIZE_16);
- channel_config_set_read_increment(&this->dma_config, false);
- channel_config_set_write_increment(&this->dma_config, true);
- channel_config_set_bswap(&this->dma_config, true);
-
- // Set PIO RX as DMA trigger. Input shift register saturates at 16 bits
- // (1 pixel), configured in data size above and in PIO setup elsewhere.
- channel_config_set_dreq(&this->dma_config,
- pio_get_dreq(pio2, this->sm, false));
- // Set up baseline DMA configuration...it's initially lacking destination
- // and count, set later (dma_change()) after resolution is known. DMA
- // isn't started until later, and is triggered in the vsync interrupt.
- dma_channel_configure(this->dma_channel, &this->dma_config, NULL,
- &pio2->rxf[this->sm], 0, false);
-
- // Set up end-of-DMA interrupt
- dma_channel_set_irq0_enabled(this->dma_channel, true);
- //prtintf("DMA init done\n");
- //? setup diffrent handlers for each cam???
- //? check with a button or smth if this fires all events or only one
- //? cuz then I would be able to do dma_finish_irq_cam1, dma_finish_irq_cam2, dma_finish_irq_cam3
- //? That would have hardcoded values for the buffers
- irq_set_exclusive_handler(dma_irq, dma_finish_irq);
- irq_set_enabled(dma_irq, true);
-
- // SET UP VSYNC INTERRUPT ------------------------------------------------
- //? This also could be redone with hardcoded values
- gpio_set_irq_enabled_with_callback(this->config.pin_vsync, GPIO_IRQ_EDGE_RISE, true,
- &vsync_irq);*/
+ printf("GPIO initialization complete\n");
+ // PIO setup for image capture
+ printf("Setting up PIO for image capture...\n");
uint offset = pio_add_program(pio2, &image_program);
image_program_init(pio2, 0, offset, this->config.pin_data_base);
+ printf("PIO setup complete\n");
- //printf("IRQ init done\n");
-
- //printf("config done");
+ // DMA setup
+ printf("Configuring DMA for image capture...\n");
dma_channel_config c = dma_channel_get_default_config(0);
channel_config_set_transfer_data_size(&c, DMA_SIZE_16);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_bswap(&c, true);
- // Set PIO RX as DMA trigger. Input shift register saturates at 16 bits
- // (1 pixel), configured in data size above and in PIO setup elsewhere.
- channel_config_set_dreq(&c,
- pio_get_dreq(pio2, 0, false));
- // Set up baseline DMA configuration...it's initially lacking destination
- // and count, set later (dma_change()) after resolution is known. DMA
- // isn't started until later, and is triggered in the vsync interrupt.
- dma_channel_configure(0, &c, &this->fb,
- &pio2->rxf[0], sizeof(this->fb), false);
-
- // Set up end-of-DMA interrupt
+ channel_config_set_dreq(&c, pio_get_dreq(pio2, 0, false));
+ dma_channel_configure(0, &c, this->fb, &pio2->rxf[0], 160 * 120, false);
+ printf("DMA configuration complete\n");
+
+ // Set up DMA interrupt
+ printf("Setting up DMA interrupt...\n");
dma_channel_set_irq0_enabled(0, true);
- irq_set_exclusive_handler(DMA_IRQ_0, iCap_dma_finish_irq);
+ irq_set_exclusive_handler(DMA_IRQ_0, iCap_dma_finish_irq); // Register DMA handler
irq_set_enabled(DMA_IRQ_0, true);
-
- // SET UP VSYNC INTERRUPT ------------------------------------------------
-
+ printf("DMA interrupt setup complete\n");
+
+ // Set up VSYNC interrupt
+ printf("Setting up VSYNC interrupt on pin %d...\n", this->config.pin_vsync);
gpio_set_irq_enabled_with_callback(this->config.pin_vsync, GPIO_IRQ_EDGE_RISE, true,
- &iCap_vsync_irq);
+ &iCap_vsync_irq); // Register VSYNC handler
+ printf("VSYNC interrupt setup complete\n");
+
+ // Test if VSYNC is toggling
+ printf("Checking if VSYNC is toggling...\n");
+ bool initial_vsync = gpio_get(this->config.pin_vsync);
+ uint32_t start_time = time_us_32();
+ bool vsync_changed = false;
+ while ((time_us_32() - start_time) < 1000000) { // 1 second timeout
+ bool current_vsync = gpio_get(this->config.pin_vsync);
+ if (current_vsync != initial_vsync) {
+ vsync_changed = true;
+ printf("VSYNC toggled from %d to %d\n", initial_vsync, current_vsync);
+ break;
+ }
+ sleep_ms(1);
+ }
+
+ if (!vsync_changed) {
+ printf("WARNING: VSYNC signal not detected during initialization\n");
+ } else {
+ printf("VSYNC signal detected successfully\n");
+ }
+
+ printf("Camera initialization complete\n");
return 0;
}
@@ -878,34 +816,42 @@ void OV2640::set_lenc(bool enable) {
this->sccb.writeMaskRegister(0xC3, 1, 1, enable?1:0);
}
+bool OV2640::isFrameReady() const {
+ return frameReady;
+}
+void OV2640::resetFrameReady() {
+ frameReady = false;
+}
void OV2640::capture_frame() {
- suspended = false;
- while(!frameReady);
- return;
- // dma_channel_config c = dma_channel_get_default_config(0);
- // channel_config_set_read_increment(&c, false);
- // channel_config_set_write_increment(&c, true);
- // channel_config_set_dreq(&c, pio_get_dreq(pio2, 0, false));
- // channel_config_set_transfer_data_size(&c, DMA_SIZE_16);
+ // Reset frame ready flag before starting capture
+ frameReady = false;
- // dma_channel_configure(
- // 0, &c,
- // &this->fb,
- // &pio2->rxf[0],
- // sizeof(this->fb),
- // false
- // );
-
- // // Wait for vsync rising edge to start frame
- // ////prtintf("pin_vsync) == true\n");
- // while (gpio_get(this->config.pin_vsync) == true);
- // ////prtintf("pin_vsync) == false\n");
- // while (gpio_get(this->config.pin_vsync) == false);
- // ////prtintf("after while loops\n");
-
- // dma_channel_start(0);
- // dma_channel_wait_for_finish_blocking(0);
+ // Allow captures to proceed
+ suspended = false;
+
+ // Wait for frame with timeout to prevent infinite loops
+ uint32_t start_time = time_us_32();
+ uint32_t timeout_us = 2000000; // 2 second timeout
+
+ while(!frameReady) {
+ // Check for timeout
+ if ((time_us_32() - start_time) > timeout_us) {
+ printf("Frame capture timeout! Sending incomplete frame...\n");
+ // Reset DMA in case it's stuck
+ dma_channel_abort(0);
+ dma_channel_set_write_addr(0, pointer, false);
+ dma_busy = false;
+ suspended = true;
+ return; // Exit without waiting for frameReady
+ }
+ // Small yield to avoid tight loop
+ sleep_us(100);
+ }
+
+ // Frame is ready, no need to do anything else
+ // The DMA handler has already set frameReady = true
+ printf("Frame captured successfully\n");
}
diff --git a/src/ov2640/camera.hpp b/src/ov2640/camera.hpp
index a38ef27..9b3624e 100644
--- a/src/ov2640/camera.hpp
+++ b/src/ov2640/camera.hpp
@@ -34,6 +34,11 @@ class OV2640 {
public:
OV2640(camera_config_t config);
~OV2640();
+ // Check if a new frame is ready
+ bool isFrameReady() const;
+
+ // Reset the frame ready flag after processing
+ void resetFrameReady();
int begin(int dma_irq);
void set_quality(int quality);