ESP32s3 LCD_CAM interrupt

TBHKpmy
Posts: 2
Joined: Fri Jan 26, 2024 4:35 am

ESP32s3 LCD_CAM interrupt

Postby TBHKpmy » Sat Jan 27, 2024 2:06 am

I am developing ESP32s3 with ArduinoIDE Ver1.8.19.
ArduinoIDE ESP32 board library version is 2.0.14

When registering LCD_CAM module interrupts, I use "esp_intr_alloc" to register the interrupt handler function as shown below, but the LCD_CAM module interrupt does not occur even when VSYNC is received from CIS.

*Since the LCD_CAM module's register settings are set to "LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 1;", an interrupt is generated when the LCD_CAM module receives CIS VSYNC?

//◇LCD_CAM_LC_DMA_INT_ENA
LCD_CAM.lc_dma_int_ena.lcd_vsync_int_ena = 0;
LCD_CAM.lc_dma_int_ena.lcd_trans_done_int_ena = 0;
LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 1;
LCD_CAM.lc_dma_int_ena.cam_hs_int_ena = 0;

// Allocate LCD_CAM interrupt, keep it disabled
esp_intr_alloc(ETS_LCD_CAM_INTR_SOURCE,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM,
&VSYNCHandler, NULL, &lcd_cam_intr_handle);

esp_intr_enable(lcd_cam_intr_handle);

How can I generate an LCD_CAM module interrupt?

Thank you.


This is the entire related source code

void ESP32CAM_Init(ESP32CAM_PIN *param , uint16_t horizon_size , uint16_t vertical_size)
{

dma_buf_size = horizon_size;

periph_module_enable(PERIPH_LCD_CAM_MODULE); // Enable LCD_CAM
periph_module_reset(PERIPH_LCD_CAM_MODULE); // Reset LCD_CAM

// Reset CAM bus
LCD_CAM.cam_ctrl1.cam_reset = 1;

//① CAM_CLK setting (register setting)
// CAM module registers configuration
//◇LCD_CAM_CAM_CTRL
LCD_CAM.cam_ctrl.cam_stop_en = 0;
LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 0;
LCD_CAM.cam_ctrl.cam_byte_order = 0;
LCD_CAM.cam_ctrl.cam_bit_order = 0;
LCD_CAM.cam_ctrl.cam_line_int_en = 1;
LCD_CAM.cam_ctrl.cam_vs_eof_en = 1;
LCD_CAM.cam_ctrl.cam_clkm_div_num = 2; // CAM_CLK_IDX = 20MHz
LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
LCD_CAM.cam_ctrl.cam_clk_sel = 1; // XTAL_CLK source (40MHz)

//◇LCD_CAM_CAM_CTRL1
LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = horizon_size;
LCD_CAM.cam_ctrl1.cam_line_int_num = 0; // Number of hsyncs that generate hs interrupts
LCD_CAM.cam_ctrl1.cam_clk_inv = 0;
LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;
LCD_CAM.cam_ctrl1.cam_2byte_en = 0;
LCD_CAM.cam_ctrl1.cam_de_inv = 0;
LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;
LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;

//◇LCD_CAM_CAM_RGB_YUV
LCD_CAM.cam_rgb_yuv.cam_conv_8bits_data_inv = 0;
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 0;
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 1;
LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 1;
LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 0;


//②Pin setting (set which pin is the data line of the camera module)
// Route CAM signals to GPIO pins
const struct
{
uint32_t pin;
uint8_t signal;
} mux[] = {
{ param->d0_pin, CAM_DATA_IN0_IDX }, // Connect CAM_DATA_IN0 to d0_pin
{ param->d1_pin, CAM_DATA_IN1_IDX }, // Connect CAM_DATA_IN1 to d1_pin
{ param->d2_pin, CAM_DATA_IN2_IDX }, // Connect CAM_DATA_IN2 to d2_pin
{ param->d3_pin, CAM_DATA_IN3_IDX }, // Connect CAM_DATA_IN3 to d3_pin
{ param->d4_pin, CAM_DATA_IN4_IDX }, // Connect CAM_DATA_IN4 to d4_pin
{ param->d5_pin, CAM_DATA_IN5_IDX }, // Connect CAM_DATA_IN5 to d5_pin
{ param->d6_pin, CAM_DATA_IN6_IDX }, // Connect CAM_DATA_IN6 to d6_pin
{ param->d7_pin, CAM_DATA_IN7_IDX }, // Connect CAM_DATA_IN7 to d7_pin
{ param->pclk_pin, CAM_PCLK_IDX }, // Connect CAM_PCLK to pclk_pin
{ param->href_pin, CAM_H_ENABLE_IDX }, // Connect CAM_H_ENABLE to href_pin
{ param->vsync_pin, CAM_V_SYNC_IDX }, // Connect CAM_V_SYNC_IDX to vsync_pin
{ GPIO_MATRIX_CONST_ONE_INPUT, CAM_H_SYNC_IDX }, // Connect CAM_H_SYNC to GPIO_MATRIX_CONST_ONE_INPUT
{ GPIO_MATRIX_CONST_ONE_INPUT, CAM_V_SYNC_IDX } // Connect CAM_V_SYNC to GPIO_MATRIX_CONST_ONE_INPUT
};

for (int i = 0; i < 13; i++)
{
if (mux.pin != GPIO_MATRIX_CONST_ONE_INPUT)
{
esp_rom_gpio_pad_select_gpio(mux.pin);
}
esp_rom_gpio_connect_in_signal(mux.pin, mux.signal, false);
}

esp_rom_gpio_pad_select_gpio(param->xclk_pin);
esp_rom_gpio_pad_set_drv(param->xclk_pin, 0); //Set drive length (- 0: 5mA - 1: 10mA - 2: 20mA - 3: 40mA)
esp_rom_gpio_connect_out_signal(param->xclk_pin, CAM_CLK_IDX, false, false);

//③Set the value of LCD_CAM_CAM_VH_DE_MODE_EN according to HSYNC.
LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 1;

//Set the value of ④LCD_CAM_CAM_UPDATE.
LCD_CAM.cam_ctrl.cam_update = 1; // Update registers

//⑤Reset
LCD_CAM.cam_ctrl1.cam_reset = 0; //Camera module reset signal
LCD_CAM.cam_ctrl1.cam_afifo_reset = 0; //Async Rx FIFO reset signal

//⑥Interrupt settings
//◇LCD_CAM_LC_DMA_INT_ENA
LCD_CAM.lc_dma_int_ena.lcd_vsync_int_ena = 0;
LCD_CAM.lc_dma_int_ena.lcd_trans_done_int_ena = 0;
LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 1;
LCD_CAM.lc_dma_int_ena.cam_hs_int_ena = 0;

// Allocate LCD_CAM interrupt, keep it disabled
esp_intr_alloc(ETS_LCD_CAM_INTR_SOURCE,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM,
&VSYNCHandler, NULL, &lcd_cam_intr_handle);

esp_intr_enable(lcd_cam_intr_handle);

//◇LCD_CAM_LC_DMA_INT_RAW_REG(Read Only)
//LCD_CAM_LCD_VSYNC_INT_RAW
//LCD_CAM_LCD_TRANS_DONE_INT_RAW
//LCD_CAM_CAM_VSYNC_INT_RAW
//LCD_CAM_CAM_HS_INT_RAW

//◇LCD_CAM_LC_DMA_INT_ST(Read Only)
//LCD_CAM_LCD_VSYNC_INT_ST
//LCD_CAM_LCD_TRANS_DONE_INT_ST
//LCD_CAM_CAM_VSYNC_INT_ST
//LCD_CAM_CAM_HS_INT_ST

//◇LCD_CAM_LC_DMA_INT_CLR(Write Only)
//LCD_CAM_LCD_VSYNC_INT_CLR
//LCD_CAM_LCD_TRANS_DONE_INT_CLR
//LCD_CAM_CAM_VSYNC_INT_CLR
//LCD_CAM_CAM_HS_INT_CLR

//◇LCD_CAM_LC_REG_DATE //*I don't know if it's necessary Is it a register to include timer.h and set the date and time?
//LCD_CAM_LC_DATE = ;


//⑦GDMA settings (register settings)
//■SYSTEM

if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) == 0) {
REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN); //1,Set SYSTEM_DMA_CLK_E in the SYSTEM_PERIP_CLK_EN1_REG register to enable the DMA clock.
REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); //2,Set SYSTEM_DMA_RST in the SYSTEM_PERIP_RST_EN1_REG register and reset the DMA.
REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
}

//■DMA

//Initialize GDMA channel
GDMA.channel[cam_dma_num].in.int_clr.val = ~0;
GDMA.channel[cam_dma_num].in.int_ena.val = 0;

//◇GDMA_IN_CONF0_CHn_REG
GDMA.channel[cam_dma_num].in.conf0.indscr_burst_en = 0;
GDMA.channel[cam_dma_num].in.conf0.in_data_burst_en = 0;
GDMA.channel[cam_dma_num].in.conf0.mem_trans_en = 1;

//◇GDMA_IN_LINK_CHn_REG
GDMA.channel[cam_dma_num].in.link.auto_ret = 1;
GDMA.channel[cam_dma_num].in.link.stop = 0;
GDMA.channel[cam_dma_num].in.link.restart = 0;

//3, To reset the GDMA receive channel and FIFO pointer state machine, first set GDMA_IN_RST_CHn in the GDMA_IN_CONF0_CHx_REG register to 1 and then to 0.
GDMA.channel[cam_dma_num].in.conf0.in_rst = 1;
GDMA.channel[cam_dma_num].in.conf0.in_rst = 0;

err = initDMAdesc();
if (err != ESP_OK) {
//Serial.println("Failed to initialize I2S and DMA");
Serial.println("Failed to initialize DMA");
}

//4, Load inlink and configure GDMA_IN_LINK_ADDR_CHn in the GDMA_IN_LINK_CHn_REG register with the address of the first receive descriptor.
GDMA.channel[cam_dma_num].in.link.addr = ((uint32_t)dma_desc) & 0xfffff;

//Check channel owner
GDMA.channel[cam_dma_num].in.conf1.in_check_owner = 0;

//◇GDMA_IN_PERI_SEL_CHn_REG
//5,Set GDMA_PERI_IN_SEL_CHn in the GDMA_IN_PERI_SEL_CHn_REG register to LCD_CAM.
GDMA.channel[cam_dma_num].in.peri_sel.sel = 5;

//6,Set GDMA_INLINK_START_CHn in the GDMA_IN_LINK_CHn_REG register to enable the GDMA receive channel for data transfer.
GDMA.channel[cam_dma_num].in.link.start = 1;

//7,Wait for the GDMA_IN_SUC_EOF_CHn_INT interrupt indicating that a data frame or packet has been received.
GDMA.channel[cam_dma_num].in.int_clr.in_suc_eof = 1; //Clear interrupt flag
GDMA.channel[cam_dma_num].in.int_ena.in_suc_eof = 1; //EOF interrupt enable

// Allocate GMDA interrupt, keep it disabled
esp_intr_alloc(ETS_DMA_IN_CH0_INTR_SOURCE,
ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM,
&ReceiveHandler, NULL, &gdma_intr_handle);

esp_intr_enable(gdma_intr_handle);

//Set the value of ④LCD_CAM_CAM_UPDATE.
LCD_CAM.cam_ctrl.cam_update = 1; // Update registers

//⑤Reset
LCD_CAM.cam_ctrl1.cam_reset = 0; //Camera module reset signal

LCD_CAM.cam_ctrl1.cam_afifo_reset = 0; //Async Rx FIFO reset signal

Serial.println("initialize LCD_CAM & DMA");

}

Yuricsson
Posts: 2
Joined: Fri Mar 29, 2024 11:02 am

Re: ESP32s3 LCD_CAM interrupt

Postby Yuricsson » Fri Mar 29, 2024 11:15 am

Hello,

I have met the same problem. Since LCD_CAM_CAM_REC_DATA_BYTELEN has maximum 0xffff value, the alternative way to get in_suc_eof interrupt is to use LCD_CAM_CAM_VS_EOF_EN, but it doesn't work. I have tried different types of signal (inverted ot not). I think it is an ESP32-S3 issue.
The only way that I can fugire out is to catch GPIO edge interrupt on VSYNC pin. Now I don't use GDMA in_suc_eof or LCD_CAM module interrupts. In my case it works fine, I am using AR0135 image sensor in snapshot(triggered) mode with windows of different size.
Thanks to GPIO system that doesn't switch off dedicated pins from basic IO interrupts. :-)

Yuricsson

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 229 guests