Урок 75
HAL. LTDC. EmWin. BMP. Multiple Buffering
Продолжаем работу с библиотекой emWin.
Работаем мы также с платой STM32F746-DISCOVERY.
Сегодня мы попробуем поработать с графикой и отобразить изображения в формате BMP, при этом используя многобуферное прорисовывание экрана.
Я думаю, все понимают, зачем это нужно. Практически мы этим уже занимались, когда выводили рисунки без использования библиотеки. Прежде чем выводить рисунки, мы копировали их содержимое в быструю память SDRAM, а затем уже подготовленный таким образом виртуальный экран мы отображали на реальном дисплее, используя операцию копирования из памяти в память.
Так как библиотека emWin поддерживает также работу с различными оконными элементами управления, то, само собой, не совсем эффективно было бы прорисовывать работу таких элементов сразу на реальном экране. Поэтому разработчики библиотеки реализовали механизм виртуальных экранов с помощью Multiple Buffering. В этом случае все прорисовки делаются в виртуальном дисплее, то есть в заднем буфере, а затем, когда уже вся работа с прорисовкой закончена, по команде, приходящей из обработчика прерываний окончания кадра, кадр из заднего буфера приходит в память основного экрана. Также можно использовать и три буфера, что ещё эффективнее, когда прорисовки очень много, причём библиотека позволяет полностью автоматизировать данный процесс, чтобы разработчик проекта не задумывался постоянно над реализацией данных прорисовок.
Мы попробуем оценить преимущество мультибуферной технологии пока на выводах изображений в формате BMP, а в дельнейших занятиях ещё и в оконном менеджере. Также известно, что библиотека emWin поддерживает вывод изображений и других форматов, но BMP-формат был выбран потому, что изображения в этом формате являются файлами большего размера, нежели изображения, кодированные в другой какой-либо поддерживаемый формат, что позволит лучше оценить преимущество виртуальных экранов.
Проект мы создадим из проекта урока 73 с именем LTDC_EMWIN_001, присвоив ему новое имя LTDC_EMWIN_MB и запустим его в 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 */
Соберём код и посмотрим результат
Картинки выводятся быстро и хорошо, но здесь применена наша функция копирования буфера, которая вряд ли сможет работать в других случаях, отличных от вывода изображений, а также она не включена в автоматизацию процессов библиотеки emWin. Поэтому будем как-то исправлять это, подключая постепенно механизмы библиотеки.
Во первых нам надо найти функцию в библиотеке, которая будет читать файлы изображений с флеш-карты. Для вывода изображений кроме функции GUI_BMP_Draw(), которая пользуется готовым буфером изображения, существует функция GUI_BMP_DrawEx()
Данная функция использует внешнюю функцию, которая и будет открывать с внешнего носителя файл с изображением. Причём пишется эта функция определённым образом и имеет строго определённый тип входящих аргументов
Добавим две глобальные переменные
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.
Только теперь вывод изображений будет идти напрямую в дисплей без использования буфера.
Соберём код, запустим его на выполнение, и увидим, что рисунки у нас выводятся, но уже не разко, а постепенно снизу вверх
Но зато вывод изображения теперь происходит исключительно с помощью библиотеки 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
Теперь мы можем спокойно удалить функцию 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 Следующий урок
Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий