Например TDA7294

РадиоКот > Статьи

Анимированная индикация на stm32 + ili9341

Автор: gcc, artem.lab@gmail.com
Опубликовано 05.04.2016.
Создано при помощи КотоРед.

Здравствуйте, коты!

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

 А что может быть необычного в простых часах? Правильно - отображение! В сети очень много часов на самых разных экранах, с имитацией часовой стрелки и прочими фичами. Чего действительно мало - это анимации. Вот и решил я остановиться на этом - сделать анимирование смены значений на экране.

 Итак, с конечной целью теперь есть определённость, а как насчёт конкретики, что и как анимировать? Загружать изображения и делать нечто подобное GIF? У stm32f103 нет столько FLASH-памяти, чтобы это переварить, плюс хотелось что-то более гибкое и сколько-нибудь настраиваемое. Что если разбить весь экран или его область на условные блоки N*M пикселей, которые двигать при переходе от одного отображения к другому!? Это позволит оперировать битмапами, которые уже можно сохранять во FLASH, а сами переходы можно сделать какими угодна и относительно неплохо их параметризировать! Решено!

От теории к практике.

 Благо на тот момент у меня уже была реализация необходимых библиотек для вывода на выбранный экран и простенькая библиотека графического интерфейса, посему было решено за единицу анимации выбрать "потомка" виджета - встречайте gui_anim_bitmap!

typedef struct _Gui_Anim_Bitmap gui_anim_bitmap_t;

struct _Gui_Anim_Bitmap {
    gui_widget_t super; //!< Суперкласс.
    graphics_color_t front_color; //!< Цвет элементов.
    graphics_size_t margin; //!< Отступ.
    graphics_t* bitmap; //!< Отображаемый битмап.
    const graphics_t* target_bitmap; //!< Целевой битмап для анимации.
    /**
     * Анимированные элементы.
     * Количество должно быть равно числу анимируемых пикселов отображаемого битмапа.
     */
    gui_anim_bitmap_item_t* anim_items;
    size_t anim_items_count; //!< Число анимированных элементов.
    gui_anim_bitmap_effect_t effect_add_type; //!< Тип эффекта добавления элемента.
    gui_anim_bitmap_effect_t effect_del_type; //!< Тип эффекта удаления элемента.
    bool anim_done; //!< Флаг завершения анимации.
    graphics_size_t item_width; //!< Ширина элемента.
    graphics_size_t item_height; //!< Высота элемента.
    uint16_t cur_pixel; //!< Текущий анимируемый пиксел битмапа.
    uint16_t max_steps; //!< Максимум шагов анимации.
};

 Подробнее с интерфейсом виджета можно ознакомиться в хедере (Doxygen - комментарии).

 Остановимся на самом важном.

gui_anim_bitmap_item_t - тип (структура) анимируемого элемента, в этой структуре хранятся данные и стостояние анимации каждого анимируемого пиксела битмапа. Анимируемому виджету требуется массив этих элементов по количеству анимируемых пикселей в битмапе (если разница между битмапами небольшая - достаточно небольшого количества элементов).

gui_anim_bitmap_effect_t - тип (перечисление) анимации:

  • GUI_ANIM_BITMAP_EFFECT_NONE - без анимации, просто один пиксел становится другим;
  • GUI_ANIM_BITMAP_EFFECT_MOVE - при появлении нового пиксела он "переезжает" от наиболее близкого исчезающего, либо имеющегося пиксела, а при исчезновении наоборот - "переезжает" в появляющийся или наиболее близкий;
  • GUI_ANIM_BITMAP_EFFECT_RESIZE - пиксел появляется точко и растёт до размера блока, либо постепенно уменьшается и исчезает будучи точкой;
  • GUI_ANIM_BITMAP_EFFECT_GRAVITY - гравитация - исчезающие пикселы осыпаются вниз, а появляющиеся насыпаются сверху.

 Например, функция инициализации виджета может выглядеть так:

/**
 * Инициализирует анимированный виджет.
 * @param anim_bitmap Анимированный виджет.
 * @param gui Графический интерфейс.
 * @param parent Родитель.
 * @param anim_graphics Текущий битмап анимированного виджета.
 * @param anim_items Анимированные элементы.
 * @param anim_items_count Число анимированных элементов.
 * @param x Координата положения виджета X.
 * @param y Координата положения виджета Y.
 * @param width Ширина виджета.
 * @param height Высота виджета.
 */
static void clock_gui_init_anim_bitmap(gui_anim_bitmap_t* anim_bitmap, gui_t* gui, gui_widget_t* parent,
                                                     graphics_t* anim_graphics, gui_anim_bitmap_item_t* anim_items, size_t anim_items_count,
                                                     graphics_pos_t x, graphics_pos_t y, graphics_pos_t width, graphics_pos_t height)
{
    graphics_clear(anim_graphics);
    
    gui_anim_bitmap_init_parent(anim_bitmap, gui, parent);
    gui_anim_bitmap_set_bitmap(anim_bitmap, anim_graphics);
    
    if(anim_items != NULL){
        gui_anim_bitmap_set_anim_items(anim_bitmap, anim_items, anim_items_count);
    }
    
    gui_anim_bitmap_set_effect_add_type(anim_bitmap, GUI_ANIM_BITMAP_EFFECT_GRAVITY);
    gui_anim_bitmap_set_effect_del_type(anim_bitmap, GUI_ANIM_BITMAP_EFFECT_GRAVITY);
    
    gui_anim_bitmap_set_max_steps(anim_bitmap, 5);
    
    gui_anim_bitmap_set_margin(anim_bitmap, CLOCK_GUI_ANIM_ITEM_MARGIN);
    
    gui_widget_move(GUI_WIDGET(anim_bitmap), x, y);
    gui_widget_resize(GUI_WIDGET(anim_bitmap), width, height);
    
    gui_widget_set_visible(GUI_WIDGET(anim_bitmap), true);
}

Чтобы запустить анимацию, где-нибудь в главном цикле анимируем наши часики:

if(system_counter_diff(&anim_counter) >= system_counter_ticks_per_sec() / 35){
    anim_counter = system_counter_ticks();
    clock_gui_animation_step();
}

35 - экспериментально подобранное оптимальное для данного случая число итераций анимации в секунду.

От программы к железу.

 Схема подключения контроллера к экрану тривиальна, для взаимодействия с экраном, кроме SPI, используем такие пины: PB10 - RST, PB11 - CE, PB12 - DC.

 

 А теперь собираем всё и заливаем прошивку!

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

Итог.

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

Спасибо за внимание!

GitHub с библиотеками, использующимися в проекте, здесь.

Исходники прикреплены ниже (arm-none-eabi-gcc).


Файлы:
Исходники


Все вопросы в Форум.


ID: 2269

Как вам эта статья?

 Нравится
 Так себе
 Не нравится

Заработало ли это устройство у вас?

 Заработало сразу
 Заработало после плясок с бубном
 Не заработало совсем

57 4 3
1
Подробно