From 76bf0c144e6b136730470168b8e2dc957712530d Mon Sep 17 00:00:00 2001 From: KubaWis Date: Thu, 6 Mar 2025 21:47:05 +0100 Subject: [PATCH] Some DMA thoughts --- src/PicoIris.cpp | 4 ++ src/ov2640/camera.cpp | 125 +++++++++++++++++++++++------------------- src/ov2640/camera.hpp | 9 ++- 3 files changed, 82 insertions(+), 56 deletions(-) diff --git a/src/PicoIris.cpp b/src/PicoIris.cpp index d8370d9..15527d9 100644 --- a/src/PicoIris.cpp +++ b/src/PicoIris.cpp @@ -8,6 +8,7 @@ #include "hardware/timer.h" #include "hardware/clocks.h" #include "hardware/uart.h" +#include "../../hardware_dma/include/hardware/dma.h" #include #include @@ -32,6 +33,9 @@ int main() { stdio_init_all(); + OV2640 left_cam = OV2640(left_eye_config); + left_cam.begin(DMA_IRQ_0); + while (true) { printf("Hello, world!\n"); sleep_ms(1000); diff --git a/src/ov2640/camera.cpp b/src/ov2640/camera.cpp index 01403b4..8c59aaf 100644 --- a/src/ov2640/camera.cpp +++ b/src/ov2640/camera.cpp @@ -22,7 +22,6 @@ static uint16_t capture_pio_opcodes[] = { 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], @@ -45,15 +44,15 @@ register_val_t OV2640_init[] = { {OV2640_REG1_COM8, 0xC0 | OV2640_COM8_BANDING | OV2640_COM8_AGC_AUTO | OV2640_COM8_EXP_AUTO}, {OV2640_REG1_COM9, OV2640_COM9_AGC_GAIN_8X | 0x08}, - {0x2C, 0x0c}, // Reserved - {0x33, 0x78}, // Reserved - {0x3A, 0x33}, // Reserved - {0x3B, 0xfB}, // Reserved - {0x3E, 0x00}, // Reserved - {0x43, 0x11}, // Reserved - {0x16, 0x10}, // Reserved - {0x4A, 0x81}, // Reserved - {0x21, 0x99}, // Reserved + {0x2C, 0x0c}, // Reserved + {0x33, 0x78}, // Reserved + {0x3A, 0x33}, // Reserved + {0x3B, 0xfB}, // Reserved + {0x3E, 0x00}, // Reserved + {0x43, 0x11}, // Reserved + {0x16, 0x10}, // Reserved + {0x4A, 0x81}, // Reserved + {0x21, 0x99}, // Reserved //{OV2640_REG1_AEW, 0x40}, // High range for AEC/AGC //{OV2640_REG1_AEB, 0x38}, // Low range for AEC/AGC {OV2640_REG1_AEW, 0xFF}, // High range for AEC/AGC @@ -230,10 +229,10 @@ register_val_t OV2640_init[] = { {OV2640_REG0_R_DVP_SP, 0x04}, // Manual DVP PCLK {0x7F, 0x00}, // Reserved //{OV2640_REG0_IMAGE_MODE, 0x00}, // YUV MSB first - {0xE5, 0x1F}, // Reserved - {0xE1, 0x67}, // Reserved - {OV2640_REG0_RESET, 0x00}, // Reset nothing? - {0xDD, 0x7F}, // Reserved + {0xE5, 0x1F}, // Reserved + {0xE1, 0x67}, // Reserved + {OV2640_REG0_RESET, 0x00}, // Reset nothing? + {0xDD, 0x7F}, // Reserved {OV2640_REG0_R_BYPASS, OV2640_R_BYPASS_DSP_ENABLE}, {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 {OV2640_REG0_RESET, OV2640_RESET_DVP}, @@ -280,8 +279,7 @@ register_val_t OV2640_init[] = { {OV2640_REG0_ZMOH, 0x78}, // OUTH low bits {OV2640_REG0_ZMHH, 0x00}, // OUTW/H high bits {OV2640_REG0_R_DVP_SP, 0x02}, // Manual DVP PCLK setting - {OV2640_REG0_RESET, 0x00} -}; // Go + {OV2640_REG0_RESET, 0x00}}; // Go // TODO: figure out how to set to JPEG and 260x260 /*OV2640_qqvga[] = {// Configure OV2640 for QQVGA output @@ -321,35 +319,41 @@ OV2640_yuv[] = { {OV2640_REG0_RESET, 0x00} }; // Go*/ -OV2640::OV2640(camera_config_t config) { +uint8_t image_buf[3][13 * 1024]; +sm_dma_config_t gpios[48]; + +OV2640::OV2640(camera_config_t config) +{ this->config = config; + this->fb = &image_buf; } -int OV2640::begin() { +int OV2640::begin(int dma_irq) +{ // Initialize peripherals for parallel+I2C camera: - + // 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); + 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 = SCCB(this->config.pin_sccb_sda, this->config.pin_sccb_scl); this->sccb.begin(this->config.sccb_ctrl); - + // 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. sleep_ms(300000); - + // Perform a soft reset (we dont wanna waste GPIO on wiring in reset pins) 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(1); // Datasheet: tS:RESET = 1 ms - + sleep_ms(1); // Datasheet: tS:RESET = 1 ms + // Init main camera settings this->sccb.writeList(OV2640_init, sizeof OV2640_init / sizeof OV2640_init[0]); @@ -359,9 +363,10 @@ int OV2640::begin() { 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); + 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, @@ -376,8 +381,6 @@ int OV2640::begin() { 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(); @@ -399,8 +402,8 @@ int OV2640::begin() { 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? - + 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, @@ -409,39 +412,51 @@ int OV2640::begin() { // 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); + &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); + + //? 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, - &iCap_vsync_irq); + &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) { +static void 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() { +} + +static void 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); + (uint8_t *)(image_buf), false); dma_hw->ints0 = 1u << archptr->dma_channel; // Clear IRQ - } +} + +//! https://forums.raspberrypi.com/viewtopic.php?t=339299 +// TODO: change to switches and experiment with state machines, check if I can kinda reserve dma's so 0-3 would indicate state machines 0-3 +/* Read the flags +uint32_t flags = read_register(PIO0_BASE, PIO_IRQ_OFFSET) +// Check the flags to determine which state machine made the request +if (flags & 0b0001) { handle_sm_0(); } +if (flags & 0b0010) { handle_sm_1(); } +if (flags & 0b0100) { handle_sm_2(); } +if (flags & 0b1000) { handle_sm_3(); } +*/ // 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 45d101e..2bbfda4 100644 --- a/src/ov2640/camera.hpp +++ b/src/ov2640/camera.hpp @@ -13,15 +13,22 @@ 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 { + uint sm; + uint dma_channel; +} sm_dma_config_t; + + class OV2640 { public: OV2640(camera_config_t config); ~OV2640(); - int begin(); + int begin(int dma_irq); private: camera_config_t config; SCCB sccb; + uint8_t* (*fb)[3]; uint sm; uint dma_channel; dma_channel_config dma_config;