STM Урок 69. HAL. LTDC. DMA2D. Часть 3
Урок 69
Часть 3
HAL. LTDC. DMA2D
В предыдущей части нашего занятия мы переписали код некоторых наших функций по выводу различной информации на дисплей с использованием функционала видеоускорителя DMA2D и проверили это в тестах.
Ну и напоследок самое интересное — смешивание. А раз интересное — значит и сложное.
Вернёмся в ltdc.c и напишем функцию, которая будет выводить изображение в формате BMP не сразу на экран, а в отдельную область памяти, как бы подготавливая виртуальный экран. Мы можем скопировать нашу функцию вывода изображения на экран и затем уже её подправить
//—————————————-
void TFT_DrawBitmapToMem(uint32_t Xpos, uint32_t Ypos, uint8_t *pbmp, uint8_t *pdst)
{
uint32_t index = 0, width = 0, height = 0, bit_pixel = 0;
/* Get bitmap data address offset */
index = *(__IO uint16_t *) (pbmp + 10);
index |= (*(__IO uint16_t *) (pbmp + 12)) << 16;
/* Read bitmap width */
width = *(uint16_t *) (pbmp + 18);
width |= (*(uint16_t *) (pbmp + 20)) << 16;
/* Read bitmap height */
height = *(uint16_t *) (pbmp + 22);
height |= (*(uint16_t *) (pbmp + 24)) << 16;
/* Read bit/pixel */
bit_pixel = *(uint16_t *) (pbmp + 28);
/* Set the address */
address = hltdc.LayerCfg[0].FBStartAdress + (((X_SIZE*Ypos) + Xpos)*(4));
/* Bypass the bitmap header */
pbmp += (index + (width * (height - 1) * (bit_pixel/8)));
//определим формат пикселя
if ((bit_pixel/8) == 4)
{
TFT_FillScreen(0xFFFF0000);
}
else if ((bit_pixel/8) == 2)
{
TFT_FillScreen(0xFF00FF00);
}
else
{
for(index=0; index < height; index++)
{
hdma2d.Init.Mode = DMA2D_M2M_PFC;
hdma2d.Init.ColorMode = DMA2D_ARGB8888;
hdma2d.Init.OutputOffset = 0;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0xFF;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB888;
hdma2d.LayerCfg[1].InputOffset = 0;
if(HAL_DMA2D_Init(&hdma2d) == HAL_OK)
{
if(HAL_DMA2D_ConfigLayer(&hdma2d, 1) == HAL_OK)
{
if (HAL_DMA2D_Start(&hdma2d, (uint32_t) pbmp, (uint32_t) pdst, width, 1) == HAL_OK)
{
HAL_DMA2D_PollForTransfer(&hdma2d, 10);
}
}
}
pdst+= (X_SIZE*4);
pbmp -= width*(bit_pixel/8);
}
}
//Установим настройки DMA2D в DMA2D_M2M_BLEND
hdma2d.Init.Mode = DMA2D_M2M_BLEND;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_ARGB8888;
hdma2d.Init.OutputOffset = 0;
hdma2d.LayerCfg[1].InputOffset = 0;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
hdma2d.LayerCfg[0].InputOffset = 0;
hdma2d.LayerCfg[0].InputColorMode = DMA2D_INPUT_ARGB8888;
hdma2d.LayerCfg[0].AlphaMode = DMA2D_REPLACE_ALPHA;
hdma2d.LayerCfg[0].InputAlpha = 0;
if(HAL_DMA2D_Init(&hdma2d) == HAL_OK)
{
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
}
//вернём формат пикселя по умолчанию
bit_pixel = 0;
}
//-------------------------------------------------
Как мы видим, изменения у нас незначительные, и касаются они в основном адреса приёмника, который нам уже определять не пришлось, так как он задан во входном параметре.
Создадим для этой функции прототип и напишем следующий кода в main(), который подготовит заранее до всех тестов первое изображение в четвёртой области памяти SDRAM сразу после монтирования файловой системы
Error_Handler();
}
//Копируем содержимое файла последнего рисунка в область памяти bmp1
OpenBMP((uint8_t *)bmp1,"image01.bmp");
//Подготовим рисунок во втором входном буфере видеоускорителя DMA2D
TFT_DrawBitmapToMem(0,0,(uint8_t *)bmp1,(uint8_t *)dma2d_in2);
Теперь начнём писать очередной тест в бесконечном цикле, который будет также выводить рисунки один за другим, но в момент уже плавного вывода следующего рисунка будет плавно убирать предыдущий
HAL_Delay(3000);
}
//плавный вывод картинок
for(j=1;j<=10;j++)
{
//Копируем содержимое файла очередного рисунка в область памяти bmp1
sprintf(str1,"image%02d.bmp",j);
OpenBMP((uint8_t *)bmp1,str1);
//Подготовим рисунок в нужном буфере видеоускорителя DMA2D в зависимости от чётности файла
if(j%2!=0)
TFT_DrawBitmapToMem(0,0,(uint8_t *)bmp1,(uint8_t *)dma2d_in1);
else
TFT_DrawBitmapToMem(0,0,(uint8_t *)bmp1,(uint8_t *)dma2d_in2);
}
}
/* USER CODE END 3 */
Сначала мы в цикле последовательно перебираем рисунки на карте SD и копируем их оттуда во вторую область памяти SDRAM (если первой считать отведённую под видеопамять), а затем мы, в зависимости от того, чётный номер рисунка или нечётный, копируем его активное содержимое с помощью функции TFT_DrawBitmapToMem в третью или четвёртую область памяти SDRAM. Это делается для того, чтобы каждый предыдущий рисунок не копировать из четвёртой области в третью.
Теперь опять вернёмся в наш файл ltdc.c и напишем там функцию, меняющую слои в видеоускорителе
//-------------------------------------------------
void DMA2D_LayersAlphaReconfig(uint32_t alpha1, uint32_t alpha2)
{
hdma2d.LayerCfg[1].InputAlpha = alpha1;
hdma2d.LayerCfg[0].InputAlpha = alpha2;
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
HAL_DMA2D_ConfigLayer(&hdma2d, 0);
}
//-------------------------------------------------
Создадим на неё прототип в хедер-файле и вернёмся к нашему тесту в бесконечный цикл, в котором плавно будем смешивать слои, убавляя прозрачность в одном слое и прибавляя в другом и после этого каждый раз вызывая функцию смешивания
TFT_DrawBitmapToMem(0,0,(uint8_t *)bmp1,(uint8_t *)dma2d_in2);
//начнём плавный вывод следующего рисунка
for(i=0;i<=255;i++)
{
DMA2D_LayersAlphaReconfig(i,255-i);
//в зависимости от чётности рисунка вызовем смешивание
if(j%2!=0)
HAL_DMA2D_BlendingStart_IT(&hdma2d, (uint32_t) dma2d_in1,(uint32_t) dma2d_in2,
LCD_FRAME_BUFFER, 480, 272);
else
HAL_DMA2D_BlendingStart_IT(&hdma2d, (uint32_t) dma2d_in2,(uint32_t) dma2d_in1,
LCD_FRAME_BUFFER, 480, 272);
HAL_Delay(10);
}
HAL_Delay(3000);
}
}
/* USER CODE END 3 */
Ну вот вроде бы и всё.
Соберём проект, прошьём контроллер и полюбуемся результатом нашей проделанной работы

Я понимаю, что невозможно показать всю красоту плавного превращения одного изображения в другое в картинке, поэтому советую всё-таки посмотреть и видеоверсию урока, ссылка на которую находится внизу страницы.
Таким образом, мы теперь неплохо умеем самостоятельно безо всяких лишних библиотек управлять работой видеоускорителя DMA2D.
Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)



Добрый день, хотельсь бы узнать будут ли такие же уроки по STM769i-disco? В плане DSI интерфейса?
Добрый день!
Отличная тема.
Но, к сожалению, я пока с этим протоколом сам не разобрался до конца. Уж очень он тяжелый попался.