DCMI+DMA no received data

Hi,

I’m using a NUCLEO-L4R5ZI board with OV2640 camera to capture pictures. Since I’m not familiar with HAL, I tested and modified a UART program which uses DMA. However, after modifying this program to work with DCMI, I cannot obtain any result from my buffer but all the debug outputs are positive. Can anyone help me out?

Here’s my source (codes that are commented using // are original UART one).

Codes
BufferedSerial pc(USBTX, USBRX, 115200);
Timer t;

/*  HW_PWR, HW_RST,
    I2C_SDA, I2C_SCL
    VSYNC, HSYNC, PCLK, 
    D0~D7 */
OV2640 camera1(
    PB_1, PA_8,
    PB_9, PB_8, // I2C1
    PB_7, PA_4, PA_6,
    PC_6, PC_7, PC_8, PC_9, PE_4, PD_3, PE_5, PE_6
);
DigitalOut flash(LED3);

// UART_HandleTypeDef uartHandle;
DCMI_HandleTypeDef dcmiHandle;
DMA_HandleTypeDef dmaHandle;

#define CAM_DIM_W 16
#define CAM_DIM_H 12
uint8_t captureBuffer[CAM_DIM_W * CAM_DIM_H * 2] = {0};

int main(void)
{
    t.start();
    printf("[%7lld] Start\n", t.elapsed_time().count()/1000);

    /* Enable clocks */
    __HAL_RCC_DMA1_CLK_ENABLE();
    __HAL_RCC_DMA2_CLK_ENABLE();
    __HAL_RCC_DMAMUX1_CLK_ENABLE();
    // __HAL_RCC_UART5_CLK_ENABLE();
    __HAL_RCC_DCMI_CLK_ENABLE();
    // __HAL_RCC_GPIOC_CLK_ENABLE();
    // __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();

    /* Initialize GPIOs (for DCMI) */
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    // GPIO_InitStruct.Alternate = GPIO_AF8_UART5;
    // GPIO_InitStruct.Pull = GPIO_PULLUP;
    // GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
    // GPIO_InitStruct.Pin = GPIO_PIN_12;
    // HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    // GPIO_InitStruct.Pin = GPIO_PIN_2;
    // HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    GPIO_InitStruct.Alternate = GPIO_AF10_DCMI;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_3;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Init OV2640 */
    camera1.powerStateSet(CAMERA_PWR_ON) == CAMERA_OK ? 
        printf("[%7lld] Power on\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Power on fail\n", t.elapsed_time().count()/1000);
    camera1.hwReset() == CAMERA_OK ? 
        printf("[%7lld] Hardware reset\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Hardware reset fail\n", t.elapsed_time().count()/1000);
    camera1.idCheck(OV2640_ADDRESS, 0x7FA2, 0x2642) == CAMERA_OK ? 
        printf("[%7lld] ID checked\n", t.elapsed_time().count()/1000):
        printf("[%7lld] ID check fail\n", t.elapsed_time().count()/1000);
    camera1.swReset(OV2640_ADDRESS) == CAMERA_OK ? 
        printf("[%7lld] Software Reset\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Software reset fail\n", t.elapsed_time().count()/1000);
    camera1.init(OV2640_ADDRESS, CAMERA_RES_SXGA) == CAMERA_OK ? 
        printf("[%7lld] Inited\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Init fail\n", t.elapsed_time().count()/1000);
    camera1.outputFormatSet(OV2640_ADDRESS, CAMERA_FMT_RGB565) == CAMERA_OK ? 
        printf("[%7lld] Output format set\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Output format set fail\n", t.elapsed_time().count()/1000);
    camera1.outputResolutionSet(OV2640_ADDRESS, CAM_DIM_W, CAM_DIM_H) == CAMERA_OK ? 
        printf("[%7lld] Output resolution set\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Output resolution set fail\n", t.elapsed_time().count()/1000);
    camera1.captureFrameRateSet(OV2640_ADDRESS, 0x0F) == CAMERA_OK ? /* set FPS */
        printf("[%7lld] Capture frame rate set\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Capture frame rate fail\n", t.elapsed_time().count()/1000);
    camera1.outputClockFreqSet(OV2640_ADDRESS, 0x7F) == CAMERA_OK ? 
        printf("[%7lld] Output frequency set\n", t.elapsed_time().count()/1000):
        printf("[%7lld] Output frequency set fail\n", t.elapsed_time().count()/1000);

    /* Peripheral handle */
    // uartHandle.Instance = UART5;
    // uartHandle.Init.BaudRate = 9600;
    // uartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    // uartHandle.Init.StopBits = 1;
    // uartHandle.Init.Parity = UART_PARITY_NONE;
    // uartHandle.Init.HwFlowCtl = UART_HWCONTROL_CTS;
    // uartHandle.Init.Mode = UART_MODE_TX_RX;
    dcmiHandle.Instance = DCMI;
    dcmiHandle.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
    dcmiHandle.Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;
    dcmiHandle.Init.VSPolarity = DCMI_VSPOLARITY_LOW;
    dcmiHandle.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
    dcmiHandle.Init.CaptureRate = DCMI_CR_ALL_FRAME;
    dcmiHandle.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
    dcmiHandle.Init.ByteSelectMode = DCMI_BSM_ALL;
    dcmiHandle.Init.JPEGMode = DCMI_JPEG_DISABLE;

    /* DMA handle */
    // dmaHandle.Instance = DMA1_Channel1;
    // dmaHandle.Init.Request = DMA_REQUEST_UART5_TX;
    // dmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;
    // dmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    // dmaHandle.Init.MemInc = DMA_MINC_ENABLE;
    // dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    // dmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
    // dmaHandle.Init.Mode = DMA_CIRCULAR;
    // dmaHandle.Init.Priority = DMA_PRIORITY_HIGH;
    dmaHandle.Instance = DMA1_Channel1;
    dmaHandle.Init.Request = DMA_REQUEST_DCMI;
    dmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    dmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
    dmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    dmaHandle.Init.MemInc = DMA_MINC_ENABLE;
    dmaHandle.Init.Mode = DMA_CIRCULAR;
    dmaHandle.Init.Priority = DMA_PRIORITY_HIGH;

    /* Init peripheral and DMA */
    // __HAL_LINKDMA(&uartHandle, hdmatx, dmaHandle);
    __HAL_LINKDMA(&dcmiHandle, DMA_Handle, dmaHandle);
    // HAL_UART_Init(&uartHandle) == HAL_OK ? 
    HAL_DCMI_Init(&dcmiHandle) == HAL_OK ? 
        printf("[%7lld] DCMI inited\n", t.elapsed_time().count()/1000):
        printf("[%7lld] DCMI init fail\n", t.elapsed_time().count()/1000);
    HAL_DMA_Init(&dmaHandle) == HAL_OK ? 
        printf("[%7lld] DMA inited\n", t.elapsed_time().count()/1000):
        printf("[%7lld] DMA init fail\n", t.elapsed_time().count()/1000);
    
    /* Start transmission */
    // uint8_t msg[] = "test";
    // HAL_UART_Transmit_DMA(&uartHandle, msg, 4) == HAL_OK ? 
    //     printf("[%7lld] UART_DMA started\n", t.elapsed_time().count()/1000):
    //     printf("[%7lld] UART_DMA start fail\n", t.elapsed_time().count()/1000);
    HAL_DCMI_Start_DMA(&dcmiHandle, DCMI_MODE_SNAPSHOT, (uint32_t)captureBuffer, CAM_DIM_W * CAM_DIM_H * 2) == HAL_OK ?
        printf("[%7lld] DCMI_DMA started\n", t.elapsed_time().count()/1000):
        printf("[%7lld] DCMI_DMA start fail\n", t.elapsed_time().count()/1000);

    while (true)
    {
        /* Determine transmission state */
        // HAL_UART_Transmit(&uartHandle, msg, 6, 1000);
        // if (__HAL_DMA_GET_FLAG(&dmaHandle, DMA_FLAG_TC1))
        // {
        //     HAL_UART_DMAStop(&uartHandle);
        //     break;
        // }
        if (__HAL_DMA_GET_FLAG(&dmaHandle, DMA_FLAG_TC1))
        {
            HAL_DCMI_Stop(&dcmiHandle);
            break;
        }

        flash = !flash;
        ThisThread::sleep_for(1s);
    }

    flash = 1;
    /* Send obtained data to computer */
    for (int i=0; i<CAM_DIM_W*CAM_DIM_H*2; i+=2)
    {
        // printf("%02X ", cap[i]);
        printf("%02X %02X %02X FF ", (
            captureBuffer[i]&0x1F)<<3, 
            (captureBuffer[i+1]&0x07)<<5 | (captureBuffer[i]&0xE0)>>3, 
            captureBuffer[i+1]&0xF8);
    }
    printf("\n");
    printf("[%7lld] Finished\n", t.elapsed_time().count()/1000);
    flash = 0;
}

By the way, the OV2640 class was written by myself and was tested working well. I can capture small images (tested that no larger than 112*84 px when minimal clock frequency applied) using another approch. Greater resolutions result in unexpected missing pixel.

Codes
// DigitalIn sig_v, sig_h, sig_c;
// BusIn dat;
uint32_t OV2640::capture(uint8_t* cap)
{
    uint32_t length = 0;

    abandonFrames(1); // throw the first (not complete) frame

    while(sig_v == 1) // start collecting
    {
        while(sig_h == 1)
        {
            while(sig_c == 0); 
            cap[length] = dat;
            while(sig_c == 1); 
            length++;
        }
    }
    
    return length;
}

Thank you for replying in advance.

Hello everyone, how are you all doing.

I am experimenting with the node MCU and some stm32 controllers to try and rip the parallel display data from some old portable gaming consoles. Theconsole im experimenting with uses an 800x240 resolution display and refreshes it at 60fps. The display is a 12 bit screen with 4 bits per rgb color. I have probed around the board with a scope and found the vertical and horizontal blanking signals as well as the clock signal, including all of the colour pins. But I struggle to be able to sample the colours and transfer them to the computer fast enough even with interrupts. I fear serial is not fast enough for the sort of an application.

The goal is to capture the display data, transfer to computer over a protocol and interface that permits such throughput and display it on the pc.

What MCUs are available to me to do the sort of a high bandwidth operation? I’m trying to sample the screen that contains 192000 pixels at 60 frames per second and dump the data to a computer

Thank you very much for posting this, I got it working.