mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
ports/stm32: Refactor MDMA code.
Moved to separate file to make omv_csi more readable. Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
This commit is contained in:
parent
85179136ab
commit
2b4d66ba23
@ -50,7 +50,6 @@
|
||||
|
||||
#if defined(OMV_MDMA_CHANNEL_DCMI_0)
|
||||
#define USE_MDMA (1)
|
||||
#define MDMA_BUFFER_SIZE (64)
|
||||
#endif
|
||||
|
||||
#if !defined(DCMIPP)
|
||||
@ -590,22 +589,12 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
bytes_per_pixel = sizeof(uint8_t);
|
||||
}
|
||||
|
||||
// For all non-JPEG and non-transposed modes image capture can be completely offload to MDMA.
|
||||
#if USE_MDMA
|
||||
// For non-JPEG, non-transposed modes, offload the capture to MDMA.
|
||||
// Note that MDMA is started here, not in FRAME/VSYNC callbacks, to
|
||||
// maximize the time before the frame has to be dropped.
|
||||
if (!csi->transpose) {
|
||||
// NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before
|
||||
// the frame has to be dropped.
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
// mdma0 will copy this line of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma0);
|
||||
csi->mdma0.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma0, (uint32_t) src, (uint32_t) dst,
|
||||
line_width_bytes, 1);
|
||||
// mdma1 will copy all remaining lines of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma1);
|
||||
csi->mdma1.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes),
|
||||
line_width_bytes, fb->v - 1);
|
||||
stm_mdma_start(csi, (uint32_t) src, (uint32_t) dst, fb->u * bytes_per_pixel, fb->v);
|
||||
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
|
||||
return;
|
||||
}
|
||||
@ -618,7 +607,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
}
|
||||
|
||||
#if USE_MDMA
|
||||
// Two MDMA channels are used to maximize the time available for each channel to finish the transfer.
|
||||
// Two MDMA channels are used to maximize the time available to finish the transfer.
|
||||
omv_csi_copy_line(csi, (buffer->offset % 2) ? &csi->mdma1 : &csi->mdma0, src, dst);
|
||||
#else
|
||||
omv_csi_copy_line(csi, NULL, src, dst);
|
||||
@ -626,125 +615,6 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_MDMA
|
||||
// Configures an MDMA channel to completely offload the CPU in copying one line of pixels.
|
||||
static void omv_csi_mdma_config(omv_csi_t *csi, MDMA_InitTypeDef *init, uint32_t bytes_per_pixel) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
init->Request = MDMA_REQUEST_SW;
|
||||
init->TransferTriggerMode = MDMA_REPEAT_BLOCK_TRANSFER;
|
||||
init->Priority = MDMA_PRIORITY_VERY_HIGH;
|
||||
init->DataAlignment = MDMA_DATAALIGN_PACKENABLE;
|
||||
init->BufferTransferLength = MDMA_BUFFER_SIZE;
|
||||
// The source address is 1KB aligned. So, a burst size of 16 beats (AHB Max) should not break.
|
||||
// Destination lines may not be aligned however so the burst size must be computed.
|
||||
init->SourceBurst = MDMA_SOURCE_BURST_16BEATS;
|
||||
init->SourceBlockAddressOffset = 0;
|
||||
init->DestBlockAddressOffset = 0;
|
||||
|
||||
if ((csi->pixformat == PIXFORMAT_RGB565 && csi->rgb_swap) ||
|
||||
(csi->pixformat == PIXFORMAT_YUV422 && csi->yuv_swap)) {
|
||||
init->Endianness = MDMA_LITTLE_BYTE_ENDIANNESS_EXCHANGE;
|
||||
} else {
|
||||
init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
|
||||
}
|
||||
|
||||
uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(csi, bytes_per_pixel);
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
|
||||
if (csi->transpose) {
|
||||
line_width_bytes = bytes_per_pixel;
|
||||
init->DestBlockAddressOffset = (fb->v - 1) * bytes_per_pixel;
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
line_width_bytes /= 2;
|
||||
if (csi->transpose) {
|
||||
init->DestBlockAddressOffset /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// The destination will be 32-byte aligned, so the line width is broken into the largest
|
||||
// power of 2. The source may have an offset, further limiting this to a sub power of 2.
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
if (!(line_width_bytes % (1 << i))) {
|
||||
for (int j = IM_MIN(i, 2); j >= 0; j--) {
|
||||
if (!(line_offset_bytes % (1 << j))) {
|
||||
init->SourceInc = MDMA_CTCR_SINC_1 | (j << MDMA_CTCR_SINCOS_Pos);
|
||||
init->SourceDataSize = j << MDMA_CTCR_SSIZE_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
init->DestinationInc = MDMA_CTCR_DINC_1 | (i << MDMA_CTCR_DINCOS_Pos);
|
||||
init->DestDataSize = i << MDMA_CTCR_DSIZE_Pos;
|
||||
|
||||
// Find the burst size we can break the destination transfer up into.
|
||||
uint32_t count = MDMA_BUFFER_SIZE >> i;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (!(count % (1 << i))) {
|
||||
init->DestBurst = i << MDMA_CTCR_DBURST_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
init->SourceInc = MDMA_SRC_INC_HALFWORD;
|
||||
init->SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
static void omv_csi_mdma_enable(omv_csi_t *csi, uint32_t bytes_per_pixel) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
omv_csi_mdma_config(csi, &csi->mdma0.Init, bytes_per_pixel);
|
||||
memcpy(&csi->mdma1.Init, &csi->mdma0.Init, sizeof(MDMA_InitTypeDef));
|
||||
HAL_MDMA_Init(&csi->mdma0);
|
||||
|
||||
// If we are not transposing the image we can fully offload image capture from the CPU.
|
||||
if (!csi->transpose) {
|
||||
// MDMA will trigger on each TC from DMA and transfer one line to the frame buffer.
|
||||
csi->mdma1.Init.Request = MDMA_REQUEST_DMA2_Stream1_TC;
|
||||
csi->mdma1.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
|
||||
// We setup MDMA to repeatedly reset itself to transfer the same line buffer.
|
||||
csi->mdma1.Init.SourceBlockAddressOffset = -(fb->u * bytes_per_pixel);
|
||||
}
|
||||
|
||||
HAL_MDMA_Init(&csi->mdma1);
|
||||
if (!csi->transpose) {
|
||||
HAL_MDMA_ConfigPostRequestMask(&csi->mdma1, (uint32_t) &DMA2->LIFCR, DMA_FLAG_TCIF1_5);
|
||||
}
|
||||
}
|
||||
|
||||
int omv_csi_dma_memcpy(omv_csi_t *csi, void *dma, void *dst, void *src, int bpp, bool transposed) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
MDMA_HandleTypeDef *handle = dma;
|
||||
|
||||
// Drop the frame if MDMA is not keeping up as the image will be corrupted.
|
||||
if (handle->Instance->CCR & MDMA_CCR_EN) {
|
||||
csi->drop_frame = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If MDMA is still running, HAL_MDMA_Start() will start a new transfer.
|
||||
__HAL_UNLOCK(handle);
|
||||
handle->State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(handle,
|
||||
(uint32_t) src,
|
||||
(uint32_t) dst,
|
||||
transposed ? bpp : (fb->u * bpp),
|
||||
transposed ? fb->u : 1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_DCMIPP
|
||||
void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) {
|
||||
uint32_t avg[3];
|
||||
@ -865,9 +735,11 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
}
|
||||
|
||||
#if USE_MDMA
|
||||
// Enable MDMA transfer from the DCMI line buffer for non-JPEG modes.
|
||||
// Configure MDMA for non-JPEG modes. MDMA will be used to either
|
||||
// completely offload the transfer, in case of non-transposed mode
|
||||
// or copy transposed lines.
|
||||
if (csi->pixformat != PIXFORMAT_JPEG) {
|
||||
omv_csi_mdma_enable(csi, bytes_per_pixel);
|
||||
stm_mdma_init(csi, bytes_per_pixel, x_crop);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -889,10 +761,9 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
}
|
||||
#if USE_MDMA
|
||||
} else if ((csi->pixformat != PIXFORMAT_JPEG) && (!csi->transpose)) {
|
||||
// Start an MDMA transfer, which completely offloads the capture to MDMA.
|
||||
// DMA to circular mode writing the same line over and over again.
|
||||
// Special transfer mode that uses DMA in circular mode and MDMA
|
||||
// to move the lines to the final destination.
|
||||
((DMA_Stream_TypeDef *) csi->dma.Instance)->CR |= DMA_SxCR_CIRC;
|
||||
// DCMI will transfer to same line and MDMA will move to final location.
|
||||
HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_CONTINUOUS,
|
||||
(uint32_t) &_line_buf, line_width_bytes / sizeof(uint32_t));
|
||||
#endif // USE_MDMA
|
||||
|
||||
@ -186,6 +186,140 @@ uint8_t stm_dma_mpu_region_size(uint32_t size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef OMV_MDMA_CHANNEL_DCMI_0
|
||||
void stm_mdma_init(omv_csi_t *csi, uint32_t bytes_per_pixel, uint32_t x_crop) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
stm_mdma_init_channel(csi, &csi->mdma0.Init, bytes_per_pixel, x_crop);
|
||||
memcpy(&csi->mdma1.Init, &csi->mdma0.Init, sizeof(MDMA_InitTypeDef));
|
||||
HAL_MDMA_Init(&csi->mdma0);
|
||||
|
||||
// If we are not transposing the image we can fully offload image capture from the CPU.
|
||||
if (!csi->transpose) {
|
||||
// MDMA will trigger on each TC from DMA and transfer one line to the frame buffer.
|
||||
csi->mdma1.Init.Request = MDMA_REQUEST_DMA2_Stream1_TC;
|
||||
csi->mdma1.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
|
||||
// We setup MDMA to repeatedly reset itself to transfer the same line buffer.
|
||||
csi->mdma1.Init.SourceBlockAddressOffset = -(fb->u * bytes_per_pixel);
|
||||
}
|
||||
|
||||
HAL_MDMA_Init(&csi->mdma1);
|
||||
if (!csi->transpose) {
|
||||
HAL_MDMA_ConfigPostRequestMask(&csi->mdma1, (uint32_t) &DMA2->LIFCR, DMA_FLAG_TCIF1_5);
|
||||
}
|
||||
}
|
||||
|
||||
// Configures an MDMA channel to completely offload the CPU in copying one line of pixels.
|
||||
static void stm_mdma_init_channel(omv_csi_t *csi,
|
||||
MDMA_InitTypeDef *init, uint32_t bytes_per_pixel, uint32_t x_crop) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
init->Request = MDMA_REQUEST_SW;
|
||||
init->TransferTriggerMode = MDMA_REPEAT_BLOCK_TRANSFER;
|
||||
init->Priority = MDMA_PRIORITY_VERY_HIGH;
|
||||
init->DataAlignment = MDMA_DATAALIGN_PACKENABLE;
|
||||
init->BufferTransferLength = MDMA_BUFFER_SIZE;
|
||||
// The source address is 1KB aligned. So, a burst size of 16 beats
|
||||
// (AHB Max) should not break. Destination lines may not be aligned
|
||||
// however so the burst size must be computed.
|
||||
init->SourceBurst = MDMA_SOURCE_BURST_16BEATS;
|
||||
init->SourceBlockAddressOffset = 0;
|
||||
init->DestBlockAddressOffset = 0;
|
||||
|
||||
if ((csi->pixformat == PIXFORMAT_RGB565 && csi->rgb_swap) ||
|
||||
(csi->pixformat == PIXFORMAT_YUV422 && csi->yuv_swap)) {
|
||||
init->Endianness = MDMA_LITTLE_BYTE_ENDIANNESS_EXCHANGE;
|
||||
} else {
|
||||
init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
|
||||
}
|
||||
|
||||
uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - x_crop;
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
|
||||
if (csi->transpose) {
|
||||
line_width_bytes = bytes_per_pixel;
|
||||
init->DestBlockAddressOffset = (fb->v - 1) * bytes_per_pixel;
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
line_width_bytes /= 2;
|
||||
if (csi->transpose) {
|
||||
init->DestBlockAddressOffset /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// The destination will be 32-byte aligned, so the line width is broken
|
||||
// into the largest power of 2. The source may have an offset, further
|
||||
// limiting this to a sub power of 2.
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
if (!(line_width_bytes % (1 << i))) {
|
||||
for (int j = IM_MIN(i, 2); j >= 0; j--) {
|
||||
if (!(line_offset_bytes % (1 << j))) {
|
||||
init->SourceInc = MDMA_CTCR_SINC_1 | (j << MDMA_CTCR_SINCOS_Pos);
|
||||
init->SourceDataSize = j << MDMA_CTCR_SSIZE_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
init->DestinationInc = MDMA_CTCR_DINC_1 | (i << MDMA_CTCR_DINCOS_Pos);
|
||||
init->DestDataSize = i << MDMA_CTCR_DSIZE_Pos;
|
||||
|
||||
// Find the burst size we can break the destination transfer up into.
|
||||
uint32_t count = MDMA_BUFFER_SIZE >> i;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (!(count % (1 << i))) {
|
||||
init->DestBurst = i << MDMA_CTCR_DBURST_Pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// YUV422 Source -> Y Destination
|
||||
if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) {
|
||||
init->SourceInc = MDMA_SRC_INC_HALFWORD;
|
||||
init->SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
void stm_mdma_start(omv_csi_t *csi, uint32_t src, uint32_t dst, uint32_t line_width, uint32_t line_count) {
|
||||
// mdma0 will copy this line of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma0);
|
||||
csi->mdma0.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma0, src, dst, line_width, 1);
|
||||
|
||||
// mdma1 will copy all remaining lines of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi->mdma1);
|
||||
csi->mdma1.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma1, src, dst + line_width, line_width, line_count - 1);
|
||||
}
|
||||
|
||||
int omv_csi_dma_memcpy(omv_csi_t *csi, void *dma, void *dst, void *src, int bpp, bool transposed) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
MDMA_HandleTypeDef *handle = dma;
|
||||
|
||||
// Drop the frame if MDMA is not keeping up as the image will be corrupted.
|
||||
if (handle->Instance->CCR & MDMA_CCR_EN) {
|
||||
csi->drop_frame = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If MDMA is still running, HAL_MDMA_Start() will start a new transfer.
|
||||
__HAL_UNLOCK(handle);
|
||||
handle->State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(handle,
|
||||
(uint32_t) src,
|
||||
(uint32_t) dst,
|
||||
transposed ? bpp : (fb->u * bpp),
|
||||
transposed ? fb->u : 1);
|
||||
return 0;
|
||||
}
|
||||
#endif // OMV_MDMA_CHANNEL_DCMI_0
|
||||
|
||||
#if defined(GPDMA1)
|
||||
static inline void stm_dma_irq_handler(size_t irqn) {
|
||||
if (dma_handle[irqn] != NULL) {
|
||||
|
||||
@ -29,4 +29,17 @@ uint8_t stm_dma_channel_to_irqn(void *dma_channel);
|
||||
uint8_t stm_dma_channel_to_id(void *dma_channel);
|
||||
int stm_dma_set_irq_descr(void *dma_channel, DMA_HandleTypeDef *dma_descr);
|
||||
uint8_t stm_dma_mpu_region_size(uint32_t size);
|
||||
|
||||
#ifdef OMV_MDMA_CHANNEL_DCMI_0
|
||||
#include "omv_csi.h"
|
||||
|
||||
#define MDMA_BUFFER_SIZE (64)
|
||||
#define MDMA_CHAN_TO_INSTANCE(x) \
|
||||
(MDMA_Channel_TypeDef *) (MDMA_Channel0_BASE + ((MDMA_Channel1_BASE - MDMA_Channel0_BASE) * x))
|
||||
|
||||
void stm_mdma_init(omv_csi_t *csi, uint32_t bytes_per_pixel, uint32_t x_crop);
|
||||
void stm_mdma_init_channel(omv_csi_t *csi, MDMA_InitTypeDef *init, uint32_t bytes_per_pixel, uint32_t x_crop);
|
||||
void stm_mdma_start(omv_csi_t *csi, uint32_t src, uint32_t dst, uint32_t line_width, uint32_t line_count);
|
||||
#endif // OMV_MDMA_CHANNEL_DCMI_0
|
||||
|
||||
#endif // __STM_DMA_H__
|
||||
|
||||
Loading…
Reference in New Issue
Block a user