Графические контроллеры FTDI FT8хx.
Анимация изображений. Часть 1.
Сергей Долгушин, dsa@efo.ru
Когда заходит разговор об анимации изображения на экране дисплея, то первым делом возникают аналогии с развлекательными приложениями. Тем не менее, анимационные эффекты широко применяются в различных технических системах. Например, в системах автоматизации успешно используются мнемосхемы, выводимые на операторские панели. На таких мнемосхемах в графическом виде можно отображать заполнение резервуаров, движение жидкости в системе и т.п. Разумеется, что данную информацию необходимо дублировать служебными надписями и цифровыми значениями. Зато анимация мнемосхемы позволяет быстро оценить общее состояние системы в текущий момент.
В данной статье мы хотим познакомить вас с функциями графических контроллеров FTDI FT8xx, с помощью которых можно “оживить” изображение на экране TFT-дисплея.
Современный графический интерфейс пользователя включает в себя не только элементы управления и индикацию. Анимационные эффекты сейчас играют немалую роль в его работе. Они могут служить не только для создания заставок с информацией о производителе и приборе. В промышленных системах с их помощью можно выводить информацию о процессах, протекающих в тех или иных узлах таких систем. Набор команд графических контроллеров FTDI дает нам возможность реализовать различные эффекты относительно не сложными способами. Далее на примерах мы покажем некоторые из них.
В качестве рабочей платформы будем использовать графический модуль FTDI на базе микроконтроллера (МК) Atmega 328P – VM800P50. В качестве программных средств будем использовать утилиту Screen Editor 1.17 и среду разработки Arduino 1.0.6. Screen Editor будет использоваться для подготовки изображений, которые будут выводиться на экран дисплея, а также для экспорта проекта в Arduino.
Команды CMD_SPINNER и CMD_SCREENSAVER
Примеры работы с анимацией начнем с двух команд: CMD_SPINNER и CMD_SCREENSAVER. Чем хороши эти команды – это тем, что они не требуют вмешательства МК в свою работу. Вызванные однажды, они будут продолжать свою работу до тех пор, пока управляющий МК не запишет в FT8xx новый дисплей-лист и не даст команду на его выполнение.
Первая команда выводит на экран одно из четырех анимированных изображений, показанных на рисунке 1. Эти элементы можно выводить на экран в процессе выполнения управляющим МК каких-либо длительных операций.
Рисунок 1
Процесс создания приложения для вывода на экран такой заставки будет следующим. Во вкладке “Toolbox” Screen Editor из списка виджетов выбираем элемент Spinner и перетаскиваем его в центральное окно утилиты (см. рисунок 2).
Рисунок 2
Во вкладке свойств добавленного элемента “Properties” можно задать координаты элемента на экране, тип (рис.1) и размер. Создание проекта для Arduino выполняется через меню “Scripts” – “Export EVE Arduino Project”. В процессе экспорта откроется новое окно среды Arduino с заготовкой нашего проекта:
FT800IMPL_SPI FTImpl(FT_CS_PIN,FT_PDN_PIN,FT_INT_PIN);
void setup() // инициализация графического контроллера
{
FTImpl.Init(FT_DISPLAY_RESOLUTION);
FTImpl.SetDisplayEnablePin(FT_DISPENABLE_PIN);
FTImpl.SetAudioEnablePin(FT_AUDIOENABLE_PIN);
FTImpl.DisplayOn();
FTImpl.AudioOn();
}
void loop() // основная исполняемая программа
{
FTImpl.DLStart();
FTImpl.Cmd_Spinner(238, 136, 0, 0); // x,y, style, scale
FTImpl.DLEnd();
FTImpl.Finish();
while(1){}
}
В экспортированный код был добавлен бесконечный цикл. Он наглядно показывает, что анимация изображения выполняется без вмешательства МК. Элемент “spinner” может быть вызван, в том числе, на фоне основного изображения. Например, добавим перед его вызовом функцию градиентной заливки экрана:
FTImpl.Cmd_Gradient(-3,32,32767,347,162,8388352);
В результате выполнения нового кода на экране дисплея увидим такое изображение (см. рис.3).
Рисунок 3
Следующая команда CMD_SCREENSAVER осуществляет плавное перемещение изображения по экрану дисплея в случайном направлении без участия управляющего МК. Покажем ее работу на следующем примере. Создадим новый проект в Screen Editor, во вкладке “Content” добавим наше изображение, которое будет использоваться в качестве анимированной заставки.
Напомним, что графические контроллеры FT8xx могут выводить на экран пользовательские растровые изображения. Изображения хранятся в памяти МК или внешнем носителе в сжатом виде и распаковываются самим FT8xx при их загрузке в RAM_G. Изображения могут быть сжаты по алгоритму deflate или может использоваться JPEG-формат. Утилита Screen Editor использует первый способ сжатия, причем мы можем выбрать формат вывода изображения на экран. Формат определяет качество выводимого изображения и объемы памяти, требуемые для его хранения в ПЗУ МК и в RAM_G FT8xx в распакованном виде. Все требуемые изображения могут быть загружены в RAM_G при начале работы, если их суммарный объем не превышает объем этой области памяти. В случае контроллеров FT80x он составляет 256 кБайт, у FT81x – 1 Мбайт.
В преддверии новогодних праздников используем для наших примеров картинку с надписью “C Новым годом!”. Добавим это изображение в проект, нажав кнопку "Add" во вкладке "Content" (см. рис.4).
Рисунок 4
Во вкладке “Properties” выберем требуемый формат изображения, (см. рисунок 5).
Рисунок 5
Оценку качества изображения осуществляем по изображению в центральном окне утилиты, одновременно контролируя размер сжатой и распакованной картинки в блоке “Information” вкладки “Properties”. Изменение формата изображения приводит к изменению размеров. В данном примере остальные настройки во вкладке “Properties” оставляем по умолчанию.
Этап добавления изображения всегда одинаков для любого проекта. Но есть отличия в вызове этого изображения в связке с командой CMD_SCREENSAVER. Это не учитывается утилитой Screen Saver при формировании псевдокода в окне “Coprocessor”, а следовательно будет некорректно перенесено в будущий скетч Arduino или MS Visual Studio (MSVC). Покажем изменения, которые необходимо внести вручную, чтобы команда CMD_SCREENSAVER корректно вызывала наше изображение.
Добавление изображения в проект осуществляется его перетаскиванием из вкладки “Content” в центральное окно Screen Editor. Одновременно с этим в окне “Coprocessor” появится следующий код:
BITMAP_HANDLE(0) // назначение указателя
BITMAP_SOURCE(0) //адрес, по которому изображение хранится в памяти FT8xx
BITMAP_LAYOUT(RGB565, 480, 147) // формат изображения
BITMAP_SIZE(NEAREST, BORDER, BORDER, 240, 147) // параметры вывода его на экран
BEGIN(BITMAPS)
VERTEX2II(125, 57, 0, 0) // вывод изображения на экран
END()
Первые четыре функции отвечают за назначение идентификатора данному изображению, указание графическому контроллеру формата, в котором оно хранится, и его размеров. В окне “Coprocessor” не отображаются функции загрузки изображения в RAM_G. При экспорте проекта в Arduino или MSVC эти функции будут добавлены.
Теперь модифицируем этот код в окне “Coprocessor” следующим образом:
CLEAR_COLOR_RGB(254, 254, 254) // установка цвета фона
CLEAR(1, 1, 1) // очистка регистров color, stencil и tag
CMD_SCREENSAVER() // вызов подпрограммы экранной заставки
BITMAP_SOURCE(0) // адрес изображения в RAM_G, которое будет использоваться в заставке
BITMAP_LAYOUT(RGB565, 480, 147)
BITMAP_SIZE(NEAREST, BORDER, BORDER, 240, 147)
BEGIN(BITMAPS) // служебная команда для начала работы с растровым изображением
MACRO(0) // запуск на выполнение подпрограммы экранной заставки
END()
В результате этих изменений наша картинка в центральном окне Screen Editor должна начать двигаться. Теперь можно экспортировать проект в Arduino. Результат экспорта будет необходимо подкорректировать, в результате получится следующий код:
#define RAM_A1 0
static PROGMEM prog_uchar a1[] = { 120,156, … 152,}; // наше изображение, при экспорте в Arduino Screen Editor записывает массив данных в h-файл с именем переменной, в примере a1. Эти данные надо самостоятельно перенести в массив {}.
FT800IMPL_SPI FTImpl(FT_CS_PIN,FT_PDN_PIN,FT_INT_PIN);
void setup()
{
// инициализация
FTImpl.Init(FT_DISPLAY_RESOLUTION);
FTImpl.SetDisplayEnablePin(FT_DISPENABLE_PIN);
FTImpl.SetAudioEnablePin(FT_AUDIOENABLE_PIN);
FTImpl.DisplayOn();
FTImpl.AudioOn();
// загрузка изображения в RAM_G
FTImpl.DLStart();
FTImpl.Cmd_Inflate(RAM_A1);
FTImpl.WriteCmdfromflash(a1, sizeof(a1));
FTImpl.Finish();
}
void loop()
{
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
FTImpl.Cmd_ScreenSaver(); // вызов подпрограммы экранной заставки
// вызов изображения
FTImpl.BitmapSource(0);
FTImpl.BitmapLayout(FT_RGB332, 240, 147);
FTImpl.BitmapSize(FT_NEAREST,FT_BORDER,FT_BORDER, 240, 147);
FTImpl.Begin(FT_BITMAPS);
//
FTImpl.Macro(0); // запуск подпрограммы заставки
FTImpl.End();
FTImpl.DLEnd();
FTImpl.Finish();
while(1){}
}
Как мы видим, изображение загружается в память один раз, перед началом рабочего цикла нашего примера. Подпрограмма экранной заставки также вызывается, один раз и более не требует вмешательства МК в свою работу, за исключением остановки ее выполнения.
Команда COLOR_A – изменение прозрачности изображения
Теперь рассмотрим работу функции COLOR_A, которая отвечает за прозрачность выводимого изображения. Вызов функции осуществляется в Arduino следующим образом: FTImpl.ColorA(z), где z может изменяться в пределах от 0 (полная прозрачность изображения) до 255 (полностью непрозрачное изображение). Проиллюстрируем работу данной функции на примере плавного перехода с одного изображения на другое. Первым изображением будет служить наша картинка “C Новым годом”, выводимая в центре экрана (см. рис. 6), вторым изображением – эта же картинка, выводимая в левом верхнем и правом нижнем углах экрана.
Рисунок 6
Код для Arduino будет выглядеть следующим образом:
#define RAM_A1 0
static PROGMEM prog_uchar a1[] = {120, ... 152,};
FT800IMPL_SPI FTImpl(FT_CS_PIN,FT_PDN_PIN,FT_INT_PIN);
void setup()
{
FTImpl.Init(FT_DISPLAY_RESOLUTION);
FTImpl.SetDisplayEnablePin(FT_DISPENABLE_PIN);
FTImpl.SetAudioEnablePin(FT_AUDIOENABLE_PIN);
FTImpl.DisplayOn();
FTImpl.AudioOn();
// загрузка изображения в память RAM_G и назначение параметров для его последующего использования в программе
FTImpl.DLStart();
FTImpl.Cmd_Inflate(RAM_A1);
FTImpl.WriteCmdfromflash(a1, sizeof(a1));
FTImpl.Finish();
FTImpl.DLStart();
FTImpl.BitmapHandle(0);
FTImpl.BitmapSource(0);
FTImpl.BitmapLayout(FT_RGB332, 240, 147);
FTImpl.BitmapSize(FT_NEAREST,FT_BORDER,FT_BORDER, 240, 147);
FTImpl.DLEnd();
FTImpl.Finish();
}
void loop()
{
uint8_t z=255, x=0; //начальные значения прозрачности для двух изображений
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
FTImpl.DLEnd();
FTImpl.Finish();
while(1)
{
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
FTImpl.ColorA(z); // изменение прозрачности первого изображения
FTImpl.Begin(FT_BITMAPS);
FTImpl.Vertex2ii(125, 57, 0, 0); // вызов картинки в центре экрана
FTImpl.End();
FTImpl.ColorA(x); // изменение прозрачности второго изображения
FTImpl.Begin(FT_BITMAPS);
FTImpl.Vertex2ii(1, 1, 0, 0); // вызов картинки в левом верхнем углу
FTImpl.Vertex2ii(234, 125, 0, 0); // вызов картинки в правом нижнем углу
FTImpl.End();
FTImpl.DLEnd();
FTImpl.Finish();
z--;
x++;
delay(25);
if (z<1)
{
z=255;
x=0;
delay(5000);
}
}
}
Результатом выполнения программы будет плавное замещение одного изображения другим. Этот пример демонстрирует принцип работы функции COLOR_A. Разумеется, возможности по применению данной функции не ограничиваются таким ее использованиемю В следующих примерах рассмотрим ее применение совместно с другими функциями FT8xx.
Команды вывода растровых изображений VERTEX2II и VERTEX2F
В предыдущем примере мы использовали для вывода изображения на экран команду VERTEX2II. Это базовая команда для вывода растрового изображения из памяти RAM_G на экран дисплея. Вызов команды в Arduino осуществляется так: FTImpl.Vertex2ii(x, y, handle, cell), где x и y – координаты экрана в пределах от 0 до 511; handle – указатель на растровое изображение; cell – индекс. Данная функция предназначена для работы с изображением в пределах экрана, координаты всегда имеют положительное значение. Указатель говорит функции, какое именно изображение или элемент шрифта необходимо вывести на экран. В примере выше указатель изображения был задан равным нулю следующим образом: FTImpl.BitmapHandle(0). Индекс при работе с изображениями в большинстве случаев равен нулю. При работе со шрифтами (шрифты, как и картинки, являются растровыми изображениями) значение индекса будет равно коду символа, который мы хотим вывести на экран.
С помощью функции VERTEX2II мы можем реализовать перемещение нашего изображения в пределах экрана. Самым простым способом реализации такого перемещения является изменение координат в цикле:
void loop()
{
uint8_t z=255, x=0;
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
FTImpl.DLEnd();
FTImpl.Finish();
while(1)
{
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
FTImpl.Begin(FT_BITMAPS);
FTImpl.Vertex2ii(x, 57, 0, 0); // вызов картинки
FTImpl.End();
FTImpl.DLEnd();
FTImpl.Finish();
z--;
x++;
delay(25);
if (z<1)
{
z=255;
x=0;
delay(5000);
}
}
}
Инициализация и загрузка картинки полностью идентична тому, как это было сделано в предыдущем примере. Изменяя координату x, мы имитируем движение изображения от левого края экрана до правого. Команда VERTEX2II всегда выводит картинку на экран целиком, т.е. мы не можем с ее помощью реализовать плавное появление изображения из-за края экрана.
Для создания эффекта появления растрового изображения из-за края экрана на помощь придет функция VERTEX2F. Эта функция, в отличие от VERTEX2II, работает в пределах зоны, изображенной на рисунке 7. C помощью этой функции не составит труда реализовать эффект появления картинки с любой стороны экрана. Формат вызова команды в Arduino такой: FTImpl.Vertex2f(x*16, y*16), где x,y – координаты в пикселях. Коэффициент 16 необходим для удобства привязки к реальным координатам(рис.7). По умолчанию функция VERTEX2F работает с координатами в диапазоне от -16384 до +16383, т.е. с точностью 1/16 пикселя.
Рисунок 7
Вызов изображения функцией VERTEX2F имеет некоторые отличия от того, как это осуществляет VERTEX2II. В первую очередь, это справедливо для случаев, когда в память RAM_G графического контроллера одновременно загружено несколько изображений.
Проиллюстрируем отличия на следующем примере (рис.8).
Рисунок 8
Добавим в наш пример изображение звездочки. Программа будет выполнять перемещение надписи слева направо, а звездочка будет двигаться в противоположном направлении:
#define RAM_A1 0 // стартовый адрес RAM_G для первого изображения
static PROGMEM prog_uchar a1[] = {120,… 152,};
#define RAM_STAR_R 70560 // стартовый адрес RAM_G для второго изображения
static PROGMEM prog_uchar star_r[] = {120,… 134,};
FT800IMPL_SPI FTImpl(FT_CS_PIN,FT_PDN_PIN,FT_INT_PIN);
void setup()
{
FTImpl.Init(FT_DISPLAY_RESOLUTION);
FTImpl.SetDisplayEnablePin(FT_DISPENABLE_PIN);
FTImpl.SetAudioEnablePin(FT_AUDIOENABLE_PIN);
FTImpl.DisplayOn();
FTImpl.AudioOn();
FTImpl.DLStart();
FTImpl.Cmd_Inflate(RAM_A1);
FTImpl.WriteCmdfromflash(a1, sizeof(a1));
FTImpl.Cmd_Inflate(RAM_STAR_R);
FTImpl.WriteCmdfromflash(star_r, sizeof(star_r));
FTImpl.Finish();
FTImpl.DLStart();
FTImpl.BitmapHandle(0);
FTImpl.BitmapSource(0);
FTImpl.BitmapLayout(FT_RGB332, 240, 147);
FTImpl.BitmapSize(FT_NEAREST,FT_BORDER,FT_BORDER, 240, 147);
FTImpl.BitmapHandle(1); //указатель на изображение звездочки
FTImpl.BitmapSource(70560);
FTImpl.BitmapLayout(FT_RGB565, 42, 20);
FTImpl.BitmapSize(FT_NEAREST,FT_BORDER,FT_BORDER, 21, 20);
FTImpl.DLEnd();
FTImpl.Finish();
}
void loop()
{
int16_t z=480, x=-240;
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
FTImpl.DLEnd();
FTImpl.Finish();
while(1)
{
FTImpl.DLStart();
FTImpl.ClearColorRGB(254, 254, 254);
FTImpl.Clear(1, 1, 1);
// ВЫЗОВ ИЗОБРАЖЕНИЯ С НУЛЕВЫМ УКАЗАТЕЛЕМ
FTImpl.Begin(FT_BITMAPS);
FTImpl.Vertex2f(x*16, 57*16); // вызов картинки с надписью
FTImpl.End();
// ВЫЗОВ ИЗОБРАЖЕНИЯ С УКАЗАТЕЛЕМ =1
FTImpl.Begin(FT_BITMAPS);
FTImpl.BitmapHandle(1);
FTImpl.Vertex2f(z*16, 20*16); // вызов изображения звездочки
FTImpl.End();
// Бегущая строка
FTImpl.ColorRGB(199, 0, 4);
FTImpl.Cmd_Text(x, 19, 28, 0, "HAPPY NEW YEAR");
FTImpl.DLEnd();
FTImpl.Finish();
z--;
x++;
delay(25);
if (x>480)
{
z=480;
x=-240;
}
}
}
По умолчанию, команда VERTEX2F выводит на экран изображение с нулевым указателем. В листинге этот блок из трех команд предваряется комментарием “ВЫЗОВ ИЗОБРАЖЕНИЯ С НУЛЕВЫМ УКАЗАТЕЛЕМ”.
Чтобы вывести на экран изображение, имеющее ненулевой указатель, необходимо в явном виде указать на это. Что мы и делаем во втором блоке, начинающемся комментарием “ВЫЗОВ ИЗОБРАЖЕНИЯ С УКАЗАТЕЛЕМ =1” с помощью команды FTImpl.BitmapHandle(1).
По аналогии работы с командами вывода растровых изображений, аналогичные эффекты можно применять ко всем элементам, функции вывода которых используют координаты. В качестве иллюстрации этого в листинг выше добавлены две команды, которые реализуют бегущую строку. Текстовая строка будет перемещаться по экрану дисплея синхронно с движением надписи.
Продолжение следует.
Полезные ссылки: