2014-10-23 02:23 AM
Hello, i have project where i need to capture image from MT9M001 camera, store it inside SRAM (2MB) and do some complex calculation on image.
In order to make it work, i cropped image from 1280x1024 to 1024x1024 so it will fit inside my SRAM, because camera is capable of 10b. I tested SRAM with ST program, where it fills SRAM with data, and checks for errors by reading each number, that works fine, so problem is not inside SRAM config or connections. Since i have scope, i see that after setting camera registers, i get VSYNC, HSYNC, PCLK, and data outputs, no anomaly found by probing So my only cooperate for this problem can be DCMI and DMA config. Can any one spot problems in code ?#define DCMI_DR_ADDRESS (uint32_t)0x50050028
#define FMC_SRAM_ADDRESS (uint32_t)0x64000000
void MT9M001_HW_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOG,ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_DCMI); //D0
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_DCMI); //D1
GPIO_PinAFConfig(GPIOG, GPIO_PinSource10, GPIO_AF_DCMI); //D2
GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_DCMI); //D3
GPIO_PinAFConfig(GPIOE, GPIO_PinSource4, GPIO_AF_DCMI); //D4
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_DCMI); //D5
GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_DCMI); //D6
GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_DCMI); //D7
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_DCMI); //D8
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_DCMI); //D9
GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_DCMI); //HSYNC
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_DCMI); //VSYNC
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI); //PCLKX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6 | GPIO_Pin_9| GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* I2C1 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
/* Configure I2C1 GPIOs */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure I2C1 */
I2C_DeInit(I2C1);
I2C_Cmd(I2C1, ENABLE);
/* Set the I2C structure parameters */
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = 00;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = 3000;
/* Initialize the I2C peripheral w/ selected parameters */
I2C_Init(I2C1, &I2C_InitStruct);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//MT9M001 Reset
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void MT9M001_Init(void)
{
DCMI_InitTypeDef DCMI_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/*** Configures the DCMI to interface with the MT9M001 camera module ***/
/* Enable DCMI clock */
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);
/* DCMI configuration */
DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous;
DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;
DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Falling;
DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_High;
DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_High;
DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;
DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_10b;
/* Configures the DMA2 to transfer Data from DCMI */
/* Enable DMA2 clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
/* DMA2 Stream1 Configuration */
DMA_DeInit(DMA2_Stream1);
DMA_InitStructure.DMA_Channel = DMA_Channel_1;
DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = FMC_SRAM_ADDRESS;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DCMI_Init(&DCMI_InitStructure);
/* DMA2 IRQ channel Configuration */
DMA_Init(DMA2_Stream1, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream1,ENABLE);
DCMI_Cmd(ENABLE);
}
Any help would be very usefull, since at this point, i don't know whats wrong
2014-10-23 09:48 AM
update:
I made program, that counts VSYNC and HSYNC, and it does work, and correlate with oscilloscope, each second i get 30 counts running at 30fps. So problem only can be with DMA or any other ideas ?void DCMI_IRQHandler(void)
{
if(DCMI_GetFlagStatus(DCMI_FLAG_VSYNCRI) == SET)
{
VSYNC++;
}
else if(DCMI_GetFlagStatus(DCMI_FLAG_LINERI) == SET)
{
LINE++;
}
DCMI_ClearFlag(DCMI_FLAG_VSYNCRI);
DCMI_ClearFlag(DCMI_FLAG_LINERI);
}
Note i am running at 48MHz PCLK, it should be below maximum, also running at 200MHz STM32F429I ( a bit overclocked)
2014-10-23 10:10 AM
DMA_InitStructure.DMA_BufferSize = 1;
// ???
2014-10-23 10:24 AM
well, i get 0 activity even if i use 0xFFFF. (well, that is good point)
so each time i get line IT, i must change SRAM address for DMA to fill SRAM with data ? if that is the case, should i use as large buffer size, or just do it each line ?2014-10-23 10:29 AM
Well with 1x 16-bit word at 48 MHz, you're processor's going to be overwhelmed with interrupts. Given the count limit of the DMA, you're probably best investigate double buffer mode, and march the address through SRAM that way.
2014-10-23 10:36 AM
any idea will it work with DMA2D ? or it's just for LTDC ?
with 48MHz CLK, and DMA2D, adress 0 pin only get to 21MHz, now i resolder to 25MHz, address pins is at happy 12Mhz, so it looks like at this point STM32F429 running at 200MHz base clock, can keep up with data. now, DMA2D can copy register to memory transfer at 1024x1024 without any problems, question is, does data is in any way synchronized with camera data output ?static void DMA2D_Config(void)
{
DMA2D_InitTypeDef DMA2D_InitStruct;
DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct;
/* Enable the DMA2D Clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2D, ENABLE);
/* DMA2D configuration */
DMA2D_DeInit();
/* Transfer mode */
DMA2D_InitStruct.DMA2D_Mode = DMA2D_R2M;//DMA2D_M2M;
/* Color mode */
DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565;
/* Output Address */
DMA2D_InitStruct.DMA2D_OutputMemoryAdd = SRAM_BANK_ADDR;
/* Output Offset */
DMA2D_InitStruct.DMA2D_OutputOffset = 0;
DMA2D_InitStruct.DMA2D_NumberOfLine = 1024;
DMA2D_InitStruct.DMA2D_PixelPerLine = 1024;
/* Initialize the alpha and RGB values */
DMA2D_InitStruct.DMA2D_OutputGreen = 0;
DMA2D_InitStruct.DMA2D_OutputBlue = 0;
DMA2D_InitStruct.DMA2D_OutputRed = 0;
DMA2D_InitStruct.DMA2D_OutputAlpha = 0;
/* Initialize the output offset */
DMA2D_InitStruct.DMA2D_OutputOffset = 0;
/* Initialize DMA2D */
DMA2D_Init(&DMA2D_InitStruct);
DMA2D_FG_StructInit(&DMA2D_FG_InitStruct);
DMA2D_FG_InitStruct.DMA2D_FGCM = DMA2D_RGB565;
DMA2D_FG_InitStruct.DMA2D_FGMA = DCMI_DR_ADDRESS;
DMA2D_FGConfig(&DMA2D_FG_InitStruct);
}
Also, now i started to get activity on SRAM address pins, again, no data on DATA pins, that is not good :(
void DCMI_IRQHandler(void)
{
if(DCMI_GetFlagStatus(DCMI_FLAG_VSYNCRI) == SET)
{
DMA2D_Config();
DMA2D_StartTransfer();
while(DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET)
{
}
VSYNC++;
DCMI_ClearFlag(DCMI_FLAG_VSYNCRI);
}
Look like happy implementation, code is running, no overflows found,but no data at the same time in sram
2014-10-23 11:46 AM
ok, find one mistake:
HSYNC and VSYNC polarity should be low instead of high. (confusing name) so now my old DMA based program does copy data, now i have to deal with switching DMA address for new offset. I can't found example how may registers i need to rewrite to make efficient DMA program inside line interrupt. ( i mean, should i just write new address inside pointer, or should i do more) It shoul work with double mode too, simply by seting dma to circular mode, and in main program just keep pulling when transfer is done, and switch to another pointer, but i can't found any example how to do it. so i made my program like this :void DCMI_IRQHandler(void)
{
if(DCMI_GetFlagStatus(DCMI_FLAG_VSYNCRI) == SET)
{
VSYNC++;
LINE=0;
}
else if(DCMI_GetFlagStatus(DCMI_FLAG_LINERI) == SET)
{
DMA_DeInit(DMA2_Stream1);
DMA_InitStructure.DMA_Memory0BaseAddr =(uint32_t) (FMC_SRAM_ADDRESS+2*LINE*1024);
DMA_Init(DMA2_Stream1, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream1,ENABLE);
LINE++;
}
else if(DCMI_GetFlagStatus(DCMI_FLAG_OVFRI) == SET)
{
OVF++;
}
DCMI_ClearFlag(DCMI_FLAG_VSYNCRI);
DCMI_ClearFlag(DCMI_FLAG_LINERI);
DCMI_ClearFlag(DCMI_FLAG_OVFRI);
}
Highest address pin does corresponds to correct FPS number, so maybe it is correct.
Tomorrow i will try to make labview program so it could copy data to computer and show it, will see do i have any problems with image shifting, and so on
If any one knows how to make DMA based program more correct, let me know ( code sample helps alot)
2014-10-24 02:41 AM
I don't use the ''library''.
There are only 5 registers to be set for a DMA stream, plus one if you use the double-buffer mode (which I recommend you to use); plus you need to clear the status bits before enabling the stream. For circular mode, you need to set it up only once. An example of double-buffer setup, periph-to-mem, with memory bursts, with no attempt to adopt it for your case:#include <stdint.h>
#include ''stm32f4xx.h''
#define OR |
#define RxDMA DMA2
#define RxDMAStream DMA2_Stream2
#define RxDMAChannel 3
#define RxDMA_IRQn DMA2_Stream2_IRQn
#define RxDMAStream_BUF_HALFSIZE 128
uint32_t rxDmaBuf1[RxDMAStream_BUF_HALFSIZE];
uint32_t rxDmaBuf2[RxDMAStream_BUF_HALFSIZE];
RxDMAStream->NDTR = RxDMAStream_BUF_HALFSIZE;
// words to transfer
RxDMAStream->M0AR = (uint32_t)&rxDmaBuf1[0];
RxDMAStream->M1AR = (uint32_t)&rxDmaBuf2[0];
RxDMAStream->PAR = (uint32_t)&(SPI1->DR);
RxDMAStream->FCR = 0
OR ( 1 * DMA_SxFCR_DMDIS )
// disable direct mode -> enable FIFO
OR (DMA_SxFCR_FTH__FULL * DMA_SxFCR_FTH_0 )
// transfer only if full (that is the only way to use bursts while word transfer at memory side)
OR ( 0 * DMA_SxFCR_FEIE )
// no interrupt
;
RxDMA->LIFCR = 0
// !! if we won't clear the TCIF flag, the DMA won't restart!
OR DMA_LIFCR_CTCIF2
OR DMA_LIFCR_CHTIF2
OR DMA_LIFCR_CTEIF2
OR DMA_LIFCR_CDMEIF2
OR DMA_LIFCR_CFEIF2
;
RxDMAStream->CR = 0
OR (RxDMAChannel * DMA_SxCR_CHSEL_0 )
// channel select
OR (DMA_SxCR_xBURST_INCR4 * DMA_SxCR_MBURST_0 )
// memory burst (only in FIFO mode)
OR (DMA_SxCR_xBURST_INCR1 * DMA_SxCR_PBURST_0 )
// peripheral burst (only in FIFO mode)
OR (0 * DMA_SxCR_ACK )
// ''reserved'' (says manual)
OR (0 * DMA_SxCR_CT )
// current target (only in double-buffer mode)
OR (1 * DMA_SxCR_DBM )
// double-buffer mode
OR (DMA_SxCR_PL_PRIORITY_VERY_HIGH * DMA_SxCR_PL_0 )
// priority level
OR (0 * DMA_SxCR_PINCOS )
// peripheral increment offset size (only if peripheral address increments, FIFO mode and PBURST is 0)
OR (DMA_SxCR_xSIZE_WORD * DMA_SxCR_MSIZE_0 )
// memory data size; in direct mode forced to the same value as PSIZE
OR (DMA_SxCR_xSIZE_WORD * DMA_SxCR_PSIZE_0 )
// peripheral data size
OR (1 * DMA_SxCR_MINC )
// memory address increments
OR (0 * DMA_SxCR_PINC )
// peripheral address increments
OR (1 * DMA_SxCR_CIRC )
// circular mode (forced to 1 if double-buffer mode, forced to 0 if flow control is peripheral)
OR (DMA_SxCR_DIR_P2M * DMA_SxCR_DIR_0 )
// data transfer direction
OR (0 * DMA_SxCR_PFCTRL )
// peripheral is the flow controller (i.e. who determines end of transfer) - only for SDIO
OR (1 * DMA_SxCR_TCIE )
// transfer complete interrupt enable
OR (0 * DMA_SxCR_HTIE )
// half transfer interrupt enable
OR (0 * DMA_SxCR_TEIE )
// transfer error interrupt enable
OR (0 * DMA_SxCR_DMEIE )
// direct mode error interrupt enable
OR (1 * DMA_SxCR_EN )
// stream enable ;
;
// Rx DMA also triggers an interrupt
// relatively low priority, as it is lengthy (and has relatively lots of time to complete)
NVIC_SetPriority(RxDMA_IRQn, 10 );
// set low priority (4 bits; 0x00 - highest, 0x0F - lowest)
NVIC_EnableIRQ(RxDMA_IRQn);
JW
2014-10-25 11:05 AM
Any idea will it work between DCMI and DMA2D ?
Since DMA2D can copy data from register to SRAM ( or at least from internal memory to other memory, like SRAM)2014-10-27 01:21 AM
No.
The register-to-memory mode does not read an arbitrary register, but outputs content of one particular register, DMA2D_OCOLR. The memory-to-memory mode reads an area, there is no ''no-increment'' option, and no mechanism to trigger transfers based on signals from the peripherals. Read the manual. JW