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

    Продолжаем серию публикаций, посвященных использованию среды ARM 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 — простым примером использования библиотеки. Для создания такого примера я снова использую проект-заготовку из первой статьи данного цикла публикаций.

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




    Подключаем к проекту-заготовке библиотеку 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();
        }
    }
    

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


    На публикации примера использования процесс создания библиотеки для датчиков HYT заканчивается.

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



    Теперь можно вернуться к нашему основному проекту, будем выводить на сенсорный 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();
        }
    }
    

    Проект доступен на developer.mbed.org.

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


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

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

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

    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, указанный в моем профиле.
    • +13
    • 5,8k
    • 4
    ЭФО
    63,00
    Поставки электронных компонентов
    Поделиться публикацией

    Комментарии 4

      0

      Может в начале статьи сделать ссылку на предыдущую часть или части? А то искать где же начало — превращается в небольшой квест.

        0
        Правда? Там же до ката, сразу под первой картинкой, приведен список с активными ссылками.
          0

          Да, спасибо, сразу не заметил, что под картинкой список публикаций, там просто не написано " часть 1", часть 2.

            0
            Оказалось, что этот список многим незаметен.

            Запоздалое спасибо вам!

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое