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:
parent
c36531c679
commit
4bb028cbc8
@ -12,6 +12,7 @@
|
||||
|
||||
#include <ov2640/SCCB.hpp>
|
||||
#include <ov2640/camera.hpp>
|
||||
#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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
#include <camera.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
#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
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include <cstdint>
|
||||
#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;
|
||||
|
||||
56
src/pio/image/image.pio.h
Normal file
56
src/pio/image/image.pio.h
Normal 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user