diff --git a/src/PicoIris.cpp b/src/PicoIris.cpp index 9ec7006..d8370d9 100644 --- a/src/PicoIris.cpp +++ b/src/PicoIris.cpp @@ -9,8 +9,11 @@ #include "hardware/clocks.h" #include "hardware/uart.h" +#include #include +// PIO2 is reserved for the cam data retreive 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 diff --git a/src/ov2640/sccb.cpp b/src/ov2640/SCCB.cpp similarity index 98% rename from src/ov2640/sccb.cpp rename to src/ov2640/SCCB.cpp index 8e9ebc7..0668f73 100644 --- a/src/ov2640/sccb.cpp +++ b/src/ov2640/SCCB.cpp @@ -1,7 +1,9 @@ -#include +#include +#include #include "hardware/i2c.h" #include "pico/stdlib.h" #include "src/pio/i2c/pio_i2c.h" + SCCB::SCCB(uint8_t sda_pin, uint8_t scl_pin) { this->sda = sda_pin; this->scl = scl_pin; diff --git a/src/ov2640/sccb.hpp b/src/ov2640/SCCB.hpp similarity index 77% rename from src/ov2640/sccb.hpp rename to src/ov2640/SCCB.hpp index 5eaa618..1cc8c4a 100644 --- a/src/ov2640/sccb.hpp +++ b/src/ov2640/SCCB.hpp @@ -1,4 +1,7 @@ -#include +typedef struct { + uint8_t reg; ///< Register address + uint8_t value; ///< Value to store +} register_val_t; class SCCB { public: diff --git a/src/ov2640/camera.cpp b/src/ov2640/camera.cpp index 8ace50d..01403b4 100644 --- a/src/ov2640/camera.cpp +++ b/src/ov2640/camera.cpp @@ -1,7 +1,34 @@ #include -#include +#include #include "pico/stdlib.h" +#include "../../hardware_dma/include/hardware/dma.h" +#include "hardware/pwm.h" +#include "hardware/gpio.h" +#include "hardware/irq.h" +#include "hardware/pio.h" + +// 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. +static uint16_t capture_pio_opcodes[] = { + // Only monitor PCLK when HSYNC is high. This is more noise-immune + // than letting it fly. + 0b0010000010000000, // WAIT 1 GPIO 0 (mask in HSYNC pin before use) + 0b0010000010000000, // WAIT 1 GPIO 0 (mask in PCLK pin before use) + 0b0100000000001000, // IN PINS 8 -- 8 bits into RX FIFO + 0b0010000000000000, // WAIT 0 GPIO 0 (mask in PCLK pin before use) +}; + + +static struct pio_program cap_pio_program = { + .instructions = capture_pio_opcodes, + .length = sizeof capture_pio_opcodes / sizeof capture_pio_opcodes[0], + .origin = -1, +}; + register_val_t OV2640_init[] = { // Ideas from rp2040_ov2640-main repo // OV2640 camera initialization after reset @@ -300,7 +327,16 @@ OV2640::OV2640(camera_config_t config) { int OV2640::begin() { // Initialize peripherals for parallel+I2C camera: - //! TODO: start XCLK before SCCB init + + // XCLK generation (~20.83 MHz) + gpio_set_function(this->config.pin_xclk, GPIO_FUNC_PWM); + uint slice_num = pwm_gpio_to_slice_num(this->config.pin_xclk); + // 6 cycles (0 to 5), 125 MHz / 6 = ~20.83 MHz wrap rate + pwm_set_wrap(slice_num, 5); + pwm_set_gpio_level(this->config.pin_xclk, 3); + pwm_set_enabled(slice_num, true); + + // Init SCCB this->sccb = SCCB(this->config.pin_sccb_sda,this->config.pin_sccb_scl); this->sccb.begin(this->config.sccb_ctrl); @@ -316,8 +352,96 @@ int OV2640::begin() { // Init main camera settings this->sccb.writeList(OV2640_init, sizeof OV2640_init / sizeof OV2640_init[0]); - + + gpio_init(this->config.pin_pclk); + gpio_set_dir(this->config.pin_pclk, GPIO_IN); + gpio_init(this->config.pin_vsync); + gpio_set_dir(this->config.pin_vsync, GPIO_IN); + gpio_init(this->config.pin_href); + gpio_set_dir(this->config.pin_href, GPIO_IN); + for (uint8_t i = 0; i < 8; i++) { + gpio_init(this->config.pin_data_base + i); + gpio_set_dir(this->config.pin_data_base + i, GPIO_IN); + } + + // 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) + 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 + + // host->pins->data[0] is data bit 0. PIO code requires all 8 data be + // contiguous. + 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); + + // SET UP DMA ------------------------------------------------------------ + + this->dma_channel = dma_claim_unused_channel(false); // don't panic + + 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); // TODO: bswap should be true or false? + + // 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); + // TODO: setup diffrent handlers for each cam??? + irq_set_exclusive_handler(DMA_IRQ_0, iCap_dma_finish_irq); + irq_set_enabled(DMA_IRQ_0, true); + + // SET UP VSYNC INTERRUPT ------------------------------------------------ + + gpio_set_irq_enabled_with_callback(this->config.pin_vsync, GPIO_IRQ_EDGE_RISE, true, + &iCap_vsync_irq); + return 0; } +// TODO: figure out how to get from where does IRQs originate from +// TODO: how to transfer data? +// TODO: what? +// TODO: eep time +// TODO: make a static array with something like a hashmap containing GPIO -> sm+dma_channel mappings +// Pin interrupt on VSYNC calls this to start DMA transfer (unless suspended). +static void iCap_vsync_irq(uint gpio, uint32_t events) { + pio_sm_clear_fifos(pio2, archptr->sm); + dma_channel_start(archptr->dma_channel); + } + +// TODO: make a static array with something like a hashmap containing GPIO -> sm+dma_channel mappings + static void iCap_dma_finish_irq() { + // Channel MUST be reconfigured each time (to reset the dest address). + dma_channel_set_write_addr(archptr->dma_channel, + (uint8_t *)(capptr->getBuffer()), false); + dma_hw->ints0 = 1u << archptr->dma_channel; // Clear IRQ + } + // TODO: add all of the specific controls like brightness and all of that scheiße \ No newline at end of file diff --git a/src/ov2640/camera.hpp b/src/ov2640/camera.hpp index 2c84226..45d101e 100644 --- a/src/ov2640/camera.hpp +++ b/src/ov2640/camera.hpp @@ -1,4 +1,3 @@ - // 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 @@ -14,11 +13,6 @@ typedef struct { uint8_t sccb_ctrl; /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */ } camera_config_t; -typedef struct { - uint8_t reg; ///< Register address - uint8_t value; ///< Value to store -} register_val_t; - class OV2640 { public: OV2640(camera_config_t config); @@ -28,6 +22,9 @@ public: private: camera_config_t config; SCCB sccb; + uint sm; + uint dma_channel; + dma_channel_config dma_config; }; //? Refer to https://github.com/adafruit/Adafruit_ImageCapture/blob/main/src/Adafruit_iCap_OV2640.h to check whatever this shit below even is??