This post was last edited by dirty on 2024-6-1 19:56
This article describes the camera driver and displays it on the LCD screen.
1. Understand the principle of camera
The development board is equipped with an OV2640 camera, DVP interface, 2 million pixels. The principle of the development board camera is as follows.
Figure 1: Camera principle
Pin function definition:
PIXCLK output clock
XCLK external clock input
VSYNC field synchronization
HSYNC line synchronization
D[0:7] parallel port data (8 bit data bit size)
I2C interface: register driver configuration
Module features:
Standard SCCB interface, compatible with I2C interface:
RaWRGB, RGB (GRB4:2:2, RGB565/555/444), YUV (4:2:2) and YCbCr (4:2:2) output formats.
Support UXGA, SXGA, VGA, QVGA, QQVGA, CIF, QCIF, etc. Support automatic exposure control, automatic gain control, automatic white balance, automatic elimination of light stripes, automatic black level calibration. Image quality control includes color saturation, hue, gamma, sharpness ANTI BLOOM and other settings.
Support image zoom, pan and window settings.
Support image compression, that is, output JPEG image data.
2. Software Code
Implementation method: Use the DCI interface to collect data, and use the TLI interface used previously to display the collected image. Here, based on the previous LVGL project, the button is used to switch the camera image to the LCD screen.
1. Add the source file of the camera driver in the SDK, as shown below
Figure 2: Adding camera driver files
2. Camera driver initialization. Here, the camera sccb pin and dci configuration are initialized, including I2C initialization and DMA initialization. Then the camera is configured by writing parameters. Finally, the camera output image size is defined as 320*240. The display screen is 480*272, so conversion is involved later.
/*!
\brief DCI camera initialization
\param[in] none
\param[out] none
\retval 0x00 or 0xFF
*/
uint8_t dci_ov2640_init(void)
{
uint8_t i;
sccb_config();
dci_config();
ckout0_init();
delay_1ms(100);
/* OV2640 reset */
if(dci_byte_write(0xFF, 0x01) != 0) {
return 0xFF;
}
if(dci_byte_write(0x12, 0x80) != 0) {
return 0xFF;
}
delay_1ms(10);
for(i = 0; i < sizeof(ov2640_svga_init_reg_tbl) / 2; i++) {
if(0 != dci_byte_write(ov2640_svga_init_reg_tbl[i][0], ov2640_svga_init_reg_tbl[i][1])) {
return 0xFF;
}
}
delay_1ms(100);
for(i = 0; i < (sizeof(ov2640_rgb565_reg_tbl) / 2); i++) {
if(0 != dci_byte_write(ov2640_rgb565_reg_tbl[i][0], ov2640_rgb565_reg_tbl[i][1])) {
return 0xFF;
}
}
delay_1ms(100);
ov2640_outsize_set(320, 240);
return 0;
}
3. Read the camera information, including manufacturer, version and pid.
/*!
\brief read the ov2640 manufacturer identifier
\param[in] ov2640id: pointer to the ov2640 manufacturer struct
\param[out] none
\retval 0x00 or 0xFF
*/
uint8_t dci_ov2640_id_read(ov2640_id_struct *ov2640id)
{
uint8_t temp;
dci_byte_write(0xFF, 0x01);
if(dci_byte_read(OV2640_MIDH, &temp) != 0) {
return 0xFF;
}
ov2640id->manufacturer_id1 = temp;
if(dci_byte_read(OV2640_MIDL, &temp) != 0) {
return 0xFF;
}
ov2640id->manufacturer_id2 = temp;
if(dci_byte_read(OV2640_VER, &temp) != 0) {
return 0xFF;
}
ov2640id->version = temp;
if(dci_byte_read(OV2640_PID, &temp) != 0) {
return 0xFF;
}
ov2640id->pid = temp;
return 0x00;
}
4. Configure DMA and DCI. Here, LVGL is displayed on LAYER0 and the camera image is displayed on LAYER1, so disable the camera first.
nvic_configuration();
/* DMA interrupt and channel enable */
dma_interrupt_enable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_enable(DMA1, DMA_CH7);
/* DCI enable */
dci_enable();
dci_capture_enable();
delay_1ms(3000);
dma_interrupt_disable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_disable(DMA1, DMA_CH7);
dci_capture_disable();
5. The captured photo data is saved to SDRAM. The video stream is not stopped here. If you want to take a photo, you can open the shield.
/*!
\brief save image to sdram
\param[in] none
\param[out] none
\retval none
*/
void image_save(void)
{
uint32_t i = 0;
// dma_interrupt_disable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
// dma_channel_disable(DMA1, DMA_CH7);
// dci_capture_disable();
/* save image to sdram */
for(i = 0; i < 32640; i++) {
*(uint32_t *)(0xC0800000 + 4 * i) = *(uint32_t *)(0xC1000000 + 4 * i);
}
}
6. Camera image display, switch layers here.
/*!
\brief display image to lcd
\param[in] display_image_addr: image display address
\param[out] none
\retval none
*/
void image_display(uint32_t display_image_addr)
{
tli_layer_parameter_struct tli_layer1_initstruct;
/* input address configuration */
tli_layer1_initstruct.layer_frame_bufaddr = (uint32_t)display_image_addr;
/* layer1 windowing configuration */
tli_layer1_initstruct.layer_window_leftpos = 162;
tli_layer1_initstruct.layer_window_rightpos = (160 + 240 - 1);
tli_layer1_initstruct.layer_window_toppos = 12;
tli_layer1_initstruct.layer_window_bottompos = (12 + 272 - 1);
/* pixel format configuration */
tli_layer1_initstruct.layer_ppf = LAYER_PPF_RGB565;
/* alpha constant configuration : the constant alpha for layer 1 is decreased
to see the layer 0 in the intersection zone*/
tli_layer1_initstruct.layer_sa = 255;
/* default color configuration (configure A,R,G,B component values) */
tli_layer1_initstruct.layer_default_blue = 0xFF;
tli_layer1_initstruct.layer_default_green = 0xFF;
tli_layer1_initstruct.layer_default_red = 0xFF;
tli_layer1_initstruct.layer_default_alpha = 0;
/* blending factors */
tli_layer1_initstruct.layer_acf1 = LAYER_ACF1_PASA;
tli_layer1_initstruct.layer_acf2 = LAYER_ACF1_PASA;
// /* configure input address : frame buffer is located at memory */
// tli_layer1_initstruct.layer_frame_bufaddr = (uint32_t)0xC1000000;
tli_layer1_initstruct.layer_frame_line_length = ((240 * 2) + 3);
tli_layer1_initstruct.layer_frame_buf_stride_offset = (240 * 2);
tli_layer1_initstruct.layer_frame_total_line_number = 272;
tli_layer_init(LAYER1, &tli_layer1_initstruct);
tli_dither_config(TLI_DITHER_ENABLE);
tli_layer_init(LAYER1, &tli_layer1_initstruct);
/* disenable layer0 */
tli_layer_disable(LAYER0);
/* enable layer1 */
tli_layer_enable(LAYER1);
/* reload configuration */
tli_reload_config(TLI_REQUEST_RELOAD_EN);
/* enable TLI */
tli_enable();
}
7. Add interrupt handling function in Project\gd32h7xx_it.c. EXTI10_15_IRQHandler is Tamper key interrupt, saves photo data and enables DMA1 and turns on camera DCI capture. DMA1_Channel7_IRQHandler processes data and converts 320*240 camera data into 240*272 screen display data.
/*!
\brief this function handles external lines 10 to 15 interrupt request
\param[in] none
\param[out] none
\retval none
*/
void EXTI10_15_IRQHandler(void)
{
if(RESET != exti_interrupt_flag_get(EXTI_13))
{
printf("Key Pressed\r\n");
exti_interrupt_flag_clear(EXTI_13);
dma_interrupt_enable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_enable(DMA1, DMA_CH7);
dci_capture_enable();
image_save();
image_display((uint32_t)image_background1);
delay_1ms(500);
image_display((uint32_t)0XC1000000);
//lv_demo_music();
}
}
/*!
\brief this function handles DMA1_Channel7_IRQ interrupt request
\param[in] none
\param[out] none
\retval none
*/
void DMA1_Channel7_IRQHandler(void)
{
/* 320*240 size image convert to 240*272 size image */
if(dma_interrupt_flag_get(DMA1, DMA_CH7, DMA_INT_FLAG_FTF))
{
//printf("\r\nprocess image\r\n");
int i = 0, x = 0, y = 0;
dma_channel_disable(DMA1, DMA_CH7);
for(x = 0; x < 320; x++) {
for(y = 0; y < 240; y++) {
if(x < 272) {
*(uint16_t *)(0xC1000000 + 2 * i) = *(uint16_t *)(0xC0000000 + 2 * ((320 * y) + x));
i++;
}
}
}
dma_interrupt_flag_clear(DMA1, DMA_CH7, DMA_INT_FLAG_FTF);
dma_channel_enable(DMA1, DMA_CH7);
}
}
8.main function
int main(void)
{
ov2640_id_struct ov2640id;
BaseType_t ret;
/* enable the CPU cache */
cache_enable();
/* initialize the LEDs */
test_status_led_init();
/* configure systick */
systick_config();
/* flash the LEDs for 2 time */
led_flash(2);
/* configure USART0 */
usart_config();
/* configure TAMPER key */
gd_eval_key_init(KEY_TAMPER, KEY_MODE_EXTI);
/* output a message on hyperterminal using printf function */
printf("\r\n ================= LVGL ================= \r\n");
/**********************LCD Application**********************/
/* config the EXMC access mode */
exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
printf("\r\nSDRAM Init \r\n");
/* camera initialization */
dci_ov2640_init();
dci_ov2640_id_read(&ov2640id);
printf("\r\nMF1:0x%02x,MF2:0x%02x,version:0x%02x,pid:0x%02x\r\n",ov2640id.manufacturer_id1,\
ov2640id.manufacturer_id2,ov2640id.version,ov2640id.pid);
nvic_configuration();
/* DMA interrupt and channel enable */
dma_interrupt_enable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_enable(DMA1, DMA_CH7);
/* DCI enable */
dci_enable();
dci_capture_enable();
delay_1ms(100);
dma_interrupt_disable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_disable(DMA1, DMA_CH7);
dci_capture_disable();
timer_config();
lcd_config();
lcd_init();
/* configure the GPIO of SPI touch panel */
touch_panel_gpio_configure();
#if (LVGL_DMA)
delay_1ms(50);
dma_config();
delay_1ms(1000);
#endif
lv_init();
lv_port_disp_init();
lv_port_indev_init();
// lv_demo_music();
lv_demo_widgets();
while(1)
{
delay_1ms(5);
lv_task_handler();
}
}
3. Test
After compiling and burning, you can see the LCD display widgets. After pressing the Tamper button, the screen creates a 240*272 camera video display. See the video for the effect. The imaging effect is average. You can adjust the ov2640_svga_init_reg_tbl register parameters for optimization.
At this point, the camera image screen display is realized.
camera_show