Как перестать бояться и полюбить mbed [Часть 2]

Продолжаем серию публикаций, посвященных использованию среды ARM mbed для создания прототипа измерительного устройства.

Напомню, что речь идет о разработке устройства с сенсорным экраном, которое служит для высокоскоростного измерения температуры и относительной влажности. Самое интересное в этой истории — подход к созданию встроенного ПО. Для написания программы используется онлайн IDE mbed, позволяющая создавать железонезависимый код, который одинаково работает на отладочных платах от SiLabs, Atmel, Wiznet, STM32, NXP и других производителей.

Сегодня начинаем работать с выводом картинки на TFT-дисплей.

f82a277aa0cb491f9e1f841922659f0c.png

Содержание цикла публикаций:

  1. Обзор использованных программных и аппаратных решений.
  2. Начало работы с графическим контроллером FT800. Использование готовых mbed-библиотек для периферийных устройств.
  3. Подключение датчика HYT-271. Создание и публикация в mbed собственной библиотеки для периферийных устройств.
  4. Разработка приложения: Структура программы, работа с сенсорным экраном.
  5. Разработка приложения: Вывод изображений на дисплей, проблемы русификации.
  6. Печать деталей корпуса. Анализ ошибок проектирования и другие выводы.

Вторая часть под катом.

В прошлой статье был дан краткий обзор использованных аппаратных модулей, в том числе сенсорного TFT-дисплея — модуля от компании Riverdi, который состоит из дисплея и встроенного графического контроллера FT800 от FTDI. Сегодня мы рассмотрим общие вопросы работы с таким модулем и «подружим» его с отладочной платой.

Поскольку для написания ПО используется исключительно онлайн IDE mbed, получившаяся программа должна без проблем запуститься на отладочных платах SLSTK3400A от SiLabs, ATSAMD21-XPRO от Atmel и WIZwiki-W7500P от Wiznet.

217d541d7aa046ba8ba126f16d9ed74c.png


Итак, все процедуры, связанные с выводом графики, сенсорным вводом и работой аудиоканала, аппаратно реализованы на графическом контроллере серии FT800. Задачей разработчика является управление микросхемой FTDI с главного микроконтроллера — грубо говоря, чтение и запись определенных регистров контроллера FT800 по интерфейсу SPI или I2C*.
* далее будем считать что хост-контроллер связан с FT800 по интерфейсу SPI. Чаще всего так и есть.
30e350f14541439d9798e661daebd89f.png

Для управления графическим контроллером используются команды трех типов:
  • Host command для управления режимом работы FT800,
  • Host Memory Read для получения информации с контроллера FTDI (например, для чтения регистра сенсорного ввода)
  • Host Memory Write для передачи команд на выполнение графических операций, на воспроизведение звука и т.д.

Все функции для взаимодействия с TFT-дисплеем состоят из приема и передачи таких команд. Компания FTDI предоставляет библиотеку, в которой реализованы все операции, поддерживаемые графическим контроллером. Это несколько десятков функций различной сложности, которые, тем не менее, всегда состоят их базовых функций — приема и передачи по SPI инструкций Host command, Host Memory Read и Host Memory Write.

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

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

Материалы от производителя
По ссылке доступен полный список материалов. Лично я рекомендую начать с AN_240 FT800 From the Ground Up, продолжить документом FT800 Series Programmers Guide, а далее искать документы связанные непосредственно с вашей задачей.

Подборка статей на русском языке
  1. Графический контроллер FT800 для управления TFT-дисплеями — обзорная статья, которая знакомит с основными возможностями графических контроллеров FTDI.
  2. Графический контроллер FT800. Программные средства разработки и отладки. — в статье рассмотрены полезные утилиты и библиотеки, которые существенно упрощают этапы освоения, разработки и тестирования графических приложений, а также помогают познакомиться с функциональными возможностями FT800/801 с помощью персонального компьютера без использования дополнительных аппаратных средств.
  3. Начинаем работать с графическим контроллером FT800 FTDI — пример использования графического контроллера FT800 с микроконтроллерами Cypress. Описаны основные этапы по адаптации библиотеки производителя для выбранного МК.
  4. Графический контроллер EVE FT800 FTDI. Работа с пользовательскими шрифтами, кнопками и сенсорным экраном. — продолжение предыдущей статьи, в которой рассмотрены основные принципы работы с элементами пользовательского интерфейса и сенсорным экраном. В частности, показано, как FT80x обрабатывает информацию от сенсорного экрана и автоматически привязывает ее к заданному элементу интерфейса.
  5. Графический контроллер EVE FT800 FTDI и микроконтроллер SAMD21 Atmel. Работаем с графическими изображениями. — Пример адаптации библиотеки производителя для использования с МК Atmel серии SAM D21 и работа с графическими объектами.
  6. Графический контроллер EVE FT800 FTDI и микроконтроллер SAMD21 Atmel. — продолжение предыдущей статьи, в которой описана методика работы с JPEG-изображениями.
  7. Графический контроллер FT800. Вывод на экран изображения стрелочного индикатора — в данном примере описана методика, которая позволяет минимизировать обмен данными между МК и графическим контроллером за счет использования встроенного ОЗУ FT80x.


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

Схема подключения



Для подключения TFT-модуля требуется восемь сигналов:
  • четыре линии SPI  — mosi, miso, sck и ss
  • Сигнал intr (interrupt от контроллера FTD хост-контроллеру),
  • Сигнал pd  — power down,
  • Питание (3.3 В) и земля.

Сигналы intr и pd могут быть реализованы на любых свободных цифровых линиях хост-контроллера.

aaed88c22f7c46d5a8eea147f4d18c5d.png

Для большинства TFT-модулей Riverdi подключение модуля к управляющему контроллеру производится через плоский шлейф. Для подключения шлейфа к отладочным платам предлагается плата-переходник Break Out Board 20. При её использовании нужно обратить внимание на то, что кроме линии питания VDD, на плате отдельно выведено питание подсветки дисплея — сигнал BLVDD. Для работы с дисплеем питание должно подаваться и на VDD, и на BLVDD, поэтому есть смысл сразу объединить линии и подавать питание только на VDD.

03093a4bb76742c0b1713df635dc4bd0.JPG

С землями GND и BLGND такой фокус не требуется.

Процедура инициализации TFT-модуля



4d02b7a46ad94e78a8413b81d58994dc.pngПроцедура инициализации состоит из трех этапов: настройка интерфейса SPI, инициализация графического контроллера FT800 и инициализация TFT-дисплея.

Интерфейс SPI хост-процессора всегда является мастером и работает в режиме «mode 0» с передачей старшего бита первым (MSB first). Максимальная рабочая частота интерфейса — 30 МГц, однако до окончания процедуры инициализации частота не должна быть задана выше 11 МГц.

Инициализация графического контроллера состоит в последовательном выполнении следующих операций.

Сначала сигнал PD переводится на 20 мс в низкий уровень, а затем ещё на 20 мс в высокий уровень. Таким образом графический контроллер переходит в режим готовности (Standby). После этого на контроллер FTDI подается несколько команд группы Host command: командой ACTIVE (0×00) контроллер переводится в активный режим, командой CORERST (0×68) сбрасываются значения всех регистров и логических цепей, далее командой CLKEXT (0×44) выбирается тактирование от внешнего источника, а после этого выбирается рабочая частота контроллера — 48 или 36 МГц командой CLK48M (0×62) или CLK36M (0×61) соответственно.

Конечно, возможны и другие сценарии настройки контроллера FT800, но чаще всего процедура выглядит именно так.
Если инициализация графического контроллера прошла успешно, то в регистре REG_ID графического контроллера устанавливается значение 0×7C. После проверки этого контрольного значения можно увеличить скорость SPI.

Последним подготовительным этапом является настройка параметров TFT-дисплея. Суть процедуры — запись в определенные регистры графического контроллера параметров конкретного TFT-дисплея. Мы один раз объясняем графическому контроллеру к какому именно дисплею он подключен, а потом используем универсальные библиотечные функции.

Что подразумевается под параметрами дисплея
b47cc1ad43d945368847109048a2fd8e.PNG

После настроек на дисплей выводится пустой экран. Далее, при необходимости, проводятся настройка сенсорного ввода и аудиоканала. Все эти процедуры многократно описаны производителем, поэтому, наверное, не нуждаются в пояснениях.

Вывод на дисплей графических примитивов и виджетов



После инициализации работа с дисплеем сводится к формированию очередного дисплей-листа и загрузке его на графический контроллер. Дисплей-лист — это список команд, в результате исполнения которых контроллер FTDI выводит на дисплей некое изображение.

Дисплей-лист начинается с записи в графический контроллер команды CMD_DLSTART и заканчивается записью команды DISPLAY (). Отображение дисплей-листа происходит по записи стандартного значения в регистр REG_DLSWAP.

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

Графический контроллер FTDI состоит из нескольких узлов, среди которых основной процессор, дополнительный графический сопроцессор, аудиосопроцессор и т.д. За выполнение простых команд дисплей-листа отвечает основной процессор FT800, однако микросхемы FTDI позволяют работать и с более сложными объектами — виджетами. Виджеты — это кнопки, слайдеры, шкалы, градиенты, текстовые строки, числа и так далее. Несмотря на то что отрисовка виджетов осуществляется сопроцессором, при формировании дисплей-листа можно вперемешку вызывать простые базовые функции и функции отрисовки виджетов.

Использование библиотеки FT800 в собственном mbed-проекте



В прошлой статье была подготовлена заготовка проекта — короткая программа, выводящая на виртуальный COM-порт счетчик секунд. Сегодня подключим к проекту библиотеку FT800 и будем выводить счетчик секунд уже на TFT-дисплей.

Среди mbed-разработчиков уже нашелся человек, имевший дело с графическим контроллером FT800. Этот самый человек, Peter Drescher, благополучно адаптировал для mbed библиотеку от FTDI, снабдил её примером использования и фотографиями. Спасибо, Peter!

Чтобы использовать библиотеку в своем проекте, кликаем на на Import library на посвященной FT800 странице, попадаем в свой компилятор и выбираем проект, к которому библиотека будет подключена. Разумеется, для всех этих телодвижений нужно предварительно зарегистрироваться на developer.mbed.org.

5fd1c9cf718f4abe84593f055638788f.png

Для использования библиотеки FT800 нужно подключить к своему main.cpp файл FT_Platform.h и объявить новый объект TFT класса FT800, также как мы объявляли объект pc класса Serial в программе из предыдущей статьи. Аргументами при объявлении объекта служат выводы платы, на которых доступны линии интерфейса SPI и сигналы PD и INT. Поскольку я буду запускать программу на трех разных платах, сразу расписываю распиновку для всех трех случаев:

#include "mbed.h"
#include "FT_Platform.h"

// SLSTK3400A
FT800 TFT (PE10, PE11, PE12, PE13, PB11, PD4); // mosi, miso, sck, ss, int, pd
// WIZwiki-W7500P
//FT800 TFT (D11, D12, D13, D10, D9, D8); // mosi, miso, sck, ss, int, pd
// ATSAMD21-XPRO
//FT800 TFT (PA18, PA16, PA19, PA17, PA20, PA21); // mosi, miso, sck, ss, int, pd

Сам конструктор класса FT800 лежит в файле FT_Gpu_Hal.cpp
FT800::FT800(PinName mosi, PinName miso, PinName sck, PinName ss, PinName intr, PinName pd):
	 _spi(mosi, miso, sck),
	 _ss(ss),
	 _pd(pd),
	 _f800_isr(InterruptIn(intr))
	 {
	 	 _spi.format(8,0);                  // 8 bit spi mode 0
	 	 _spi.frequency(2000000);           // start with 10 Mhz SPI clock
	 	 _ss = 1;                           // cs high
	 	 _pd = 1;                           // PD high
		 Bootup();
	 }

В конструктор класса FT800 уже включена вся процедура инициализации TFT-модуля — настройка интерфейса SPI и вызов функции Bootup (), которая описывает подачу 20-милисекундного импульса на линию PD, отправку нужной последовательности Host-команд, проверку REG_ID графического контроллера, дальнейшее задание частоты SPI в 20 МГц, а также настройка параметров TFT-дисплея.
Смотреть код можно здесь (листать в конец страницы).

Важно отметить, что дефайны для параметров TFT-дисплея задаются в файле FT_LCD_Type.h, где по умолчанию прописаны параметры дисплея стандарта WQVGA с разрешением 480 на 272.

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

Приведу пример простой программы для вывода на экран счетчика секунд, круглых часов и ещё нескольких объектов.

074c3351679d48d484f0db4d371a0e65.jpg

Единственный дисплей-лист будет состоять из следующих операций:

1. Начало дисплей-листа, установка белого цвета как цвета по умолчанию:

    TFT.DLstart();
    TFT.DL(CLEAR_COLOR_RGB(255, 255, 255));
    TFT.DL(CLEAR(1, 1, 1));

2. Установка черного цвета, вывод двух текстовых строк:
    TFT.DL(COLOR_RGB(0, 0, 0));
    TFT.Text(11, 15, 30, 0, "Demo-project for habrahabr.ru");
    TFT.Text(13, 15 + 40, 28, 0, "Using FT800 library");

3. Установка темно-зеленого цвета, вывод графического примитива «Прямоугольник»:
    TFT.DL(COLOR_RGB(9, 35, 5));
    TFT.DL(BEGIN(RECTS));
    TFT.DL(VERTEX2II(11, 105, 0, 0));
    TFT.DL(VERTEX2II(11 + 275, 105 + 100, 0, 0));

4. Установка белого цвета, вывод текста и числа секунд:
    TFT.DL(COLOR_RGB(255, 255, 255));
    TFT.Text(11 + 10, 105 + 10, 29, 0, "Number of seconds:");
    TFT.Number(11 + 10, 105 + 10 + 30, 31, 0, seconds);

5. Вывод одного из поддерживаемых на FT800 виджетов — часов:
    TFT.Clock(390, 105 + 70, 70, 0, 4, 20, (seconds % 60), 0);

6. Установка черного цвета, вывод текстовой строки:
    TFT.DL(COLOR_RGB(0, 0, 0));
    TFT.Text(11, 240, 28, 0, "e-mail: xk@efo.ru");

7. Вывод графического примитива «Прямая линия»:
    TFT.DL(BEGIN(LINES));
    TFT.DL(LINE_WIDTH(8));
    TFT.DL(VERTEX2II(11, 15 + 40 + 30, 0, 0));
    TFT.DL(VERTEX2II(460, 15 + 40 + 30, 0, 0));

8. Конец дисплей-листа, загрузка картинки на дисплей:
    TFT.DL(DISPLAY());
    TFT.Swap();

Обращаю внимание, что команды TFT.DL () — это описанные выше простые команды дисплей-листа, а команды TFT.Text (), TFT.Number () и TFT.Clock () — это инструкции сопроцессора.
Важно!
Для графических контроллеров серии FT800 в mbed доступно две библиотеки одного и того же автора — FT800 и FT800_2. Отличаются они друг от друга только названиями функций: в первой библиотеке используются те же названия, что в материалах от FTDI, а во второй все названия сокращены:
TFT.Ft_Gpu_CoCmd_Dlstart ();
TFT.Ft_App_WrCoCmd_Buffer (CLEAR_COLOR_RGB (255, 255, 255));
TFT.Ft_App_WrCoCmd_Buffer (CLEAR (1, 1, 1));

TFT.Ft_App_WrCoCmd_Buffer (BEGIN (LINES));
TFT.Ft_App_WrCoCmd_Buffer (VERTEX2II (11, 15 + 40 + 30, 0, 0));
TFT.Ft_App_WrCoCmd_Buffer (VERTEX2II (460, 15 + 40 + 30, 0, 0));

TFT.Ft_App_WrCoCmd_Buffer (DISPLAY ());
TFT.Ft_Gpu_CoCmd_Swap ();

TFT.DLstart ();
TFT.DL (CLEAR_COLOR_RGB (255, 255, 255));
TFT.DL (CLEAR (1, 1, 1));

TFT.DL (BEGIN (LINES));
TFT.DL (VERTEX2II (11, 15 + 40 + 30, 0, 0));
TFT.DL (VERTEX2II (460, 15 + 40 + 30, 0, 0));

TFT.DL (DISPLAY ());
TFT.Swap ();

Я использую второй вариант, однако у использования длинных названий функций есть неоспоримое преимущество: освоившись в таких именах вам будет гораздо легче использовать примеры от производителя и утилиты для автоматической генерации кода от FTDI.
Возвращаемся к примеру работы с дисплеем, программе для вывода счетчика секунд на TFT. За основу берем проект-заготовку из прошлой статьи, в котором с помощью базовой библиотеки mbed на последовательный интерфейс выводится счетчик секунд.
Вывод счетчика секунд на последовательный интерфейс
#include "mbed.h"

Serial              pc(USBTX, USBRX);
Ticker              timeKeeping;
volatile uint64_t     seconds = 0;

void secondsCallback(void) {
    pc.printf("Number of seconds: %x\r\n", seconds);
    seconds ++;
}
 
int main() {
    timeKeeping.attach(&secondsCallback, 1.0f);
    while(1) {}
}

4ecc316fec904a41a9aee760f174fcd3.png


Вносим соответствующие изменения — подключаем библиотеку FT800, объявляем объект класса FT800 и вы бесконечном цикле вызываем функцию, формирующую описанный выше дисплей-лист.
#include "mbed.h"
#include "FT_Platform.h"

FT800 TFT (PE10, PE11, PE12, PE13, PB11, PD4); // mosi, miso, sck, ss, int, pd [SLSTK3400A]
//FT800 TFT (D11, D12, D13, D10, D9, D8); // mosi, miso, sck, ss, int, pd [WIZwiki-W7500P]
//FT800 TFT (PA18, PA16, PA19, PA17, PA20, PA21); // mosi, miso, sck, ss, int, pd [ATSAMD21-XPRO]

Ticker              timeKeeping;
volatile uint64_t     seconds = 0;

/***********************************************************************************************************************/
void secondsCallback(void)
{
    seconds ++;
}
 
/***********************************************************************************************************************/
/* Construct the screen and downloasd it to the TFT */
void drawTimeScreen(void)
{
    TFT.DLstart();
    TFT.DL(CLEAR_COLOR_RGB(255, 255, 255));
    TFT.DL(CLEAR(1, 1, 1));
 
    TFT.DL(COLOR_RGB(0, 0, 0));
    TFT.Text(11, 15, 30, 0, "Demo-project for habrahabr.ru");
    TFT.Text(13, 15 + 40, 28, 0, "Using FT800 library");
 
    TFT.DL(COLOR_RGB(9, 35, 5));
    TFT.DL(BEGIN(RECTS));
    TFT.DL(VERTEX2II(11, 105, 0, 0));
    TFT.DL(VERTEX2II(11 + 275, 105 + 100, 0, 0));
 
    TFT.DL(COLOR_RGB(255, 255, 255));
    TFT.Text(11 + 10, 105 + 10, 29, 0, "Number of seconds:");
    TFT.Number(11 + 10, 105 + 10 + 30, 31, 0, seconds);
    
    TFT.Clock(390, 105 + 70, 70, 0, 4, 20, (seconds % 60), 0);
    
    TFT.DL(COLOR_RGB(0, 0, 0));
    TFT.Text(11, 240, 28, 0, "e-mail: xk@efo.ru");
    
    TFT.DL(BEGIN(LINES));
    TFT.DL(LINE_WIDTH(8));
    TFT.DL(VERTEX2II(11, 15 + 40 + 30, 0, 0));
    TFT.DL(VERTEX2II(460, 15 + 40 + 30, 0, 0));
    
    TFT.DL(DISPLAY());
    TFT.Swap();
}
 
/***********************************************************************************************************************/
int main()
{
    timeKeeping.attach(&secondsCallback, 1.0f);
    while(1) {
        drawTimeScreen();
    }
}

Проект доступен по ссылке.

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

f82a277aa0cb491f9e1f841922659f0c.png

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


Заключение



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

Комментарии (3)

  • 22 сентября 2016 в 13:43

    +1

    При всем уважении к автору — разработка для непростых устойств без отладки в 2016 году выглядит странно, чтобы не сказать больше (это я про mbed).
    • 22 сентября 2016 в 13:56

      0

      По своему небольшому опыту работы с mbed могу сказать, что там можно экспортировать проект во взрослую IDE (Keil uVision, IAR и т.д.) и отлаживать уже там. Здесь, на мой взгляд, суть такая же как у Arduino.

    • 22 сентября 2016 в 14:13

      +1

      Да «Странно» — это вы очень мягко выразились))
      Для создания чего-либо серьезного mbed не годится, но у неё, как по мне, другое назначение:
      Самоцитата
      Здесь пора заметить один из главных минусов mbed: printf — это по факту единственное доступное средство отладки. Кроме того что вам недоступна отладка с breakpoint-ами и доступом к регистрам микроконтроллера, у mbed существуют и другие очевидные ограничения. Например, универсальные драйверы периферии по определению не могут поддерживать все особенности периферии каждого контроллера, то есть в mbed могут отсутствовать библиотеки для необычных модулей или поддержка каких-то нестандартных режимов работы периферийных устройств вашего микроконтроллера. А это могут быть именно те режимы и модули, из-за которых вы и выбрали свой клисталл.

      Ещё, конечно, mbed — не лучший инструмент для создания сложных приложений, связанных с планированием задач и многопоточностью (хотя я не в курсе что умеет новая mbed OS 5). Ну и в конце концов, список поддерживаемых аппаратных платформ пока далеко не полон.

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

      а) для прототипирования и быстрого освоения новых компонентов,
      б) для знакомства с программированием микроконтроллеров.

      Ключевыми свойствами mbed являются универсальность, доступность и простота использования программных библиотек, а также возможность экспорта написанного в mbed софта во «взрослые» IDE.

      При прототипировании становится возможен сценарий «быстро запустил рабочее решение из коробки → экспортировал программу в привычную IDE и довел до ума», а для освоения программирования микроконтроллеров упрощается переход от мигания светодиодом на arduino к более серьезным вещам.



      Mbed — это правда очень круто, если нужно опробовать новый компонент. У меня так и было — никак не получалось подружить дисплей с SLSTK3400A, шалил SPI. Пару дней вместе с коллегами чесали репу, а потом я ради эксперимента запустила готовую демо-программу из mbed, а оно возьми и заработай.

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

© Habrahabr.ru