STM Урок 55. Датчик влажности HTS221. Часть 2

 

 

 

    Урок 55

 

Часть 2

 

Датчик влажности HTS221

 

 

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

Продолжим писать нашу функцию чтения температуры

 

tmp = Humidity_IO_Read(HTS221_ADDRESS,HTS221_T0_T1_DEGC_H2);

T0_degC_x8_u16 = (((uint16_t)(tmp & 0x03)) << 8) | ((uint16_t)buffer[0]);

T1_degC_x8_u16 = (((uint16_t)(tmp & 0x0C)) << 6) | ((uint16_t)buffer[1]);

T0_degC = T0_degC_x8_u16 >> 3;

T1_degC = T1_degC_x8_u16 >> 3;

 

Как делать расчёты, определено в технической документации

 

image07

 

Считаем следующие величины

 

  T1_degC = T1_degC_x8_u16 >> 3;

        buffer[0]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_T0_OUT_L);

        buffer[1]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_T0_OUT_H);

        buffer[2]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_T1_OUT_L);

        buffer[3]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_T1_OUT_H);

T0_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];

T1_out = (((uint16_t)buffer[3]) << 8) | (uint16_t)buffer[2];

 

Здесь используются уже другие 4 регистра

 

image11

image09

 

Прочитаем показания датчика по температуре и положим их в соответствующую переменную

 

T1_out = (((uint16_t)buffer[3]) << 8) | (uint16_t)buffer[2];

        buffer[0]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_TEMP_OUT_L_REG);

        buffer[1]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_TEMP_OUT_H_REG);

T_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];

 

Показания хранятся в данных регистрах

 

image10

 

Передвинем начало комментария. В раскомментированном участке уберём лишнее и заменим raw_data на T_out

 

T_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];

        btnstat=HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);

        //Если кнопка не нажата,

        //то вызовем фильтр скользящего среднего

        if(btnstat!=0)        T_out =  MovingAverageTemp(T_out);

        /*

 

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

 

        if(btnstat!=0)        T_out =  MovingAverageTemp(T_out);

        *pData = (float)(T_out — T0_out) * (float)(T1_degC — T0_degC) /

                (float)(T1_out — T0_out)  +  T0_degC;

}

 

По сути, в калибровочных ячейках находятся обычные числа, просто они в данных ячейках (регистрах) «умеют» запоминаться после внесения изменений и, следовательно, откалиброванный датчик уже не зависит от кода. Раньше другие датчики мы калибровали в коде, и, если бы мы данный датчик подключили бы к другому контроллеру, то, сочиняя код для него, нам бы пришлось заново заняться калибровкой. И, если вникнуть в данные расчеты, приведённые выше, то можно без особого труда разобраться, какие именно данные туда вносить, чтобы откалибровать показания. Потому что ошибка в показаниях бывает не только в сдвиге показаний, а ещё и в различии ошибки при разных температурах. И, используя данные ячейки, можно все эти ошибки откорректировать. Но мы в данном занятии не будем этим заниматься, а будем надеяться, что производитель датчика уже эту работу добросовестно проделал. Также мы не будем этим заниматься ещё и потому, что у нас нет никаких эталонных измерителей температуры и влажности, сравнивая с показаниями которых, мы могли бы калибровать датчик.

На этом функция считывания показания температуры закончена.

Приступим к измерению относительной влажности воздуха. Что это такое, приведено в самом начале урока.

Функцию Press_Get_Press переименуем в Humidity_Get_Hum, также добавим некоторые переменные и передвинем комментарий ниже

 

void Humidity_Get_Hum(float* pData)

{

        uint8_t btnstat;

        uint8_t buffer[2];

int16_t H0_T0_out, H1_T0_out, H_T_out;

int16_t H0_rh, H1_rh;

float   tmp_f;

        /*

 

Также подправим имя функции и в её вызове в функции Humidity_Read

 

        Humidity_Get_Hum(&press);

 

Посмотрим в документации (HTS221 Interpreting humidity and temperature readings), как именно происходит вычисление относительной влажности воздуха с учётом калибровочных регистров

 

image12

 

Мы с вами видим, что в данной формуле очень активно используются также показания температуры вместе с калибровкой. Но мы с вами уже знаем из определения относительной влажности воздуха, что это так должно и быть.

Считаем следующие величины и занесём их в переменные

 

float   tmp_f;

        buffer[0]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_H0_RH_X2);

        buffer[1]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_H1_RH_X2);

  H0_rh = buffer[0] >> 1;

  H1_rh = buffer[1] >> 1;

        /*

 

Здесь мы используем следующие 2 калибровочных регистра

 

image13

 

Считаем следующие 4 калибровочных регистра и занесём данные в соответствующие переменные

 

        buffer[0]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_H0_T0_OUT_L);

        buffer[1]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_H0_T0_OUT_H);

H0_T0_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];        

        buffer[0]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_H1_T0_OUT_L);

        buffer[1]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_H1_T0_OUT_H);

H1_T0_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];

 

 

Посмотрим в документации, что это за регистры

 

image14

 

Из данных 3-й колонки мы видим, что данные величины знаковые (s(16)) (s – signed, 16 – 16-битный)

Считаем неоткалиброванные показания влажности

 

        buffer[0]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_HR_OUT_L_REG);

        buffer[1]=Humidity_IO_Read(HTS221_ADDRESS,HTS221_HR_OUT_H_REG);

H_T_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];

        /*

 

Здесь всё стандартно. 2 восьми-битных регистра собираем в одну величину

 

image15

 

Здесь у нас также 16-битная знаковая величина.

Передвигаем комментарий и подправляем код (убрав лишнее, так как в предыдущем занятии у нас была 24-битная величина) для использования или неиспользования (в зависимости от состояния кнопки) скользящего среднего, также меняем переменную raw_data на H_T_out

 

H_T_out = (((uint16_t)buffer[1]) << 8) | (uint16_t)buffer[0];

        btnstat=HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);

        //Если кнопка не нажата,

        //то вызовем фильтр скользящего среднего

        if(btnstat!=0)        H_T_out =  MovingAverage(H_T_out);

        /*

 

Теперь удаляем всю закомментированность и исправляем расчёт величины относительной влажности воздуха и заносим её в переменную с плавающей точкой

 

        if(btnstat!=0)        H_T_out =  MovingAverage(H_T_out);

  tmp_f = (float)(H_T_out — H0_T0_out) * (float)(H1_rh — H0_rh) /

                (float)(H1_T0_out — H0_T0_out)  +  H0_rh;

  *pData = ( tmp_f > 100.0f ) ? 100.0f

           : ( tmp_f <    0.0f ) ?    0.0f

           : tmp_f;        

}

 

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

 

void Humidity_Read(void)

{

        float temper,hum;

        Humidity_Get_Temp(&temper);

        Humidity_Get_Hum(&hum);

        sprintf(str1,"TEMP:%06f; HUMIDITY:%06f;rn", temper, hum);

        HAL_UART_Transmit(&huart2, (uint8_t*)str1,strlen(str1),0x1000);

//        uint8_t *t=(uint8_t*)&temper;

//        uint8_t *p=(uint8_t*)&press;

//        buf2[0]=0x11;

//        buf2[1]=0x55;

//        buf2[2]=(uint8_t)(*(uint32_t*)t);

//        buf2[3]=(uint8_t)((*(uint32_t*)t)>>8);

//        buf2[4]=(uint8_t)((*(uint32_t*)t)>>16);

//        buf2[5]=(uint8_t)((*(uint32_t*)t)>>24);

//        buf2[6]=(uint8_t)(*(uint32_t*)p);

//        buf2[7]=(uint8_t)((*(uint32_t*)p)>>8);

//        buf2[8]=(uint8_t)((*(uint32_t*)p)>>16);

//        buf2[9]=(uint8_t)((*(uint32_t*)p)>>24);

//        HAL_UART_Transmit(&huart2,buf2,10,0×1000);

        HAL_Delay(200);

}

 

Раскомментируем код в бесконечном цикле в функции main()

 

  /* USER CODE BEGIN 3 */

                Humidity_Read();

  }

 

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

 

image16

 

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

Закомментируем в функции обработки и отправки данных код для отправки в текстовом виде, раскомментируем код для отправки в виде значений с плавающей точкой. Как именно отправлять значения с плавающей точкой, мы подробно изучили в предыдущем уроке. Также закомментируем задержку и подправим кое-какие переменные

 

        //        HAL_UART_Transmit(&huart2, (uint8_t*)str1,strlen(str1),0x1000);

        uint8_t *t=(uint8_t*)&temper;

        uint8_t *h=(uint8_t*)&hum;

        buf2[0]=0x11;

        buf2[1]=0x55;

        buf2[2]=(uint8_t)(*(uint32_t*)t);

        buf2[3]=(uint8_t)((*(uint32_t*)t)>>8);

        buf2[4]=(uint8_t)((*(uint32_t*)t)>>16);

        buf2[5]=(uint8_t)((*(uint32_t*)t)>>24);

        buf2[6]=(uint8_t)(*(uint32_t*)h);

        buf2[7]=(uint8_t)((*(uint32_t*)h)>>8);

        buf2[8]=(uint8_t)((*(uint32_t*)h)>>16);

        buf2[9]=(uint8_t)((*(uint32_t*)h)>>24);

        HAL_UART_Transmit(&huart2,buf2,10,0×1000);

//        HAL_Delay(200);

 

Также в main.c закомментируем вызов функции в бесконечном цикле и раскомментируем в функции-обработчике прерываний от таймера

 

  /* USER CODE BEGIN 3 */

        //        Humidity_Read();

  }

  /* USER CODE END 3 */

 

        if(huart2.RxXferCount==0)

        {

                Humidity_Read();

                HAL_UART_Receive_IT(&huart2,(uint8_t*)str,8);

        }

 

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

Измеряя температуру, подносим палец руки к датчику постепенно и также постепенно его убираем, сначала без кнопки потом с кнопкой. Небольшая разница, но всё-таки есть.

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

 

image17_500

image18_500

 

Мы с вами наблюдаем разницу с применением фильтра скользящего среднего и без такового. Также последний скачок показаний влажности вызван тем, что я частично погрузил плату с датчиком в бокал с небольшим количеством кофе (! Именно в бокал, а не в само кофе, а то ещё удумаете чего!). Просто как раз под руку попался бокал с недопитым кофе.

 

Предыдущая часть Программирование МК STM32 Следующий урок

 

Исходный код

Техническая документация на датчик:

HTS221 Datasheet

HTS221 Interpreting humidity and temperature readings

Программа Hyper Terminal

Программа NS Port Monitor для значений с плавающей точкой

 

 

Отладочную плату можно приобрести здесь Nucleo STM32F401RE

Оценочную плату можно приобрести здесь STM32 X-NUCLEO-IKS01A1

 

 

Смотреть ВИДЕОУРОК

 

STM32 Датчик влажности HTS221

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*