Easyelectronics.ru

Электроника для всех
Текущее время: 24 фев 2018, 17:13

Часовой пояс: UTC + 5 часов



    • JLCPCB - Платы прототипов всего за 2$ c бесплатной доставкой (при первом заказе)
    • 10 PCBs за $2 для 2 слоев, $15 для 4 слойной, $74 для 6 слойной платы.
    • Крупнейший китайский производитель прототипных плат. 290000+ клиентов & 8000+ заказов в день!
    • LCSC - Крупнейший китайский онлайн магазин радиодеталей.

Начать новую тему Ответить на тему  [ Сообщений: 13 ] 
Автор Сообщение
 Заголовок сообщения: Работа с графическим LCD
СообщениеДобавлено: 28 окт 2017, 23:41 
Заглядывает иногда

Зарегистрирован: 03 окт 2010, 01:49
Сообщения: 176
Здравствуйте.

Осваиваю потихоньку FreeRTOS, дошел до работы с графическим дисплеем.
И случился у меня затык(

В классической реализации работы с графическим дисплеем есть разные функции для строк, отдельных символов и прочей графики.
В ОСРВ есть задача обслуживающая дисплей, работающая с очередью.
Так вот если со строками проблем нет (они легко и непринужденно ложатся в очередь), то с графикой не знаю как поступить?

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

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

_________________
Все микрухи работают на волшебном дыме. Стоит только его выпустить - микруха мертва.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 29 окт 2017, 00:11 
Старожил
Аватара пользователя

Зарегистрирован: 22 июл 2017, 11:48
Сообщения: 1168
А что с графикой? С графикой вы работаете через двухмерный массив, являющийся видеобуфером дисплея. Через очередь имеет смысл передавать только указатель на координаты вывода и указатель на массив, содержащий графический объект. Любые большие массивы графических данных не должны целиком передаваться через очереди, поскольку очередь работает по принципу "взял - скопировал - принес - скопировал", т.е. полное побайтное копирование.
Модуль растровой графики (т.е формирования картинки в координатах дисплея X-Y) может работать как обычная задача, внутри которой есть указатель на видеобуфер дисплея, находящийся физически вне кучи, а так же принимаемый из очереди указатель на графический объект, который надо вывести на дисплей, и принимаемые из очереди координаты вывода, размер, способ наложения. Если есть аппаратный модуль наложения, то использовать следует именно его, для ускорения.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 29 окт 2017, 01:05 
Заглядывает иногда

Зарегистрирован: 03 окт 2010, 01:49
Сообщения: 176
До растровой графики дело пока не дошло.
Под графикой я подразумевал векторную, типа DrawLine(x1,y1,x2,y2) и т.п.
Задача под вывод строк сейчас имеет очередь формата Queue(x,y,*p,cmd), где x,y координаты вывода, *p - указатель на строку, cmd - формат вывода (инверсия, шрифт и т.п.)
Но такой формат не подходит даже для вывода отдельных символов.

Сейчас ковыряю исходники такой реализации:
FreeRTOS LCD driver for G12864

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

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


Вложения:
GLCD_EXAMPLE_34.ZIP [27.12 Кб]
Скачиваний: 16

_________________
Все микрухи работают на волшебном дыме. Стоит только его выпустить - микруха мертва.
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 29 окт 2017, 11:21 
Старожил
Аватара пользователя

Зарегистрирован: 22 июл 2017, 11:48
Сообщения: 1168
Аа, вон какой дисплей. Ясно.
Вобщем, дело обстоит так:

- для дисплея нужен (очень желателен) видеобуфер размером в полный дисплей, 1024 байт (128*64/8). Этот видеобуфер - глобально объявленный массив, находящийся вне кучи и существующий постоянно. В видеобуфере гораздо легче проводить наложение по маске, нежели сначала читать из дисплея а потом перезаписывать. Заодним, красиво решается вопрос со всеми этими указателями и очередями, откуда каждый раз брать данные для дисплея.

- для вывода на дисплей нужна задачка-драйвер, которая в нужном порядке дергает ногами дисплея. Причем, дергание ногами может быть так же реализовано и через модуль FMSC или DMA, в зависимости от микроконтроллера и распиновки. У распространенных дисплеев 12864 на KS0107/0108 дисплей поделен на левую и правую области, переключаемые внешним сигналом. Задача-драйвер не имеет очередей, она берет указатель на видеобуфер и инкрементирует его по кругу в порядке вывода. Задача может работать либо постоянно, либо только на время обновления (перерисовки) дисплея, а затем приостанавливаться (Suspend). Если вывод чисто программный, задача-драйвер может работать с повышенным приоритетом. Но тут надо найти компромисс, чтобы не "повесить" остальную работу.

- теперь нужна задача, рисующая изображение. Раз нужна векторная графика, значит будет задача по отрисовке векторной графики. Определимся, какие базовые фигуры нам нужны. Пусть это будут: прямая линия с координатами начала и конца, прямоугольник с координатами верхнего левого и нижнего правого углов, и окружность с координатой центра и радиусом. Значит, какие команды должна принять задача? правильно, тип (код) фигуры, координаты, размеры, ну и режим наложения (лог.суммирование, инверсия). Вот, именно эти данные и будут передаваться в очереди в задачу векторной графики. Передавать в очередь будет задача-компоновщик кадра (текущего изображения), а принимать из очереди будет задача векторной графики. Приняв из очереди код фигуры и ее параметры, задача вызывает функцию просчета соответствующей фигуры в заданных координатах. Указатель на видеобуфер - он постоянный, не меняется, а значит, его не надо передавать через очередь. Для ускорения можно так же повысить приоритет задачи.
Завершив просчет фигуры, можно запустить задачу-драйвер дисплея и обновить (перерисовать) дисплей.
(Задача-компоновщик текущего кадра собственно и составляет картинку, формируя команды "DrawLine(0,0,50,0); DrawRectangle(20,20,50,50);" и т.п. Для каждого кадра - свой набор команд отрисовки. А какой кадр сформировать - принимается из очереди команда с кодом нужного кадра).

Аналогично и с растровой графикой - битмап-картинками и, в том числе, растровыми шрифтами, которые, в принципе, являются битмапами. Задача растровой графики принимает из очереди координаты прямоугольника вывода, указатель на массив битмапы и режим наложения. Отправляет в очередь задача-декодер битмап-массива или задача-декодер шрифта.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 29 окт 2017, 12:01 
Старожил

Зарегистрирован: 16 ноя 2012, 07:47
Сообщения: 2511
WG12864
Не совсем понимаю зачем там какие-то очереди.
Есть видеобуфер, который низкоприоритетный таск выводит с нужной частотой.
Другие задачи просто пишут в этот видео-буфер нужную инфу, совместный доступ через мьютексы. Херово, что приходится тасовать байты между собой, т.к. адресация там не попиксельная, а построчная, есть некоторые неудобства. А так:
Show

Всякие рамки и строчки я рисовал тупо битмапами, поленился, флеша обычно дохера, а на таком экране нормальный интерфейс все равно тяжко делать, маловато пикселей.
Наброски лежат тут - https://www.dropbox.com/s/wagkethpnoglh ... 4.zip?dl=0 . Немного сыровато, но под мою задачу хватило.
BMP делал в Image-generate-v2.004, шрифты в TheDotFactory-0.1.4, под его формат и заточена функция, выводящая шрифты. Можно любой импортнуть.
Изображение
Задача, которая выводит эту тестовую инфу:
Код:
      G_LcdClearScreen();

      G_LcdPutBitMap(0,0,64,128, (char*)lcd_image_frame);
      G_LcdPutString5x8(2   ,40   ,G_LCD_PIXEL   ,   (const char*)"Параметры:");

      if(LoadPlateMotorCurve.CurveMode != STP_MOTOR_STOP)
      {
         G_LcdPutBitMap(14,6,16,12, (char*)lcd_image_play);
         sprintf((char *)UsartDebugBuffer, "F: %4u об/мин",(LoadPlateMotorCurve.CurrSpeed*60)/LOAD_PLATE_STEP_PER_ROUND);
      }
      else
      {
         G_LcdPutBitMap(14,6,16,12, (char*)lcd_image_stop);
         sprintf((char *)UsartDebugBuffer, "F: %4u об/мин",(LoadPlateMotorCurve.CurrSpeed*60)/LOAD_PLATE_STEP_PER_ROUND);
      }
      G_LcdPutStringUserFonts(13   ,22, &tahoma_7ptFontInfo,   (const char*)UsartDebugBuffer);

      G_LcdPutBitMap(27,3,16,16, (char*)lcd_image_clock);
      G_LcdPutStringUserFonts(29   ,22, &tahoma_7ptFontInfo,   (const char*)"00:29:56");

      G_LcdPutBitMap(27,60,16,16, (char*)lcd_image_temp);
      G_LcdPutStringUserFonts(29   ,76, &tahoma_7ptFontInfo,   (const char*)"343");
      G_LcdPutBitMap(29,93,8,4, (char*)lcd_image_gradus);
      G_LcdPutStringUserFonts(29   ,97, &tahoma_7ptFontInfo,   (const char*)"C");
      G_LcdPutBitMap(27,105,16,16, (char*)lcd_image_heater);

      G_LcdPutBitMap(45,3,16,16, (char*)lcd_image_power);
      G_LcdPutStringUserFonts(47   ,22, &tahoma_7ptFontInfo,   (const char*)"1853 Вт 218 В 93%");

UPD: а вообще, возможно быстрее будет прикрутить STemWin. Помнится они обещали работу на любых дисплеях. Надо лишь прикрутить драйвер на чтение/запись одного пикселя.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 30 окт 2017, 23:41 
Заглядывает иногда

Зарегистрирован: 03 окт 2010, 01:49
Сообщения: 176
Спасибо большое за ответы!

STemWin избыточен для моего дисплея (у меня простой MT-12232). Да и устанешь в нем рисовать все нужные экранчики (на данный момент около 70 шт.).

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

Так как дополнительные "плюшки" в виде наложений по маске и пр. не используются, разумно сделать компромиссное решение:
- Будет задача компоновщик, в моем случае на основе MicroMenu, принимающая через очередь нажатые клавиши
- Компоновщик будет ставить команды в очередь задачам Векторной и пр. графики
- Задачи отрисовки графики (Векторной и пр.) будут напрямую писать в дисплей

Для себя вижу такие плюсы в этом решении:
- Экономия на видеопамяти
- На дисплей отправляется только то, что действительно нужно (изменения)
- Нет необходимости писать "драйвер" для работы с дисплеем через видеопамять, ровно как и переписывать функции для записи в видеопамять (а это, судя по сырцам Hold, то еще удовольствие))

Граблей пока не вижу.

_________________
Все микрухи работают на волшебном дыме. Стоит только его выпустить - микруха мертва.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 00:28 
Старожил
Аватара пользователя

Зарегистрирован: 22 июл 2017, 11:48
Сообщения: 1168
С помощью очередей можно передавать не только данные, но и команды (задания). Таким способом обеспечивается взаимодействие и разделение процессов на уровне операционки и переносимость кода. Написанный модуль компоновщика кадра не зависит от конкретного дисплея, потому что оперирует только типом объекта и его координатами в пространстве видеобуфера. Так же, заменив драйвер дисплея, можно, не переписывая остального кода, вместо WG12864 на KS0107 поставить OLED на SSD1306 аналогичного размера и структуры видеопамяти.

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

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

И пишем функции, реализующие задачи.
Компоновщик кадра:
Show

Выбор и просчет векторных объектов:
Show

и, наконец, задача-драйвер дисплея:
Show


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

Вот так вот и получается.... Вроде ниче не забыл написать? Остальные навороты - по желанию и потребности.

В случае аппаратной реализации интерфейса дисплея полная перерисовка вообще не страшна. Аппаратная реализация зависит от конкретного МК и распиновки выводов.
Полная перерисовка дисплея ничего страшного в себе не таит. В любом случае, вам для этого хватит 20-25 мс, быстрее и не надо (40-50 fps, выше все равно не позволит медленные кристаллы дисплея. Итого, за 20 мс потребуется передать 488 байт для дисплея 122*32. Обеспечить 24 кБайт/с даже программно - это не такая уж сложная задача.

Частичная перерисовка дисплея - это часто слишком большие накладные расходы из-за постраничной структуры видеопамяти дисплея. Чтобы нарисовать горизонтальную линию с координатой Y = 5, да еще не затерев нарисованную ранее линию по координате Y = 7, которые обе находятся в пределах одной страницы (байта!!), придется вначале прочитать эту страницу дисплея, выполнить лог. ИЛИ, а затем записать в дисплей страницу заново.
Немного попроще будет, если полный видеобуфер будет размещен в ОЗУ МК, тогда для драйвера дисплея надо будет передать дополнительно координаты окна перерисовки, по границам целых страниц.
Граблей - это вы пока что не видите.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 08:38 
Старожил

Зарегистрирован: 16 ноя 2012, 07:47
Сообщения: 2511
Точные цифры не вспомню, но обновление всего экрана 128x64 занимало порядка единиц мс, не более 5мс на stm32f103 в 72 мгц Экран все равно тормозной, даже обновление в 100 гц даёт смазанную картинку на динамичных объектах, адекватная частота порядка 20-30 Гц., глянул свои исходники, частота обновления всего 10 Гц, видать больше было бессмысленно. Раз в 100 мс, занять проц на 3-5 мс не так уже страшно, к тому же низкоприоритетным таском, который кто угодно может перебить. У вас экран всего лишь 122х32, обновляться будет быстрее. Если смотрели исходники, то заметили, что основной гемор там это уже рисование шрифтов. А полностью перерисовывать экран из буфера как раз таки очень просто:
Код:
void WgLcdPutVideoBuffer(uint8_t* data)
{
   List_WG12864_Def   CSx;
   uint8_t   col   ,x = 0;

   WgLcdWriteCmd(WG_LCD_ON_CMD | WG_LCD_ON   ,LCD_CS1_2   ,LCD_USE_BUSY_DELAY   );
   WgLcdWriteCmd(WG_LCD_START_LINE         ,LCD_CS1_2   ,LCD_USE_BUSY_DELAY   );

   for(uint16_t i = 0;   i < WG_LCD_WIDTH*(WG_LCD_HEIGHT/8);   i++   ,data++   ,col++   )      //Заливаем весь экран
   {
      if(!(i % WG_LCD_WIDTH) || i == 0)      // Дошли до конца строки, переключаем на CS1
      {
         if (i) x++;
         col = 0;
         CSx = LCD_CS1;
         WgLcdWriteCmd(WG_LCD_SET_Y      ,CSx   ,LCD_USE_BUSY_READ   );
         WgLcdWriteCmd(WG_LCD_SET_X + x   ,CSx   ,LCD_USE_BUSY_READ   );
      }
      if((i / (WG_LCD_WIDTH/2))&0x01 && !(i % (WG_LCD_WIDTH/2)) && i)      // Дошли до середины, переключаем ны CS2
      {
         col = 0;
         CSx = LCD_CS2;
         WgLcdWriteCmd(WG_LCD_SET_Y      ,CSx   ,LCD_USE_BUSY_READ   );
         WgLcdWriteCmd(WG_LCD_SET_X + x   ,CSx   ,LCD_USE_BUSY_READ   );
      }
      WgLcdWriteData(*data   ,CSx   ,LCD_USE_BUSY_READ   );
   }
}

Доступ к ЖК напрямую конечно возможен, однако тут возможно непрогнозируемые задержки в основном коде, т.к. вывод на экран все равно занимает время. К тому же, постоянное обновление из буфера даёт некую гарантию, что на экране не будет мусора из-за помех на линии, т.к. при следующем обновлении правильные данные все равно выведутся на экран. Возможно даже горячее переподключение экрана, все равно будет работать. Экономить на видеобуфере не стоит, это всего-лишь 1 кбайт данных на 128x64, фигня. Зато экономится куча времени на чтение данных из экрана, да и тупо проще алгоритмически.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 09:20 
Старожил
Аватара пользователя

Зарегистрирован: 22 июл 2017, 11:48
Сообщения: 1168
Hold писал(а):
Возможно даже горячее переподключение экрана, все равно будет работать.

:) :) не будет - программные инит и включение питания - уже не выдается


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 09:25 
Старожил

Зарегистрирован: 16 ноя 2012, 07:47
Сообщения: 2511
у меня выдается каждый раз при обновлении(код выше):
Код:
   WgLcdWriteCmd(WG_LCD_ON_CMD | WG_LCD_ON   ,LCD_CS1_2   ,LCD_USE_BUSY_DELAY   );
   WgLcdWriteCmd(WG_LCD_START_LINE         ,LCD_CS1_2   ,LCD_USE_BUSY_DELAY   );

KS0107 в целом куда приятнее того же hd44780, проще работать, нет кучи таймингов.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 09:36 
Старожил
Аватара пользователя

Зарегистрирован: 22 июл 2017, 11:48
Сообщения: 1168
Я бы так не делал. Потому что при передергивании питания могут слетать стартовые позиции строк и столбцов, внутри самого дисплея. И придется каждый раз вначале слать всю процедуру инита. Вобщем, это излишнее. Лучше надежно посадите и зафиксируйте дисплей, больше будет толку.


Последний раз редактировалось BusMaster 31 окт 2017, 09:38, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 09:37 
Старожил

Зарегистрирован: 16 ноя 2012, 07:47
Сообщения: 2511
Это и есть вся процедура инита, тем контроллер и хорош.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Работа с графическим LCD
СообщениеДобавлено: 31 окт 2017, 09:40 
Старожил
Аватара пользователя

Зарегистрирован: 22 июл 2017, 11:48
Сообщения: 1168
А как же Display Start Line, Set Page, Set Address?? Как я помню, без Display Start Line начало строки вообще может быть черт знает где...


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 13 ] 

Часовой пояс: UTC + 5 часов


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB