STM Урок 121. Датчик температуры, давления и влажности BME280. Часть 4



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

 

Теперь давление.

Посмотрим регистр

 

 

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

 

float press_float = 0.0f;

uint32_t press_raw, pres_int;

BME280_ReadTemperature(); // must be done first to get t_fine

BME280_ReadReg_BE_U24(BME280_REGISTER_PRESSUREDATA,&press_raw);

press_raw >>= 4;

 

Думаю, можно в терминальной программе, сырые показатели не смотреть и не тратить на это время, всё будет нормально, я это уже не раз проверил.

Поэтому сначала добавим локальные переменные для промежуточных расчётов

 

uint32_t press_raw, pres_int;

int64_t val1, val2, p;

 

И затем рассчитаем давление, следуя примеру из даташита

 

press_raw >>= 4;

val1 = ((int64_t) temper_int) - 128000;

val2 = val1 * val1 * (int64_t)CalibData.dig_P6;

val2 = val2 + ((val1 * (int64_t)CalibData.dig_P5) << 17);

val2 = val2 + ((int64_t)CalibData.dig_P4 << 35);

val1 = ((val1 * val1 * (int64_t)CalibData.dig_P3) >> 8) + ((val1 * (int64_t)CalibData.dig_P2) << 12);

val1 = (((((int64_t)1) << 47) + val1)) * ((int64_t)CalibData.dig_P1) >> 33;

if (val1 == 0) {

  return 0; // avoid exception caused by division by zero

}

p = 1048576 - press_raw;

p = (((p << 31) - val2) * 3125) / val1;

val1 = (((int64_t)CalibData.dig_P9) * (p >> 13) * (p >> 13)) >> 25;

val2 = (((int64_t)CalibData.dig_P8) * p) >> 19;

p = ((p + val1 + val2) >> 8) + ((int64_t)CalibData.dig_P7 << 4);

pres_int = ((p >> 8) * 1000) + (((p & 0xff) * 390625) / 100000);

press_float = pres_int / 100.0f;

 

 

Перейдём в функцию main() файла main.c и в бесконечном цикле также прочитаем и отобразим давление в различных величинах

 

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

pf = BME280_ReadPressure();

sprintf(str1, "Pressure: %.3f Pa; %.3f hPa; %.3f mmHg\r\n", pf, pf/1000.0f, pf * 0.000750061683f);

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

HAL_Delay(1000);

 

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

 

 

Теперь вернёмся в файл BME280.c и напишем тело функции расчёта величины высоты над уровнем моря (данная величина зависит от атмосферного давления, хотя, признаться, зависимость эта, как мной замечено, непостоянная. Вернее непостоянная получается на выходе величина высоты над уровнем моря)

 

float att = 0.0f;

float atm = BME280_ReadPressure();

att = 44330.0 * (1.0 - pow(atm / seaLevel, 0.1903));

 

Теперь нам нужны будут ещё некоторые коэффициенты.

Перейдём в заголовочный файл BME280.h и добавим для них макросы

 

#define LED_TGL HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN)

//------------------------------------------------

#define SEALEVELPRESSURE_HPA (1013.25)

#define SEALEVELPRESSURE_PA (1013250)

//------------------------------------------------

 

Перейдём в функцию main() файла main.c и в бесконечном цикле также вычислим и отобразим высоту над уровнем моря

 

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

af = BME280_ReadAltitude(SEALEVELPRESSURE_PA);

sprintf(str1, "Altitude: %.3f m\r\n", af);

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

HAL_Delay(1000);

 

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

 

 

Осталось нам прочитать только влажность.

Для этого вернёмся в файл BME280.c в функцию BME280_ReadHumidity и попробуем пока прочитать сырое значение из регистра, который у нас двухбайтный

 

 

float hum_float = 0.0f;

int16_t hum_raw;

BME280_ReadTemperature(); // must be done first to get t_fine

BME280_ReadReg_S16(BME280_REGISTER_HUMIDDATA,&hum_raw);

sprintf(str1, "Humidity RAW: 0x%08X\r\n", hum_raw);

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

 

Также и здесь мы видим, что температуру нам надо читать обязательно, так как её значение влияет и на расчёт влажности.

 

 

Перейдём в функцию main() файла main.c и в бесконечном цикле вызовем функцию считывания и расчёта влажности воздуха

 

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

hf = BME280_ReadHumidity();

sprintf(str1, "Humidity: %.3f %%\r\n", hf);

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

HAL_Delay(1000);

 

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

 

 

Как мы видим, у нас колеблется опять не тот байт.

Поэтому идём в в файл BME280.c и добавим там соответствующую функцию считывания наоборот после функции BME280_ReadReg_S16

 

//------------------------------------------------

void BME280_ReadReg_BE_S16(uint8_t Reg, int16_t *Value)

{

  I2Cx_ReadData16(BME280_ADDRESS,Reg,(uint16_t*)Value);

  *(uint16_t *) Value = be16toword(*(uint16_t *) Value);

}

//------------------------------------------------

 

Теперь вернёмся в функцию BME280_ReadHumidity и изменим вызываемую функцию

 

BME280_ReadReg_BE_S16(BME280_REGISTER_HUMIDDATA,&hum_raw);

 

Добавим некоторые локальные переменные для промежуточных расчётов

 

int16_t hum_raw;

int32_t hum_raw_sign, v_x1_u32r;

 

Присвоим считанное значение 32-битной знаковой переменной и смотреть в терминальной программе будем уже его

 

BME280_ReadReg_BE_S16(BME280_REGISTER_HUMIDDATA,&hum_raw);

hum_raw_sign = ((int32_t)hum_raw)&0x0000FFFF;

sprintf(str1, "Humidity RAW: 0x%08X\r\n",hum_raw_sign);

 

Соберём код, прошьём контроллер и посмотрим значение

 

 

Теперь у нас пляшет младший байт — так должно и быть.

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

 

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

v_x1_u32r = (temper_int - ((int32_t)76800));

v_x1_u32r = (((((hum_raw_sign << 14) - (((int32_t)CalibData.dig_H4) << 20) -

(((int32_t)CalibData.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) *

(((((((v_x1_u32r * ((int32_t)CalibData.dig_H6)) >> 10) *

(((v_x1_u32r * ((int32_t)CalibData.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +

((int32_t)2097152)) * ((int32_t)CalibData.dig_H2) + 8192) >> 14));

v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *

((int32_t)CalibData.dig_H1)) >> 4));

v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r;

v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r;

hum_float = (v_x1_u32r>>12);

hum_float /= 1024.0f;

 

Соберём код, прошьём контроллер и посмотрим результат

 

 

Все показания прекрасно читаются и обновляются!

Можно теперь удалить отображение сырого значения

 

sprintf(str1, "Humidity RAW: 0x%08X\r\n", hum_raw_sign);

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

 

Осталось нам подключить дисплей и отобразить наши показания на нём, так как с дисплеем красивее, чем в терминальной программе, и более автономно, так как не надо будет схему подключать к ПК.

Для этого мы скопируем файлы lcd.h и lcd.c из проекта урока 105  NRF24_RX_LCD в наш проект.

В файле main.c подключим данную библиотеку

 

#include "BME280.h"

#include "lcd.h"

 

Также подключим файл lcd.c к дереву проекта.

Перейдём в файл  и удалим объявление строковой переменной, так как она там не нужна

 

char str1[100];

 

Вернёмся в файл main.c и в функции main() вызовем инициализацию дисплея

 

/* USER CODE BEGIN 2 */

LCD_ini();

BME280_Init();

 

В бесконечном цикле отобразим различные показатели на нашем дисплее, вставив в нужные места соответствующий код

 

tf = BME280_ReadTemperature();

sprintf(str1, "Temperature: %.3f *C\r\n", tf);

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

LCD_SetPos(0, 0);

sprintf(str1, "%11.3f *C", tf);

LCD_String(str1);

pf = BME280_ReadPressure();

sprintf(str1, "Pressure: %.3f Pa; %.3f hPa; %.3f mmHg\r\n", pf, pf/1000.0f, pf * 0.000750061683f);

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

LCD_SetPos(0, 1);

sprintf(str1, "%11.3f hPa", pf/1000.0f);

LCD_String(str1);

LCD_SetPos(0, 2);

sprintf(str1, "%11.3f mmHg", pf * 0.000750061683f);

LCD_String(str1);

af = BME280_ReadAltitude(SEALEVELPRESSURE_PA);

sprintf(str1, "Altitude: %.3f m\r\n", af);

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

hf = BME280_ReadHumidity();

sprintf(str1, "Humidity: %.3f %%\r\n", hf);

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

LCD_SetPos(0, 3);

sprintf(str1, "%7.3f %% %4.1f m", hf, af);

LCD_String(str1);

HAL_Delay(1000);

 

Осталось нам собрать код, прошить контроллер, отключить логический анализатор и подключить дисплей.

Полюбуемся результатом работы нашего кода (нажмите на картинку для увеличения изображения)

 

 

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

Спасибо всем за внимание!

 

 

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

 

Исходный код

 

 

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

Логический анализатор 16 каналов можно приобрести здесь

Датчик температуры, давления и влажности BME280 можно приобрести здесь BME280

Дисплей LCD 20×4 можно приобрести тут: LCD 20×4

Переходник I2C to LCD1602 2004 можно приобрести здесь: I2C to LCD1602 2004

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM Name

10 комментариев на “STM Урок 121. Датчик температуры, давления и влажности BME280. Часть 4
  1. Алексей:

    Спасибо большое! Очень помогло разобраться с датчиком.

  2. Денис:

    Большое спасибо за уроки. Не давно перешёл с AVR, с STM все гораздо круче!

  3. Сергей:

    Атмосферное давление в Па почему-то на порядок выше, чем обычно. Стандартная атмосфера 101325Па. У вас в листингах получается где-то в 10 раз больше. Т.е. не 100кПа, а 1МПа.

  4. Сергей:

    З.Ы. а за уроки, безусловно, большущее спасибо! Очень подробно, внятно и с исходниками!
    Ошибки мелкие есть везде. Без них было бы скучно 🙂
    Удачи и продолжений!

  5. Валерий:

    Спасибо за уроки!!!!! Сэкономил кучу времени и нервов для себя!

  6. Алексей:

    Спасибо большое! Датчик работает. Экран подключу уже завтра.

    Единственно, что мой датчик BME280 показывает почему-то давление на 40 Гексапаскалей меньше. Я проверяю по своей другой метеостанции и по данным с официальной метеостанции. Я почистил осторожно его сверху, продул сжатым воздухом, но не помогает.

    Как его можно откалибровать? Какую величину изменять? Я это пока не очень хорошо понимаю.

    Может будет полезно для начинающих:
    — в примерах кода в этой статье есть кое-где rn, которые нужно переправлять в \r\n.
    — мне пришлось включать в BME280.h #include , чтобы заработала функция sprintf().

  7. Алексей:

    — мне пришлось включать в BME280.h #include <stdio.h>, чтобы заработала функция sprintf().

  8. Алексей:

    Поправка: на 40 Гектопаскалей, гПа

  9. ruskuw:

    писал функцию вычисления на СИ (атхмега Атмел студио7)
    v_x1_u32r = (((((adc_H << 14) — (((int32_t)dig_H4) <> 15)*(((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10)*(((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10)+((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
    v_x1_u32r = (v_x1_u32r — (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *((int32_t)dig_H1)) >> 4));
    v_x1_u32r = (v_x1_u32r 419430400) ? 419430400 : v_x1_u32r;
    h = (v_x1_u32r>>12);
    занижало влажность на 20 % и работало неправильно. Перепроверил этот же код на ПК — все работает. Видимо какоето переполнение в мк атхмега.

    В итоге на атхмега заработал такой код :
    var_H = (adc_H — (((double)dig_H4) * 64.0 + ((double)dig_H5) / 16384.0 * var_H)) * (((double)dig_H2) / 65536.0 * (1.0 + ((double)dig_H6) / 67108864.0 * var_H * (1.0 + ((double)dig_H3) / 67108864.0 * var_H)));
    var_H = var_H * (1.0 — ((double)dig_H1) * var_H / 524288.0);
    h = var_H;

  10. aske12345:

    Здравствуйте!
    Суть проблемы: Соединил по i2c STM32F4-Discovery и датчик температуры BME-280, написал прошивку по Вашему примеру — температура считывается нормально. Развел плату, запаял туда мк STM32f407vgt6 зашил ту же прошивку, поставил тот же датчик — i2C не включается ни в какую, осциллом смотрел даже посылка не идет от мк, даже в 0 не падает. Перепаял мк, сравнивал те же пины, что и на дискавери, единственное различие на дискавери стоит мк версии z, а в самодельной плате версии 2. Смотрел errata, но с моим английским разницы не нашел. В чём может быть подвох?

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

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

*