Урок 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;
Как делать расчёты, определено в технической документации
Считаем следующие величины
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 регистра
Прочитаем показания датчика по температуре и положим их в соответствующую переменную
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];
Показания хранятся в данных регистрах
Передвинем начало комментария. В раскомментированном участке уберём лишнее и заменим 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), как именно происходит вычисление относительной влажности воздуха с учётом калибровочных регистров
Мы с вами видим, что в данной формуле очень активно используются также показания температуры вместе с калибровкой. Но мы с вами уже знаем из определения относительной влажности воздуха, что это так должно и быть.
Считаем следующие величины и занесём их в переменные
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 калибровочных регистра
Считаем следующие 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];
Посмотрим в документации, что это за регистры
Из данных 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 восьми-битных регистра собираем в одну величину
Здесь у нас также 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();
}
Скомпилируем код, прошьём контроллер и посмотрим результат в терминальной программе. В процессе просмотра несколько раз притронемся к датчику пальцем на продолжительное время и увидим изменения температуры и влажности
Теперь мы попытаемся посмотреть результат без кнопки и с кнопкой, но уже в программе визуализации.
Закомментируем в функции обработки и отправки данных код для отправки в текстовом виде, раскомментируем код для отправки в виде значений с плавающей точкой. Как именно отправлять значения с плавающей точкой, мы подробно изучили в предыдущем уроке. Также закомментируем задержку и подправим кое-какие переменные
// 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);
}
Скомпилируем код, прошьём контроллер и посмотрим результаты показаний температуры и влажности с кнопкой и без кнопки в программе визуализации, также применяя различные значения пределов и сдвига показаний
Измеряя температуру, подносим палец руки к датчику постепенно и также постепенно его убираем, сначала без кнопки потом с кнопкой. Небольшая разница, но всё-таки есть.
Теперь давление. Тут без изменений, так как повлиять на атмосферное давление тяжело, разве только отладочную плату засунуть в шину автомобильную и накачать (шутка). Также попробуем без кнопки и с кнопкой, применив другие значения делителя и сдвига в программе визуализации
Мы с вами наблюдаем разницу с применением фильтра скользящего среднего и без такового. Также последний скачок показаний влажности вызван тем, что я частично погрузил плату с датчиком в бокал с небольшим количеством кофе (! Именно в бокал, а не в само кофе, а то ещё удумаете чего!). Просто как раз под руку попался бокал с недопитым кофе.
Предыдущая часть Программирование МК STM32 Следующий урок
Техническая документация на датчик:
HTS221 Interpreting humidity and temperature readings
Программа NS Port Monitor для значений с плавающей точкой
Отладочную плату можно приобрести здесь Nucleo STM32F401RE
Оценочную плату можно приобрести здесь STM32 X-NUCLEO-IKS01A1
Смотреть ВИДЕОУРОК
Добавить комментарий