Getting closer to working

Sends data from camera directly to serial
JPEG dosen't work (huffman table is incorrent *???*) but raw rgb565 images work
This commit is contained in:
KubaWis 2025-03-14 15:50:58 +01:00
parent c36531c679
commit 4bb028cbc8
5 changed files with 210 additions and 73 deletions

View File

@ -12,6 +12,7 @@
#include <ov2640/SCCB.hpp> #include <ov2640/SCCB.hpp>
#include <ov2640/camera.hpp> #include <ov2640/camera.hpp>
#include "cstring"
// PIO2 is reserved for the cam data retreive functions // 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_sccb_scl = 5, /*!< GPIO pin for camera SCL line */
.pin_data_base = 6, /*!< this pin + 7 consecutive will be used D0-D7 */ .pin_data_base = 6, /*!< this pin + 7 consecutive will be used D0-D7 */
.pin_vsync = 16, /*!< GPIO pin for camera VSYNC line */ .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 .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 */ .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(); stdio_init_all();
sleep_ms(4000); sleep_ms(4000);
printf("Hello World!"); //printf("Hello World!");
printf("construct OV2640!"); //printf("construct OV2640!");
OV2640 left_cam = OV2640(left_eye_config); OV2640 left_cam = OV2640(left_eye_config);
printf("begin OV2640!"); //printf("begin OV2640!");
left_cam.begin(DMA_IRQ_0); left_cam.begin(DMA_IRQ_0);
//left_cam.capture_frame();
//fwrite(left_cam.fb, 1, sizeof(left_cam.fb), stdout);
while (true) { while (true) {
//printf("Hello, world!\n"); //printf("Hello, world!\n");
//sleep_ms(1000); //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));
} }
} }

View File

@ -76,6 +76,7 @@ void SCCB::writeRegister(uint8_t reg, uint8_t value)
}else { }else {
//pio_i2c_write_blocking(this->pio, this->sm, OV2640_ADDR, buf, 2); //pio_i2c_write_blocking(this->pio, this->sm, OV2640_ADDR, buf, 2);
} }
sleep_ms(20);
} }
void SCCB::writeList(const register_val_t *cfg, 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++) for (int i = 0; i < len; i++)
{ {
writeRegister(cfg[i].reg, cfg[i].value); 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
} }
} }

View File

@ -2,6 +2,7 @@
#include <camera.hpp> #include <camera.hpp>
#include <stdio.h> #include <stdio.h>
#include <cstring>
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "../../hardware_dma/include/hardware/dma.h" #include "../../hardware_dma/include/hardware/dma.h"
@ -10,6 +11,8 @@
#include "hardware/irq.h" #include "hardware/irq.h"
#include "hardware/pio.h" #include "hardware/pio.h"
#include "src/pio/image/image.pio.h"
// TODO: Stolen from Adafruit repo (Check if correct) // TODO: Stolen from Adafruit repo (Check if correct)
// PIO code in this table is modified at runtime so that PCLK is // 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_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_REG0_IMAGE_MODE, {OV2640_REG0_IMAGE_MODE,
OV2640_IMAGE_MODE_DVP_RGB565 | OV2640_IMAGE_MODE_BYTE_SWAP}, OV2640_IMAGE_MODE_DVP_RGB565},
{0x98, 0x00}, // Reserved {0x98, 0x00}, // Reserved
{0x99, 0x00}, // Reserved {0x99, 0x00}, // Reserved
{0x00, 0x00}, // Reserved {0x00, 0x00}, // Reserved
@ -304,11 +307,30 @@ register_val_t OV2640_qqvga[] =
{OV2640_REG0_R_DVP_SP, 0x08}, // Manual DVP PCLK setting {OV2640_REG0_R_DVP_SP, 0x08}, // Manual DVP PCLK setting
{OV2640_REG0_RESET, 0x00} {OV2640_REG0_RESET, 0x00}
}; // Go }; // 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}
};
register_val_t OV2640_rgb[] = { register_val_t OV2640_rgb[] = {
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP}, {OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565 | {OV2640_REG0_IMAGE_MODE, OV2640_IMAGE_MODE_DVP_RGB565},
OV2640_IMAGE_MODE_BYTE_SWAP},
{0xD7, 0x03}, // Mystery init values {0xD7, 0x03}, // Mystery init values
{0xE1, 0x77}, // seen in other examples {0xE1, 0x77}, // seen in other examples
{OV2640_REG0_RESET, 0x00} {OV2640_REG0_RESET, 0x00}
@ -317,39 +339,56 @@ register_val_t OV2640_yuv[] = {
{OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0 {OV2640_REG_RA_DLMT, OV2640_RA_DLMT_DSP}, // DSP bank select 0
{OV2640_REG0_RESET, OV2640_RESET_DVP}, {OV2640_REG0_RESET, OV2640_RESET_DVP},
{OV2640_REG0_IMAGE_MODE, {OV2640_REG0_IMAGE_MODE,
OV2640_IMAGE_MODE_DVP_YUV | OV2640_IMAGE_MODE_BYTE_SWAP}, OV2640_IMAGE_MODE_JPEG | OV2640_IMAGE_MODE_JPEG_HREF},
{0xD7, 0x01}, // Mystery init values {0xD7, 0x01}, // Mystery init values
{0xE1, 0x67}, // seen in other examples {0xE1, 0x67}, // seen in other examples
{OV2640_REG0_RESET, 0x00} {OV2640_REG0_RESET, 0x00}
}; // Go }; // Go
uint8_t image_buf[352*288*2];
sm_dma_config_t gpios[48]; // Stolem from esp32-camera
int dma_channel; register_val_t OV2640_jpeg[] = {
int sm; {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) OV2640::OV2640(camera_config_t config)
{ {
this->config = 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). // Pin interrupt on VSYNC calls this to start DMA transfer (unless suspended).
static void vsync_irq(uint gpio, uint32_t events) // static void vsync_irq(uint gpio, uint32_t events)
{ // {
pio_sm_clear_fifos(pio2, sm); // pio_sm_clear_fifos(pio2, sm);
dma_channel_start(dma_channel); // dma_channel_start(dma_channel);
} // }
static void dma_finish_irq() // static void dma_finish_irq()
{ // {
// Channel MUST be reconfigured each time (to reset the dest address). // // Channel MUST be reconfigured each time (to reset the dest address).
dma_channel_set_write_addr(dma_channel, // dma_channel_set_write_addr(dma_channel,
(uint8_t *)(image_buf), false); // (uint8_t *)(image_buf), false);
dma_hw->ints0 = 1u << dma_channel; // Clear IRQ // dma_hw->ints0 = 1u << dma_channel; // Clear IRQ
uint32_t data = pio2->rxf[sm]; // uint32_t data = pio2->rxf[sm];
fwrite(&data, 1, 32, stdout); // fwrite(&data, 1, 32, stdout);
} // }
int OV2640::begin(int dma_irq) int OV2640::begin(int dma_irq)
{ {
@ -364,10 +403,10 @@ int OV2640::begin(int dma_irq)
pwm_set_enabled(slice_num, true); pwm_set_enabled(slice_num, true);
// Init SCCB // 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 = SCCB(this->config.pin_sccb_sda, this->config.pin_sccb_scl);
this->sccb.begin(this->config.sccb_ctrl); this->sccb.begin(this->config.sccb_ctrl);
printf("Inited SCCB\n"); //prtintf("Inited SCCB\n");
// ENABLE AND/OR RESET CAMERA -------------------------------------------- // ENABLE AND/OR RESET CAMERA --------------------------------------------
// Unsure of camera startup time from beginning of input clock. // 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 sleep_ms(300); // Datasheet: tS:RESET = 1 ms
// Init main camera settings // 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]); this->sccb.writeList(OV2640_init, sizeof OV2640_init / sizeof OV2640_init[0]);
printf("Write list of qqvga\n"); //prtintf("Write list of qqvga\n");
this->sccb.writeList(OV2640_qqvga, sizeof OV2640_qqvga / sizeof OV2640_qqvga[0]); this->sccb.writeList(ov2640_uxga_cif, sizeof ov2640_uxga_cif / sizeof ov2640_uxga_cif[0]);
printf("Write list of rgb\n"); //prtintf("Write list of jpeg\n");
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->set_quality(10);
printf("Gpio init\n"); //prtintf("Gpio init\n");
gpio_init(this->config.pin_pclk);
gpio_set_dir(this->config.pin_pclk, GPIO_IN);
gpio_init(this->config.pin_vsync); gpio_init(this->config.pin_vsync);
gpio_set_dir(this->config.pin_vsync, GPIO_IN); gpio_set_dir(this->config.pin_vsync, GPIO_IN);
gpio_pull_down(this->config.pin_vsync); gpio_pull_down(this->config.pin_vsync);
gpio_init(this->config.pin_href); for (uint8_t i = 0; i < 10; i++)
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_init(this->config.pin_data_base + i);
gpio_set_dir(this->config.pin_data_base + i, GPIO_IN); 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, // 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. // 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 // 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) // TODO: check if this is correct (code made for rp2040 not rp2350 so it might differ)
printf("PIO init\n"); //prtintf("PIO init\n");
capture_pio_opcodes[0] |= (this->config.pin_href & 31); //! 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[1] |= (this->config.pin_pclk & 31);
capture_pio_opcodes[3] |= (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_init(pio2, this->sm, offset, &c);
pio_sm_set_enabled(pio2, this->sm, true); pio_sm_set_enabled(pio2, this->sm, true);
printf("PIO init done\n"); //prtintf("PIO init done\n");
// SET UP DMA ------------------------------------------------------------ // SET UP DMA ------------------------------------------------------------
printf("DMA init\n"); //prtintf("DMA init\n");
this->dma_channel = dma_claim_unused_channel(false); // don't panic this->dma_channel = dma_claim_unused_channel(false); // don't panic
dma_channel = this->dma_channel; dma_channel = this->dma_channel;
this->dma_config = dma_channel_get_default_config(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 // Set up end-of-DMA interrupt
dma_channel_set_irq0_enabled(this->dma_channel, true); 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??? //? setup diffrent handlers for each cam???
//? check with a button or smth if this fires all events or only one //? 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 //? 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 ------------------------------------------------ // SET UP VSYNC INTERRUPT ------------------------------------------------
//? This also could be redone with hardcoded values //? This also could be redone with hardcoded values
gpio_set_irq_enabled_with_callback(this->config.pin_vsync, GPIO_IRQ_EDGE_RISE, true, gpio_set_irq_enabled_with_callback(this->config.pin_vsync, GPIO_IRQ_EDGE_RISE, true,
&vsync_irq); &vsync_irq);*/
printf("IRQ init done\n");
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; 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 //! https://forums.raspberrypi.com/viewtopic.php?t=339299

View File

@ -1,6 +1,7 @@
#include <cstdint> #include <cstdint>
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "../../hardware_dma/include/hardware/dma.h" #include "../../hardware_dma/include/hardware/dma.h"
// TODO: Add XCLK settings // TODO: Add XCLK settings
typedef struct { typedef struct {
int pin_pwdn; /*!< GPIO pin for camera power down line */ //? Also called PWDN, or set to -1 and tie to GND 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_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_sda; /*!< GPIO pin for camera SDA line */
int pin_sccb_scl; /*!< GPIO pin for camera SCL 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_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 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 */ uint8_t sccb_ctrl; /* Select i2c controller ctrl: 0 - i2c0, 1 - i2c1, 2 - pio0, 3 - pio1, 4 - pio2 */
} camera_config_t; } camera_config_t;
@ -28,10 +27,14 @@ public:
~OV2640(); ~OV2640();
int begin(int dma_irq); int begin(int dma_irq);
private: void set_quality(int quality);
void capture_frame();
uint8_t fb[160*120*3];
camera_config_t config; camera_config_t config;
private:
SCCB sccb; SCCB sccb;
uint8_t* (*fb)[3];
int sm; int sm;
int dma_channel; int dma_channel;
dma_channel_config dma_config; dma_channel_config dma_config;

56
src/pio/image/image.pio.h Normal file
View File

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