Работаем с дисплеем ILI9341 по DMA

Я уже писал о работе с дисплеем ILI9341.
Частота обновления экрана при этом оставляла желать лучшего. Даже после игр с частотой SPI ничего приемлемого добиться не удалось. На этой почве бросил это дело на пол пути. А недавно увидел вот такое:

Т.е. возможно и кино крутить на этом дисплее.
Сразу подумалось о DMA. Быстренько набросал такое:

u32 pixels_left = 0;
u8* p_buffer;

void spi1_init_dma(u8* buffer,u32 count)
{
    DMA_InitTypeDef  DMA_InitStructure;
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
 
    /* Deinitialize DMA Streams */
    DMA_DeInit(DMA2_Stream3); //SPI1_TX_DMA_STREAM

    if (count > MAX_DMA_REQUEST)
    {
        pixels_left = count - MAX_DMA_REQUEST;
        count = MAX_DMA_REQUEST;
    } else {
        pixels_left = 0;
    }
    
    //tx       
    DMA_InitStructure.DMA_Channel = DMA_Channel_3;  
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = (uint32_t)(count);
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream3, &DMA_InitStructure);

    NVIC_EnableIRQ(DMA2_Stream3_IRQn);
    
    /* Разрешаем прерывание по окончанию передачи данных */
    DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, ENABLE);

    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
}

void start_dma(u8* buffer,u32 count)
{
    spi_transfer = 1;
    TFT_DC_SET;
    TFT_CS_RESET;
    p_buffer = buffer;
    
    spi1_init_dma(p_buffer, count);
    
    /* Включаем DMA */
    DMA_Cmd(DMA2_Stream3, ENABLE);
}

Тут есть тонкий момент. Несмотря на то, что поле BufferSize в структуре имеет 32 бита, на самом деле DMA умеет передавать только 65535 байт. А нам нужно передать 320*240*2, что несколько больше. Пришлось разбить на несколько тразакций.

//tx
void DMA2_Stream3_IRQHandler()
{
    /* Проверяем наше ли это прерывание ;) */
    if(DMA_GetITStatus(DMA2_Stream3, DMA_IT_TCIF3))
    {
        spi_transfer = 0;
        if (pixels_left)
        {
            p_buffer+=MAX_DMA_REQUEST;
            spi1_init_dma(p_buffer, pixels_left);
            spi_transfer = 1;
            DMA_Cmd(DMA2_Stream3, ENABLE);
        } else {
            TFT_CS_SET;
        }
        /* Очищаем бит обработки прерывания */
        DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3);  
    } 
}

Скорость увеличилась в разы. Но все равно заметно обновление кадра. Но при обновлении части экрана должно работать отлично.

PS обнаружилась проблема. GIMP экспортирует картинку в массив. Только вот слать нужно меняя байты местами. Как это сделать с DMA пока не придумал ;(

PPS Испытал дисплей со своей малиной

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий

  1. Антон

    Пытаюсь тоже побороть тормазнутость этого дисплея, с дма натолкнулся на грабли что нужно программно дёргать DC и CS, поэтому начал делать на прерываниях SPI, но всё равно не то пальто.

    Можешь поделится проектом? ну пожааааааалуйста

    Ответить
  2. lamazavr

    я по dma слал только данные изображения

    https://www.dropbox.com/s/9oj0w8yae4mfs0c/tft_lcd_spi.rar

    Ответить