Продолжаем серию публикаций, посвященных использованию среды ARM mbed для создания прототипа измерительного устройства.
Напомню, что речь идет о разработке устройства с сенсорным экраном, которое служит для высокоскоростного измерения температуры и относительной влажности. Самое интересное в этой истории — подход к созданию встроенного ПО. Для написания программы используется онлайн IDE mbed, позволяющая создавать железонезависимый код, который одинаково работает на отладочных платах от SiLabs, Atmel, Wiznet, STM32, NXP и других производителей.
Сегодня подключаем датчик.
Содержание цикла публикаций:
- Обзор использованных программных и аппаратных решений.
- Начало работы с графическим контроллером FT800. Использование готовых mbed-библиотек для периферийных устройств.
- Подключение датчика HYT-271. Создание и публикация в mbed собственной библиотеки для периферийных устройств.
- Разработка приложения: Структура программы, работа с сенсорным экраном.
- Разработка приложения: Вывод изображений на дисплей, проблемы русификации.
- Печать деталей корпуса. Анализ ошибок проектирования и другие выводы.
Третья часть под катом.
Предыдущая статья закончилась описанием проекта, который создан в mbed IDE и реализует вывод счетчика секунд на TFT-дисплей от Riverdi.
Сегодня подключаем датчик температуры и относительной влажности серии HYT. Датчик представляет собой законченный цифровой узел — он содержит емкостный чувствительный элемент для измерения влажности, датчик температуры и схему обработки сигналов, кроме того, как и большинство подобных датчиков, HYT имеет заводскую калибровку. Всё это значит одно — для использования датчика достаточно подключить его к управляющему контроллеру и освоить протокол, по которому датчик опрашивается.
Формат посылок и пример реализации соответствующих функций на Си подробно описан в этой статье. Сейчас я постараюсь не сильно дублировать уже сказанное и сосредоточусь на вопросах использовании датчика HYT-271* в mbed-проекте.
* серия HYT состоит из трех моделей — HYT-271, HYT-221 и HYT-939. Они отличаются только корпусом, поэтому всё что я пишу о включении HYT-271 актуально и для остальных моделей.
Схема включения
Для подключения датчиков серии HYT обычно используется интерфейс I2C (датчики с SPI доступны под заказ). Соответственно, для подключения датчика нужно четыре линии:
- Питание (подойдут и 3.3, и 5 В)
- Земля
- Линия данных SDA
- Линия тактирования SLC
Рекомендованный номинал подтягивающих резисторов для линий SDA и SLC — 2.4 кОм.
Порядок опроса датчика
Датчики серии HYT поддерживают несколько типов команд. Для непосредственного опроса датчика служат команды Measuring Request и Data Fetch, а остальные инструкции понадобятся только при необходимости смены стандартного адреса датчика на шине I2C.
Сразу скажу, что в mbed-библиотеке HYT отсутствуют функции для смены I2C-адреса. Причины этого — моя природная лень и отсутствие прямой необходимости, ведь в большинстве случаев на шине висит единственный датчик и для работы достаточно реализовать процедуры опроса и получения результатов измерений.
Итак, по получении команды Measuring Request датчик HYT выходит из режима сна, проводит измерения и формирует посылку с данными о температуре и влажности. Чтобы получить данные, на датчик нужно отправить команду Data Fetch, причем между передачей этих двух команд должна быть предусмотрена задержка в 100 мс, т.к. именно столько времени требуется датчику для измерений и формирования посылки.
Measuring Request — это пакет, состоящий только из заголовочного байта — адреса датчика и флага записи. Соответствующая функция MRCommand() осуществляет отправку на I2C пустого пакета.
Команда Data Fetch — это заголовочный байт с флагом чтения и четыре байта данных, которые контроллер получает с датчика.
Первый бит первого байта — служебный 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;
}
Чтобы использовать библиотеку в собственном проекте, достаточно перейти по ссылке на страницу с её описанием и импортировать библиотеку в свой онлайн компилятор.
При публикации mbed-библиотеки для нового компонента полагается не только создать и должным образом задокументировать код. Помимо этого желательно снабдить библиотеку описанием аппаратного модуля, типовой схемой включения устройства и Hello World program — простым примером использования библиотеки. Для создания такого примера я снова использую проект-заготовку из первой статьи данного цикла публикаций.
#include "mbed.h"
Serial pc(USBTX, USBRX);
Ticker timeKeeping;
volatile uint64_t seconds = 0;
void secondsCallback(void) {
pc.printf("Number of seconds: %xrn", seconds);
seconds ++;
}
int main() {
timeKeeping.attach(&secondsCallback, 1.0f);
while(1) {}
}
Подключаем к проекту-заготовке библиотеку 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: %.1frn%", SENSOR.humidity);
pc.printf("Temperature level: %.1frn%", SENSOR.temperature);
pc.printf("-------------------------------rn%", SENSOR.temperature);
}
int main()
{
timeKeeping.attach(&secondsCallback, 1.0f);
while(1) {
dataUpdate();
}
}
Проект доступен по ссылке и корректно работает на разных отладочных платах, которые поддерживаются в ARM mbed.
На публикации примера использования процесс создания библиотеки для датчиков температуры и относительной влажности серии HYT заканчивается. Получив соответствующие права, я добавила новый компонент и в базу поддерживаемых в mbed датчиков.
Использование библиотеки в собственном проекте
Теперь можно вернуться к нашему основному проекту, будем выводить на сенсорный TFT-дисплей не счетчик секунд, а данные, полученные с датчика HYT-271.
Для этого создаем в 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.
В прошлой статье, когда мы рассматривали демо-пример для вывода информации на TFT-дисплей, для перехода с одной платы на другую потребовалось:
а) сменить целевую платформу (в правом верхнем углу компилятора),
б) переназначить использованные для подключения дисплея выводы (в коде),
в) подключить дисплей к новой плате.
Переход, как помнит читатель, прошел гладко и mbed-овская программа с ходу заработала на всех трех платах.
Для новой программы понадобится также:
г) переназначить использованные для подключения дисплея выводы (в коде),
д) подключить дисплей к новой плате, не забывая о подтягивающих резисторах.
Вот при подключении датчика наконец-то начали вылезать различия отладочных плат.
Wiznet сработал без сюрпризов — достаточно было подключить датчик по рекомендованной схеме и загрузить прошивку.
Данные с платы SiLabs удалось получить даже без использования дискретных резисторов. Дело в том, что все GPIO микроконтроллеров EFM32 имеют встроенные подтягивающие резисторы, при работе в десктопной IDE Simplicity Studio режим работы каждой линии (open-drain push-pull и т.д.) настраивается вручную. Судя по всему, mbed автоматически подтянул к питанию те линии, на которых реализованы SDA и SCL.
Слабым звеном сегодня объявляем Atmel — на плате ATSAMD21-XPRO интерфейс I2C (он же TWI) доступен на линиях, которые как-то использует отладчик Atmel Embedded Debugger. Я не разбиралась в этом вопросе подробно, но факт остается фактом: при питании платы от USB прием данных по I2C прекращается через 2-3 секунды. Если же подать питание на отдельный разъем 5.0V IN, то вся программа работает корректно.
Однако нужно признать, что по работе софта нареканий так и не возникло, а значит имеет смысл продолжить разработку с использованием mbed.
В следующей статье будет дан небольшой обзор структуры программы и подробные инструкции по работе с сенсорным вводом на TFT-модуле Riverdi. Таким образом мы всё ближе и ближе подбираемся к полноценному приложению, работа которого продемонстрирована на видео.
Заключение
В заключении традиционно благодарю читателя за внимание и напоминаю, что вопросы по применению продукции, о которой мы пишем на хабре, можно также задавать на email, указанный в моем профиле.
Автор: ЭФО