CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

Introduction

A new learning project has arrived! This time we bring the learning notes related to the external OV2640 camera module in JPEG mode for the CH32V307 Red Toad board. This project is actually a supplement to the previous tutorial, addressing everyone’s questions about JPEG mode image transmission. As usual, students with weak foundations can refer to the Red Toad tutorial directory on the forum (Click to read the original text), the tutorial for this article: Tutorial: Use the Red Toad to read the video stream from the OV2640 camera and display it on the LCD. This article will use the external ports on the board, OV2640, and UartDisplay.

DVP Interface Related

DVP stands for Digital Video Port, the digital video interface is located below the TF card slot on the right side of the Red Toad development board, and it can be compatible with various cameras using the DVP interface, such as OV2640 and OV5640. Meanwhile, the chip also has a built-in DVP data collection module for receiving video or image data. This time we are still using OV2640, but the content is also applicable to other compatible models.

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

The code for this project is modified based on the DVP_UART example provided by Qinheng in the CH32V307EVT regarding the use of the DVP interface.

Related Issues on DVP Interface Definition

For specific introductions, please refer to this article: Tutorial: Use the Red Toad to read the video stream from the OV2640 camera and display it on the LCD.

In this article, students found that there is no 0xFFD9 at the beginning and 0xFFD8 at the end of the output data when learning the DVP_UART example, which is because only the 10th bit (D9-D0) is connected, the connection method is shown in the figure below.

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

The development board is connected according to 10bit, and the data we take in JPEG mode is the 8-bit data in the red box, so first, the data we get needs to occupy 2 bytes, and secondly, it must be right-shifted by two bits before transmission through the serial port, so that the correct image data can be obtained.

Display and Save Image

The data sent in JPEG mode can be displayed normally on UartDisplay.

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

This software can be downloaded from various websites, reference link: https://download.csdn.net/download/marqf/11268949

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

In this software, simply select the serial port, set the baud rate, and open the serial port to receive images. Check the option to save the image to a file in the lower right corner to save the received images to your selected path. The specific demonstration will be shown later.

Necessary Preparations

This section will list the problems and function descriptions that students may encounter.

Select the Correct UARTx

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

In the usage example of the DVP interface provided in the CH32V307EVT, if you directly extract and use it, you will find that in debug.h, it selects UART1, while the actual transmission of image data is UART2, which may lead to data errors.

Select Image Pixel

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

Due to transmission rate issues, it is not recommended to use the 1024*768 pixel selection from the example for speed and quality; setting it smaller is more convenient for experimentation.

Main Program

#include "debug.h"
#include "ov.h"

/*
 *@Note
  DVP operation OV2640 camera JPEG mode example:
    Output image data through UART2(PA2), can display images through serial port image software, or take 0xFF, 0xD8 at the beginning;
     0xFF ,0xD9 at the end of the data, modify the file format to display images.

    Note: Use UART2(PA2) serial output, set #define DEBUG   DEBUG_UART2 in debug.h
    UART1(PA9) is occupied by DVP
 */


/* DVP Work Mode */
#define JPEG_MODE     1
/* DVP Work Mode Selection */
#define DVP_Work_Mode    JPEG_MODE


UINT32  JPEG_DVPDMAaddr0 = 0x20005000;
UINT32  JPEG_DVPDMAaddr1 = 0x20005000 + OV2640_JPEG_WIDTH*2;// Each byte of data actually occupies two bytes of RAM

UINT32  RGB565_DVPDMAaddr0 = 0x20005000;
UINT32  RGB565_DVPDMAaddr1 = 0x20005000 + RGB565_COL_NUM;

volatile UINT32 frame_cnt = 0;
volatile UINT32 addr_cnt = 0;
volatile UINT32 href_cnt = 0;

void DVP_IRQHandler (void) __attribute__((interrupt("WCH-Interrupt-fast")));

/
*******************************************************************************
* Function Name  : UART2_Send_Byte
* Description    : UART2 send one byte data.
* Input          : t: UART send Data.
* Return         : None
*******************************************************************************
*/
void UART2_Send_Byte(u8 t)
{
    while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);// Wait for the last transmission to complete
    USART_SendData(USART2, t);
}

/
*******************************************************************************
* Function Name  : DVP_Init
* Description    : Init DVP
* Input          : None
* Return         : None
*******************************************************************************
*/
void DVP_Init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DVP, ENABLE);// Open DVP module clock signal

    DVP->CR0 &= ~RB_DVP_MSK_DAT_MOD;// Clear DVP configuration register CR0


#if (DVP_Work_Mode == JPEG_MODE)

    DVP->CR0 |= RB_DVP_D10_MOD | RB_DVP_V_POLAR | RB_DVP_JPEG;// Set DVP working mode (10bit width), sync signal polarity (low effective VSYNC signal), open DVP JPEG mode
    DVP->CR1 &= ~(RB_DVP_ALL_CLR| RB_DVP_RCV_CLR);// Configure register CR0 to clear DVP cache and flags

    DVP->COL_NUM = OV2640_JPEG_WIDTH;// Set image signal width

    /* Configure DMA target address */
    DVP->DMA_BUF0 = JPEG_DVPDMAaddr0;        //DMA addr0
    DVP->DMA_BUF1 = JPEG_DVPDMAaddr1;        //DMA addr1
#endif

    /* Set DVP frame capture rate */
    DVP->CR1 &= ~RB_DVP_FCRC;
    DVP->CR1 |= DVP_RATE_25P ;//DVP_RATE_25P;  //25%

    //Interupt Enable
    DVP->IER |= RB_DVP_IE_STP_FRM;
    DVP->IER |= RB_DVP_IE_FIFO_OV;
    DVP->IER |= RB_DVP_IE_FRM_DONE;
    DVP->IER |= RB_DVP_IE_ROW_DONE;
    DVP->IER |= RB_DVP_IE_STR_FRM;

    NVIC_InitStructure.NVIC_IRQChannel = DVP_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DVP->CR1 |= RB_DVP_DMA_EN;  //enable DMA
    DVP->CR0 |= RB_DVP_ENABLE;  //enable DVP
}

u32 DVP_ROW_cnt=0;

/
*******************************************************************************
* Function Name  : DVP_IRQHandler
* Description    : This function handles DVP exception.
* Input          : None
* Return         : None
*******************************************************************************
*/
void DVP_IRQHandler(void)
{
    if (DVP->IFR & RB_DVP_IF_ROW_DONE)
    {
        /* Write 0 clear 0 */
        DVP->IFR &= ~RB_DVP_IF_ROW_DONE;  //clear Interrupt

#if (DVP_Work_Mode == JPEG_MODE)
        href_cnt++;

        if (addr_cnt%2)     //buf1 done
        {
            addr_cnt++;
            DVP->DMA_BUF1 += OV2640_JPEG_WIDTH *4;
        }
        else                //buf0 done
        {
            addr_cnt++;
            DVP->DMA_BUF0 += OV2640_JPEG_WIDTH *4;
        }

#endif

    }

    if (DVP->IFR & RB_DVP_IF_FRM_DONE)
    {
        DVP->IFR &= ~RB_DVP_IF_FRM_DONE;  //clear Interrupt

#if (DVP_Work_Mode == JPEG_MODE)
        DVP->CR0 &= ~RB_DVP_ENABLE;       //disable DVP

        //Use uart2 send JPEG data.
        {
            UINT32 i;
            UINT16 val;

            href_cnt = href_cnt*OV2640_JPEG_WIDTH;

            for(i=0; i<href_cnt; i++){
                val = *(UINT16*)(0x20005000+i*2);
                UART2_Send_Byte((UINT8)(val>>2));// | 0xC0);//((val>>4)|(val));
            }

        }

        DVP->CR0 |= RB_DVP_ENABLE;  //enable DVP

        DVP->DMA_BUF0 = JPEG_DVPDMAaddr0;        //DMA addr0
        DVP->DMA_BUF1 = JPEG_DVPDMAaddr1;        //DMA addr1
        href_cnt = 0;

        addr_cnt =0;

#endif

    }

    if (DVP->IFR & RB_DVP_IF_STR_FRM)
    {
        DVP->IFR &= ~RB_DVP_IF_STR_FRM;  //clear Interrupt

        frame_cnt++;
    }

    if (DVP->IFR & RB_DVP_IF_STP_FRM)
    {
        DVP->IFR &= ~RB_DVP_IF_STP_FRM;  //clear Interrupt

    }

    if (DVP->IFR & RB_DVP_IF_FIFO_OV)
    {
        DVP->IFR &= ~RB_DVP_IF_FIFO_OV;   //clear Interrupt

        printf("FIFO OV\r\n");
    }
}

/
*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None PA2 - UART2
* Return         : None
*******************************************************************************
*/
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	Delay_Init();
	USART_Printf_Init(115200);// Set UART2 baud rate
	printf("SystemClk:%d\r\n",SystemCoreClock);

    while(OV2640_Init())
    {
        printf("Camera Model Err\r\n");
        Delay_Ms(1000);
    }

    Delay_Ms(1000);

    RGB565_Mode_Init();
    Delay_Ms(1000);

#if (DVP_Work_Mode == JPEG_MODE)
    printf("JPEG_MODE\r\n");
    JPEG_Mode_Init();
    Delay_Ms(1000);

#endif

    DVP_Init();

    while(1);
}

This is the main program needed for this project, and the following will explain it.

Header File and Parameter Definitions

#include "debug.h"
#include "ov.h"

/* DVP Work Mode */
#define JPEG_MODE     1
/* DVP Work Mode Selection */
#define DVP_Work_Mode    JPEG_MODE


UINT32  JPEG_DVPDMAaddr0 = 0x20005000;
UINT32  JPEG_DVPDMAaddr1 = 0x20005000 + OV2640_JPEG_WIDTH*2;// Each byte of data actually occupies two bytes of RAM

UINT32  RGB565_DVPDMAaddr0 = 0x20005000;
UINT32  RGB565_DVPDMAaddr1 = 0x20005000 + RGB565_COL_NUM;

volatile UINT32 frame_cnt = 0;
volatile UINT32 addr_cnt = 0;
volatile UINT32 href_cnt = 0;

The difference between this part and the tutorial is that the selected working mode is JPEG mode. As mentioned earlier, the data collected actually occupies the address above D9D2, so the line pixel address must be multiplied by 2, that is, the corresponding JPEG width multiplied by 2.

UART Transmission Data Function

void UART2_Send_Byte(u8 t)
{
    while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);// Wait for the last transmission to complete
    USART_SendData(USART2, t);
}

This part consists of functions provided in the library, and its function is to transmit data through UART2 into the serial port.

DVP Related Functions

void DVP_Init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DVP, ENABLE);// Open DVP module clock signal

    DVP->CR0 &= ~RB_DVP_MSK_DAT_MOD;// Clear DVP configuration register CR0


#if (DVP_Work_Mode == JPEG_MODE)

    DVP->CR0 |= RB_DVP_D10_MOD | RB_DVP_V_POLAR | RB_DVP_JPEG;// Set DVP working mode (10bit width), sync signal polarity (low effective VSYNC signal), open DVP JPEG mode
    DVP->CR1 &= ~(RB_DVP_ALL_CLR| RB_DVP_RCV_CLR);// Configure register CR0 to clear DVP cache and flags

    DVP->COL_NUM = OV2640_JPEG_WIDTH;// Set image signal width

    /* Configure DMA target address */
    DVP->DMA_BUF0 = JPEG_DVPDMAaddr0;        //DMA addr0
    DVP->DMA_BUF1 = JPEG_DVPDMAaddr1;        //DMA addr1
#endif

    /* Set DVP frame capture rate */
    DVP->CR1 &= ~RB_DVP_FCRC;
    DVP->CR1 |= DVP_RATE_25P ;//DVP_RATE_25P;  //25%

    //Interupt Enable
    DVP->IER |= RB_DVP_IE_STP_FRM;
    DVP->IER |= RB_DVP_IE_FIFO_OV;
    DVP->IER |= RB_DVP_IE_FRM_DONE;
    DVP->IER |= RB_DVP_IE_ROW_DONE;
    DVP->IER |= RB_DVP_IE_STR_FRM;

    NVIC_InitStructure.NVIC_IRQChannel = DVP_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DVP->CR1 |= RB_DVP_DMA_EN;  //enable DMA
    DVP->CR0 |= RB_DVP_ENABLE;  //enable DVP
}

u32 DVP_ROW_cnt=0;

/
*******************************************************************************
* Function Name  : DVP_IRQHandler
* Description    : This function handles DVP exception.
* Input          : None
* Return         : None
*******************************************************************************
*/
void DVP_IRQHandler(void)
{
    if (DVP->IFR & RB_DVP_IF_ROW_DONE)
    {
        /* Write 0 clear 0 */
        DVP->IFR &= ~RB_DVP_IF_ROW_DONE;  //clear Interrupt

#if (DVP_Work_Mode == JPEG_MODE)
        href_cnt++;

        if (addr_cnt%2)     //buf1 done
        {
            addr_cnt++;
            DVP->DMA_BUF1 += OV2640_JPEG_WIDTH *4;
        }
        else                //buf0 done
        {
            addr_cnt++;
            DVP->DMA_BUF0 += OV2640_JPEG_WIDTH *4;
        }

#endif

    }

    if (DVP->IFR & RB_DVP_IF_FRM_DONE)
    {
        DVP->IFR &= ~RB_DVP_IF_FRM_DONE;  //clear Interrupt

#if (DVP_Work_Mode == JPEG_MODE)
        DVP->CR0 &= ~RB_DVP_ENABLE;       //disable DVP

        //Use uart2 send JPEG data.
        {
            UINT32 i;
            UINT16 val;

            href_cnt = href_cnt*OV2640_JPEG_WIDTH;

            for(i=0; i<href_cnt; i++){
                val = *(UINT16*)(0x20005000+i*2);
                UART2_Send_Byte((UINT8)(val>>2));// | 0xC0);//((val>>4)|(val));
            }

        }

        DVP->CR0 |= RB_DVP_ENABLE;  //enable DVP

        DVP->DMA_BUF0 = JPEG_DVPDMAaddr0;        //DMA addr0
        DVP->DMA_BUF1 = JPEG_DVPDMAaddr1;        //DMA addr1
        href_cnt = 0;

        addr_cnt =0;

#endif

    }

    if (DVP->IFR & RB_DVP_IF_STR_FRM)
    {
        DVP->IFR &= ~RB_DVP_IF_STR_FRM;  //clear Interrupt

        frame_cnt++;
    }

    if (DVP->IFR & RB_DVP_IF_STP_FRM)
    {
        DVP->IFR &= ~RB_DVP_IF_STP_FRM;  //clear Interrupt

    }

    if (DVP->IFR & RB_DVP_IF_FIFO_OV)
    {
        DVP->IFR &= ~RB_DVP_IF_FIFO_OV;   //clear Interrupt

        printf("FIFO OV\r\n");
    }
}

/
*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None PA2 - UART2
* Return         : None
*******************************************************************************
*/
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	Delay_Init();
	USART_Printf_Init(115200);// Set UART2 baud rate
	printf("SystemClk:%d\r\n",SystemCoreClock);

    while(OV2640_Init())
    {
        printf("Camera Model Err\r\n");
        Delay_Ms(1000);
    }

    Delay_Ms(1000);

    RGB565_Mode_Init();
    Delay_Ms(1000);

#if (DVP_Work_Mode == JPEG_MODE)
    printf("JPEG_MODE\r\n");
    JPEG_Mode_Init();
    Delay_Ms(1000);

#endif

    DVP_Init();

    while(1);
}

In JPEG mode, only RGB565 in the tutorial is changed to JPEG, and the image does not need to be cropped. Among them:

        {
            UINT32 i;
            UINT16 val;

            href_cnt = href_cnt*OV2640_JPEG_WIDTH;

            for(i=0; i<href_cnt; i++){
                val = *(UINT16*)(0x20005000+i*2);
                UART2_Send_Byte((UINT8)(val>>2));// | 0xC0);//((val>>4)|(val));
            }

        }

This is the code that right shifts the image data by two bits to transmit to the serial port.

Main Function

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	Delay_Init();
	USART_Printf_Init(115200);// Set UART2 baud rate
	printf("SystemClk:%d\r\n",SystemCoreClock);

    while(OV2640_Init())
    {
        printf("Camera Model Err\r\n");
        Delay_Ms(1000);
    }

    Delay_Ms(1000);

    RGB565_Mode_Init();
    Delay_Ms(1000);

#if (DVP_Work_Mode == JPEG_MODE)
    printf("JPEG_MODE\r\n");
    JPEG_Mode_Init();
    Delay_Ms(1000);

#endif

    DVP_Init();

    while(1);
}

In the main function, the baud rate is the key point, which can be modified to change the transmission rate, and will also affect the image output. Here we choose 115200, which should not cause errors.

Display Effect

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

After programming, select the baud rate of 115200 and open the serial port, and it can be displayed on the right side of the software.

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

After selecting the directory to save the image to a file, you can find the saved .jpg file in the selected directory.

Conclusion

This concludes the learning notes on the JPEG mode of the OV2640 camera. This learning note answers some questions encountered in the previous tutorial and supplements the JPEG mode example. I hope this tutorial helps you in learning about the OV2640 camera connected to your Red Toad board.

As usual, the programs involved in this project are placed in the gitee repository with the filename:

  • Carmer_uart

https://gitee.com/Yuimerlin/ch32-v307-exercise

Welcome everyone to refer!

CH32V307 Red Toad Board Tutorial: Image Transmission Based on OV2640 Camera Module

Leave a Comment

×