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

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

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

Сегодня подключаем датчик.

5df3f4ad6fdf451a9cbc1c655cfaad9b.png

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

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

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

Предыдущая статья закончилась описанием проекта, который создан в mbed IDE и реализует вывод счетчика секунд на TFT-дисплей от Riverdi.

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

Формат посылок и пример реализации соответствующих функций на Си подробно описан в этой статье. Сейчас я постараюсь не сильно дублировать уже сказанное и сосредоточусь на вопросах использовании датчика HYT-271* в mbed-проекте.

* серия HYT состоит из трех моделей — HYT-271, HYT-221 и HYT-939. Они отличаются только корпусом, поэтому всё что я пишу о включении HYT-271 актуально и для остальных моделей.

4308990f8a524989a8a4a793c77fe05b.png

Схема включения
Для подключения датчиков серии HYT обычно используется интерфейс I2C (датчики с SPI доступны под заказ). Соответственно, для подключения датчика нужно четыре линии:
  • Питание (подойдут и 3.3, и 5 В)
  • Земля
  • Линия данных SDA
  • Линия тактирования SLC

c7796625ece2487f8ab410f68070e9d6.png

Рекомендованный номинал подтягивающих резисторов для линий SDA и SLC — 2.4 кОм. Порядок опроса датчика
Датчики серии HYT поддерживают несколько типов команд. Для непосредственного опроса датчика служат команды Measuring Request и Data Fetch, а остальные инструкции понадобятся только при необходимости смены стандартного адреса датчика на шине I2C.

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

3b329c38bbeb4fdd8051048a290f18ff.png

Итак, по получении команды Measuring Request датчик HYT выходит из режима сна, проводит измерения и формирует посылку с данными о температуре и влажности. Чтобы получить данные, на датчик нужно отправить команду Data Fetch, причем между передачей этих двух команд должна быть предусмотрена задержка в 100 мс, т.к. именно столько времени требуется датчику для измерений и формирования посылки.

Measuring Request — это пакет, состоящий только из заголовочного байта — адреса датчика и флага записи. Соответствующая функция MRCommand () осуществляет отправку на I2C пустого пакета.

Команда Data Fetch — это заголовочный байт с флагом чтения и четыре байта данных, которые контроллер получает с датчика.

68c2973ceb2a4eb1bce5c022f534f539.png

Первый бит первого байта — служебный CMode bit. Если он установлен в »1», то датчик находится в специальном режиме работы, в котором можно сменить его адрес на шине.

Второй бит первого байта — служебный Stale bit. Если он установлен в »1», значит после выполнения очередного цикла измерений получены те же значения температуры и влажности, что и в предыдущем цикле.

Следующие 14 разрядов кодируют относительную влажность, следующие 14 разрядов — температуру. Далее идут два незначащих бита.

Соответствующая функция DFCommand () осуществляет передачу соответствующего пакета на I2C и декодирование полученных с датчика данных. Значения относительной влажности и температуры вычисляются по следующим формулам:

RH [%] = (100 / (214 — 1)) * RHвх
T [°C] = (165 / (214 — 1)) * Tвх — 40

Таким образом, библиотека опроса датчика HYT должна реализовывать две функции — MRCommand () и DFCommand ().Создание и публикация библиотеки в mbed
Напомню, что mbed поддерживает различные ARM-платформы и сопряженные устройства (компоненты) — дисплеи, приводы, датчики, приемопередатчики и так далее.

Естественно, поддержку новой отладочной платы может обеспечить только производитель микроконтроллеров, а вот с поддержкой новых периферийных компонентов всё обстоит интереснее. Ещё пару месяцев назад новые устройства мог добавлять любой пользователь и над списком поддерживаемых компонентов висела кнопка Add new component. Сейчас ARM меняет политику: любой зарегистрированный пользователь может опубликовать библиотеку и описание для нового компонента, однако для того чтобы добавить свой драйвер в список поддерживаемых компонентов требуются расширенные права (у меня такие теперь есть, хи-хи-хи).

Все mbed-библиотеки для периферийных устройств представляют собой С++ классы, содержащие необходимые для работы с устройством функции. Как правило, конструктор такого класса принимает в качестве аргументов линии ввода/вывода, к которым подключается компонент. Если при подключении устройства требуется некая процедура инициализации, то эту процедуру тоже логично включить в конструктор класса. Примером библиотеки, содержащей процедуру инициализации, служит библиотека графического контроллера FT800, подробное описание которой приводилось в предыдущей статье.

В случае с датчиком HYT всё гораздо проще: инициализации датчика не требуется, а для получения результатов измерений необходимо всего две функции.

Файл HYT.h

#define HYT_ADDR 0x50 // 01010000
 
class HYT
{
public:
    /**
     * HYT constructor.
     *
     * @param   sda mbed pin to use for SDA line of I2C interface.
     * @param   scl mbed pin to use for SCL line of I2C interface.
     * 
     * Remember about pull-up resistors on sda and scl. Recommended value is 2.4 kΩ
     */
    HYT(PinName sda, PinName scl);
    /**
     * @brief   The totals (temperature in Celsius, relative humidity in percentages)
     */
    float humidity;
    float temperature;
    /**
     * @brief   Send "Measuring Request" command
     * @details Initiates a measuring cycle of HYT sensor
     * @details More information: http://www.ist-ag.com/eh/ist-ag/resource.nsf/imgref/Download_AHHYTM_E2.2.5.pdf/$FILE/AHHYTM_E2.2.5.pdf
     */     
    void MRCommand(void);
    /**
     * @brief   Send "Data Fetch" command & processing the data
     * @details Fetch the last measured value of humidity and temperature from sensor
     * @details Calculate values of temperature in Celsius, relative humidity in percentages
     * @details More information: http://www.ist-ag.com/eh/ist-ag/resource.nsf/imgref/Download_AHHYTM_E2.2.5.pdf/$FILE/AHHYTM_E2.2.5.pdf
     * @returns 0 if no errors, -1 if no new value received from sensor.
     */    
     int DFCommand(void);
private:
    I2C _i2c;
};

Файл HYT.cpp
#include "HYT.h"
#include "mbed.h"
 
HYT::HYT(PinName sda, PinName scl) : _i2c(sda, scl)
{
}
 
/*************************************************************************************************************************/
void HYT::MRCommand(void)
{
    _i2c.write(HYT_ADDR, 0, 0);
}
 
/*************************************************************************************************************************/
int HYT::DFCommand(void)
{
    char    dataI2C[4];
    int     stateBit;
    int     humidityRaw;
    int     temperatureRaw;
 
    _i2c.read(HYT_ADDR, dataI2C, 4);
 
    stateBit = (dataI2C[0] & 0x40) >> 6;
    if (stateBit == 0) {
        humidityRaw = ((dataI2C[0] & 0x3F) << 8) | dataI2C[1];
        temperatureRaw = ((dataI2C[2] << 8) | dataI2C[3]) >> 2;
        if (temperatureRaw < 0x3FFF && humidityRaw < 0x3FFF) {
            temperature = ((float)(temperatureRaw) * 165.0f / 16383.0f) - 40.0f;
            humidity = (float)humidityRaw * 100.0f / 16383.0f;
        } else {   // sensor returns wrong data (1111...11)
            return -1;
        }
    } else {   // no new value received from sensor
        return 0;    
    } 
    return 0;
}

Чтобы использовать библиотеку в собственном проекте, достаточно перейти по ссылке на страницу с её описанием и импортировать библиотеку в свой онлайн компилятор.

dedcc0fe4aea4c599f7f51b086ffaf51.png

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

Напомню, что проект-заготовка — это простая программа, выводящая счетчик секунд на последовательный интерфейс (на виртуальный COM-порт)
#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


Подключаем к проекту-заготовке библиотеку HYT и вносим нехитрые изменения:
  • создаем объект SENSOR класса HYT, передавая в качестве аргументов называния выводов, на которых доступен интерфейс I2C
  • добавляем функцию опроса датчика dataUpdate () и вызываем её в бесконечном цикле
  • вместо счетчика секунд выводим на последовательный интерфейс полученные данные о температуре и относительной влажности

Таким образом получаем демо-пример для созданной библиотеки:
#include "mbed.h"
#include "HYT.h"
 
Serial              pc(USBTX, USBRX);
Ticker              timeKeeping;

HYT                 SENSOR (PD6, PD7); // sda, scl [SLSTK3400A]
//HYT               SENSOR (D14, D15); // sda, scl [WIZwiki-W7500P]
//HYT               SENSOR (PA08, PA09); // sda, scl [ATSAMD21-XPRO]
 
// HYT sensor polling cycle
void dataUpdate(void)
{
    SENSOR.MRCommand();
    wait_ms(100);
    SENSOR.DFCommand();
}
 
void secondsCallback(void) {
    pc.printf("Humidity level: %.1f\r\n%", SENSOR.humidity);
    pc.printf("Temperature level: %.1f\r\n%", SENSOR.temperature);
    pc.printf("-------------------------------\r\n%", SENSOR.temperature);
}
 
int main()
{
    timeKeeping.attach(&secondsCallback, 1.0f);
    while(1) {
        dataUpdate();
    }
}

Проект доступен по ссылке и корректно работает на разных отладочных платах, которые поддерживаются в ARM mbed.
e3ff0dc7820b45e99e11556fc1717a49.png

На публикации примера использования процесс создания библиотеки для датчиков температуры и относительной влажности серии HYT заканчивается. Получив соответствующие права, я добавила новый компонент и в базу поддерживаемых в mbed датчиков.Использование библиотеки в собственном проекте
Теперь можно вернуться к нашему основному проекту, будем выводить на сенсорный TFT-дисплей не счетчик секунд, а данные, полученные с датчика HYT-271.
ed6387b630ca480b8e9212c58e35f11b

Для этого создаем в mbed новый проект и импортируем туда библиотеки HYT и FT800_2.
Затем создаем объекты классов HYT и FT800, используя в качестве аргументов названия линий ввода/вывода, на которых доступны интерфейсы I2C и SPI соответственно:
HYT SENSOR (PD6, PD7); // sda, scl [SLSTK3400A]
FT800 TFT (PE10, PE11, PE12, PE13, PB11, PD4); // mosi, miso, sck, ss, int, pd [SLSTK3400A]
//HYT SENSOR (D14, D15); // sda, scl [WIZwiki-W7500P]
//FT800 TFT (D11, D12, D13, D10, D9, D8); // mosi, miso, sck, ss, int, pd [WIZwiki-W7500P]
//HYT SENSOR (PA08, PA09); // sda, scl [ATSAMD21-XPRO]
//FT800 TFT (PA18, PA16, PA19, PA17, PA20, PA21); // mosi, miso, sck, ss, int, pd [ATSAMD21-XPRO]

Добавляем функцию опроса датчика:
void dataUpdate(void)
{
    SENSOR.MRCommand();
    wait_ms(100);
    SENSOR.DFCommand();
}

Добавляем функцию, формирующую и загружающую на графический контроллер дисплей-лист:
  • Начало дисплей-листа, установка белого цвета как цвета по умолчанию
  • Установка черного цвета, вывод двух текстовых строк
  • Установка темно-зеленого цвета, вывод графического примитива «Прямоугольник»
  • Установка белого цвета, вывод текстовой строки и числа (значения относительной влажности)
  • Установка темно-синего цвета, вывод графического примитива «Прямоугольник»
  • Установка белого цвета, вывод текстовой строки и числа (значения температуры)
  • Установка черного цвета, вывод текстовой строки
  • Вывод графического примитива «Прямая линия»
  • Конец дисплей-листа, загрузка картинки на дисплей

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 and HYT library");

    TFT.DL(COLOR_RGB(9, 40, 3));
    TFT.DL(BEGIN(RECTS));
    TFT.DL(VERTEX2II(11, 105, 0, 0));
    TFT.DL(VERTEX2II(11 + 222, 105 + 100, 0, 0));

    TFT.DL(COLOR_RGB(255, 255, 255));
    TFT.Text(11 + 10, 105 + 10, 28, 0, "Relative humidity, %");
    TFT.Number(11 + 10, 105 + 10 + 30, 31, 0, SENSOR.humidity);

    TFT.DL(COLOR_RGB(9, 3, 40));
    TFT.DL(BEGIN(RECTS));
    TFT.DL(VERTEX2II(11 + 222 + 14, 105, 0, 0));
    TFT.DL(VERTEX2II(11 + 222 + 14 + 222, 105 + 100, 0, 0));

    TFT.DL(COLOR_RGB(255, 255, 255));
    TFT.Text(11 + 222 + 14 + 10, 105 + 10, 28, 0, "Temperature, C");
    TFT.Number(11 + 222 + 14 + 10, 105 + 10 + 30, 31, 0, SENSOR.temperature);
    
    TFT.DL(COLOR_RGB(0, 0, 0));
    TFT.Text(300, 105 + 100 + 35, 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()
{
    while(1) {
        dataUpdate();
        drawTimeScreen();
    }
}

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

Как и на остальных этапах разработки проекта, я запускаю полученную программу на трех разных платах — SLSTK3400A от SiLabs, ATSAMD21-XPRO от Atmel и WIZwiki-W7500P от Wiznet.

217d541d7aa046ba8ba126f16d9ed74c.png

В прошлой статье, когда мы рассматривали демо-пример для вывода информации на TFT-дисплей, для перехода с одной платы на другую потребовалось:

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

Переход, как помнит читатель, прошел гладко и mbed-овская программа с ходу заработала на всех трех платах.

Для новой программы понадобится также:

г) переназначить использованные для подключения дисплея выводы (в коде),
д) подключить дисплей к новой плате, не забывая о подтягивающих резисторах.

Вот при подключении датчика наконец-то начали вылезать различия отладочных плат.

Wiznet сработал без сюрпризов — достаточно было подключить датчик по рекомендованной схеме и загрузить прошивку.

98e0cb793633496bbe68b05cfb309338.JPG

Данные с платы SiLabs удалось получить даже без использования дискретных резисторов. Дело в том, что все GPIO микроконтроллеров EFM32 имеют встроенные подтягивающие резисторы, при работе в десктопной IDE Simplicity Studio режим работы каждой линии (open-drain push-pull и т.д.) настраивается вручную. Судя по всему, mbed автоматически подтянул к питанию те линии, на которых реализованы SDA и SCL.

d8f89c9bc8cf43dfbf1cb71ed77da464.jpg

Слабым звеном сегодня объявляем Atmel — на плате ATSAMD21-XPRO интерфейс I2C (он же TWI) доступен на линиях, которые как-то использует отладчик Atmel Embedded Debugger. Я не разбиралась в этом вопросе подробно, но факт остается фактом: при питании платы от USB прием данных по I2C прекращается через 2–3 секунды. Если же подать питание на отдельный разъем 5.0V IN, то вся программа работает корректно.

75dcf0857168465c94885801b0c149db.JPG

Однако нужно признать, что по работе софта нареканий так и не возникло, а значит имеет смысл продолжить разработку с использованием mbed.

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

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

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

© Habrahabr.ru