From 4bb028cbc8a84c86e508f4f906750b644a6d18a8 Mon Sep 17 00:00:00 2001 From: KubaWis Date: Fri, 14 Mar 2025 15:50:58 +0100 Subject: [PATCH] Getting closer to working Sends data from camera directly to serial JPEG dosen't work (huffman table is incorrent *???*) but raw rgb565 images work --- src/PicoIris.cpp | 17 ++-- src/ov2640/SCCB.cpp | 3 +- src/ov2640/camera.cpp | 194 ++++++++++++++++++++++++++------------ src/ov2640/camera.hpp | 13 ++- src/pio/image/image.pio.h | 56 +++++++++++ 5 files changed, 210 insertions(+), 73 deletions(-) create mode 100644 src/pio/image/image.pio.h diff --git a/src/PicoIris.cpp b/src/PicoIris.cpp index e3fb0ca..ee22d59 100644 --- a/src/PicoIris.cpp +++ b/src/PicoIris.cpp @@ -12,6 +12,7 @@ #include #include +#include "cstring" // PIO2 is reserved for the cam data retreive functions @@ -23,8 +24,6 @@ camera_config_t left_eye_config = { .pin_sccb_scl = 5, /*!< GPIO pin for camera SCL line */ .pin_data_base = 6, /*!< this pin + 7 consecutive will be used D0-D7 */ .pin_vsync = 16, /*!< GPIO pin for camera VSYNC line */ - .pin_href = 15, /*!< GPIO pin for camera HREF line */ - .pin_pclk = 14, /*!< GPIO pin for camera PCLK line */ .xclk_freq_hz = 0, /*!< Frequency of XCLK signal, in Hz. */ //! Figure out the highest it can go to .sccb_ctrl = 0, /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */ }; @@ -34,17 +33,19 @@ int main() stdio_init_all(); sleep_ms(4000); - printf("Hello World!"); + //printf("Hello World!"); - printf("construct OV2640!"); + //printf("construct OV2640!"); OV2640 left_cam = OV2640(left_eye_config); - printf("begin OV2640!"); + //printf("begin OV2640!"); left_cam.begin(DMA_IRQ_0); - - + //left_cam.capture_frame(); + //fwrite(left_cam.fb, 1, sizeof(left_cam.fb), stdout); while (true) { //printf("Hello, world!\n"); //sleep_ms(1000); - tight_loop_contents(); + left_cam.capture_frame(); + fwrite(left_cam.fb, 1, sizeof(left_cam.fb), stdout); + memset(&left_cam.fb, 0x00, sizeof(left_cam.fb)); } } diff --git a/src/ov2640/SCCB.cpp b/src/ov2640/SCCB.cpp index a6ea7a0..b738ce4 100644 --- a/src/ov2640/SCCB.cpp +++ b/src/ov2640/SCCB.cpp @@ -76,6 +76,7 @@ void SCCB::writeRegister(uint8_t reg, uint8_t value) }else { //pio_i2c_write_blocking(this->pio, this->sm, OV2640_ADDR, buf, 2); } + sleep_ms(20); } void SCCB::writeList(const register_val_t *cfg, @@ -84,6 +85,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(10); // 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 e134e90..aed2948 100644 --- a/src/ov2640/camera.cpp +++ b/src/ov2640/camera.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "pico/stdlib.h" #include "../../hardware_dma/include/hardware/dma.h" @@ -10,6 +11,8 @@ #include "hardware/irq.h" #include "hardware/pio.h" +#include "src/pio/image/image.pio.h" + // TODO: Stolen from Adafruit repo (Check if correct) // PIO code in this table is modified at runtime so that PCLK is @@ -259,7 +262,7 @@ register_val_t OV2640_init[] = { {OV2640_REG0_R_BYPASS, OV2640_R_BYPASS_DSP_ENABLE}, {OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565}, {OV2640_REG0_IMAGE_MODE, - OV2640_IMAGE_MODE_DVP_RGB565 | OV2640_IMAGE_MODE_BYTE_SWAP}, + OV2640_IMAGE_MODE_DVP_RGB565}, {0x98, 0x00}, // Reserved {0x99, 0x00}, // Reserved {0x00, 0x00}, // Reserved @@ -304,52 +307,88 @@ register_val_t OV2640_qqvga[] = {OV2640_REG0_R_DVP_SP, 0x08}, // Manual DVP PCLK setting {OV2640_REG0_RESET, 0x00} }; // Go -register_val_t OV2640_rgb[] = { - {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 - {OV2640_REG0_RESET, OV2640_RESET_DVP}, - {OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565 | - OV2640_IMAGE_MODE_BYTE_SWAP}, - {0xD7, 0x03}, // Mystery init values - {0xE1, 0x77}, // seen in other examples - {OV2640_REG0_RESET, 0x00} - }; // Go -register_val_t OV2640_yuv[] = { - {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 - {OV2640_REG0_RESET, OV2640_RESET_DVP}, - {OV2640_REG0_IMAGE_MODE, - OV2640_IMAGE_MODE_DVP_YUV | OV2640_IMAGE_MODE_BYTE_SWAP}, - {0xD7, 0x01}, // Mystery init values - {0xE1, 0x67}, // seen in other examples - {OV2640_REG0_RESET, 0x00} -}; // Go + register_val_t ov2640_uxga_cif[] = { + {0xff, 0x00}, + {0xe0, 0x04}, + {0xc0, 0xc8}, + {0xc1, 0x96}, + {0x86, 0x35}, + {0x50, 0x92}, + {0x51, 0x90}, + {0x52, 0x2c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x88}, + {0x57, 0x00}, + {0x5a, 0x58}, + {0x5b, 0x48}, + {0x5c, 0x00}, + {0xd3, 0x08}, + {0xe0, 0x00} + }; -uint8_t image_buf[352*288*2]; -sm_dma_config_t gpios[48]; -int dma_channel; -int sm; + register_val_t OV2640_rgb[] = { + {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 + {OV2640_REG0_RESET, OV2640_RESET_DVP}, + {OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565}, + {0xD7, 0x03}, // Mystery init values + {0xE1, 0x77}, // seen in other examples + {OV2640_REG0_RESET, 0x00} + }; // Go + register_val_t OV2640_yuv[] = { + {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 + {OV2640_REG0_RESET, OV2640_RESET_DVP}, + {OV2640_REG0_IMAGE_MODE, + OV2640_IMAGE_MODE_JPEG | OV2640_IMAGE_MODE_JPEG_HREF}, + {0xD7, 0x01}, // Mystery init values + {0xE1, 0x67}, // seen in other examples + {OV2640_REG0_RESET, 0x00} + }; // Go + + +// Stolem from esp32-camera +register_val_t OV2640_jpeg[] = { + {0xFF, 0x0}, + {0xE0, 0x10 | 0x04}, + {0xDA, 0x10 | 0x02}, + {0xD7, 0x03}, + {0xE1, 0x77}, + {0xE5, 0x1F}, + {0xD9, 0x10}, + {0xDF, 0x80}, + {0x33, 0x80}, + {0x3C, 0x10}, + {0xEB, 0x30}, + {0xDD, 0x7F}, + {0xE0, 0x00}, + {0, 0} +}; + +//uint8_t image_buf[160*120*3]; OV2640::OV2640(camera_config_t config) { this->config = config; - //this->fb = &image_buf; + memset(&this->fb, 0x00, sizeof(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) -{ - pio_sm_clear_fifos(pio2, sm); - dma_channel_start(dma_channel); -} +// static void vsync_irq(uint gpio, uint32_t events) +// { +// pio_sm_clear_fifos(pio2, sm); +// dma_channel_start(dma_channel); +// } -static void dma_finish_irq() -{ - // Channel MUST be reconfigured each time (to reset the dest address). - dma_channel_set_write_addr(dma_channel, - (uint8_t *)(image_buf), false); - dma_hw->ints0 = 1u << dma_channel; // Clear IRQ - uint32_t data = pio2->rxf[sm]; - fwrite(&data, 1, 32, stdout); -} +// static void dma_finish_irq() +// { +// // Channel MUST be reconfigured each time (to reset the dest address). +// dma_channel_set_write_addr(dma_channel, +// (uint8_t *)(image_buf), false); +// dma_hw->ints0 = 1u << dma_channel; // Clear IRQ +// uint32_t data = pio2->rxf[sm]; +// fwrite(&data, 1, 32, stdout); +// } int OV2640::begin(int dma_irq) { @@ -364,10 +403,10 @@ int OV2640::begin(int dma_irq) pwm_set_enabled(slice_num, true); // Init SCCB - printf("Init SCCB\n"); + //prtintf("Init SCCB\n"); this->sccb = SCCB(this->config.pin_sccb_sda, this->config.pin_sccb_scl); this->sccb.begin(this->config.sccb_ctrl); - printf("Inited SCCB\n"); + //prtintf("Inited SCCB\n"); // ENABLE AND/OR RESET CAMERA -------------------------------------------- // Unsure of camera startup time from beginning of input clock. @@ -380,36 +419,34 @@ int OV2640::begin(int dma_irq) sleep_ms(300); // Datasheet: tS:RESET = 1 ms // Init main camera settings - printf("Write list of init\n"); + //prtintf("Write list of init\n"); this->sccb.writeList(OV2640_init, sizeof OV2640_init / sizeof OV2640_init[0]); - printf("Write list of qqvga\n"); - this->sccb.writeList(OV2640_qqvga, sizeof OV2640_qqvga / sizeof OV2640_qqvga[0]); - printf("Write list of rgb\n"); - this->sccb.writeList(OV2640_rgb, sizeof OV2640_rgb / sizeof OV2640_rgb[0]); + //prtintf("Write list of qqvga\n"); + this->sccb.writeList(ov2640_uxga_cif, sizeof ov2640_uxga_cif / sizeof ov2640_uxga_cif[0]); + //prtintf("Write list of jpeg\n"); + this->sccb.writeList(OV2640_jpeg, sizeof OV2640_jpeg / sizeof OV2640_jpeg[0]); + this->set_quality(10); - printf("Gpio init\n"); - gpio_init(this->config.pin_pclk); - gpio_set_dir(this->config.pin_pclk, GPIO_IN); + //prtintf("Gpio init\n"); gpio_init(this->config.pin_vsync); gpio_set_dir(this->config.pin_vsync, GPIO_IN); gpio_pull_down(this->config.pin_vsync); - gpio_init(this->config.pin_href); - gpio_set_dir(this->config.pin_href, GPIO_IN); - for (uint8_t i = 0; i < 8; i++) + 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); } - printf("Gpio init done\n"); + //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) - printf("PIO init\n"); - capture_pio_opcodes[0] |= (this->config.pin_href & 31); + //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); @@ -428,10 +465,10 @@ int OV2640::begin(int dma_irq) pio_sm_init(pio2, this->sm, offset, &c); pio_sm_set_enabled(pio2, this->sm, true); - printf("PIO init done\n"); + //prtintf("PIO init done\n"); // SET UP DMA ------------------------------------------------------------ - printf("DMA init\n"); + //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); @@ -452,7 +489,7 @@ int OV2640::begin(int dma_irq) // Set up end-of-DMA interrupt dma_channel_set_irq0_enabled(this->dma_channel, true); - printf("DMA init done\n"); + //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 @@ -463,12 +500,51 @@ int OV2640::begin(int dma_irq) // 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("IRQ init done\n"); + &vsync_irq);*/ + + uint offset = pio_add_program(pio2, &image_program); + image_program_init(pio2, 0, offset, this->config.pin_data_base); + + //prtintf("IRQ init done\n"); return 0; } +void OV2640::set_quality(int quality) { + if(quality < 0) { + quality = 0; + } else if(quality > 63) { + quality = 63; + } + this->sccb.writeRegister(0xFF, 0x00); // Select bank 0 + this->sccb.writeRegister(0x44, quality); // set quality +} + +void OV2640::capture_frame() { + 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_8); + + 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); +} //! https://forums.raspberrypi.com/viewtopic.php?t=339299 diff --git a/src/ov2640/camera.hpp b/src/ov2640/camera.hpp index 316440e..e23f28e 100644 --- a/src/ov2640/camera.hpp +++ b/src/ov2640/camera.hpp @@ -1,6 +1,7 @@ #include #include "pico/stdlib.h" #include "../../hardware_dma/include/hardware/dma.h" + // TODO: Add XCLK settings typedef struct { int pin_pwdn; /*!< GPIO pin for camera power down line */ //? Also called PWDN, or set to -1 and tie to GND @@ -8,10 +9,8 @@ typedef struct { int pin_xclk; /*!< GPIO pin for camera XCLK line */ //? in theory could be shared or perhaps ommited? int pin_sccb_sda; /*!< GPIO pin for camera SDA line */ int pin_sccb_scl; /*!< GPIO pin for camera SCL line */ - int pin_data_base; /*!< this pin + 7 consecutive will be used D0-D7 */ + int pin_data_base; /*!< this pin + 9 consecutive will be used D0-D7 PCLK HREF */ int pin_vsync; /*!< GPIO pin for camera VSYNC line */ - int pin_href; /*!< GPIO pin for camera HREF line */ - int pin_pclk; /*!< GPIO pin for camera PCLK line */ int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. */ //! Figure out the highest it can go to uint8_t sccb_ctrl; /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */ } camera_config_t; @@ -28,10 +27,14 @@ public: ~OV2640(); int begin(int dma_irq); -private: + void set_quality(int quality); + void capture_frame(); + + uint8_t fb[160*120*3]; camera_config_t config; + +private: SCCB sccb; - uint8_t* (*fb)[3]; int sm; int dma_channel; dma_channel_config dma_config; diff --git a/src/pio/image/image.pio.h b/src/pio/image/image.pio.h new file mode 100644 index 0000000..91d02b0 --- /dev/null +++ b/src/pio/image/image.pio.h @@ -0,0 +1,56 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ----- // +// image // +// ----- // + +#define image_wrap_target 0 +#define image_wrap 3 +#define image_pio_version 1 + +static const uint16_t image_program_instructions[] = { + // .wrap_target + 0x20a9, // 0: wait 1 pin, 9 + 0x20a8, // 1: wait 1 pin, 8 + 0x4008, // 2: in pins, 8 + 0x2028, // 3: wait 0 pin, 8 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program image_program = { + .instructions = image_program_instructions, + .length = 4, + .origin = -1, + .pio_version = image_pio_version, +#if PICO_PIO_VERSION > 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config image_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + image_wrap_target, offset + image_wrap); + return c; +} + +void image_program_init(PIO pio, uint sm, uint offset, uint pin_base) { + pio_sm_set_consecutive_pindirs(pio, sm, pin_base, 10, false); + pio_sm_config c = image_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin_base); + sm_config_set_in_shift(&c, false, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif +