cancel
Showing results for 
Search instead for 
Did you mean: 

DCMI Camera interface - max Resolution Camera

momede
Associate II
Posted on August 29, 2011 at 17:31

Hello at all...

I'm developing a camera that acquires a frame when it has a trigger and stores the data in the external ram with the DMA.

The camera has a relolution 1280 x 1024 pixel (each one of 16bit) and all pins signal are compatibles with the DCMI interface on the micro STM32F207.

I begun with the DCMI example in the ST-library but it's work with low resolution 320x240 pixel.

 I configured the DMA as below:

DMA_InitTypeDef DMA_InitStructure;

  /* Enable DMA2 clock */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);  

 

  DMA_ClearFlag(DMA2_Stream1, DMA_FLAG_FEIF1 | DMA_FLAG_DMEIF1 | DMA_FLAG_TEIF1 | DMA_FLAG_HTIF1 | DMA_FLAG_TCIF1);

  /* DMA2 Stream3  or Stream6 disable */

  DMA_Cmd(DMA2_Stream1, DISABLE);

  /* DMA2 Stream3  or Stream6 Config */

  DMA_DeInit(DMA2_Stream1);

  DMA_InitStructure.DMA_Channel = DMA_Channel_1;  

  DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;    

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) pBufferPtr;

 

  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;  

  DMA_Init(DMA2_Stream1, &DMA_InitStructure); 

  DMA_FlowControllerConfig(DMA2_Stream1, DMA_FlowCtrl_Peripheral);

My problem is that the max resolution that I can use is 128 x 1024 = 131072 pixel.

I suppose that the reason is the DMA counter limitations on 16 bit (65536) because each transfer on DMA is of 2 pixel (32 bit) and the max tranfer is 131072 pixel

The flow controller is configured to acquire the data controlled by the peripheral.

Could anyone suggest me any solution to increase the acquiring resolution?
19 REPLIES 19
Posted on August 29, 2011 at 18:23

You are using DMA in a circular fashion, you should be able to specify a smaller DMA buffer, and then copy the data to it's final destination on the TC (Full/Half) interrupts.

Bandwidth might be an issue with 2MB+ of streaming data, but you might be able to address that with an FPGA, or a more appropriate micro.

It never ceases to amaze me that in 2011 chip designers can't use 32-bit counters and timers on a 32-bit micro.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
momede
Associate II
Posted on August 29, 2011 at 19:09

Hi Clive thank for your reply.

I read on the RM that there is a possibility to use the double buffer in the DMA, but I don't find any example code to understand how it works.

Do you think that it can be a solution?

Do you know where I can find example code on double buffer in the DMA?

many thanks.
Posted on August 29, 2011 at 22:38

By servicing the data TC/HT (Transfer Complete, Half Transfer) interrupts you can treat the buffer in two halves (Ping/Pong). You copy half the data to it's final destination at each interrupt. I don't have an example, but the logic is pretty simple.

I think what I suggested was a ''Double Buffering'' technique, in that the DMA is pulling the data into a transitory location, and then forwarding it where you want it to go.

You could also perhaps do the DMA in smaller chunks directly where you want it to go, and advance the pointers, and reload the counters, at the TC interrupt.

You might well have to experiment with a couple of techniques/methods, I'm not that invested.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
infoinfo989
Associate III
Posted on August 30, 2011 at 03:28

I'm also trying to use the DCMI port with an image sensor at the moment. I have it partially working; I still have issues I need to solve. The DCMI example in the std peripheral library is a cute start but be warned, errors are ignored and the data amount there is trivially small. Don't assume that extrapolating that example to any real-world system is easy.

Double-buffer mode is pretty easy to use conceptually, and it's what we're using. You have two buffers, pointed to with M0AR and M1AR registers, and the DMA writes into one buffer, then the other. It'll give you an interrupt when it flips between buffers, so you know a buffer's been filled and ready to read out. There's a bit which tells you which buffer is currently being written to (so you can read the other one). I started off by using the standard peripheral library functions, but have changed (downgraded?) to hitting the registers directly, because it makes it easier to see exactly when things are happening, particularly in interrupt code. Well that's my excuse anyway. I'm not aware of any double-buffer example code, but it's only a small step from single-buffer code.

The nice thing with double-buffer mode is you can extend it (with code in your DMA interrupt routine) to any number of buffers. Because you're allowed to update the non-active buffer address register while the DMA is running.

I find double-buffer mode easier to use than circular-buffer, because it's easier to keep track of where the DMA is writing to, and therefore to know where it's safe to read data from. In circular-buffer mode (and also in double-buffer mode) the address registers M0AR and M1AR don't change. Particularly in circular buffer mode, it would make things a lot easier if they incremented as they were writing, so you'd directly know where they are. But they don't. So instead you need to read the NDTR register, convert it to bytes (if it's not already), ''invert'' it (because NDTR counts down remember), and add the result to the address register, all to determine where the DMA is writing. (The math can sometimes be simplified if you subtract NDTR  from the end-address of your buffer instead.) For double-buffer mode you don't really need to bother with any of this messiness - just wait until a buffer is complete, then read it. It's a lot simpler I think.

Keeping track of the amount of received data is a real challenge with the DCMI port. Notice that the DCMI port does not have a ''number of received bytes counter'' register, and the DMA controllers don't have one either. So you have to write code to count the data yourself, by counting DMA interrupts and the values you write into the NDTR register, while allowing for errors that may occur, etc. Depending upon what else you're doing with your data buffers (inserting image file headers and trailers etc), this can quickly become non-trivial. It should be obvious that counting the received data is pretty vital to knowing whether you received the correct amount or not, however the DCMI port doesn't make this easy to do.

According to the documentation, if the DCMI port has an overflow (it contains a small 4-word output FIFO), it'll reset and wait for the start of the next frame. In our experience this is hit-and-miss; sometimes it does, sometimes it doesn't, but we haven't managed to nail it down concisely enough to start an ST tech support discussion yet. We're seeing a lot of overflow errors early in the frame (ie the overflow flag is being set), however sometimes the DCMI port ignores its own error and continues accepting data right through to the end of the frame, and sometimes it'll stop accepting data partway through the frame. As far as I can tell, there's no way to tell the DCMI port to ignore an overflow error and keep going (this would be very handy indeed for constant framerate applications in some circumstances, but appears to be impossible with this part).

We've found it very useful to route the VSYNC pin to a GPIO so that we can easily have frame-start and frame-end interrupts independent of what the DCMI is doing. Particularly if the DCMI port has an overflow error within a frame, you might not get a frame-end interrupt from the DCMI at all.

Hope this helps.

momede
Associate II
Posted on August 31, 2011 at 07:17

Hi, today I worked to find a solution on my trouble.

I modified the code as below, but it doesn't work.

DMA_InitTypeDef DMA_InitStructure;

  /* Enable DMA2 clock */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);  

 

  DMA_ClearFlag(DMA2_Stream1, DMA_FLAG_FEIF1 | DMA_FLAG_DMEIF1 | DMA_FLAG_TEIF1 | DMA_FLAG_HTIF1 | DMA_FLAG_TCIF1);

  /* DMA2 Stream3  or Stream6 disable */

  DMA_Cmd(DMA2_Stream1, DISABLE);

  /* DMA2 Stream3  or Stream6 Config */

  DMA_DeInit(DMA2_Stream1);

  DMA_InitStructure.DMA_Channel = DMA_Channel_1;  

  DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;    

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) pBufferPtr;

 

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = (PixelColumn >> 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;  

  DMA_Init(DMA2_Stream1, &DMA_InitStructure);  

 

  DMA_DoubleBufferModeCmd(DMA2_Stream1, ENABLE);

  DMA_MemoryTargetConfig(DMA2_Stream1,((uint32_t)(ImageBuffer->ImageData))+(PixelColumn>>1), DMA_Memory_1);

  DMA_ITConfig(DMA2_Stream1, DMA_IT_TC, ENABLE );

 

  DMA_FlowControllerConfig(DMA2_Stream1, DMA_FlowCtrl_Peripheral);

in the interrupt I change the destination area where I would like that the DCMI writes.

void DMA2_Stream1_IRQHandler(void)

{

  /* This DMA is used for transferring the DCMI Data */

  if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TC) != RESET)

  {

    {

  /* This DMA is used for transferring the DCMI Data */

  if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TC) != RESET)

  {

    DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TC);

    if(LineNumber >> 1 == 0)

    {

      DMA2_Stream1->M0AR = (uint32_t)ImageBuffer->ImageData + ((PixelColumn >>1) * LineNumber);      

    }

    else

    {

      DMA2_Stream1->M1AR = (uint32_t)ImageBuffer->ImageData + ((PixelColumn>>1) * LineNumber);

    }

  LineNumber++;

  }

}

My variables are: PixelColumn is the number of pixel for each row and LineNumber is the line that I will write and it's initialized at 2

What I expect is that the second line is written in the buffer M0AR and the odd line in the buffer M1AR,  but what I expect isn't what I see...

The micro doesn't go in the interrupt and the buffers aren't refreshed and the captured image has the same problem that I explain above, it has the limitations of the DMA counter.

Could someone explain to me where I wrong? Why the interrupt is never called?

many thanks for your help.

infoinfo989
Associate III
Posted on September 01, 2011 at 00:12

Well, the DMA interrupt won't be called unless it completes a DMA transaction, and that data is coming from the DCMI port. In your code example you don't show the setup of the DCMI port at all. Perhaps the problem lies in there. Are you getting any DCMI interrupts?

Your DMA interrupt code looks a little strange. Why are you performing this check twice?:

  /* This DMA is used for transferring the DCMI Data */

  if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TC) != RESET)

  {

    {

  /* This DMA is used for transferring the DCMI Data */

  if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TC) != RESET)

  {

Also, before you try to write to M0AR or M1AR with the DMA enabled, you need to check which one is currently active, because you cannot write to the active address register. (You can only write to the inactive address register.) That's the CT bit in the DMA status register.

In your code you appear to check your ''line number'' variable, but if this is out of sync with the CT bit for any reason, your address register update will fail.

infoinfo989
Associate III
Posted on September 01, 2011 at 00:15

Oh, and I assume you added your DMA interrupt to the NVIC?

momede
Associate II
Posted on September 01, 2011 at 17:56

Hi Frank, thanks for your reply.

I wrong the cut and paste, my code has only one test for the DMA_IT_TC flag.

I changed also the test in the interrupt function, instead to use my LineNumber variable I use the function:

DMA_GetCurrentMemoryTarget(DMA2_Stream1)

to see where the DMA is writing.

The Dma interrupt didn't occur because I wronged the nvic initialization, now all work properly.

I had to remove also the line

DMA_FlowControllerConfig(DMA2_Stream1, DMA_FlowCtrl_Peripheral);

because this

inhibits

the double buffer.

Many thanks for your help.
infoinfo989
Associate III
Posted on September 02, 2011 at 05:19

I had to remove also the line

 

DMA_FlowControllerConfig(DMA2_Stream1, DMA_FlowCtrl_Peripheral);

 

because this

inhibits

the double buffer.

Have a read of the DMA chapter - you'll see that only the SDIO port is allowed to be the dma peripheral flow controller (if I remember rightly) - for all other peripherals the dma itself needs to be the flow controller.

Many thanks for your help.

You're welcome - glad you got it working.