Подключение HTU21D к STM32 c HAL по I2C (датчик температуры и влажности)

Основные параметры

Диапазон измерения температуры: от -40 °C до +125 °C
Диапазон измерения относительной влажности: 0-100%
Погрешность температуры: Δ0.4 °C
Погрешность влажности: Δ3%
Напряжение питания: от 1.5 до 3.6 В
Разрядность АЦП: до 14-бит
Особенности: калибровка с завода
Проверка данных: CRC
Связь: I²C, до 400 кГц

Размеры: 3.00 x 3.00 x 0.9 мм
Цена: от 1.2$

Купить на Aliexpress

Temperature Humidity Sensor HTU21D
Цена: 1.22$ + 0.15$ = 1.37$

Module Temperature Sensor HTU21D
Цена: 1.20$ + 0.15$ = 1.35$

Temperature and humidity sensor HTU21D sensor module
Цена: 1.36$ + 0.40$ = 1.76$

HTU21D Temperature and Humidity Sensor Module
Цена: 1.37$ + 0.86$ = 2.23$

Схема подключения HTU21D и STM32

На плате по умолчанию не подключены подтягивающие резисторы 4.7 кОм, нужно замкнуть перемычку или подключить отдельно резисторы между  DA и CL и плюсом питания.

На примере синей пилюли STM32F103C8T6:

VDD VSS I2C1_SDA I2C1_SCL
+ - DA CL

Создание проекта в STM32Cube

Файл, новый проект:  File -> New Project

Выбираем нужный МК: STM32F103C8

Включаем отладчик: System Core -> SYS -> Debug: Serial Wire

Включаем I2C: Connectivity -> I2C1
Изменяем частоту на 400 кГц (не обязательно): I2C1 -> Parametr Settings -> I2C Speed Mode: Fast Mode
И включаем прерывание: NVIC Settings -> I2C event interrupt -> Enable

Любое название и для Keil:
Project Name: I2C_HTU21D
Toolchain/IDE: MDK-ARM, Min Version: V5.27

Генерируем код и открываем проект:  GENERATE CODE -> Open Project

Программирование

Данные из тех.док на датчик

Нужно узнать адрес датчика:

И команду, которую нужно отправить для измерения температуры:

Также нужно выражение для получения температуры в градусах:

Код

Создадим несколько глобальных переменных (чтобы потом удобно просматривать их) для сырых значений с датчика (после АЦП) и для вычисленной температуры в грудусах Цельсия:

/* Variables for HTU21D */ uint8_t HTU21D_RX_Data[3]; float HTU21D_Temperature; uint16_t HTU21D_ADC_Raw; uint8_t HTU21D_Temp_Cmd = 0xE3; #define HTU21D_Adress (0x40 << 1)

Данные отправляются побайтно (8-бит, а адрес 7-бит), таким образом передается адрес устройства и бит, который определяет что будет производится:

0 - запись;
1 - чтение;

Нам нужна запись, именно поэтому сдвигая адрес влево на один бит получаем число 0x80, то есть 7-бит адрес + 1-бит направление (чтение/запись).

Можно использовать обычную функцию, блокирующую:

/* TEMPERATURE OBTAINING from HTU21D, I2C */ HAL_I2C_Mem_Read(&hi2c1, HTU21D_Adress, HTU21D_Temp_Cmd, I2C_MEMADD_SIZE_8BIT, (uint8_t*) HTU21D_RX_Data, 2, 1000);

Но лучше применить функцию с прерыванием:

HAL_I2C_Mem_Read_IT(&hi2c1, HTU21D_Adress, HTU21D_Temp_Cmd, I2C_MEMADD_SIZE_8BIT, (uint8_t*) HTU21D_RX_Data, 2);

Читаются два байта, для получения сырого значения нужно соединить их ( uint8_t и uint8_t) в uint16_t, причем первый полученный будет старший.

Для этого первое полученное RX_Data_HTU21D[0] сдвигаем на 8 влево и применяем "ИЛИ" для обоих:

HTU21D_ADC_Raw = ((uint16_t)(HTU21D_RX_Data[0] << 8) | (HTU21D_RX_Data[1]));<br>

Ну и по формуле получаем температуру в градусах Цельсия:

HTU21D_Temperature = (float)(HTU21D_ADC_Raw * 175.72 / 65536.00) - 46.85;

Таким образом:

/* USER CODE BEGIN WHILE */ while (1) { /* TEMPERATURE OBTAINING from HTU21D, I2C */ HAL_I2C_Mem_Read_IT(&hi2c1, HTU21D_Adress, HTU21D_Temp_Cmd, I2C_MEMADD_SIZE_8BIT, (uint8_t*) HTU21D_RX_Data, 2); HTU21D_ADC_Raw = ((uint16_t)(HTU21D_RX_Data[0] << 8) | (HTU21D_RX_Data[1])); HTU21D_Temperature = (float)(HTU21D_ADC_Raw * 175.72 / 65536.00) - 46.85; HAL_Delay(100); /* USER CODE END WHILE */

Также для блокирующей функции:

/* USER CODE BEGIN WHILE */ while (1) { /* TEMPERATURE OBTAINING from HTU21D, I2C */ HAL_I2C_Mem_Read(&hi2c1, HTU21D_Adress, HTU21D_Temp_Cmd, I2C_MEMADD_SIZE_8BIT, (uint8_t*) HTU21D_RX_Data, 2, 1000); HTU21D_ADC_Raw = ((uint16_t)(HTU21D_RX_Data[0] << 8) | (HTU21D_RX_Data[1])); HTU21D_Temperature = (float)(HTU21D_ADC_Raw * 175.72 / 65536.00) - 46.85; HAL_Delay(100); /* USER CODE END WHILE */

Еще можно выполнить то же самое (передачу и прием) отдельными функциями:

HAL_I2C_Master_Transmit(&hi2c1, HTU21D_Adress, &HTU21D_Temp_Cmd, 1, 100); HAL_Delay(100); HAL_I2C_Master_Receive(&hi2c1, 0x80, (uint8_t*)HTU21D_RX_Data, 2, 100); HTU21D_ADC_Raw = ((uint16_t)(HTU21D_RX_Data[0] << 8) | (HTU21D_RX_Data[1])); HTU21D_Temperature = (float)(HTU21D_ADC_Raw * 175.72 / 65536.00) - 46.85; HAL_Delay(20);

Теперь все вместе (получение температуры и влажности):

/* USER CODE BEGIN PV */ uint8_t HTU21D_RX_Data[2]; float HTU21D_Temperature; float HTU21D_Humidity; uint16_t HTU21D_ADC_Raw; uint8_t HTU21D_Temp_Cmd = 0xE3; uint8_t HTU21D_Humi_Cmd = 0xE5; #define HTU21D_Adress (0x40 << 1) /* USER CODE END PV */ /* USER CODE BEGIN WHILE */ while (1) { /******************************* HTU21D **********************************/ /* Temperature ---->; */ HAL_I2C_Mem_Read_IT(&hi2c1, HTU21D_Adress, HTU21D_Temp_Cmd, I2C_MEMADD_SIZE_8BIT, HTU21D_RX_Data, 2); HTU21D_ADC_Raw = ((uint16_t)(HTU21D_RX_Data[0] << 8) | (HTU21D_RX_Data[1])); HTU21D_Temperature = (float)(HTU21D_ADC_Raw * 175.72 / 65536.00) - 46.85; HAL_Delay(100); /* Humidity ---->; */ HAL_I2C_Mem_Read_IT(&hi2c1, HTU21D_Adress, HTU21D_Humi_Cmd, I2C_MEMADD_SIZE_8BIT, HTU21D_RX_Data, 2); HTU21D_ADC_Raw = ((uint16_t)(HTU21D_RX_Data[0] << 8) | (HTU21D_RX_Data[1])); HTU21D_Humidity = (float)(HTU21D_ADC_Raw * 125.0 / 65536.0) - 6.0; HAL_Delay(100); /* USER CODE END WHILE */

После успешной компиляции ( Project -> Build Target) заходим в режим отладки (Debug -> Start/Stop Debug Session).

Добавляем переменные Temperature_HTU21D и в окно Watch 1, убираем отображение в HEX.

Запускаем роботу ( Debug -> Run). Все отлично, имеем актуальные значения температуры и относительной влажности.

Регистр

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

Проверка целостности данных (CRC)

И хоть получаемые показания довольно стабильные, но в настоящих проектах не лишним и обязательным будет проверка целостности полученных данных.

Суть:

К передаваемым данным с АЦП цепляется значение CRC, вычисленное датчиком.

Мы принимаем значение с АЦП вместе с CRC, вычисляем значение CRC и сравниваем с полученным, если совпадает, то работаем с данными, если нет, то снимаем показания еще раз.

Программная реализация

Сначала полином:

#define HTU21D_POLY 0x13100

Функция:

uint8_t HTU21D_CRC8(uint16_t HTU21D_ADC, uint8_t* HTU21D_CRC) { for (uint8_t i = 0; i < 16; i++) HTU21D_ADC = ((HTU21D_ADC & 0x8000) ? ((HTU21D_ADC << 1) ^ HTU21D_POLY) : (HTU21D_ADC <<= 1)); HTU21D_ADC >>= 8; if(HTU21D_ADC == *HTU21D_CRC) return 255; else return 0; }

Если ADC и CRC будет равно нулю, то  проверка также пройдет, так что рекомендую еще вставить условие на то, что: CRC != 0

Логический анализатор

Здесь можно рассмотреть что же на самом отправляется и принимается.

Скачать

htu21d_datasheet_temperature_humidity_sensor.pdf
Полный документ на HTU21D.

https://github.com/Egoruch/HTU21D-STM32-HAL
(ссылка на GitHub)

HTU21D-STM32-HAL-master.zip
(прямая ссылка)
Рабочий проект STM32CubeMX + Keil.

Видеообзор

Итого

Неплохой датчик, его преимуществами является малые размеры (3.0x3.0x0.9 мм) и маленькое время измерения температуры (до 50 мс для 14-бит) и относительной влажности (до 16 мс при 12-бит), но для ответственных применений его конечно применять не следует, но многие любительские устройства могут быть собраны с применением HTU21D. Плохо конечно, что только один адрес только...

Существует еще множество других цифровых датчиков температуры в этом же корпусе DFN-6, что позволит легко заменить искомый на другой: HTU31, SHT21, SI7021, HDC2080...

Также советую ознакомится (будет полезным!):

705
RSS
kvark85
01:00

Подскажите пожалуйста, не сталкивались с проблемой что HTU21D_RX_Data[2] всегда равен HTU21D_RX_Data[1]. То-есть я всегда получаю неверный CRC от датчика Si7021.

21:24

Не, такого не было.

Хорошо бы точно удостовериться с помощью логического анализатора, что от там шлет точно.

Загрузка...