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.
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.
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.
This software can be downloaded from various websites, reference link: https://download.csdn.net/download/marqf/11268949
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
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
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
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.
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!