Merge pull request #801 from kwagyeman/kwabena/high_res_jpeg

Fixed OV5640 Imaging Modes
This commit is contained in:
Ibrahim Abd Elkader 2020-05-20 15:51:11 +02:00 committed by GitHub
commit f4cf2117c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 129 additions and 55 deletions

View File

@ -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, &reg);
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;
}

View File

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