mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Merge pull request #801 from kwagyeman/kwabena/high_res_jpeg
Fixed OV5640 Imaging Modes
This commit is contained in:
commit
f4cf2117c9
@ -146,7 +146,7 @@ static const uint8_t default_regs[][3] = {
|
||||
{ 0x30, 0x2e, 0x00 },
|
||||
{ 0x43, 0x00, 0x30 },
|
||||
{ 0x50, 0x1f, 0x00 },
|
||||
{ 0x47, 0x13, 0x03 },
|
||||
{ 0x47, 0x13, 0x04 }, // { 0x47, 0x13, 0x03 },
|
||||
{ 0x44, 0x07, 0x04 },
|
||||
{ 0x46, 0x0b, 0x35 },
|
||||
{ 0x46, 0x0c, 0x22 },
|
||||
@ -331,6 +331,8 @@ static const uint8_t default_regs[][3] = {
|
||||
{ 0x3a, 0x0d, 0x08 },
|
||||
{ 0x3a, 0x14, 0x07 },
|
||||
{ 0x3a, 0x15, 0xae },
|
||||
{ 0x44, 0x01, 0x0d }, // | Read SRAM enable when blanking | Read SRAM at first blanking
|
||||
{ 0x47, 0x23, 0x01 }, // DVP JPEG Mode456 Skip Line Number
|
||||
|
||||
// End.
|
||||
|
||||
@ -420,13 +422,14 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
uint8_t reg;
|
||||
int ret = 0;
|
||||
|
||||
if (((pixformat == PIXFORMAT_BAYER) || (pixformat == PIXFORMAT_JPEG))
|
||||
if ((((pixformat == PIXFORMAT_BAYER) || (pixformat == PIXFORMAT_JPEG))
|
||||
&& ((sensor->framesize == FRAMESIZE_QQCIF) // doesn't work with bayer/jpeg, unknown why
|
||||
|| (sensor->framesize == FRAMESIZE_QQSIF) // doesn't work with bayer/jpeg, not a multiple of 8
|
||||
|| (sensor->framesize == FRAMESIZE_HQQQVGA) // doesn't work with bayer/jpeg, unknown why
|
||||
|| (sensor->framesize == FRAMESIZE_HQQVGA) // doesn't work with bayer/jpeg, unknown why
|
||||
|| (resolution[sensor->framesize][0] % 8) // w/h must be divisble by 8
|
||||
|| (resolution[sensor->framesize][1] % 8))) {
|
||||
|| (resolution[sensor->framesize][1] % 8)))
|
||||
|| (sensor->framesize == FRAMESIZE_QQQQVGA)) { // Doesn't work for anything
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -461,13 +464,6 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
ret |= cambus_readb2(&sensor->i2c, sensor->slv_addr, CLOCK_ENABLE_02, ®);
|
||||
ret |= cambus_writeb2(&sensor->i2c, sensor->slv_addr, CLOCK_ENABLE_02, (reg & 0xD7) | ((pixformat == PIXFORMAT_JPEG) ? 0x28 : 0x00));
|
||||
|
||||
// Adjust JPEG quality.
|
||||
|
||||
if (pixformat == PIXFORMAT_JPEG) {
|
||||
bool high_res = (resolution[sensor->framesize][0] > (readout_w / 2)) || (resolution[sensor->framesize][1] > (readout_h / 2));
|
||||
ret |= cambus_writeb2(&sensor->i2c, sensor->slv_addr, JPEG_CTRL07, high_res ? 0x10 : 0x4);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -478,16 +474,19 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
uint16_t w = resolution[framesize][0];
|
||||
uint16_t h = resolution[framesize][1];
|
||||
|
||||
if (((sensor->pixformat == PIXFORMAT_BAYER) || (sensor->pixformat == PIXFORMAT_JPEG))
|
||||
if ((((sensor->pixformat == PIXFORMAT_BAYER) || (sensor->pixformat == PIXFORMAT_JPEG))
|
||||
&& ((framesize == FRAMESIZE_QQCIF) // doesn't work with bayer/jpeg, unknown why
|
||||
|| (framesize == FRAMESIZE_QQSIF) // doesn't work with bayer/jpeg, not a multiple of 8
|
||||
|| (framesize == FRAMESIZE_HQQQVGA) // doesn't work with bayer/jpeg, unknown why
|
||||
|| (framesize == FRAMESIZE_HQQVGA) // doesn't work with bayer/jpeg, unknown why
|
||||
|| (w % 8) // w/h must be divisble by 8
|
||||
|| (h % 8))) {
|
||||
|| (h % 8)))
|
||||
|| (framesize == FRAMESIZE_QQQQVGA)) { // Doesn't work for anything
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 0: Clamp readout settings.
|
||||
|
||||
readout_w = IM_MAX(readout_w, w);
|
||||
readout_h = IM_MAX(readout_h, h);
|
||||
|
||||
@ -506,6 +505,12 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
sensor_div = 2;
|
||||
}
|
||||
|
||||
uint16_t sensor_hts_mul = 1;
|
||||
|
||||
if (w > 640) { // Slow Down
|
||||
sensor_hts_mul = 2;
|
||||
}
|
||||
|
||||
// Step 2: Determine horizontal and vertical start and end points.
|
||||
|
||||
uint16_t sensor_w = readout_w + DUMMY_WIDTH_BUFFER; // camera hardware needs dummy pixels to sync
|
||||
@ -520,6 +525,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
// Step 3: Determine scaling window offset.
|
||||
|
||||
float ratio = IM_MIN((readout_w / sensor_div) / ((float) w), (readout_h / sensor_div) / ((float) h));
|
||||
|
||||
uint16_t w_mul = w * ratio;
|
||||
uint16_t h_mul = h * ratio;
|
||||
uint16_t x_off = ((sensor_w / sensor_div) - w_mul) / 2;
|
||||
@ -527,7 +533,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
|
||||
// Step 4: Compute total frame time.
|
||||
|
||||
uint16_t sensor_hts = IM_MAX((sensor_w / sensor_div) + HSYNC_TIME, (SENSOR_WIDTH + HSYNC_TIME) / 2); // Fix to prevent crashing.
|
||||
uint16_t sensor_hts = IM_MAX(((sensor_w / sensor_div) * sensor_hts_mul) + HSYNC_TIME, (SENSOR_WIDTH + HSYNC_TIME) / 2); // Fix to prevent crashing.
|
||||
uint16_t sensor_vts = IM_MAX((sensor_h / sensor_div) + VYSNC_TIME, (SENSOR_HEIGHT + VYSNC_TIME) / 8); // Fix to prevent crashing.
|
||||
|
||||
uint16_t sensor_x_inc = (((sensor_div * 2) - 1) << 4) | (1 << 0); // odd[7:4]/even[3:0] pixel inc on the bayer pattern
|
||||
@ -580,10 +586,6 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
ret |= cambus_writeb2(&sensor->i2c, sensor->slv_addr, VFIFO_VSIZE_H, h >> 8);
|
||||
ret |= cambus_writeb2(&sensor->i2c, sensor->slv_addr, VFIFO_VSIZE_L, h);
|
||||
|
||||
// Adjust JPEG quality.
|
||||
|
||||
ret |= cambus_writeb2(&sensor->i2c, sensor->slv_addr, JPEG_CTRL07, (sensor_div > 1) ? 0x4 : 0x10);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
148
src/omv/sensor.c
148
src/omv/sensor.c
@ -59,7 +59,7 @@ const int resolution[][2] = {
|
||||
{64, 32 }, /* 64x32 */
|
||||
{64, 64 }, /* 64x64 */
|
||||
{128, 64 }, /* 128x64 */
|
||||
{128, 128 }, /* 128x64 */
|
||||
{128, 128 }, /* 128x128 */
|
||||
// Other
|
||||
{128, 160 }, /* LCD */
|
||||
{128, 160 }, /* QQVGA2 */
|
||||
@ -525,6 +525,8 @@ int sensor_set_pixformat(pixformat_t pixformat)
|
||||
return -1;
|
||||
}
|
||||
|
||||
systick_sleep(100); // wait for the camera to settle
|
||||
|
||||
// Set pixel format
|
||||
sensor.pixformat = pixformat;
|
||||
|
||||
@ -553,6 +555,8 @@ int sensor_set_framesize(framesize_t framesize)
|
||||
return -1;
|
||||
}
|
||||
|
||||
systick_sleep(100); // wait for the camera to settle
|
||||
|
||||
// Set framebuffer size
|
||||
sensor.framesize = framesize;
|
||||
|
||||
@ -888,10 +892,57 @@ static void sensor_check_buffsize()
|
||||
}
|
||||
}
|
||||
|
||||
// ARM Cortex-M4/M7 Processors can access memory using unaligned 32-bit reads/writes.
|
||||
void *unaligned_2_to_1_memcpy(void *dest, void *src, size_t n)
|
||||
{
|
||||
uint32_t *dest32 = (uint32_t *) dest;
|
||||
uint32_t *src32 = (uint32_t *) src;
|
||||
|
||||
#if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
|
||||
for (; n > 4; n -= 4) {
|
||||
uint32_t tmp1 = *src32++;
|
||||
uint32_t tmp2 = *src32++;
|
||||
*dest32++ = (tmp1 & 0xff) | ((tmp1 >> 8) & 0xff00) | ((tmp2 & 0xff) << 16) | ((tmp2 & 0xff0000) << 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t *dest8 = (uint8_t *) dest32;
|
||||
uint16_t *src16 = (uint16_t *) src32;
|
||||
|
||||
for (; n > 0; n -= 1) {
|
||||
*dest8++ = *src16++;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// ARM Cortex-M4/M7 Processors can access memory using unaligned 32-bit reads/writes.
|
||||
void *unaligned_memcpy(void *dest, void *src, size_t n)
|
||||
{
|
||||
#if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
|
||||
uint32_t *dest32 = (uint32_t *) dest;
|
||||
uint32_t *src32 = (uint32_t *) src;
|
||||
|
||||
for (; n > 4; n -= 4) {
|
||||
*dest32++ = *src32++;
|
||||
}
|
||||
|
||||
uint8_t *dest8 = (uint8_t *) dest32;
|
||||
uint8_t *src8 = (uint8_t *) src32;
|
||||
|
||||
for (; n > 0; n -= 1) {
|
||||
*dest8++ = *src8++;
|
||||
}
|
||||
|
||||
return dest;
|
||||
#else
|
||||
return memcpy(dest, src, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function is called back after each line transfer is complete,
|
||||
// with a pointer to the line buffer that was used. At this point the
|
||||
// DMA transfers the next line to the other half of the line buffer.
|
||||
// Note: For JPEG this function is called once (and ignored) at the end of the transfer.
|
||||
void DCMI_DMAConvCpltUser(uint32_t addr)
|
||||
{
|
||||
uint8_t *src = (uint8_t*) addr;
|
||||
@ -900,6 +951,35 @@ void DCMI_DMAConvCpltUser(uint32_t addr)
|
||||
uint16_t *src16 = (uint16_t*) addr;
|
||||
uint16_t *dst16 = (uint16_t*) dest_fb;
|
||||
|
||||
if (sensor.pixformat == PIXFORMAT_JPEG) {
|
||||
if (sensor.chip_id == OV5640_ID) {
|
||||
// JPEG MODE 4:
|
||||
//
|
||||
// The width and height are fixed in each frame. The first two bytes are valid data
|
||||
// length in every line, followed by valid image data. Dummy data (0xFF) may be used as
|
||||
// padding at each line end if the current valid image data is less than the line width.
|
||||
//
|
||||
// In this mode line holds the size of all jpeg data transferred.
|
||||
//
|
||||
uint16_t size = __REV16(*src16);
|
||||
unaligned_memcpy(MAIN_FB()->pixels + line, src16 + 1, size);
|
||||
line += size;
|
||||
} else {
|
||||
// JPEG MODE 3:
|
||||
//
|
||||
// Compression data is transmitted with programmable width. The last line width maybe
|
||||
// different from the other line (there is no dummy data). In each frame, the line
|
||||
// number may be different.
|
||||
//
|
||||
// In this mode line will be incremented by one after 262,140 Bytes have been
|
||||
// transferred. If 524,280 Bytes have been transferred line will be incremented again.
|
||||
// The DMA counter must be used to get the amount of data transferred between.
|
||||
//
|
||||
line += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip lines outside the window.
|
||||
if (line >= MAIN_FB()->y && line <= (MAIN_FB()->y + MAIN_FB()->h)) {
|
||||
if (!sensor.transpose) {
|
||||
@ -907,45 +987,26 @@ void DCMI_DMAConvCpltUser(uint32_t addr)
|
||||
case PIXFORMAT_BAYER:
|
||||
dst += (line - MAIN_FB()->y) * MAIN_FB()->w;
|
||||
src += MAIN_FB()->x;
|
||||
memcpy(dst, src, MAIN_FB()->w);
|
||||
unaligned_memcpy(dst, src, MAIN_FB()->w);
|
||||
break;
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
dst += (line - MAIN_FB()->y) * MAIN_FB()->w;
|
||||
if (sensor.gs_bpp == 1) {
|
||||
// 1BPP GRAYSCALE.
|
||||
src += MAIN_FB()->x;
|
||||
memcpy(dst, src, MAIN_FB()->w);
|
||||
unaligned_memcpy(dst, src, MAIN_FB()->w);
|
||||
} else {
|
||||
uint32_t tmp1, tmp2, pix, *s, *d;
|
||||
src16 += MAIN_FB()->x;
|
||||
s = (uint32_t *)src16;
|
||||
d = (uint32_t *)dst;
|
||||
// Extract Y channel from YUV.
|
||||
if (((uint32_t)dst & 3) == 0 && ((uint32_t)src16 & 3) == 0) {
|
||||
for (int i = MAIN_FB()->w; i>=4; i-=4) {
|
||||
// destination mem is cached; coalesce the writes to improve throughput
|
||||
tmp1 = *s++;
|
||||
tmp2 = *s++;
|
||||
pix = tmp1 & 0xff; // merge 4 pixels to not saturate CPU write buffer
|
||||
pix |= ((tmp1 >> 8) & 0xff00);
|
||||
pix |= ((tmp2 & 0xff) << 16);
|
||||
pix |= ((tmp2 & 0xff0000) << 8);
|
||||
*d++ = pix;
|
||||
}
|
||||
} else {
|
||||
for (int i = MAIN_FB()->w; i; i--) {
|
||||
*dst++ = (uint8_t)*src16++; // low byte is Y channel
|
||||
}
|
||||
}
|
||||
src16 += MAIN_FB()->x;
|
||||
unaligned_2_to_1_memcpy(dst, src16, MAIN_FB()->w);
|
||||
}
|
||||
break;
|
||||
case PIXFORMAT_YUV422:
|
||||
case PIXFORMAT_RGB565:
|
||||
dst16 += (line - MAIN_FB()->y) * MAIN_FB()->w;
|
||||
src16 += MAIN_FB()->x;
|
||||
memcpy(dst16, src16, MAIN_FB()->w * sizeof(uint16_t));
|
||||
unaligned_memcpy(dst16, src16, MAIN_FB()->w * sizeof(uint16_t));
|
||||
break;
|
||||
case PIXFORMAT_JPEG:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -986,7 +1047,6 @@ void DCMI_DMAConvCpltUser(uint32_t addr)
|
||||
dst16 += h;
|
||||
}
|
||||
break;
|
||||
case PIXFORMAT_JPEG:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1049,9 +1109,17 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
|
||||
addr = (uint32_t) &_line_buf;
|
||||
break;
|
||||
case PIXFORMAT_JPEG:
|
||||
// Sensor has hardware JPEG set max frame size.
|
||||
length = MAX_XFER_SIZE;
|
||||
addr = (uint32_t) (MAIN_FB()->pixels);
|
||||
if (sensor->chip_id == OV5640_ID) {
|
||||
// The JPEG image needs to be transferred to the line buffer.
|
||||
// There is no small limit on the amount of data transferred.
|
||||
length = w * h;
|
||||
addr = (uint32_t) &_line_buf;
|
||||
} else {
|
||||
// The JPEG image will be directly transferred to the frame buffer.
|
||||
// The DCMI hardware can transfer up to 524,280 bytes.
|
||||
length = MAX_XFER_SIZE * 2;
|
||||
addr = (uint32_t) (MAIN_FB()->pixels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
@ -1080,7 +1148,7 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
if ((sensor->pixformat == PIXFORMAT_JPEG) && (sensor->chip_id != OV5640_ID)) {
|
||||
// Start a regular transfer
|
||||
HAL_DCMI_Start_DMA(&DCMIHandle,
|
||||
DCMI_MODE_SNAPSHOT, addr, length/4);
|
||||
@ -1137,13 +1205,17 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
|
||||
break;
|
||||
case PIXFORMAT_JPEG:
|
||||
// Read the number of data items transferred
|
||||
MAIN_FB()->bpp = ((MAX_XFER_SIZE/4) - __HAL_DMA_GET_COUNTER(&DMAHandle))*4;
|
||||
#if defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
|
||||
// In JPEG mode, the DMA uses the frame buffer memory directly instead of the line buffer, which is
|
||||
// located in a cacheable region and therefore must be invalidated before the CPU can access it again.
|
||||
// Note: The frame buffer address is 32-byte aligned, and the size is a multiple of 32-bytes for all boards.
|
||||
SCB_InvalidateDCache_by_Addr((uint32_t*)MAIN_FB()->pixels, OMV_RAW_BUF_SIZE);
|
||||
#endif
|
||||
if (sensor->chip_id == OV5640_ID) {
|
||||
MAIN_FB()->bpp = line;
|
||||
} else {
|
||||
MAIN_FB()->bpp = (line * MAX_XFER_SIZE) + ((MAX_XFER_SIZE/4) - __HAL_DMA_GET_COUNTER(&DMAHandle))*4;
|
||||
#if defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
|
||||
// In JPEG mode, the DMA uses the frame buffer memory directly instead of the line buffer, which is
|
||||
// located in a cacheable region and therefore must be invalidated before the CPU can access it again.
|
||||
// Note: The frame buffer address is 32-byte aligned, and the size is a multiple of 32-bytes for all boards.
|
||||
SCB_InvalidateDCache_by_Addr((uint32_t*)MAIN_FB()->pixels, OMV_RAW_BUF_SIZE);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user