|
Теги статьи: |
Анимированная индикация на 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 - тип (перечисление) анимации:
Например, функция инициализации виджета может выглядеть так:
/**
* Инициализирует анимированный виджет.
* @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).
Файлы:
Исходники
Все вопросы в Форум.