STM Урок 73. HAL. LTDC. EmWin. Подключение библиотеки. Часть 2

 

 

 

 

Урок 73

 

Часть 2

 

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

 

 

В предыдущей части занятия мы настроили проект и подключили файлы библиотеки.

 

Теперь зайдём в файл GUIConf.c и исправим там количество памяти

 

#define GUI_NUMBYTES (1024*150)

 

Перейдём теперь в файл конфигурации LCDConf.c и подключим туда main.h

 

#include "GUI.h"

#include "GUIDRV_Lin.h"

#include "main.h"

 

Двигаемся дальше по файлу. Исправим размер экрана на наш

 

#define XSIZE_PHYS 480

#define YSIZE_PHYS 272

 

Удалим следующие дефайны

 

//

// Color conversion

//

#define COLOR_CONVERSION GUICC_8888

//

// Display driver

//

#define DISPLAY_DRIVER GUIDRV_WIN32

 

Добавим количество буферов до трёх

 

#define NUM_BUFFERS 3 // Number of multiple buffers to be used

 

Добавим адрес буфера экрана

 

#define NUM_VSCREENS 1 // Number of virtual screens to be used

#define FRAME_BUFFER_ADDRESS 0xC0200000 /* Address in SDRAM for frame buffer */

 

Удалим следующий код:

 

#ifndef VRAM_ADDR

#define VRAM_ADDR 0 // TBD by customer: This has to be the frame buffer start address

#endif

#ifndef XSIZE_PHYS

#error Physical X size of display is not defined!

#endif

#ifndef YSIZE_PHYS

#error Physical Y size of display is not defined!

#endif

#ifndef COLOR_CONVERSION

#error Color conversion not defined!

#endif

#ifndef DISPLAY_DRIVER

#error No display driver defined!

#endif

#ifndef NUM_VSCREENS

#define NUM_VSCREENS 1

#else

#if (NUM_VSCREENS <= 0)

#error At least one screeen needs to be defined!

#endif

#endif

#if (NUM_VSCREENS > 1) && (NUM_BUFFERS > 1)

#error Virtual screens and multiple buffers are not allowed!

#endif

 

Добавим несколько глобальных переменных

 

#define FRAME_BUFFER_ADDRESS 0xC0200000 /* Address in SDRAM for frame buffer */

/* Variables */

int32_t bufferIndex = 0;

__IO int32_t pending_buffer = 0;

LTDC_LayerCfgTypeDef pLayerCfg;

LTDC_HandleTypeDef hltdc;

uint8_t charvar[4]; //for debug parameters

 

Добавим также прототипы функций

 

uint8_t charvar[4]; //for debug parameters

/* Function prototypes */

static void CUSTOM_CopyBuffer(int LayerIndex, int IndexSrc, int IndexDst);

static void DMA2D_CopyBuffer(void * pSrc, void * pDst, U32 xSize, U32 ySize, U32 OffLineSrc, U32 OffLineDst);

static void CUSTOM_CopyRect(int LayerIndex, int x0, int y0, int x1, int y1, int xSize, int ySize);

static void CUSTOM_FillRect(int LayerIndex, int x0, int y0, int x1, int y1, U32 PixelIndex);

static void DMA2D_FillBuffer(void * pDst, U32 xSize, U32 ySize, U32 OffLine, U32 ColorIndex);

static void CUSTOM_DrawBitmap32bpp(int LayerIndex, int x, int y, U8 const * p, int xSize, int ySize, int BytesPerLine);

 

Сразу после прототипов добавим функцию инициализации шины LTDC

 

static void CUSTOM_DrawBitmap32bpp(int LayerIndex, int x, int y, U8 const * p, int xSize, int ySize, int BytesPerLine);

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

void LTDC_init(void) {

  hltdc.Instance = LTDC;

  hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;

  hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;

  hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;

  hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;

  hltdc.Init.HorizontalSync = 40;

  hltdc.Init.VerticalSync = 9;

  hltdc.Init.AccumulatedHBP = 53;

  hltdc.Init.AccumulatedVBP = 11;

  hltdc.Init.AccumulatedActiveW = 533;

  hltdc.Init.AccumulatedActiveH = 283;

  hltdc.Init.TotalWidth = 565;

  hltdc.Init.TotalHeigh = 285;

  hltdc.Init.Backcolor.Blue = 0;

  hltdc.Init.Backcolor.Green = 0;

  hltdc.Init.Backcolor.Red = 0;

  HAL_LTDC_Init(&hltdc);

  HAL_LTDC_ProgramLineEvent(&hltdc, 0);

  HAL_LTDC_EnableDither(&hltdc);

  /* Assert display enable LCD_DISP pin */

  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_12, GPIO_PIN_SET);

  /* Assert backlight LCD_BL_CTRL pin */

  HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);

}

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

 

Код стандартный и в объяснении не нуждается.

Теперь работаем с функцией LCD_X_Config. Эта функция вызывается сразу после функции GUI_X_Config (). Основная цель этой функции — создание и подключение драйвера дисплея и выбор подпрограмм преобразования цветов, а также установка размера дисплея.

Тело функции LCD_X_Config очистим полностью и вызовем там нашу только что написанную функцию инициализации интерфейса LTDC

 

void LCD_X_Config(void) {

  LTDC_init();

 

Подключим драйвер дисплея

 

LTDC_init();

/* Set display driver and color conversion for the 1st layer */

GUI_DEVICE_CreateAndLink(GUIDRV_LIN_32, GUICC_M8888I, 0, 0);

 

Установим размер памяти для виртуального и физического дисплея

 

GUI_DEVICE_CreateAndLink(GUIDRV_LIN_32, GUICC_M8888I, 0, 0);

/* Sets the size of the virtual display area for the 1st layer*/

LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS);

/* Sets the size of the physical display area for the 1st layer*/

LCD_SetSizeEx(0, XSIZE_PHYS, YSIZE_PHYS);

 

Назначим адрес для видеопамяти

 

LCD_SetSizeEx(0, XSIZE_PHYS, YSIZE_PHYS);

/* Sets the address of the VRAM for the 1st layer*/

LCD_SetVRAMAddrEx(0, (void *) FRAME_BUFFER_ADDRESS);

 

Сконфигурируем мультибуфер

 

LCD_SetVRAMAddrEx(0, (void *) FRAME_BUFFER_ADDRESS);

/* For multiple buffers */

GUI_MULTIBUF_Config(NUM_BUFFERS);

 

Подключим пользовательские функции

 

  GUI_MULTIBUF_Config(NUM_BUFFERS);

  /* Set custom functions for several operations */

  LCD_SetDevFunc(0, LCD_DEVFUNC_COPYBUFFER, (void (*)(void)) CUSTOM_CopyBuffer);

  LCD_SetDevFunc(0, LCD_DEVFUNC_COPYRECT, (void (*)(void)) CUSTOM_CopyRect);

  LCD_SetDevFunc(0, LCD_DEVFUNC_FILLRECT, (void (*)(void)) CUSTOM_FillRect); //still can't dispose of bugs

  LCD_SetDevFunc(0, LCD_DEVFUNC_DRAWBMP_32BPP, (void (*)(void)) CUSTOM_DrawBitmap32bpp);

}

 

Перейдём к функции LCD_X_DisplayDriver, которая является обратным вызовом (callback) драйвера дисплея

Уберём из её тела все комментарии и приведём её кейсы (case) в более стандартный вид, убрав из них фигурные скобки и вместо return 0 написав break.

Добавим в эту функцию две локальные переменные для координат

 

int r;

int xPos, yPos;

 

 

Прежде чем мы продолжим писать дальнейший код тела нашей функции, добавим функцию инициализации слоя (или в последствии слоёв, так как пока мы будем пользоваться только одним слоем) после функции LTDC_init

 

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

void Layer_init() {

  pLayerCfg.WindowX0 = 0;

  pLayerCfg.WindowX1 = 480;

  pLayerCfg.WindowY0 = 0;

  pLayerCfg.WindowY1 = 272;

  pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888;

  pLayerCfg.Alpha = 255;

  pLayerCfg.Alpha0 = 0;

  pLayerCfg.Backcolor.Blue = 0;

  pLayerCfg.Backcolor.Green = 0;

  pLayerCfg.Backcolor.Red = 0;

  pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;

  pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;

  pLayerCfg.ImageWidth = 480;

  pLayerCfg.ImageHeight = 272;

  pLayerCfg.FBStartAdress = (uint32_t) 0xC0200000;

  HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0);

}

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

 

Здесь у нас стандартные настройки — практически те же что и в проектогенераторе, за исключением того, что мы сразу присваиваем стартовый адрес памяти для слоя.

Вернёмся в нашу функцию LCD_X_DisplayDriver и вызовем там только что добавленную функцию инициализации слоя (или слоёв)

 

case LCD_X_INITCONTROLLER:

  Layer_init();

  break;

 

Назначим адрес начала видеопамяти

 

case LCD_X_SETORG:

  HAL_LTDC_SetAddress(&hltdc, FRAME_BUFFER_ADDRESS, 0);

  break;

 

Включим LTDC

 

case LCD_X_ON:

  __HAL_LTDC_ENABLE(&hltdc);

  break;

 

Выключим LTDC

 

case LCD_X_OFF:

  __HAL_LTDC_DISABLE(&hltdc);

  break;

 

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

Поэтому присвоим ему адрес. Команда, которую мы сейчас обрабатываем, может быть вызвана по прерыванию либо непосредственно. Мы применяем прерывание, поэтому вызывать мы её не будем никак, она вызовется сама

 

case LCD_X_SHOWBUFFER:

  pending_buffer = ((LCD_X_SHOWBUFFER_INFO *) pData)->Index;

  break;

 

Добавим ещё одну команду, которая настроит размер слоя

 

case LCD_X_SETSIZE:

  GUI_GetLayerPosEx(0, &xPos, &yPos);

  HAL_LTDC_SetWindowPosition(&hltdc, xPos, yPos, 0);

  break;

 

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

 

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

static void DMA2D_CopyBuffer(void * pSrc, void * pDst, U32 xSize, U32 ySize, U32 OffLineSrc, U32 OffLineDst) {

  DMA2D->CR = 0x00000000UL | (1 << 9);

  DMA2D->FGMAR = (U32) pSrc;

  DMA2D->OMAR = (U32) pDst;

  DMA2D->FGOR = OffLineSrc;

  DMA2D->OOR = OffLineDst;

  DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;

  DMA2D->NLR = (U32) (xSize << 16) | (U16) ySize;

  DMA2D->CR |= DMA2D_CR_START;

  while (DMA2D->CR & DMA2D_CR_START) {

  }

}

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

 

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

 

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

static void CUSTOM_CopyBuffer(int LayerIndex, int IndexSrc, int IndexDst) {

  U32 BufferSize, AddrSrc, AddrDst;

  BufferSize = XSIZE_PHYS * YSIZE_PHYS * (LCD_GetBitsPerPixel() >> 3); //in bytes

  AddrSrc = FRAME_BUFFER_ADDRESS + BufferSize * IndexSrc;

  AddrDst = FRAME_BUFFER_ADDRESS + BufferSize * IndexDst;

  DMA2D_CopyBuffer((void *) AddrSrc, (void *) AddrDst, XSIZE_PHYS, YSIZE_PHYS, 0, 0);

  bufferIndex = IndexDst;

}

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

 

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

 

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

static void CUSTOM_CopyRect(int LayerIndex, int x0, int y0, int x1, int y1, int xSize, int ySize) {

  U32 AddrSrc, AddrDst;

  AddrSrc = FRAME_BUFFER_ADDRESS + (y0 * XSIZE_PHYS + x0) * (LCD_GetBitsPerPixel() >> 3);

  AddrDst = FRAME_BUFFER_ADDRESS + (y1 * XSIZE_PHYS + x1) * (LCD_GetBitsPerPixel() >> 3);

  DMA2D_CopyBuffer((void *) AddrSrc, (void *) AddrDst, xSize, ySize, (XSIZE_PHYS - xSize), (XSIZE_PHYS - xSize));

}

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

 

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

 

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

static void CUSTOM_DrawBitmap32bpp(int LayerIndex, int x, int y, U8 const * p, int xSize, int ySize, int BytesPerLine) {

  U32 AddrDst;

  int OffLineSrc, OffLineDst;

  AddrDst = FRAME_BUFFER_ADDRESS + (XSIZE_PHYS * YSIZE_PHYS * (LCD_GetBitsPerPixel() >> 3) * bufferIndex) + (y * XSIZE_PHYS + x) * (LCD_GetBitsPerPixel() >> 3);

  OffLineSrc = (BytesPerLine / 4) - xSize;

  OffLineDst = XSIZE_PHYS - xSize;

  DMA2D_CopyBuffer((void *) p, (void *) AddrDst, xSize, ySize, OffLineSrc, OffLineDst);

}

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

 

В самом конце файла добавим функцию заполнения буфера с помощью видеоускорителя DMA2D

 

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

static void DMA2D_FillBuffer(void * pDst, U32 xSize, U32 ySize, U32 OffLine, U32 ColorIndex) {

  DMA2D->CR = 0x00030000UL | (1 << 9);

  DMA2D->OCOLR = ColorIndex;

  DMA2D->OMAR = (U32) pDst;

  DMA2D->OOR = OffLine;

  DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;

  DMA2D->NLR = (U32) (xSize << 16) | (U16) ySize;

  DMA2D->CR |= DMA2D_CR_START;

  while (DMA2D->CR & DMA2D_CR_START) {

  }

}

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

 

Данная функция также работает с регистрами.

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

 

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

static void CUSTOM_FillRect(int LayerIndex, int x0, int y0, int x1, int y1, U32 PixelIndex) {

  U32 AddrDst;

  int xSize, ySize;

  xSize = x1 - x0 + 1;

  ySize = y1 - y0 + 1;

  AddrDst = FRAME_BUFFER_ADDRESS + (XSIZE_PHYS * YSIZE_PHYS * (LCD_GetBitsPerPixel() >> 3) * bufferIndex) + (y0 * XSIZE_PHYS + x0) * (LCD_GetBitsPerPixel() >> 3);

  DMA2D_FillBuffer((void *) AddrDst, xSize, ySize, XSIZE_PHYS - xSize, PixelIndex);

}

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

 

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

 

Предыдущая часть Программирование МК STM32 Следующая часть

 

 

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

 

 

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

 

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

2 комментария на “STM Урок 73. HAL. LTDC. EmWin. Подключение библиотеки. Часть 2
  1. Дмитрий Юнушкин:

    Здравствуйте!!! Очень толковые уроки у Вас.Благодаря Вам отдельно взятый НИИ даже переходит на стм32)) с микрочипа. Хотел бы спросить а как заставить например кнопку или другой виджет откликатся на нажатие потому как в примерах от Сигера вроде все понятно даже собираются и экран рисуется с элементами но на события нажатия никак не реагирует может быть вы смогли бы объяснить как правильно настраивать высокоуровневые вещи в библиотеке. Это было бы очень полезно для аудитории скорей всего( т.е. WM_ и обработчики событий). Ещё иногда при построении интерфейса с картинкой происходят странные вещи не прорисовывается или прорисовывается не весь остальное темное дрожит и кракозябры бывают. Это возможно что то с памятью. Вы с таким не сталкивались? 

    • Спасибо за внимание к ресурсу!
      Вот как раз ответы на все эти вопросы, а также многие другие, мы получим в следующих уроках по emWin. Ещё как получим!

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

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

*