Як перестати боятися і любити mbed [Частина 3]

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

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

Сьогодні підключаємо датчик.



Зміст циклу публікацій:

  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 актуально і для інших моделей.


Схема включення

Для підключення датчиків серії 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);
/**
* @The brief totals (Celsius temperature in, relative humidity in percentages)
*/
float humidity;
float temperature;
/**
* @brief Send "Measuring Request" command
* @details Initiates a cycle of measuring 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.0 f / 16383.0 f) - 40.0 f;
humidity = (float)humidityRaw * 100.0 f / 16383.0 f;
} else { // sensor returns wrong data (1111...11)
return -1;
}
} else { // no new value received from sensor
return 0; 
} 
return 0;
}

Щоб використовувати бібліотеку у власному проекті, достатньо перейти по посиланню на сторінку з її описом і імпортувати бібліотеки в свій онлайн компілятор.



При публікації 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.0 f);
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: %.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.0 f);
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.ua");
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.0 V IN, то вся програма працює коректно.



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

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



Висновок

У висновку традиційно дякую за увагу читача і нагадую, що питання щодо застосування продукції, про яку ми пишемо на хабре, можна також задавати на email, вказаний в моєму профілі.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.