Hi everyone,
I'd like to know what is wrong with my approach.
Goal: getting 16384 bytes (16K) buffer filled by camera via I2S->FIFO->DMA
Platform: ESP32 and IDF 5.0.1, FreeRTOS
Problem: can't get IN_SUC_EOF interrupt triggered.
General approach:
After configuring all the I2S/FIFO/DMA control registers and prior to starting off I perfrom the following key steps:
Setting up all 5 in_link descriptors for the maximum size of 4092 bytes:
- in_link[0].length = 4092
- in_link[1].length = 4092
- in_link[2].length = 4092
- in_link[3].length = 4092
- in_link[4].length = 16
FIFO is also configured to a maximum of 64 words (256 bytes):
- I2S0.fifo_conf.rx_data_num = 0b111111
I tell the DMA to read the total of 16384 bytes and fire the IN_SUC_EOF interrupt:
- I2S0.rx_eof_num = 16384
- I2S0.int_ena.in_suc_eof = 1
- in_link[0].eof = 0
- in_link[1].eof = 0
- in_link[2].eof = 0
- in_link[3].eof = 0
- in_link[4].eof = 1
Next, I start off DMA/in_link/FIFO and allow I2S interrupts:
- I2S0.in_link.start = 1
- I2S0.conf.rx_start = 1
- I2S0.fifo_conf.dscr_en = 1
- esp_intr_enable(i2s_handle)
After that the transmission looks to start as I can get all 5 IN_DONE interrupts if I set them appropriately.
However, IN_SUC_EOF interrupt won't ever get fired. I can't figure out why this is happening...
I haven't looked into the data being received yet and cannot say whether I'm receiving garbage or not. Currently, I'm stuck with the interrupt.
Oh yes, IDF is also configured to put I2S stuff into IRAM.
PS. Also, I've noticed a wierd change in "in_link[4].value" AFTER in_link[4] has been processed (IN_DONE triggered): in_link[4].value = 4092 instead of 16! Anyways, it might be an intended behaviour of I2S in_link internals whatsoever... So I don't actually care.
So the question is: how do you get IN_SUC_EOF triggered in this case? Am I missing any key step?
ESP32 :: I2S DMA interrupt IN_SUC_EOF problem
-
- Posts: 1708
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: ESP32 :: I2S DMA interrupt IN_SUC_EOF problem
Did you set the 'owner' of the DMA descriptors to 1 = 'DMA controller'?
Re: ESP32 :: I2S DMA interrupt IN_SUC_EOF problem
Yes, sure.
Below is my sample debug function to fill-in all the links and its output:
00. [ addr: 3FFB13BC ] bytes: 4092 / 4092, buf = 3FFB13F8, next_addr = 3FFB13C8 EOF = 0
01. [ addr: 3FFB13C8 ] bytes: 4092 / 4092, buf = 3FFB23F4, next_addr = 3FFB13D4 EOF = 0
02. [ addr: 3FFB13D4 ] bytes: 4092 / 4092, buf = 3FFB43EC, next_addr = 3FFB13E0 EOF = 0
03. [ addr: 3FFB13E0 ] bytes: 4092 / 4092, buf = 3FFB73E0, next_addr = 3FFB13EC EOF = 0
04. [ addr: 3FFB13EC ] bytes: 16 / 4092, buf = 3FFBB3D0, next_addr = 00000000 EOF = 1
Filler function:
And if it might matter below is the init code for the I2S peripheral:
* * *
Finally, I enable GPIO interrupt to get a VSYNC signal and effectively launch the whole system:
The interrupt handler for I2S is responsible to catch I2S interrupts and post a message to the the worker-task:
And the worker-task waits for the message. But it fails for some reason...
However, IN_DONE interrupts are received...
Below is my sample debug function to fill-in all the links and its output:
00. [ addr: 3FFB13BC ] bytes: 4092 / 4092, buf = 3FFB13F8, next_addr = 3FFB13C8 EOF = 0
01. [ addr: 3FFB13C8 ] bytes: 4092 / 4092, buf = 3FFB23F4, next_addr = 3FFB13D4 EOF = 0
02. [ addr: 3FFB13D4 ] bytes: 4092 / 4092, buf = 3FFB43EC, next_addr = 3FFB13E0 EOF = 0
03. [ addr: 3FFB13E0 ] bytes: 4092 / 4092, buf = 3FFB73E0, next_addr = 3FFB13EC EOF = 0
04. [ addr: 3FFB13EC ] bytes: 16 / 4092, buf = 3FFBB3D0, next_addr = 00000000 EOF = 1
Filler function:
Code: Select all
uint8_t dma_links_init(bool printInfo, lldesc_t *ptrLink, uint32_t *dma_buf32){
int32_t total_bytes = DMA_JUNK_BUFFER_MAX_BYTES; // defined as 16384 bytes
uint8_t i=0;
do{
total_bytes -= DMA_LINK_DATA_MAX_BYTES; // defined as 4092
ptrLink->owner = 1; // = DMA
ptrLink->eof = (total_bytes<=0)? 1 : 0;
ptrLink->sosf = 0;
ptrLink->offset = 0;
ptrLink->size = DMA_LINK_DATA_MAX_BYTES;
ptrLink->length = (total_bytes>=0)? DMA_LINK_DATA_MAX_BYTES : (DMA_LINK_DATA_MAX_BYTES + total_bytes);
ptrLink->buf = (const volatile uint8_t*) dma_buf32;
ptrLink->empty = (uint32_t) ((total_bytes <= 0)? 0 : (ptrLink + 1));
if(printInfo){
printf("%02d. [ addr: %08lX ] bytes: %d / %d, buf = %08lX, next_addr = %08lX EOF = %d\n",
i,
(uint32_t)ptrLink,
ptrLink->length,
ptrLink->size,
(uint32_t)ptrLink->buf,
(uint32_t)(ptrLink->empty),
ptrLink->eof);
fflush(0);
}
i++;
dma_buf32 += (i*DMA_LINK_DATA_MAX_BYTES/4);
ptrLink++;
}while(total_bytes > 0);
return i;
}
And if it might matter below is the init code for the I2S peripheral:
Code: Select all
periph_module_enable(PERIPH_I2S0_MODULE);
// Module Reset = TRM p.312 "In order to finish a reset operation, the corresponding bit should be set and then cleared by software."
I2S0.conf.val |= (BIT_RX_RESET | BIT_RX_FIFO_RESET); // = 0b1 | 0b100
I2S0.conf.val &= ~(BIT_RX_RESET | BIT_RX_FIFO_RESET);
I2S0.lc_conf.val |= (BIT_DMA_FSM_IN_RESET | BIT_DMA_AHBM_FIFO_RESET | BIT_DMA_ABM_RESET); // = 0b1 | 0b100 | 0b1000
I2S0.lc_conf.val &= ~(BIT_DMA_FSM_IN_RESET | BIT_DMA_AHBM_FIFO_RESET | BIT_DMA_ABM_RESET);
I2S0.conf.rx_slave_mod = 1;
I2S0.conf.rx_right_first = 0;
I2S0.conf.rx_msb_right = 0;
I2S0.conf.rx_msb_shift = 0;
I2S0.conf.rx_mono = 0;
I2S0.conf.rx_short_sync = 0;
I2S0.conf2.lcd_en = 1;
I2S0.conf2.camera_en = 1;
I2S0.clkm_conf.clkm_div_a = 0;
I2S0.clkm_conf.clkm_div_b = 0;
I2S0.clkm_conf.clkm_div_num = 2;
I2S0.fifo_conf.dscr_en = 1;
I2S0.fifo_conf.rx_fifo_mod = SM_0A00_0B00;
I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
I2S0.fifo_conf.rx_data_num = 0b111111;
I2S0.conf_chan.rx_chan_mod = 1;
I2S0.sample_rate_conf.rx_bits_mod = 0;
I2S0.timing.val = 0;
I2S0.timing.rx_dsync_sw = 1;
I2S0.int_ena.val = 0;
I2S0.int_clr.val = I2S0.int_raw.val;
I2S0.in_link.addr = ((uint32_t)(&dma_links)) & 0b11111111111111111111; // 20-bit length. just to look fancy :)
I2S0.rx_eof_num = DMA_JUNK_BUFFER_MAX_BYTES; // = 16834
esp_intr_enable(hCam->hIntr);
* * *
Finally, I enable GPIO interrupt to get a VSYNC signal and effectively launch the whole system:
Code: Select all
void IRAM_ATTR I2S_Vsync_ISR(){
I2S0.int_clr.in_suc_eof = 1;
I2S0.int_ena.in_suc_eof = 1;
// I2S0.int_clr.in_done = 1; // for testing and debugging purposes
// I2S0.int_ena.in_done = 1; // for testing and debugging purposes
// here's an optional call to set all the link descriptors any other call (in case the content DOES CHANGE; still to be examined)
dma_links_init(0, (lldesc_t*)&dma_links, (uint32_t*)&dma_junk_buf);
// start off DMA
I2S0.in_link.start = 1;
I2S0.conf.rx_start = 1;
gpio_intr_disable(hCam->pin_vsync); // self-disabling
}
The interrupt handler for I2S is responsible to catch I2S interrupts and post a message to the the worker-task:
Code: Select all
void IRAM_ATTR I2S_ISR(){
BaseType_t Woken = 0;
uint32_t msg = MSG_DMA_IN_SUC_EOF;
// NOTE: the int flag is cleared only on successful QueueSend() so that in case of failure (queue is full) ESP32 re-enters the handler again:
if(xQueueSendFromISR(hCam->hQueue, &msg, &Woken) == pdTRUE) I2S0.int_clr.in_suc_eof = 1;
else{
// perform some termination stuff
// ..
ESP_DRAM_LOGE("in_suc_eof", "ERROR: xQueueSendFromISR() failed\n"); // just for debugging purposes
}
if(Woken) portYIELD_FROM_ISR();
}
And the worker-task waits for the message. But it fails for some reason...
However, IN_DONE interrupts are received...
Re: ESP32 :: I2S DMA interrupt IN_SUC_EOF problem
hi
change
I2S0.rx_eof_num = 16384
to
I2S0.rx_eof_num = 16384 / sizeof(uint32_t); // count in 32 bit word
from esp32 referense manual(page 124):
However, unlike the SPI DMA channels, the data size for a single transfer is one word, or four bytes.
REG_I2S_RX_EOF_NUM[31:0] bit in I2S_RXEOF_NUM_REG is used for configuring the data size of a single transfer operation, in multiples of one word.
change
I2S0.rx_eof_num = 16384
to
I2S0.rx_eof_num = 16384 / sizeof(uint32_t); // count in 32 bit word
from esp32 referense manual(page 124):
However, unlike the SPI DMA channels, the data size for a single transfer is one word, or four bytes.
REG_I2S_RX_EOF_NUM[31:0] bit in I2S_RXEOF_NUM_REG is used for configuring the data size of a single transfer operation, in multiples of one word.
Re: ESP32 :: I2S DMA interrupt IN_SUC_EOF problem
Oopppsss....
I've read that section for bazillion of times and knew that quite well! And the trick was that I took that "4" divisor in the popup window for this very thing whereas it stands for another purpose!
Right you are, the correct value would be:
I2S0.rx_eof_num = DMA_JUNK_BUFFER_MAX_BYTES/4;
It was really my blurred eye ((((
THANK YOU SO MUCH FOR TAKING ME OUT OF THIS HELL )))))))) !!!!!!!
I've read that section for bazillion of times and knew that quite well! And the trick was that I took that "4" divisor in the popup window for this very thing whereas it stands for another purpose!
Right you are, the correct value would be:
I2S0.rx_eof_num = DMA_JUNK_BUFFER_MAX_BYTES/4;
It was really my blurred eye ((((
THANK YOU SO MUCH FOR TAKING ME OUT OF THIS HELL )))))))) !!!!!!!
Re: ESP32 :: I2S DMA interrupt IN_SUC_EOF problem
FYI:
If continuous read operations of constant length are expected then it's a good idea to configure link descriptors once only!
The only field will change is the in_link.owner - this bit is set to NULL once DMA has processed each link. Found this while fiddling with DMA...
If continuous read operations of constant length are expected then it's a good idea to configure link descriptors once only!
The only field will change is the in_link.owner - this bit is set to NULL once DMA has processed each link. Found this while fiddling with DMA...
Who is online
Users browsing this forum: Google [Bot] and 107 guests