Esp32 i2s parallel driver with FreeRtos
Posted: Mon Mar 02, 2020 7:46 pm
Hi,
I am working in a i2s parallel driver that will use lvgl hmi library, in 2 buffer mode.
Each buffer can store 7680 pixels(1/20 display size).
The i2s parallel driver at firt is working, but i want to put 2 buffer to work synchronously with dma interrupt ie: enable first buffer transfer with dma, while the second buffer are rendered by cpu.
If the cpu finish render second buffer while dma are transfer first buffer yet, the new dma transfer need to wait for dma finish first transfer.
I dont want to polling a flag to know if dma are free or not.
This use cpu for nothing.
The problem are that littlevgl “display_flush” function happens asynchronously and the two buffer need be send synchronously by the driver.
I want to use FreeRtos in some way, but i have many doubts.
I think that my problem can be solved in some way with this:
“https://www.freertos.org/RTOS-task-notifications.html”
I also need to know if the “dma / i2s io” resource is free or not, but mutex cannot be used within the interrupt subroutine.
I am using esp-idf.
What i want to achieve ?
Using FreeRtos to sync two buffers to be sent by “i2s dma io” driver.
In the future i also want to synchronize the sending buffer with the tearing signal of the display.
Below folow the code of what i want to do:
Thank's.
I am working in a i2s parallel driver that will use lvgl hmi library, in 2 buffer mode.
Each buffer can store 7680 pixels(1/20 display size).
The i2s parallel driver at firt is working, but i want to put 2 buffer to work synchronously with dma interrupt ie: enable first buffer transfer with dma, while the second buffer are rendered by cpu.
If the cpu finish render second buffer while dma are transfer first buffer yet, the new dma transfer need to wait for dma finish first transfer.
I dont want to polling a flag to know if dma are free or not.
This use cpu for nothing.
The problem are that littlevgl “display_flush” function happens asynchronously and the two buffer need be send synchronously by the driver.
I want to use FreeRtos in some way, but i have many doubts.
I think that my problem can be solved in some way with this:
“https://www.freertos.org/RTOS-task-notifications.html”
I also need to know if the “dma / i2s io” resource is free or not, but mutex cannot be used within the interrupt subroutine.
I am using esp-idf.
What i want to achieve ?
Using FreeRtos to sync two buffers to be sent by “i2s dma io” driver.
In the future i also want to synchronize the sending buffer with the tearing signal of the display.
Below folow the code of what i want to do:
Code: Select all
// Main function: Test only without littlevgl yet.
void main( )
{
lv_area_t area;
area.x1 = 0x0010;
area.x2 = 0x013f;
area.y1 = 0x0005;
area.y2 = 0x01df;
while(1)
{
display_flush ( NULL, &area );
// vTaskDelay( 1000 / portTICK_PERIOD_MS );
}
}
void display_flush ( lv_disp_drv_t* drv, lv_area_t* area )
{
static bool flag = 1;
flag = !flag;
if ( flag == 0 )
{
commands_to_init_pixels_write ( area ); // fills command buffer with some display commands.
i2s_lcd_write_pixels_a ( (uint32_t) 15360 );
}
else
{
commands_to_init_pixels_write ( area );
i2s_lcd_write_pixels_b ( (uint32_t) 15360 );
}
}
int i2s_lcd_write_pixels_a ( uint32_t length ) // length = bytes number. length <= 15360. 2 bytes per pixel.
{
uint32_t i;
uint8_t* ptr;
if ( length > pixels_size_in_bytes )
{
printf( "error: length > pixels_size_in_bytes.\n\n" );
return -1;
}
ptr = (uint8_t *) &buffer_a; // buffer_a are lv_color_t(uint16_t) the buffer registered to littlevgl driver.
for ( i = 0 ; i < length ; i = i + 2 )
{
buf_a[ i ] = ptr[ i ]; // buf_a are uint32_t buffer.
buf_a[ i + 1 ] = ptr[ i + 1 ];
}
fill_dma_descriptor_a( length );
fill_dma_descriptor_command ( 11 );
Are the shared resource i2s0 free ?
If i2s0 is free, blocks access to the i2s peripheral and calls the task "task_send_buffer_a_to_display ()"
to fire the dma transfer. After the dma ends, it releases access to the i2s peripheral within the dma interrupt (i2s is still sending data through the physical port, because of the 64 byte fifo memory).
If i2s0 is not free, the task need waiting here until "dma/i2s" isr subroutine send message(unlock).
Here the function did not init dma transfer, but need return from this function i think ( confuse ).
return 2019;
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// tasks can be static inline ?
static inline void task_send_buffer_a_to_display( )
{
i2s_dma_trans_start_interrupt( (uint32_t) &dma_desc_buf_command );
while(!I2SX.state.tx_idle); // Wait the peripheral i2s stay free. It is not worth switching tasks on FreeRtos here because sending 11 bytes takes = ~ 1 us and the TICKRATE is around 1 ms - 10 ms.
pixels_flag = 1; // Informs that it will transfer pixels and not commands. Reset inside "i2s/dma" isr.
i2s_dma_trans_start_interrupt( (uint32_t) &dma_desc_buf_a[0] );
}
Same for "int i2s_lcd_write_pixels_b ( uint32_t length )" and for "static inline void task_send_buffer_b_to_display( )" .
static void IRAM_ATTR i2s_isr ( )
{
if ( I2SX.int_st.out_eof )
{
if ( pixels_flag ) // send pixels flag indication.
{
pixels_flag = 0;
lv_disp_t * disp = lv_refr_get_disp_refreshing();
lv_disp_flush_ready(&disp->driver);
// Release i2s/dma resource. Cannot use mutex inside isr.
// xTaskNotifyFromISR(); // Notify the task to init dma transfer immediately if the other buffer is already ready to send.
}
}
I2SX.int_clr.val = I2SX.int_st.val;
}
Thank's.