STM Урок 78. HAL. LTDC. EmWin. Window Manager



 

Урок 78

 

HAL. LTDC. EmWin. Window Manager

 

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

 

 

Продолжаем работать над изучением графической библиотеки EmWin.

И сегодня настал наконец тот день, когда мы добьёмся от нашего дисплея осознанного отклика на наши действия. То есть мы научимся создавать диалог с элементами управления и научим их реагировать на наши действия. В этом нам поможет набор функций библиотеки emWin — Window Manager.

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

Проект с именем EMWIN_WM01 был создан на основе проекта EMWIN_MultiLayer из урока 76.

Запускаем его в Cube MX и, ничего не трогая, генерируем проект для System Workbench.

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

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

 

#define GUI_NUM_LAYERS 1

 

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

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

Можно убрать и очистку экрана, он и так очистится — никуда не денется.

После всех чисток в пользовательском коде главной функции у нас останется вот это

 

/* USER CODE BEGIN 2 */

MT48LC4M32B2_init(&hsdram1);

Touch_Ini();

__HAL_RCC_CRC_CLK_ENABLE();

GUI_Init();

GUI_Clear();

if(f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0) != FR_OK)

{

  TFT_FillScreen(LCD_COLOR_RED); //в случае неудачи окрасим экран в красный цвет

  Error_Handler();

}

/* USER CODE END 2 */

 

Также в бесконечном цикле задержку изменим вот на такую функцию

 

/* USER CODE BEGIN 3 */

  GUI_Exec();

}

 

Ну и теперь, чтобы не мучиться и не составлять вручную код нашего окна и элементов управления и не думать над их размещением на дисплее, воспользуемся утилитой, предоставленной нам в комплекте с библиотекой — GUIBuilder, которая находится по пути Репозиторий\STM32Cube_FW_F7_Vx.x.x\Middlewares\ST\STemWin\Software.

Запустим одноимённый исполняемый файл и добавим новое окно

 

image00

 

Размер окна настроим согласно размеру нашего дисплея

 

image01

 

 

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

 

image02

 

В свойствах появится новое поле

 

image03

 

Теперь давайте добавим на нашу, так скажем, форму несколько элементов — кнопку, Два прогресс-бара, слайдер, окно редактирования и spinbox. Также мы можем поредактировать шрифт, текст на кнопке и т.д. по нашему усмотрению. Безусловно, потом мы можем это проделать и в коде, но сначала давайте максимально воспользуемся возможностями построителя диалога.

 

image04

 

Сохраним нашу форму в файл с исходным кодом, у нас появится в папке с утилитой файл WindowDLG.c, который мы переместим в папку с нашим проектом в каталог Src.

 

 

Вернёмся в наш проект и начнём всё это подключать по-маленьку.

Сделаем проекту fefresh, чтобы файл появился в дереве, затем зайдём в main.h и подключим там некоторый файлик

 

#include "WM.h"

#include "DIALOG.h"

 

Здесь же добавим прототип функции создания окна

 

#include "stdint.h"

WM_HWIN CreateWindow(void);

 

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

 

  Error_Handler();

}

GUI_SelectLayer(1);

CreateWindow();

 

Соберём код, прошьём контроллер и увидим красивую картину, чем-то очень напоминающую нашу форму в конструкторе

 

 image05

 

Всё это очень даже хорошо. Но осталась одна небольшая проблема — мы этим всем не можем управлять, так как мы ещё не подключили TouchScreen, ну и, соответственно не писали обработчики наших действий над управляющими элементами. Сейчас мы это дело исправим.

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

 

char str1[30];

TS_StateTypeDef ts;

static GUI_PID_STATE TS_State;

uint8_t ts_press_cnt=0;

 

 

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

 

/* USER CODE BEGIN 4 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

  TS_GetState(&ts);

  if (ts.touchDetected)

  {

    TS_State.Pressed = ts.touchDetected;

    TS_State.y = ts.touchY[0];

    TS_State.x = ts.touchX[0];

    GUI_PID_StoreState(&TS_State);

    ts_press_cnt++;

  }

  else

  {

    if(ts_press_cnt)

    {

      TS_State.Pressed = 0;

      GUI_PID_StoreState(&TS_State);

      ts_press_cnt=0;

    }

  }

}

/* USER CODE END 4 */

 

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

Конечно, в обработчик мы никогда не попадём, пока не запустим таймер. Сделаем это в main()

 

GUI_Clear();

HAL_TIM_Base_Start_IT(&htim6);

 

Также для TS нужно тоже определить слой, иначе не заработает

 

GUI_SelectLayer(1);

TS_State.Layer = 1;

CreateWindow();

 

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

Теперь давайте попробуем поуправлять нашими действиями над элементами

Обработаем слайдер. Для этого зайдём в файл WindowDLG.c и в функции _cbDialog добавим локальную переменную

 

// USER START (Optionally insert additional variables)

WM_HWIN hItem1;

// USER END

 

Добавим также парочку глобальных переменных

 

// USER START (Optionally insert additional static code)

static int n=0;

char str01[10]={0};

// USER END

 

Также подключим парочку хедеров

 

// USER START (Optionally insert additional includes)

#include "stm32f7xx_hal.h"

#include "main.h"

// USER END

 

Вернёмся в диалоговую функцию и в кейс с идентификатором слайдера ID_SLIDER_0 добавим следующий код в его подкейс (назовём это так)

 

case WM_NOTIFICATION_VALUE_CHANGED:

  // USER START (Optionally insert code for reacting on notification message)

  hItem = WM_GetDialogItem(pMsg->hWin, ID_EDIT_0);

  hItem1 = WM_GetDialogItem(pMsg->hWin, ID_SLIDER_0);

  n = SLIDER_GetValue(hItem1);

  sprintf(str01,"%d",n);

  EDIT_SetText(hItem, str01);

  hItem = WM_GetDialogItem(pMsg->hWin, ID_PROGBAR_0);

  PROGBAR_SetValue(hItem, n);

  // USER END

  break;

 

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

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

 

case WM_NOTIFICATION_VALUE_CHANGED:

  // USER START (Optionally insert code for reacting on notification message)

  hItem1 = WM_GetDialogItem(pMsg->hWin, ID_SPINBOX_0);

  n = SPINBOX_GetValue(hItem1);

  hItem = WM_GetDialogItem(pMsg->hWin, ID_PROGBAR_1);

  PROGBAR_SetValue(hItem, n);

  // USER END

 

Также давайте немного увеличим высоту спинбокса прямо в коде

 

{ SPINBOX_CreateIndirect, "Spinbox", ID_SPINBOX_0, 79, 76, 80, 40, 0, 0x0, 0 },

 

А по нажатию (вернее отжатию) кнопки всё будет возвращаться в первоначальное положение

 

case WM_NOTIFICATION_RELEASED:

  // USER START (Optionally insert code for reacting on notification message)

  hItem = WM_GetDialogItem(pMsg->hWin, ID_EDIT_0);

  EDIT_SetText(hItem, "0");

  hItem = WM_GetDialogItem(pMsg->hWin, ID_SPINBOX_0);

  SPINBOX_SetValue(hItem,0);

  hItem = WM_GetDialogItem(pMsg->hWin, ID_PROGBAR_0);

  PROGBAR_SetValue(hItem,0);

  hItem = WM_GetDialogItem(pMsg->hWin, ID_PROGBAR_1);

  PROGBAR_SetValue(hItem,0);

  // USER END

 

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

 

image06

 

Всё работает, есть небольшие недочёты, но практически на глаз они не видны. Все вопросы решатся включение мультибуфера. В случае с оконным менеджером это делается одним махом в main()

 

TS_State.Layer = 1;

WM_MULTIBUF_Enable(1);

CreateWindow();

 

Запустим код на выполнение и проверим результат — также всё работает, только ещё шустрее, тяжело определить конечно в таком слабонагруженном окне. Результат будет более очевиден, когда будем работать с графиком, но это будет в другом уроке. Мы подключим к нашей плате акселерометр. Я давал тест этого проекта уже в качестве видеоролика.

Результат мы не будем здесь показывать, так как картинкой его не передашь.

Также попробуем включить теперь второй слой, включив это в конфиге LCDConf.c

 

#define GUI_NUM_LAYERS 2

 

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

 

case WM_NOTIFICATION_CLICKED:

// USER START (Optionally insert code for reacting on notification message)

  GUI_SetFont(&GUI_Font24B_1);

  GUI_DispStringAt("Button pressed", 0, 0);

// USER END

 

image07

 

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

Я думаю, мы ещё не раз вернёмся к этой теме, так как она не просто интересная, но и необходимая. С помощью оконного менеджера мы можем работать с нашими программами поистине интерактивно.

 

 

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

 

Исходный код

 

 

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

 

 

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

 

STM HAL. LTDC. EmWin. Window Manager

3 комментария на “STM Урок 78. HAL. LTDC. EmWin. Window Manager
  1. Андрей:

    Не подскажите как закрыть диалоговое окно. Создать и открыть его получилось, а вот закрыть не как не могу.(диалоговое окно для ввода параметра). Заранее спасибо.

  2. Добрый день!
    Не подскажите, по какой причине виджеты окна (кнопки) могут не реагировать на передачу координат нажатия:
    GUI_TOUCH_StoreStateEx(&PID_STATE) или
    GUI_PID_StoreState(&PID_STATE).
    Нужно ли передавать дополнительные сообщения для Windows Manager?
    Правильно ли я понимаю, что вызов CallBack функции для перерисовки — это задача библиотеки?

  3. Maxim:

    Доброго времени суток. А не подскажите как поменять ориентацию экрана?

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

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

*