uGFX — графическая библиотека для микроконтроллеров. Часть 2

№ 11’2014
PDF версия
Статья продолжает знакомить читателя с графической библиотекой uGFX. В предыдущей статье [1] было показано, как запустить библиотеку uGFX в среде настольной операционной системы Windows. В данной статье речь идет о запуске uGFX на ARM Cortex-M3 микроконтроллере, к которому подключен модуль цветного TFT-дисплея с контроллером ILI9341. Приводится оценка объема памяти, потребляемой библиотекой uGFX.

Все статьи цикла

Аппаратная часть

Микроконтроллер

Библиотека uGFX не накладывает никаких ограничений на используемый микроконтроллер [1] как по архитектуре, так и по разрядности. Однако надо понимать, что при проектировании устройства с графическим интерфейсом выбор слабого микроконтроллера, с малым объемом памяти программ и ОЗУ, может привести к ситуации, когда вся доступная память микроконтроллера будет исчерпана. Это связано с необходимостью хранения во флэш-памяти различных растровых графических образов (например, шрифтов), занимающих большой по меркам микроконтроллеров объем памяти. Кроме этого, ограничивающим фактором является быстродействие микроконтроллера, которого должно хватать для вывода графической информации без существенных временных задержек.

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

Целесообразно использовать достаточно мощный микроконтроллер и обойтись без дополнительных носителей информации. Автор использовал 32‑разрядный микроконтроллер с ядром ARM Cortex-M начального уровня — STM32F100RB. Микроконтроллер имеет на борту 128 кбайт флэш-памяти и 8 кбайт ОЗУ, работает на частотах до 24 МГц.

Забегая вперед надо сказать, что ресурсов выбранного микроконтроллера оказалось недостаточно для проверки всех возможностей uGFX. Однако ничто не мешает в реальном проекте применить более мощный микроконтроллер с таким ядром. Самые совершенные образцы микроконтроллеров с ядром ARM Cortex-M имеют встроенную флэш-память объемом до 2 Мбайт, а ОЗУ — объемом до 256 кбайт. Быстродействие же достигает 225 млн операций в секунду на частоте в 180 МГц [2].

Для того чтобы упростить построение макета устройства, была выбрана отладочная плата STM32VLDISCOVERY [3]. Ее отличительными чертами являются:

  • Микроконтроллер STM32F100RB — 32‑битное ядро ARM Cortex-M3, 128 кбайт флэш-памяти, 8 кбайт ОЗУ, максимальная рабочая частота 24 МГц.
  • Расположенный на плате программатор-отладчик ST-LINK, что позволяет обойтись без приобретения отдельного программатора.
  • Все выводы микроконтроллера выведены на плату в виде однорядных разъемов с шагом 2,54 мм, что облегчает макетирование устройства.
  • Питание по USB-шине позволяет обойтись без дополнительного блока питания.

Внешний вид отладочной платы приведен на рис. 1. Более подробно с данной отладочной платой можно ознакомиться в [4].

Отладочная плата STM32VLDISCOVERY

Рис. 1. Отладочная плата STM32VLDISCOVERY

Дисплейный модуль

В качестве дисплейного модуля автор выбрал недорогой модуль азиатского производства, основанный на контроллере ILI9341. На модуль установлен цветной дисплей с диагональю 2,2 дюйма и разрешением 240×320 пикселей [5] (рис. 2). Надо сказать, что это максимально возможное разрешение для контроллера ILI9341.

Дисплейный модуль

Рис. 2. Дисплейный модуль

Хотя контроллер ILI9341 фирмы Ilitek [6] имеет также параллельный интерфейс для подключения к микроконтроллеру, на плату модуля выведен лишь последовательный SPI-интерфейс.

С одной стороны, подключение по SPI-интерфейсу ограничит скорость передачи информации между микроконтроллером и графическим контроллером, а с другой — потребует гораздо меньше выводов микроконтроллера и, соответственно, соединительных линий.

На плате дисплейного модуля присутствуют следующие выводы:

  • VCC — напряжение питания модуля, +5 или +3,3 В;
  • GND — общий провод («земля»);
  • CS — выбор кристалла;
  • RESET — сброс ILI9341 в исходное состояние;
  • D/C — выбор типа передаваемой информации: данные, команда;
  • SDI (MOSI) — последовательные данные из микроконтроллера в контроллер ILI9341;
  • SCK — тактовый сигнал интерфейса SPI;
  • LED — положительное напряжение подсветки дисплея;
  • SDO (MISO) — последовательные данные из контроллера ILI9341 в микроконтроллер.

Напряжение питания дисплейного модуля может составлять как +5 В, так и +3,3 В. Выбор производится с помощью перемычки J1 на плате модуля. Если перемычки нет, то должно подаваться напряжение +5 В, которое понижается до +3,3 В стабилизатором, установленным на плате модуля. Важно, чтобы логические уровни на выводах SCK, SDI (MOSI), CS, RESET и D/C были 3,3 В.

Подключение дисплейного модуля

Очевидно, что SPI-интерфейс графического контроллера ILI9341 (выводы SDI (MOSI), SCK и SDO (MISO)) должен быть подключен к аппаратному SPI-интерфейсу микроконтроллера STM32F100RB. Микроконтроллер содержит два SPI-интерфейса: SPI1 и SPI2. Выбираем интерфейс SPI1, чьи сигналы соответствуют выводам PA5, PA6, PA7 микроконтроллера.

Позднее необходимо будет внести некоторые изменения в файл поддержки платформы (board file) так, чтобы uGFX при записи информации в ILI9341 обращалась именно к интерфейсу SPI1 микроконтроллера.

Для подключения выводов CS, RESET, D/C, которые управляют процессом передачи информации и состоянием контроллера ILI9341, можно использовать любые выводы общего назначения (GPIO) микроконтроллера. Автор использовал выводы PC6, PC7, PC8 (рис. 3).

Схема подключения дисплейного модуля к отладочной плате STM32VLDISCOVERY

Рис. 3. Схема подключения дисплейного модуля к отладочной плате STM32VLDISCOVERY

Опять-таки, в файле с исходным кодом драйвера выводы PC6, PC7, PC8 необходимо соответствующим образом настроить и обеспечить к ним доступ со стороны uGFX.

Номинал резистора R1 подобран так, чтобы обеспечить ток через светодиоды подсветки дисплея порядка 60 мА.

 

Программная часть

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

Программное обеспечение

Итак, имеем ARM Cortex-M3 микроконтроллер, к которому по интерфейсу SPI подключен графический контроллер ILI9341. Также имеем библиотеку uGFX, представленную в исходных кодах (автор использовал версию 2.1 от 19.06.2014). Теперь необходимо выбрать и настроить программное обеспечение для сборки пробного приложения и его загрузки во флэш-память выбранного микроконтроллера. Подразумевается, что на рабочей машине установлена операционная система Windows.

В качестве программного обеспечения для сборки и прошивки приложения хорошо подходит бесплатная интегрированная среда разработки CooCox CoIDE [7] в связке с компилятором GNU GCC для микроконтроллеров ARM Cortex-M [8].

Основные достоинства CooCox IDE:

  • Среда разработки, специально созданная для микроконтроллеров ARM Cortex.
  • Основана на популярной среде Eclipse, полностью бесплатна.
  • Поддержка множества ARM Cortex-M микроконтроллеров таких фирм, как ST, Atmel, NXP, Freescale, TI и др., в том числе и выбранного STM32F100RB.
  • Поддержка множества программаторов‑отладчиков, в том числе и ST-LINK, установленного на выбранной плате STM32VLDISCOVERY.
  • Удобная сборка, прошивка и отладка программы.
  • Встроенные библиотеки для работы с периферией микроконтроллеров (например, CMSIS — библиотека для всего семейства ARM Cortex-M и ST Standard Peripherals Library — библиотека, созданная компанией STMicroelectronics для поддержки своих микроконтроллеров).

Недостаток среды CooCox CoIDE — отсутствие поддержки Make-файлов, активно используемых в библиотеке uGFX, следовательно, придется подготовить библиотеку uGFX для работы в среде CoIDE.

Настройка среды разработки

Необходимо загрузить последнюю версию CooCox CoIDE с официального сайта [7] и установить в любой каталог на диске, например в c:\CooCox\CoIDE\. Автор работал с версией CooCox CoIDE 1.7.7. В состав CooCox CoIDE не входит компилятор, поэтому до создания проекта компилятор необходимо скачать и установить отдельно.

Рекомендуется использовать официальный компилятор от компании ARM — ARM GCC, хотя возможно применение и других GCC-компиляторов, например CodeSoucery GCC. Скачать компилятор ARM GCC можно по адресу [8]. Автор использовал версию 4.8 от 2014.06.09. Рекомендуется скачать установщик для Windows (название файла, который скачивал автор: gcc-arm-none-eabi‑4_8-2014q2-20140609‑win32.exe), после чего выполнить установку компилятора. Образец пути установки: c:\Program Files (x86)\GNU Tools ARM Embedded\4.8 2014q2\.

После того как компилятор GCC установлен, следует в настройках CooCox CoIDE указать путь к нему, чтобы среда разработки могла с его помощью собирать проекты. Для этого нужно запустить CooCox CoIDE, выбрать пункт меню Project Select Toolchain Path и ввести путь к ранее установленному ARM GCC-компилятору, например, так: C:\Program Files (x86)\GNU Tools ARM Embedded\4.8 2014q2\bin.

Далее можно создать пустой проект и убедиться, что он успешно собирается. Для этого следует выбрать пункт меню Project New Project и ввести название проекта, например ugfx_stm32. Далее будет предложено выбрать тип микроконтроллера, следует выбрать ST STM32F100x STM32F100RB. После выбора типа микроконтроллера среда создаст каталог с названием, соответствующим названию проекта: c:\CooCox\CoIDE\workspace\ugfx_stm32\.

После этого среда CoIDE откроет вкладку Repository, где предложит выбрать компоненты библиотек CMSIS и Standard Peripherals Library, необходимые прикладной программе для обращения к аппаратуре микроконтроллера так, как описано ниже.

В разделе COMMON нужно выбрать пункты C Library и CMSIS core. В разделе BOOT — CMSIS Boot. В разделе PERIPHERAL.ST — пункты RCC, GPIO, SPI, MISC (рис. 4). Выбор пунктов приводит к появлению соответствующих каталогов и исходных файлов в каталоге проекта.

Настройка библиотек в среде CoIDE

Рис. 4. Настройка библиотек в среде CoIDE

Пункты RCC, GPIO, SPI, MISC раздела PERIPHERAL.ST предписывают использование соответствующих частей библиотеки Standard Peripherals Library. Для того чтобы в работе не возникло препятствий, следует отредактировать файл stm32f10x_conf.h в каталоге \ugfx_stm32\cmsis_boot. В файле надо убрать комментарий со строк, соответствующих применяемым частям Standard Peripherals Library:

#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_spi.h"
#include "misc.h"

Далее можно проверить сборку пустого проекта — выбрать пункт меню Project Build. Сборка должна завершиться без ошибок.

Если выбрать пункт меню Flash Program Download, то полученная прошивка должна загрузиться во флэш-память микроконтроллера. При этом плата STM32VLDISCOVERY должна быть подключена к рабочей машине через USB. Таким образом, можно проверить правильность соединений и настроек программатора/отладчика (по умолчанию должен быть выбран ST-LINK, и никаких дополнительных настроек не требуется).

Адаптация библиотеки uGFX

К сожалению, при всем своем удобстве среда CooCox CoIDE не поддерживает Make-файлы, широко применяемые в библиотеке uGFX. Вместо этого CoIDE использует собственные форматы для хранения информации об исходных файлах, подлежащих обработке, и правилах этой обработки.

Поэтому для совместного использования CooCox CoIDE и uGFX придется один раз проделать рутинную работу по ручному добавлению файлов библиотеки в проект на CooCox CoIDE.

В каталог проекта CoIDE (в нашем случае c:\CooCox\CoIDE\workspace\ugfx_stm32\) необходимо скопировать часть библиотеки uGFX так, чтобы каталог проекта содержал подкаталог ugfx с внутренней структурой, показанной на рис. 5.

Копировать следует только файлы с исходным кодом (расширения *.c и *.h). В каталог \ugfx\src\gos\ можно не копировать файлы, нужные при использовании операционных систем (ChibiOS, FreeRTOS и т. д.). Необходимо и достаточно скопировать файлы gfx_raw32.c, gfx_raw32.h, sys_defs.h, sys_options.h, sys_rules.h из соответствующего каталога дистрибутива uGFX.

Теперь можно добавить все исходные файлы uGFX в проект CoIDE. Предварительно надо создать структуру проекта, соответствующую структуре каталогов (рис. 5). Для этого в окне Project, содержащем дерево проекта, надо вызвать контекстное меню (щелчок правой кнопкой мыши по названию проекта ugfx_stm32) и выбрать пункт Add Group. Далее ввести название создаваемой группы в соответствии с именем каталога, это будет ugfx (рис. 5). Таким же образом создать все подгруппы в соответствии с рис. 5. Затем каждую группу следует наполнить файлами, выбирая пункт контекстного меню Add Files и выбирая файлы *.c и *.h из соответствующего по названию каталога.

Структура каталогов необходимой части uGFX

Рис. 5. Структура каталогов необходимой части uGFX

Файлы из каталога \ugfx\src\gdisp\fonts\ добавлять в проект не следует, иначе возникнут ошибки компоновки при сборке приложения, работающего со шрифтами. Файлы из каталога \ugfx\src\gdisp\fonts\ включаются в сборку из исходных файлов с помощью инструкций #include.

Написание файла платформы

В составе uGFX уже имеется файл платформы для контроллера ILI9341, подключенного по интерфейсу SPI. Файл называется board_ILI9341_spi.h и находится в каталоге \ugfx\boards\addons\gdisp\. Однако при его анализе выяснилось, что он подходит только для операционной системы ChibiOS. Автор же выбрал вариант без операционной системы, поэтому файл платформы board_ILI9341_spi.h пришлось переписать. При этом для инициализации периферии использовалась библиотека Standard Peripherals Library, ранее включенная в проект средствами CoIDE.

Структура библиотеки uGFX подразумевает, что файлы платформы должны находиться в каталоге \ugfx\boards\addons\gdisp\, однако в целях упрощения структуры файлов проекта автор поместил файл платформы в каталог с драйвером контроллера ILI9341: \ugfx\drivers\gdisp\ILI9341\.

Анализ файла платформы выявил, что следующие функции нуждаются в реализации:

1) void init_board(GDisplay *g) — отвечает за инициализацию периферии микроконтроллера, в нашем случае это SPI1 и порты GPIOA и GPIOC. Вызывается во время инициализации библиотеки uGFX API-функцией gdispInit ().

2) void setpin_reset(GDisplay *g, bool_t state) — должна устанавливать вывод RESET в состояние, заданное своим аргументом state.

3) void write_index(GDisplay *g, uint16_t index) — должна передавать в ILI9341 адрес регистра или адрес в видеопамяти, куда будет осуществляться дальнейшая запись.

4) void write_data(GDisplay *g, uint16_t data) — должна осуществлять запись по адресу, предварительно установленному функцией write_index().

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

#ifndef _GDISP_LLD_BOARD_H
#define _GDISP_LLD_BOARD_H

#include "stm32f10x.h"

static void send_data(uint16_t data);

static inline void init_board(GDisplay *g) {
    g->board = 0;

    /* Структуры для инициализации SPI и GPIO. */
    SPI_InitTypeDef SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Включить тактирование контроллера SPI1, портов GPIOA и GPIOC. */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    /* Инициализация выводов PA5, PA6, PA7 порта GPIOA, куда выведен интерфейс SPI1. */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Инициализация выводов PC6, PC7, PC8 порта GPIOC. */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    /* Структуру SPI_InitStructure — в исходное состояние. */
    SPI_StructInit(&SPI_InitStructure);

    /* Настройка SPI1. */
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;

    SPI_Init(SPI1, &SPI_InitStructure);

    /* Разрешить работу SPI1. */
    SPI_Cmd(SPI1, ENABLE);
}

static inline void setpin_reset(GDisplay *g, bool_t state) {
    (void) g;
    if (state == TRUE) {
        GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_RESET);
    } else {
        GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_SET);
    }
}

static inline void send_data(uint16_t data) {
    /* Ожидать окончания предыдущей передачи, если она идет. */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    /* Передать младший байт data */
    SPI_I2S_SendData(SPI1, data);
}

static inline void write_index(GDisplay *g, uint16_t index) {
    (void) g;
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
    /* Вывод CS — высокий уровень. */
    GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);
    /* Вывод D/C — низкий уровень. */
    GPIO_WriteBit(GPIOC, GPIO_Pin_6, Bit_RESET);
    /* Вывод CS — низкий уровень. */
    GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);
    /* Передать адрес */
    send_data(index);
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
    /* Вывод D/C — высокий уровень. */
    GPIO_WriteBit(GPIOC, GPIO_Pin_6, Bit_SET);             
}

static inline void write_data(GDisplay *g, uint16_t data) {
    (void) g;
    send_data(data);
}

#endif /* _GDISP_LLD_BOARD_H */

Для обмена с контроллером ILI9341 интерфейс SPI1 микроконтроллера настроен следующим образом:

  • Режим работы — мастер.
  • Тактовый сигнал SCK — в промежутках между передачами низкий уровень, стробирование данных по первому перепаду.
  • Размер передаваемого слова — 8 бит.
  • Последовательность передачи бит — старшим битом вперед.

Реализация функции send_data() — передача байта данных по SPI — выполнена самым компактным по размеру кода способом (используется опрос состояния SPI-контроллера в бесконечном цикле). Однако такая реализация является неэффективной с точки зрения затрат процессорного времени. В целях повышения эффективности в реальном проекте следует буферизировать запись или использовать контроллер прямого доступа к памяти DMA.

Адаптация модуля GOS

В случае когда библиотека uGFX работает под операционной системой, она вызывает ряд API-функций данной операционной системы, например API-функцию для приостановки выполнения программы (или потока) на заданное количество миллисекунд.

В случае когда uGFX работает без операционной системы, возникает задача адаптировать модуль GOS к периферии выбранного микроконтроллера.

Задача адаптации сводится к следующему:

  • Организовать отсчет равных временных интервалов — тиков.
  • Организовать счетчик этих тиков и функцию, возвращающую значение этого счетчика.
  • Реализовать функцию для перевода заданного количества миллисекунд в количество тиков.

В нашем случае используется микроконтроллер с архитектурой ARM Cortex-M3, особенностью всех микроконтроллеров с такой архитектурой (даже от разных производителей) является наличие специального 24-битного таймера/счетчика SysTick, который идеально подходит для нашей задачи.

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

Итак, таймер SysTick должен отсчитывать время, равное одной миллисекунде, после чего счетчик тиков должен увеличиться на единицу.

Адаптация модуля GOS сводится к реализации следующих функций в файле \ugfx\src\gos\gfx_raw32.c:

  • void_gosInit(void) — сюда следует добавить дополнительную функциональность: инициализацию таймера SysTick;
  • void SysTick_Handler(void) — обработчик прерывания от таймера SysTick, в котором должен инкрементироваться счетчик тиков;
  • systemticks_t gfxSystemTicks(void) — функция должна возвращать текущее значение счетчика тиков;
  • systemticks_t gfxMillisecondsToTicks(delaytime_t ms) — функция должна переводить заданное время в миллисекундах в количество тиков.

Изменения в файле gfx_raw32.c:

/* ... */

#include "stm32f10x.h"

/* ... */

/* MsTickCounter — счетчик тиков */
static systemticks_t MsTickCounter;
/* SystemCoreClock — частота тактирования в МГц */
extern uint32_t SystemCoreClock;

/* ... */

void _gosInit(void) {
  /* ... */

  /* Настроить тактирование микроконтроллера. */
  SystemInit();
  /* Записать действующую частоту тактирования в SystemCoreClock */
  SystemCoreClockUpdate();
  /* Конфигурация таймера SysTick. Разрешение прерывания от него. */
  /* 1/1000 секунды — интервал срабатывания прерывания от таймера SysTick. */

  SysTick_Config(SystemCoreClock / 1000);

  /* ... */
}

/* ... */

/* Обработчик прерывания от таймера SysTick */
void SysTick_Handler(void) {
  /* Инкремент счетчика тиков */
  MsTickCounter++;
}

systemticks_t gfxMillisecondsToTicks(delaytime_t ms) {
  return ms;
}

systemticks_t gfxSystemTicks(void) {
  return MsTickCounter;
}

/* ... */

Функция SystemInit() является частью библиотеки CMSIS, ее реализация находится в файле \ugfx_stm32\cmsis_boot\system_stm32f10x. c. Функция настраивает тактирование микроконтроллера так, чтобы при подключенном внешнем кварцевом резонаторе на 8 МГц (как на плате STM32VLDISCOVERY) системная частота составляла 24 МГц.

Функция SystemCoreClockUpdate() — также часть библиотеки CMSIS. Она устанавливает значение глобальной переменной SystemCoreClock равным действующей системной частоте тактирования. Значение этой частоты необходимо для правильной конфигурации таймера SysTick.

Функция SysTick_Config() инициализирует таймер SysTick, запускает его и разрешает прерывание от него. При этом период срабатывания прерывания устанавливает равным количеству периодов системной частоты, заданному своим аргументом. Если задать аргумент равным системной частоте, то срабатывания будут раз в секунду. Соответственно аргумент SystemCoreClock/1000 предписывает задать прерывания раз в миллисекунду.

Статическая переменная MsTickCounter — это счетчик тиков, его значение увеличивается на 1 внутри прерывания от таймера SysTick. Таймер SysTick настроен так, чтобы на заданной частоте тактирования прерывание от него срабатывало каждую миллисекунду. Таким образом, MsTickCounter является счетчиком миллисекунд.

Функция gfxSystemTicks() просто возвращает значение счетчика MsTickCounter. А функция gfxMillisecondsToTicks(), которая должна переводить время, заданное в миллисекундах, в количество тиков, просто возвращает свой аргумент.

При компиляции проекта выявились ошибки, связанные с тем, что в файле gfx_raw32.h переопределяются типы данных (size_t, int8_t, uint32_t и др.) из стандартной библиотеки языка С. Чтобы обойти эту проблему, следует добавить в начало файла gfx_raw32.h строки:

#include <stdint.h>
#include <stddef.h>

Адаптация драйвера ILI9341

В процессе компиляции проекта выяснилось, что драйвер графического контроллера ILI9341 — файл \ugfx\drivers\gdisp\ILI9341\gdisp_lld_ILI9341.c также нуждается в небольшой доработке. Дело в том, что для организации временных задержек в этом файле используется вызов функции chThdSleepMilliseconds(), являющейся частью операционной системы ChibiOS (сказывается то, что ранее uGFX была частью ChibiOS).

Наиболее простой путь обойти эту проблему — заменить вызовы функции chThdSleepMilliseconds() на вызовы gfxSleepMilliseconds().

Запуск демонстрационного проекта

Теперь можно собирать демонстрационное приложение из дистрибутива uGFX. Пусть это будет вывод графиков функций средствами модулей GWIN и GDISP. Для этого следует в рабочую папку проекта (\ugfx_stm32\ в нашем случае) скопировать из каталога \ugfx\demos\modules\gwin\graph\ файл, содержащий непосредственно код приложения, — main.c и файл конфигурации библио-теки uGFX — gfxconf.h. Далее файлы main.c и gfxconf.h следует добавить в проект в среде CoIDE, как было описано выше.

Затем нужно сконфигурировать uGFX для работы без операционной системы, для чего в файл gfxconf.h следует добавить строчку:

#define GFX_USE_OS_RAW32                   TRUE

Сборка проекта должна пройти без ошибок. Далее следует записать полученный образ прошивки во флэш-память микроконтроллера. Для этого при подключенной по USB плате STM32VLDISCOVERY надо выбрать пункт меню Flash Program Download.

После этого, если все сделано правильно, на экране должен появиться логотип uGFX, а спустя мгновение — двумерный график функции, нарисованный с помощью окна Graph, созданного средствами модуля GWIN (рис. 6).

Работа библиотеки uGFX совместно c дисплеем на контроллере ILI9341

Рис. 6. Работа библиотеки uGFX совместно c дисплеем на контроллере ILI9341

По умолчанию дисплей выводит изображение в портретной ориентации. Можно развернуть изображение в альбомную ориентацию, выполнив следующее.

В файле gfxconf.h надо разрешить управление изображением с помощью API-функций, добавив строку:

#define GDISP_NEED_CONTROL              TRUE

В файл main.c после вызова gfxInit() следует добавить строку:

gdispSetOrientation(GDISP_ROTATE_LANDSCAPE);

Драйвер ILI9341 поддерживает поворот изображения, драйверы других контроллеров могут и не иметь такой функции.

 

Потребление памяти

Интересно оценить объем памяти, потребляемой библиотекой uGFX. Это можно сделать, выполнив сборку различных демонстрационных проектов из состава uGFX, определяя размер получаемых двоичных файлов прошивки. Размер файла прошивки в байтах выводит компилятор GCC в окне Console в среде CoIDE. В таблице приведено потребление памяти для различных демонстрационных проектов.

Таблица. Потребление памяти программ библиотекой uGFX

Демонстрационное приложение

Путь

Объем памяти программ, байт

Без оптимизации (–O0)

Оптимизация по размеру кода (–Os)

Окно Graph — двумерный график

\demos\modules\gwin\graph\

27 388

21 328

Отображение текстовых строк двумя различными шрифтами высотой 10 и 11 пикселей. Шрифты хранятся в памяти программ

\demos\modules\gdisp\fonts\

32 452

26 544

Полноценный графический интерфейс пользователя

\demos\modules\gwin\widgets\

75 724

50 084

Компилятор ARM GCC позволяет оптимизировать получаемый файл прошивки. В таблице приведены полученные размеры прошивок для двух случаев — компиляция без оптимизации (опция компилятора –O0) и с оптимизацией по размеру файла (опция  –Os).

Надо отметить, что автор не получил работающее приложение с полноценным графическим интерфейсом пользователя (таблица) по причине нехватки памяти ОЗУ (у выбранного микроконтроллера всего 8 кбайт).

Результаты, приведенные в таблице, подтверждают сказанное в начале статьи о необходимости выбора микроконтроллера с достаточно большими объемами памяти. При этом объемы памяти, указанные в таблице, — отправная точка, в реальном приложении дополнительная память потребуется как на само приложение, так и на хранение растровых графических объектов (например, шрифтов). Память ОЗУ также будет расходоваться на создание каждого графического элемента (окно, кнопка, консоль и т. п.).

 

Выводы

Библиотека uGFX оказалась действительно платформенно независимой. По усмотрению автора был выбран микроконтроллер, дисплейный модуль с графическим контроллером из списка поддерживаемых, которые были произвольным образом соединены между собой. Библиотеку uGFX удалось адаптировать к данной аппаратной платформе и запустить приложение, ее использующее.

Адаптация библиотеки uGFX к выбранной аппаратной платформе все-таки требует определенных усилий, однако эта работа выполняется только один раз в самом начале и впоследствии программист не возвращается к низкоуровневым функциям, работающим с аппаратурой, а оперирует высокоуровневыми API-функциями.

Библиотека uGFX испытывает сильное влияние операционной системы ChibiOS, частью которой она являлась ранее. Это относится к драйверам и файлам платформы. Такая ситуация может потребовать дополнительных усилий по адаптации uGFX при ее использовании на операционных системах, отличных от ChibiOS.

Из данных, приведенных в таблице, следует, что uGFX для работы на 32‑разрядном микроконтроллере требуется не менее 22 кбайт памяти программ. А памяти ОЗУ размером 8 кбайт оказалось недостаточно, чтобы задействовать все возможности uGFX.

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

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