Some DMA thoughts

This commit is contained in:
KubaWis 2025-03-06 21:47:05 +01:00
parent 5049ef6e9d
commit 76bf0c144e
3 changed files with 82 additions and 56 deletions

View File

@ -8,6 +8,7 @@
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/uart.h"
#include "../../hardware_dma/include/hardware/dma.h"
#include <ov2640/SCCB.hpp>
#include <ov2640/camera.hpp>
@ -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);

View File

@ -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

View File

@ -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;