Optimized Sensor Driver for Double FPS

This commit is contained in:
Kwabena W. Agyeman 2020-05-23 14:03:05 -07:00
parent 8feaf6df78
commit 1fe9d62958
5 changed files with 271 additions and 123 deletions

View File

@ -332,7 +332,7 @@ static const uint8_t default_regs[][3] = {
{ 0x3a, 0x14, 0x07 }, { 0x3a, 0x14, 0x07 },
{ 0x3a, 0x15, 0xae }, { 0x3a, 0x15, 0xae },
{ 0x44, 0x01, 0x0d }, // | Read SRAM enable when blanking | Read SRAM at first blanking { 0x44, 0x01, 0x0d }, // | Read SRAM enable when blanking | Read SRAM at first blanking
{ 0x47, 0x23, 0x01 }, // DVP JPEG Mode456 Skip Line Number { 0x47, 0x23, 0x03 }, // DVP JPEG Mode456 Skip Line Number
// End. // End.

View File

@ -8,20 +8,17 @@
* *
* Sensor abstraction layer. * Sensor abstraction layer.
*/ */
#include <stdlib.h>
#include <string.h>
#include "mp.h" #include "mp.h"
#include "irq.h" #include "irq.h"
#include "cambus.h" #include "cambus.h"
#include "ov9650.h"
#include "ov2640.h" #include "ov2640.h"
#include "ov5640.h"
#include "ov7725.h" #include "ov7725.h"
#include "ov7690.h" #include "ov7690.h"
#include "ov5640.h" #include "ov9650.h"
#include "mt9v034.h" #include "mt9v034.h"
#include "lepton.h" #include "lepton.h"
#include "hm01b0.h" #include "hm01b0.h"
#include "sensor.h"
#include "systick.h" #include "systick.h"
#include "framebuffer.h" #include "framebuffer.h"
#include "omv_boardconfig.h" #include "omv_boardconfig.h"
@ -33,9 +30,10 @@ TIM_HandleTypeDef TIMHandle = {0};
DMA_HandleTypeDef DMAHandle = {0}; DMA_HandleTypeDef DMAHandle = {0};
DCMI_HandleTypeDef DCMIHandle = {0}; DCMI_HandleTypeDef DCMIHandle = {0};
static volatile int line = 0;
extern uint8_t _line_buf; extern uint8_t _line_buf;
static uint8_t *dest_fb = NULL; static uint8_t *dest_fb = NULL;
static volatile int line = 0;
static volatile bool waiting_for_data = false;
const int resolution[][2] = { const int resolution[][2] = {
{0, 0 }, {0, 0 },
@ -118,6 +116,40 @@ static int extclk_config(int frequency)
return 0; return 0;
} }
static int dma_config()
{
// DMA Stream configuration
DMAHandle.Instance = DMA2_Stream1; /* Select the DMA instance */
#if defined(MCU_SERIES_H7)
DMAHandle.Init.Request = DMA_REQUEST_DCMI; /* DMA Channel */
#else
DMAHandle.Init.Channel = DMA_CHANNEL_1; /* DMA Channel */
#endif
DMAHandle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* Peripheral to memory transfer */
DMAHandle.Init.MemInc = DMA_MINC_ENABLE; /* Memory increment mode Enable */
DMAHandle.Init.PeriphInc = DMA_PINC_DISABLE; /* Peripheral increment mode Enable */
DMAHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* Peripheral data alignment : Word */
DMAHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; /* Memory data alignment : Word */
DMAHandle.Init.Mode = DMA_NORMAL; /* Normal DMA mode */
DMAHandle.Init.Priority = DMA_PRIORITY_HIGH; /* Priority level : high */
DMAHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE; /* FIFO mode enabled */
DMAHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /* FIFO threshold full */
DMAHandle.Init.MemBurst = DMA_MBURST_INC4; /* Memory burst */
DMAHandle.Init.PeriphBurst = DMA_PBURST_SINGLE; /* Peripheral burst */
// Initialize the DMA stream
HAL_DMA_DeInit(&DMAHandle);
if (HAL_DMA_Init(&DMAHandle) != HAL_OK) {
// Initialization Error
return -1;
}
// Configure and enable DMA IRQ Channel
NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21);
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
return 0;
}
static int dcmi_config(uint32_t jpeg_mode) static int dcmi_config(uint32_t jpeg_mode)
{ {
// DCMI configuration // DCMI configuration
@ -131,7 +163,7 @@ static int dcmi_config(uint32_t jpeg_mode)
// PXCLK clock polarity // PXCLK clock polarity
DCMIHandle.Init.PCKPolarity = SENSOR_HW_FLAGS_GET(&sensor, SENSOR_HW_FLAGS_PIXCK) ? DCMIHandle.Init.PCKPolarity = SENSOR_HW_FLAGS_GET(&sensor, SENSOR_HW_FLAGS_PIXCK) ?
DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING; DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING;
// Setup capture parameters.
DCMIHandle.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE; // Enable Hardware synchronization DCMIHandle.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE; // Enable Hardware synchronization
DCMIHandle.Init.CaptureRate = DCMI_CR_ALL_FRAME; // Capture rate all frames DCMIHandle.Init.CaptureRate = DCMI_CR_ALL_FRAME; // Capture rate all frames
DCMIHandle.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B; // Capture 8 bits on every pixel clock DCMIHandle.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B; // Capture 8 bits on every pixel clock
@ -159,43 +191,23 @@ static int dcmi_config(uint32_t jpeg_mode)
return 0; return 0;
} }
static int dma_config() static void abort_dcmi()
{ {
// DMA Stream configuration DCMI->CR &= ~DCMI_CR_ENABLE;
DMAHandle.Instance = DMA2_Stream1; /* Select the DMA instance */ HAL_DMA_Abort(&DMAHandle);
#if defined(MCU_SERIES_H7) }
DMAHandle.Init.Request = DMA_REQUEST_DCMI; /* DMA Channel */
#else
DMAHandle.Init.Channel = DMA_CHANNEL_1; /* DMA Channel */
#endif
DMAHandle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* Peripheral to memory transfer */
DMAHandle.Init.MemInc = DMA_MINC_ENABLE; /* Memory increment mode Enable */
DMAHandle.Init.PeriphInc = DMA_PINC_DISABLE; /* Peripheral increment mode Enable */
DMAHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* Peripheral data alignment : Word */
DMAHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; /* Memory data alignment : Word */
DMAHandle.Init.Mode = DMA_NORMAL; /* Normal DMA mode */
DMAHandle.Init.Priority = DMA_PRIORITY_HIGH; /* Priority level : high */
DMAHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE; /* FIFO mode enabled */
DMAHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /* FIFO threshold full */
DMAHandle.Init.MemBurst = DMA_MBURST_INC4; /* Memory burst */
DMAHandle.Init.PeriphBurst = DMA_PBURST_SINGLE; /* Peripheral burst */
// Configure and disable DMA IRQ Channel void check_abort_dcmi()
NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21); {
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); if (DCMI->CR & DCMI_CR_ENABLE) {
abort_dcmi();
// Initialize the DMA stream
HAL_DMA_DeInit(&DMAHandle);
if (HAL_DMA_Init(&DMAHandle) != HAL_OK) {
// Initialization Error
return -1;
} }
return 0;
} }
void sensor_init0() void sensor_init0()
{ {
check_abort_dcmi();
// Save fb_enabled flag state // Save fb_enabled flag state
int fb_enabled = JPEG_FB()->enabled; int fb_enabled = JPEG_FB()->enabled;
@ -427,6 +439,8 @@ int sensor_init()
int sensor_reset() int sensor_reset()
{ {
check_abort_dcmi();
// Reset the sesnor state // Reset the sesnor state
sensor.sde = 0; sensor.sde = 0;
sensor.pixformat = 0; sensor.pixformat = 0;
@ -453,9 +467,6 @@ int sensor_reset()
return -1; return -1;
} }
// Just in case there's a running DMA request.
HAL_DMA_Abort(&DMAHandle);
// Disable VSYNC EXTI IRQ // Disable VSYNC EXTI IRQ
HAL_NVIC_DisableIRQ(DCMI_VSYNC_IRQN); HAL_NVIC_DisableIRQ(DCMI_VSYNC_IRQN);
return 0; return 0;
@ -468,6 +479,8 @@ int sensor_get_id()
int sensor_sleep(int enable) int sensor_sleep(int enable)
{ {
check_abort_dcmi();
if (sensor.sleep == NULL if (sensor.sleep == NULL
|| sensor.sleep(&sensor, enable) != 0) { || sensor.sleep(&sensor, enable) != 0) {
// Operation not supported // Operation not supported
@ -478,6 +491,8 @@ int sensor_sleep(int enable)
int sensor_shutdown(int enable) int sensor_shutdown(int enable)
{ {
check_abort_dcmi();
if (enable) { if (enable) {
DCMI_PWDN_HIGH(); DCMI_PWDN_HIGH();
} else { } else {
@ -519,6 +534,8 @@ int sensor_set_pixformat(pixformat_t pixformat)
return -1; return -1;
} }
check_abort_dcmi();
if (sensor.set_pixformat == NULL if (sensor.set_pixformat == NULL
|| sensor.set_pixformat(&sensor, pixformat) != 0) { || sensor.set_pixformat(&sensor, pixformat) != 0) {
// Operation not supported // Operation not supported
@ -548,6 +565,8 @@ int sensor_set_framesize(framesize_t framesize)
return 0; return 0;
} }
check_abort_dcmi();
// Call the sensor specific function // Call the sensor specific function
if (sensor.set_framesize == NULL if (sensor.set_framesize == NULL
|| sensor.set_framesize(&sensor, framesize) != 0) { || sensor.set_framesize(&sensor, framesize) != 0) {
@ -824,7 +843,10 @@ int sensor_set_lens_correction(int enable, int radi, int coef)
int sensor_ioctl(int request, ... /* arg */) int sensor_ioctl(int request, ... /* arg */)
{ {
check_abort_dcmi();
int ret = -1; int ret = -1;
if (sensor.ioctl != NULL) { if (sensor.ioctl != NULL) {
va_list ap; va_list ap;
va_start(ap, request); va_start(ap, request);
@ -832,6 +854,7 @@ int sensor_ioctl(int request, ... /* arg */)
ret = sensor.ioctl(&sensor, request, ap); ret = sensor.ioctl(&sensor, request, ap);
va_end(ap); va_end(ap);
} }
return ret; return ret;
} }
@ -898,6 +921,7 @@ void *unaligned_2_to_1_memcpy(void *dest, void *src, size_t n)
uint32_t *dest32 = (uint32_t *) dest; uint32_t *dest32 = (uint32_t *) dest;
uint32_t *src32 = (uint32_t *) src; uint32_t *src32 = (uint32_t *) src;
// TODO: Make this faster using only 32-bit aligned reads/writes with data shifting.
#if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7) #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
for (; n > 4; n -= 4) { for (; n > 4; n -= 4) {
uint32_t tmp1 = *src32++; uint32_t tmp1 = *src32++;
@ -919,6 +943,7 @@ void *unaligned_2_to_1_memcpy(void *dest, void *src, size_t n)
// ARM Cortex-M4/M7 Processors can access memory using unaligned 32-bit reads/writes. // ARM Cortex-M4/M7 Processors can access memory using unaligned 32-bit reads/writes.
void *unaligned_memcpy(void *dest, void *src, size_t n) void *unaligned_memcpy(void *dest, void *src, size_t n)
{ {
// TODO: Make this faster using only 32-bit aligned reads/writes with data shifting.
#if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7) #if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) || defined(MCU_SERIES_H7)
uint32_t *dest32 = (uint32_t *) dest; uint32_t *dest32 = (uint32_t *) dest;
uint32_t *src32 = (uint32_t *) src; uint32_t *src32 = (uint32_t *) src;
@ -940,13 +965,38 @@ void *unaligned_memcpy(void *dest, void *src, size_t n)
#endif #endif
} }
// Stop allowing new data in on the end of the frame and let snapshot know that the frame has been
// received. Note that DCMI_DMAConvCpltUser() is called before DCMI_IT_FRAME is enabled by
// DCMI_DMAXferCplt() so this means that the last line of data is *always* transferred before
// waiting_for_data is set to false.
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
waiting_for_data = false;
}
// This function is called back after each line transfer is complete, // 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 // 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. // DMA transfers the next line to the other half of the line buffer.
void DCMI_DMAConvCpltUser(uint32_t addr) void DCMI_DMAConvCpltUser(uint32_t addr)
{ {
// If snapshot was not already waiting to receive data then we have missed this frame and have
// to drop it. So, abort this and future transfers. Snapshot will restart the process.
if (!waiting_for_data) {
DCMI->CR &= ~DCMI_CR_ENABLE;
HAL_DMA_Abort_IT(&DMAHandle);
return;
}
// We are transferring the image from the DCMI hardware to line buffers so that we have more
// control to post process the image data before writing it to the frmae buffer. This requires
// more CPU, but, allows us to crop and rotate the image as the data is received.
// Additionally, the line buffers act as very large fifos which hide SDRAM memory access times
// on the OpenMV Cam H7 Plus. When SDRAM refreshes the row you are trying to write to the fifo
// depth on the DCMI hardware and DMA hardware is not enough to prevent data loss.
uint8_t *src = (uint8_t*) addr; uint8_t *src = (uint8_t*) addr;
uint8_t *dst = dest_fb; uint8_t *dst = (uint8_t*) dest_fb;
uint16_t *src16 = (uint16_t*) addr; uint16_t *src16 = (uint16_t*) addr;
uint16_t *dst16 = (uint16_t*) dest_fb; uint16_t *dst16 = (uint16_t*) dest_fb;
@ -959,7 +1009,11 @@ void DCMI_DMAConvCpltUser(uint32_t addr)
// length in every line, followed by valid image data. Dummy data (0xFF) may be used as // 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. // 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. // In this mode `line` holds the size of all jpeg data transferred.
//
// Note: We are using this mode for the OV5640 because it allows us to use the line
// buffers to fifo the JPEG image data input so we can handle SDRAM refresh hiccups
// that will cause data loss if we make the DMA hardware write directly to the FB.
// //
uint16_t size = __REV16(*src16); uint16_t size = __REV16(*src16);
unaligned_memcpy(MAIN_FB()->pixels + line, src16 + 1, size); unaligned_memcpy(MAIN_FB()->pixels + line, src16 + 1, size);
@ -971,16 +1025,21 @@ void DCMI_DMAConvCpltUser(uint32_t addr)
// different from the other line (there is no dummy data). In each frame, the line // different from the other line (there is no dummy data). In each frame, the line
// number may be different. // number may be different.
// //
// In this mode line will be incremented by one after 262,140 Bytes have been // 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. // 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. // The DMA counter must be used to get the amount of data transferred between.
// //
// Note: In this mode the JPEG image data is written directly to the frame buffer. This
// is not optimal. However, it works okay for the OV2640 since the PCLK is much lower
// than the OV5640 PCLK. The OV5640 drops data in this mode. Hence using mode 4 above.
//
line += 1; line += 1;
} }
return; return;
} }
// Skip lines outside the window. // Implement per line, per pixel cropping, and image transposing (for image rotation) in
// in software using the CPU to transfer the image from the line buffers to the frame buffer.
if (line >= MAIN_FB()->y && line <= (MAIN_FB()->y + MAIN_FB()->h)) { if (line >= MAIN_FB()->y && line <= (MAIN_FB()->y + MAIN_FB()->h)) {
if (!sensor.transpose) { if (!sensor.transpose) {
switch (sensor.pixformat) { switch (sensor.pixformat) {
@ -1060,10 +1119,18 @@ void DCMI_DMAConvCpltUser(uint32_t addr)
// uses the DCMI and DMA to capture frames and each line is processed in the DCMI_DMAConvCpltUser function. // uses the DCMI and DMA to capture frames and each line is processed in the DCMI_DMAConvCpltUser function.
int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_cb) int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_cb)
{ {
uint32_t frame = 0;
bool streaming = (streaming_cb != NULL); // Streaming mode. bool streaming = (streaming_cb != NULL); // Streaming mode.
bool doublebuf = false; // Use double buffers in streaming mode. uint32_t frame = 0;
uint32_t addr, length, tick_start;
// In streaming mode the image pointer must be valid.
if (streaming) {
if (image == NULL) {
return -1;
}
// Clear the first image in to not trigger the streaming_cb in double buffer mode.
image->pixels = NULL;
}
// Compress the framebuffer for the IDE preview, only if it's not the first frame, // Compress the framebuffer for the IDE preview, only if it's not the first frame,
// the framebuffer is enabled and the image sensor does not support JPEG encoding. // the framebuffer is enabled and the image sensor does not support JPEG encoding.
@ -1089,9 +1156,15 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
// done in the line function using the diemensions stored in MAIN_FB()->x,y,w,h. // done in the line function using the diemensions stored in MAIN_FB()->x,y,w,h.
uint32_t w = resolution[sensor->framesize][0]; uint32_t w = resolution[sensor->framesize][0];
uint32_t h = resolution[sensor->framesize][1]; uint32_t h = resolution[sensor->framesize][1];
uint32_t length, addr;
// Setup the size and address of the transfer // Setup the size and address of the transfer
switch (sensor->pixformat) { switch (sensor->pixformat) {
case PIXFORMAT_GRAYSCALE:
// 1/2BPP Grayscale.
length = (w * h * sensor->gs_bpp);
addr = (uint32_t) &_line_buf;
break;
case PIXFORMAT_RGB565: case PIXFORMAT_RGB565:
case PIXFORMAT_YUV422: case PIXFORMAT_YUV422:
// RGB/YUV read 2 bytes per pixel. // RGB/YUV read 2 bytes per pixel.
@ -1103,15 +1176,10 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
length = (w * h * 1); length = (w * h * 1);
addr = (uint32_t) &_line_buf; addr = (uint32_t) &_line_buf;
break; break;
case PIXFORMAT_GRAYSCALE:
// 1/2BPP Grayscale.
length = (w * h * sensor->gs_bpp);
addr = (uint32_t) &_line_buf;
break;
case PIXFORMAT_JPEG: case PIXFORMAT_JPEG:
if (sensor->chip_id == OV5640_ID) { if (sensor->chip_id == OV5640_ID) {
// The JPEG image needs to be transferred to the line buffer. // The JPEG image needs to be transferred to the line buffer.
// There is no small limit on the amount of data transferred. // There is no limit on the amount of data transferred.
length = w * h; length = w * h;
addr = (uint32_t) &_line_buf; addr = (uint32_t) &_line_buf;
} else { } else {
@ -1125,73 +1193,136 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
return -1; return -1;
} }
if (streaming_cb) {
image->pixels = NULL;
}
// If two frames fit in ram, use double buffering in streaming mode. // If two frames fit in ram, use double buffering in streaming mode.
doublebuf = ((length*2) <= OMV_RAW_BUF_SIZE); bool doublebuf = ((length*2) <= OMV_RAW_BUF_SIZE);
do { do {
// Clear line counter // Clear the line counter variable before we allow more data to be received.
line = 0; line = 0;
// Snapshot start tick // If DCMI_DMAConvCpltUser() happens before waiting_for_data = true; below then the
tick_start = HAL_GetTick(); // transfer is stopped and it will be re-enabled again right afterwards. We know the
// transfer was stopped by checking DCMI_CR_ENABLE.
// Enable DMA IRQ waiting_for_data = true;
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
// We will be in one of the following states now:
// 1. No transfer is currently running right now and DCMI_CR_ENABLE is not set.
// 2. A transfer is running and we are waiting for the data to be received.
// We are not using DCMI_CR_CAPTURE because while this bit when cleared stops the capture...
// It does not actually go low. DCMI_CR_ENABLE stops the capture when cleared and stays low.
//
// When DCMI_CR_ENABLE is cleared during a DCMI transfer the hardware will automatically
// wait for the start of the next frame when it's re-enabled again below. So, we do not
// need to wait till there's no frame happening before enabling.
if (!(DCMI->CR & DCMI_CR_ENABLE)) {
// Note that HAL_DCMI_Start_DMA and HAL_DCMI_Start_DMA_MB are effectively the same
// method. The only difference between them is how large the DMA transfer size gets
// set at. For both of them DMA doesn't actually care how much data the DCMI hardware
// generates. It's just trying to move fixed size DMA transfers from the DCMI hardware
// to one memory address or another memory address. After transfering X bytes to one
// address it will switch to the next address and transfer X bytes again. Both of these
// methods set the addresses right after each other. So, effectively DMA is just writing
// data to a circular buffer with an interrupt every time 1/2 of it is written.
if ((sensor->pixformat == PIXFORMAT_JPEG) && (sensor->chip_id != OV5640_ID)) {
// Start a transfer where the whole frame buffer is located where the DMA is writing
// data to. We only use this for JPEG mode for the OV2640. Since we don't know the
// line size of data being transfered we just examine how much data was transferred
// once DMA hardware stalls waiting for data. Note that because we are writing
// directly to the frame buffer we do not have the option of aborting the transfer
// if we are not ready to move data from a line buffer to the frame buffer.
HAL_DCMI_Start_DMA(&DCMIHandle,
DCMI_MODE_SNAPSHOT, addr, length/4);
// In this mode the DMA hardware is just treating the frame buffer as two large
// DMA buffers. At the end of the frame less data may be transferred than requested.
} else {
// Start a multibuffer transfer (line by line). The DMA hardware will ping-pong
// transferring data between the uncached line buffers. Since data is continously
// being captured the ping-ponging will stop at the end of the frame and then
// continue when the next frame starts.
HAL_DCMI_Start_DMA_MB(&DCMIHandle,
DCMI_MODE_CONTINUOUS, addr, length/4, h);
}
}
// Let the camera know we want to trigger it now.
#if defined(DCMI_FSYNC_PIN) #if defined(DCMI_FSYNC_PIN)
if (SENSOR_HW_FLAGS_GET(sensor, SENSOR_HW_FLAGS_FSYNC)) { if (SENSOR_HW_FLAGS_GET(sensor, SENSOR_HW_FLAGS_FSYNC)) {
DCMI_FSYNC_HIGH(); DCMI_FSYNC_HIGH();
} }
#endif #endif
if ((sensor->pixformat == PIXFORMAT_JPEG) && (sensor->chip_id != OV5640_ID)) { // DCMI_DMAConvCpltUser() will start triggering now. Since waiting_for_data = true; the
// Start a regular transfer // data will be transferred to the frame buffer.
HAL_DCMI_Start_DMA(&DCMIHandle,
DCMI_MODE_SNAPSHOT, addr, length/4);
} else {
// Start a multibuffer transfer (line by line)
HAL_DCMI_Start_DMA_MB(&DCMIHandle,
DCMI_MODE_SNAPSHOT, addr, length/4, h);
}
// Before we wait for the next frame try to get some work done. If we are in double buffer
// mode then we can start processing the previous image buffer.
if (streaming_cb && doublebuf && image->pixels != NULL) { if (streaming_cb && doublebuf && image->pixels != NULL) {
// Call streaming callback function with previous frame. // Call streaming callback function with previous frame.
// Note: Image pointer should Not be NULL in streaming mode. // Note: Image pointer should Not be NULL in streaming mode.
streaming = streaming_cb(image); streaming = streaming_cb(image);
} }
// Wait for frame // In camera sensor JPEG mode 4 we will not necessarily see every line in the frame and
while ((DCMI->CR & DCMI_CR_CAPTURE) != 0) { // in camera sensor JPEG mode 3 we will definately not see every line in the frame. Given
// Wait for interrupt // this, we need to enable the end of frame interrupt before we have finished necessarily
// finished transferring all JEPG data. This works as long as the end of the frame comes
// much later after all JPEG data has been transferred. If this is violated the JPEG image
// will be corrupted.
if (DCMI->CR & DCMI_JPEG_ENABLE) {
__HAL_DCMI_ENABLE_IT(&DCMIHandle, DCMI_IT_FRAME);
}
// Wait for the frame data. __WFI() below will exit right on time because of DCMI_IT_FRAME.
// While waiting SysTick will trigger allowing us to timeout.
for (uint32_t tick_start = HAL_GetTick(); waiting_for_data; ) {
__WFI(); __WFI();
// If we haven't exited this loop before the timeout then we need to abort the transfer.
if ((HAL_GetTick() - tick_start) >= 3000) { if ((HAL_GetTick() - tick_start) >= 3000) {
// Sensor timeout, most likely a HW issue. waiting_for_data = false;
// Abort the DMA request. abort_dcmi();
HAL_DMA_Abort(&DMAHandle);
return -1; return -1;
} }
} }
// We have to abort the JPEG data transfer since it will be stuck waiting for data.
// line will contain how many transfers we completed.
// The DMA counter must be used to get the number of remaining words to be transferred.
if ((sensor->pixformat == PIXFORMAT_JPEG) && (sensor->chip_id != OV5640_ID)) {
abort_dcmi();
}
// We're done receiving data.
#if defined(DCMI_FSYNC_PIN) #if defined(DCMI_FSYNC_PIN)
if (SENSOR_HW_FLAGS_GET(sensor, SENSOR_HW_FLAGS_FSYNC)) { if (SENSOR_HW_FLAGS_GET(sensor, SENSOR_HW_FLAGS_FSYNC)) {
DCMI_FSYNC_LOW(); DCMI_FSYNC_LOW();
} }
#endif #endif
// Abort DMA transfer. // After the above loop we have received all data in the frame. The DCMI hardware is left
// Note: In JPEG mode the DMA will still be waiting for data since // running to look for the start of the next frame which it needs to sync to to capture
// the max frame size is set, so we need to abort the DMA transfer. // data. If it misses the start of the frame then the DCMI hardware will not capture that
HAL_DMA_Abort(&DMAHandle); // frame. Assuming our processing is fast enough to start waiting for data again before
// DCMI_DMAConvCpltUser() is called we can receive the next frame. If we are not fast
// enough DCMI_DMAConvCpltUser() will automatically abort the transfer on being called.
//
// In the case of the OV2640 in JPEG mode since we are writing to the main FB we do not
// put the DCMI hardware into continous mode. So, we will drop frames more easily in that
// mode and may be able to only achieve 1/2 the max FPS.
// Disable DMA IRQ //
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); // Next, prepare the frame buffer w/h/bpp values given the image type.
//
// Fix the BPP // Fix resolution if transposed.
if (sensor->transpose) {
MAIN_FB()->w = MAIN_FB()->v; // v==h -> w
MAIN_FB()->h = MAIN_FB()->u; // u==w -> h
}
// Fix the BPP.
switch (sensor->pixformat) { switch (sensor->pixformat) {
case PIXFORMAT_GRAYSCALE: case PIXFORMAT_GRAYSCALE:
MAIN_FB()->bpp = 1; MAIN_FB()->bpp = 1;
@ -1204,10 +1335,13 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
MAIN_FB()->bpp = 3; MAIN_FB()->bpp = 3;
break; break;
case PIXFORMAT_JPEG: case PIXFORMAT_JPEG:
// Read the number of data items transferred
if (sensor->chip_id == OV5640_ID) { if (sensor->chip_id == OV5640_ID) {
// Line contains the sum of all the bytes transferred from the line buffers
// while in DCMI_DMAConvCpltUser().
MAIN_FB()->bpp = line; MAIN_FB()->bpp = line;
} else { } else {
// line contains the number of MAX_XFER_SIZE transfers completed. To get the number of bytes transferred
// within a transfer we have to look at the DMA counter and see how much data was moved.
MAIN_FB()->bpp = (line * MAX_XFER_SIZE) + ((MAX_XFER_SIZE/4) - __HAL_DMA_GET_COUNTER(&DMAHandle))*4; 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) #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 // In JPEG mode, the DMA uses the frame buffer memory directly instead of the line buffer, which is
@ -1221,11 +1355,9 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
break; break;
} }
// Fix resolution if transposed. //
if (sensor->transpose) { // Finally, return an image object.
MAIN_FB()->w = MAIN_FB()->v; // v==h -> w //
MAIN_FB()->h = MAIN_FB()->u; // u==w -> h
}
// Set the user image. // Set the user image.
if (image != NULL) { if (image != NULL) {
@ -1251,7 +1383,9 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, streaming_cb_t streaming_c
// Next frame will be transfered to the first half. // Next frame will be transfered to the first half.
dest_fb = MAIN_FB()->pixels; dest_fb = MAIN_FB()->pixels;
} }
frame ^= 1; // Switch frame buffers.
// Switch frame buffers.
frame ^= 1;
} }
} }
} }

View File

@ -317,8 +317,8 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
hdcmi->DMA_Handle->XferAbortCallback = NULL; hdcmi->DMA_Handle->XferAbortCallback = NULL;
/* Reset transfer counters value */ /* Reset transfer counters value */
hdcmi->XferCount = 0U; hdcmi->XferCount = 1U;
hdcmi->XferTransferNumber = 0U; hdcmi->XferTransferNumber = 1U;
if(Length <= 0xFFFFU) if(Length <= 0xFFFFU)
{ {
@ -343,7 +343,6 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
} }
/* Update DCMI counter and transfer number*/ /* Update DCMI counter and transfer number*/
hdcmi->XferCount = (hdcmi->XferCount - 2U);
hdcmi->XferTransferNumber = hdcmi->XferCount; hdcmi->XferTransferNumber = hdcmi->XferCount;
/* Update second memory address */ /* Update second memory address */
@ -399,9 +398,9 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA_MB(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI
/* Initialise transfer parameters */ /* Initialise transfer parameters */
hdcmi->pBuffPtr = pData; hdcmi->pBuffPtr = pData;
hdcmi->XferCount = (Count - 2); hdcmi->XferCount = Count;
hdcmi->XferSize = Length/Count; hdcmi->XferSize = Length/Count;
hdcmi->XferTransferNumber = 0; hdcmi->XferTransferNumber = Count;
/* Update second memory address */ /* Update second memory address */
SecondMemAddress = (uint32_t)(pData + (4U*hdcmi->XferSize)); SecondMemAddress = (uint32_t)(pData + (4U*hdcmi->XferSize));
@ -877,7 +876,9 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
} }
/* Check if the frame is transferred */ /* Check if the frame is transferred */
if(hdcmi->XferCount == hdcmi->XferTransferNumber) { if(hdcmi->XferCount == 0) {
/* Reload XferCount */
hdcmi->XferCount = hdcmi->XferTransferNumber;
/* Enable the Frame interrupt */ /* Enable the Frame interrupt */
__HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME);

View File

@ -331,8 +331,8 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
hdcmi->DMA_Handle->XferAbortCallback = NULL; hdcmi->DMA_Handle->XferAbortCallback = NULL;
/* Reset transfer counters value */ /* Reset transfer counters value */
hdcmi->XferCount = 0; hdcmi->XferCount = 1U;
hdcmi->XferTransferNumber = 0; hdcmi->XferTransferNumber = 1U;
if(Length <= 0xFFFF) if(Length <= 0xFFFF)
{ {
@ -345,7 +345,7 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt; hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt;
/* Initialize transfer parameters */ /* Initialize transfer parameters */
hdcmi->XferCount = 1; hdcmi->XferCount = 1U;
hdcmi->XferSize = Length; hdcmi->XferSize = Length;
hdcmi->pBuffPtr = pData; hdcmi->pBuffPtr = pData;
@ -357,7 +357,6 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
} }
/* Update DCMI counter and transfer number*/ /* Update DCMI counter and transfer number*/
hdcmi->XferCount = (hdcmi->XferCount - 2);
hdcmi->XferTransferNumber = hdcmi->XferCount; hdcmi->XferTransferNumber = hdcmi->XferCount;
/* Update second memory address */ /* Update second memory address */
@ -412,9 +411,10 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA_MB(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI
hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt; hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt;
/* Initialise transfer parameters */ /* Initialise transfer parameters */
hdcmi->XferCount = Count-2; hdcmi->XferCount = Count;
hdcmi->XferSize = Length/Count; hdcmi->XferSize = Length/Count;
hdcmi->pBuffPtr = pData; hdcmi->pBuffPtr = pData;
hdcmi->XferTransferNumber = Count;
/* Update second memory address */ /* Update second memory address */
SecondMemAddress = (uint32_t)(pData + (4*hdcmi->XferSize)); SecondMemAddress = (uint32_t)(pData + (4*hdcmi->XferSize));
@ -875,7 +875,6 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
{ {
DCMI_HandleTypeDef* hdcmi; DCMI_HandleTypeDef* hdcmi;
hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent; hdcmi = (DCMI_HandleTypeDef*) ((DMA_HandleTypeDef*)hdma)->Parent;
//hdcmi->State= HAL_DCMI_STATE_READY;
// Note: we don't need to adjust memory addresses because they stay the same. // Note: we don't need to adjust memory addresses because they stay the same.
if (hdcmi->XferCount != 0) { if (hdcmi->XferCount != 0) {
@ -890,11 +889,18 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M0AR); DCMI_DMAConvCpltUser(hdcmi->DMA_Handle->Instance->M0AR);
} }
if (__HAL_DCMI_GET_FLAG(hdcmi, DCMI_FLAG_FRAMERI) != RESET) { /* Check if the frame is transferred */
/* Re-enable frame interrupt */ if(hdcmi->XferCount == 0) {
/* Reload XferCount */
hdcmi->XferCount = hdcmi->XferTransferNumber;
/* Enable the Frame interrupt */
__HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME);
/* When snapshot mode, set dcmi state to ready */
if((hdcmi->Instance->CR & DCMI_CR_CM) == DCMI_MODE_SNAPSHOT) {
hdcmi->State= HAL_DCMI_STATE_READY; hdcmi->State= HAL_DCMI_STATE_READY;
} }
}
} }
/** /**
* @brief DMA error callback * @brief DMA error callback

View File

@ -569,8 +569,8 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
hdcmi->DMA_Handle->XferAbortCallback = NULL; hdcmi->DMA_Handle->XferAbortCallback = NULL;
/* Reset transfer counters value */ /* Reset transfer counters value */
hdcmi->XferCount = 0; hdcmi->XferCount = 1U;
hdcmi->XferTransferNumber = 0; hdcmi->XferTransferNumber = 1U;
if(Length <= 0xFFFFU) if(Length <= 0xFFFFU)
{ {
@ -593,7 +593,7 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt; hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt;
/* Initialize transfer parameters */ /* Initialize transfer parameters */
hdcmi->XferCount = 1; hdcmi->XferCount = 1U;
hdcmi->XferSize = Length; hdcmi->XferSize = Length;
hdcmi->pBuffPtr = pData; hdcmi->pBuffPtr = pData;
@ -605,7 +605,6 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mo
} }
/* Update DCMI counter and transfer number*/ /* Update DCMI counter and transfer number*/
hdcmi->XferCount = (hdcmi->XferCount - 2U);
hdcmi->XferTransferNumber = hdcmi->XferCount; hdcmi->XferTransferNumber = hdcmi->XferCount;
/* Update second memory address */ /* Update second memory address */
@ -670,9 +669,10 @@ HAL_StatusTypeDef HAL_DCMI_Start_DMA_MB(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI
hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt; hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAXferCplt;
/* Initialise transfer parameters */ /* Initialise transfer parameters */
hdcmi->XferCount = Count-2; hdcmi->XferCount = Count;
hdcmi->XferSize = Length/Count; hdcmi->XferSize = Length/Count;
hdcmi->pBuffPtr = pData; hdcmi->pBuffPtr = pData;
hdcmi->XferTransferNumber = Count;
/* Update second memory address */ /* Update second memory address */
SecondMemAddress = (uint32_t)(pData + (4*hdcmi->XferSize)); SecondMemAddress = (uint32_t)(pData + (4*hdcmi->XferSize));
@ -1178,11 +1178,18 @@ static void DCMI_DMAXferCplt(DMA_HandleTypeDef *hdma)
DCMI_DMAConvCpltUser(stream->M0AR); DCMI_DMAConvCpltUser(stream->M0AR);
} }
if (__HAL_DCMI_GET_FLAG(hdcmi, DCMI_FLAG_FRAMERI) != RESET) { /* Check if the frame is transferred */
if (hdcmi->XferCount == 0) {
/* Reload XferCount */
hdcmi->XferCount = hdcmi->XferTransferNumber;
/* Re-enable frame interrupt */ /* Re-enable frame interrupt */
__HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME); __HAL_DCMI_ENABLE_IT(hdcmi, DCMI_IT_FRAME);
/* When snapshot mode, set dcmi state to ready */
if((hdcmi->Instance->CR & DCMI_CR_CM) == DCMI_MODE_SNAPSHOT) {
hdcmi->State= HAL_DCMI_STATE_READY; hdcmi->State= HAL_DCMI_STATE_READY;
} }
}
} }
/** /**