STM Урок 75. HAL. LTDC. EmWin. BMP. Multiple Buffering

 

 

 

 

Урок 75

 

HAL. LTDC. EmWin. BMP. Multiple Buffering

 

 

Продолжаем работу с библиотекой emWin.

Работаем мы также с платой STM32F746-DISCOVERY.

Сегодня мы попробуем поработать с графикой и отобразить изображения в формате BMP, при этом используя многобуферное прорисовывание экрана.

Я думаю, все понимают, зачем это нужно. Практически мы этим уже занимались, когда выводили рисунки без использования библиотеки. Прежде чем выводить рисунки, мы копировали их содержимое в быструю память SDRAM, а затем уже подготовленный таким образом виртуальный экран мы отображали на реальном дисплее, используя операцию копирования из памяти в память.

Так как библиотека emWin поддерживает также работу с различными оконными элементами управления, то, само собой, не совсем эффективно было бы прорисовывать работу таких элементов сразу на реальном экране. Поэтому разработчики библиотеки реализовали механизм виртуальных экранов с помощью Multiple Buffering. В этом случае все прорисовки делаются в виртуальном дисплее, то есть в заднем буфере, а затем, когда уже вся работа с прорисовкой закончена, по команде, приходящей из обработчика прерываний окончания кадра, кадр из заднего буфера приходит в память основного экрана. Также можно использовать и три буфера, что ещё эффективнее, когда прорисовки очень много, причём библиотека позволяет полностью автоматизировать данный процесс, чтобы разработчик проекта не задумывался постоянно над реализацией данных прорисовок.

Мы попробуем оценить преимущество мультибуферной технологии пока на выводах изображений в формате BMP, а в дельнейших занятиях ещё и в оконном менеджере. Также известно, что библиотека emWin поддерживает вывод изображений и других форматов, но BMP-формат был выбран потому, что изображения в этом формате являются файлами большего размера, нежели изображения, кодированные в другой какой-либо поддерживаемый формат, что позволит лучше оценить преимущество виртуальных экранов.

Проект мы создадим из проекта урока 73 с именем LTDC_EMWIN_001, присвоив ему новое имя LTDC_EMWIN_001 и запустим его в Cube MX.

Пока ничего не меняем здесь.

Сгенерируем проект, запустим System Workbench и подключим там его.

Настройки проекта делаем точно такие же, как и в прошлом уроке.

Соберём проект и запустим его, чтобы проверить, что он работает.

Пока давайте попробуем вывести изображения с подготовкой экрана с помощью нашей функции, которую мы использовали ранее.

В функции main() удалим наш текстовый тест, который мы написали на прошлом занятии

 

GUI_SetFont(&GUI_Font32B_1);

GUI_SetTextAlign(GUI_TA_CENTER);

GUI_SetColor(GUI_ORANGE);

GUI_DispStringAt("Hello STemWin!!!", 240, 120);

/* USER CODE END 2 */

 

Попробуем вывести на экран последовательно несколько рисунков

 

    Error_Handler();

  }

  OpenBMP((uint8_t *)bmp1,"image01.bmp");

  GUI_BMP_Draw((void*) bmp1,0,0);

  HAL_Delay(2000);

  OpenBMP((uint8_t *)bmp1,"image02.bmp");

  GUI_BMP_Draw((void*) bmp1,0,0);

  HAL_Delay(2000);

  OpenBMP((uint8_t *)bmp1,"image03.bmp");

  GUI_BMP_Draw((void*) bmp1,0,0);

  HAL_Delay(2000);

  OpenBMP((uint8_t *)bmp1,"image04.bmp");

  GUI_BMP_Draw((void*) bmp1,0,0);

  HAL_Delay(2000);

  /* USER CODE END 2 */

 

Соберём код и посмотрим результат

 

image00 image01 image02 image03

 

Картинки выводятся быстро и хорошо, но здесь применена наша функция копирования буфера, которая вряд ли сможет работать в других случаях, отличных от вывода изображений, а также она не включена в автоматизацию процессов библиотеки emWin. Поэтому будем как-то исправлять это, подключая постепенно механизмы библиотеки.

Во первых нам надо найти функцию в библиотеке, которая будет читать файлы изображений с флеш-карты. Для вывода изображений кроме функции GUI_BMP_Draw(), которая пользуется готовым буфером изображения, существует функция GUI_BMP_DrawEx()

 

image04

 

Данная функция использует внешнюю функцию, которая и будет открывать с внешнего носителя файл с изображением. Причём пишется эта функция определённым образом и имеет строго определённый тип входящих аргументов

 

image05

 

Добавим две глобальные переменные

 

extern char SD_Path[4]; /* SD logical drive path */

FRESULT fresult;

uint8_t _acBuffer[4096];

 

Теперь напишем данную функцию выше функции main()

 

//-----------------------------------------------------------------------------

int APP_GetData(void * p, const U8 **ppData, unsigned NumBytesReq, U32 Off){

  //You must create an pointer on structure

  FIL *phFile;

  //and then initialize the pointer value is passed to the function APP_GetData

  phFile=(FIL*) p;

  //And then use this pointer to your function

  f_lseek(phFile,Off);

  fresult=f_read(phFile,_acBuffer,NumBytesReq,(void*)&bytesread);

  *ppData = _acBuffer;

  return bytesread;

}

//-----------------------------------------------------------------------------

/* USER CODE END 0 */

 

Как мы можем видеть, в качестве входящих аргументов здесь присутствуют указатель на данные, на временный буфер для блока, количество считанных байтов, а также смещение считываемых данных. В теле функции мы устанавливаем указатель на место считывания в файле и собственно считываем данные в нужном количестве. Затем мы устанавливаем указатель на считанные данные и возвращаем их количество. Всё просто, причём в функции считывается только один блок, видимо механизм движения по блокам реализован где-то внутри библиотеки.

 

 

Теперь исправим код в функции main() для вывода рисунков

 

  Error_Handler();

}

fresult=f_open(&MyFile,"image01.bmp", FA_READ);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

f_close(&MyFile);

HAL_Delay(2000);

fresult=f_open(&MyFile,"image02.bmp", FA_READ);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

f_close(&MyFile);

HAL_Delay(2000);

fresult=f_open(&MyFile,"image03.bmp", FA_READ);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

f_close(&MyFile);

HAL_Delay(2000);

fresult=f_open(&MyFile,"image04.bmp", FA_READ);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

f_close(&MyFile);

HAL_Delay(2000);

/* USER CODE END 2 */

 

Мы видим, что теперь мы выводим рисунки на экран уже без использования нашей функции OpenBMP.

Только теперь вывод изображений будет идти напрямую в дисплей без использования буфера.

Соберём код, запустим его на выполнение, и увидим, что рисунки у нас выводятся, но уже не разко, а постепенно снизу вверх

 

image06

 

Но зато вывод изображения теперь происходит исключительно с помощью библиотеки emWin.

Но только всё равно это непорядок. Надо как-то этот вопрос торможения вывода на экран решать.

Так как автоматическая мультибуферизация поддерживатеся только при использовании оконного менеджера, то нам придется включать и выключать её при необходимости вручную.

Поэтому вствим это в наш код вывода картинок

 

fresult=f_open(&MyFile,"image01.bmp", FA_READ);

GUI_MULTIBUF_BeginEx(0);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

GUI_MULTIBUF_EndEx(0);

f_close(&MyFile);

HAL_Delay(2000);

fresult=f_open(&MyFile,"image02.bmp", FA_READ);

GUI_MULTIBUF_BeginEx(0);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

GUI_MULTIBUF_EndEx(0);

f_close(&MyFile);

HAL_Delay(2000);

fresult=f_open(&MyFile,"image03.bmp", FA_READ);

GUI_MULTIBUF_BeginEx(0);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

GUI_MULTIBUF_EndEx(0);

f_close(&MyFile);

HAL_Delay(2000);

fresult=f_open(&MyFile,"image04.bmp", FA_READ);

GUI_MULTIBUF_BeginEx(0);

GUI_BMP_DrawEx(APP_GetData, &MyFile,0,0); // Draw image

GUI_MULTIBUF_EndEx(0);

f_close(&MyFile);

HAL_Delay(2000);

 

Если мы сейчас попробоем запустить код на выполнение, то рисунки будут выводиться быстро, но при переключении на другой рисунок у нас будут определённые артефакты.

Чтобы этого избежать, в бесконечный цикл вставим вот такую задержку

 

/* USER CODE BEGIN 3 */

  GUI_Delay(100);

}

/* USER CODE END 3 */

 

Как не парадоксально, но это поможет на 100%. А парадоксально это потому, что мы вроде в коде ещё не дошли до бесконечного цикла.

Соберём код, запустим его на выполнение, и увидим, что рисунки наши опять стали выводиться мгновенно, причём, как мне кажется, это происходит даже лучше, чем у нас было раньше без применения библиотеки emWin

 

image00image01 image02 image03

 

Теперь мы можем спокойно удалить функцию OpenBMP, ибо при работе с библиотекой она теперь нам вряд ли потребуется.

Также удалим ряд локальных переменных в функции main()

 

uint16_t i,j;

uint16_t x=0, y=0;

static uint32_t tscnt[5]={0};

static uint16_t xstart[5]={0}, ystart[5]={0};

bmp1 = (uint8_t *) 0xC00FF000;

//Для файла bmp выделим на всякий случай место как под 32-битный,

//так как хоть рисунок максимум 24-битный, но в файле также

//присутствует служебная информация

dma2d_in1 = (uint8_t *) 0xC017E800;

dma2d_in2 = (uint8_t *) 0xC01FE000;

 

Уберём ещё некоторый лишний код из функции main()

 

MT48LC4M32B2_init(&hsdram1);

HAL_LTDC_SetAddress(&hltdc,LCD_FRAME_BUFFER,0);

TFT_FillScreen(LCD_COLOR_BLACK);

Touch_Ini();

 

А также удалим несколько глобальных переменных в файле main.c

 

#define LCD_FRAME_BUFFER SDRAM_DEVICE_ADDR

FATFS SDFatFs; /* File system object for SD card logical drive */

FIL MyFile; /* File object */

extern char SD_Path[4]; /* SD logical drive path */

FRESULT fresult;

uint8_t _acBuffer[4096];

uint8_t sect[4096];

uint32_t bytesread = 0;

uint8_t* bmp1;

uint8_t* dma2d_in1;

uint8_t* dma2d_in2;

char str1[30];

 

Ещё раз соберём код, запустим его на выполнение, чтобы убедиться, что мы не удалили чего-нибудь лишнего и у нас всё по-прежнему работает.

Таким образом, сегодня мы научились также применять мултибуферный вывод изображений на экран при использовании библиотеки emWin.

Всем спасибо за внимание!

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

 

Отладочную плату можно приобрести здесь 32F746G-DISCOVERY

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM HAL. LTDC. EmWin. Подключение библиотеки

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*