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

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

 

Теперь оверсемплинг и фильтры.

Я просмотрел несколько примеров, а также много чего позавидовал из библиотек Arduino, и решил использовать подобные настройки.

Начнём с фильтра. Коэффициент фильтрации настраивается также с помощью соответствующих битов конфигурационного регистра

 

 

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

 

#define BME280_STBY_20 0xE0

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

#define BME280_FILTER_MSK 0x1C

#define BME280_FILTER_OFF 0x00

#define BME280_FILTER_2 0x04

#define BME280_FILTER_4 0x08

#define BME280_FILTER_8 0x0C

#define BME280_FILTER_16 0x10

 

Вернёмся в файл BME280.c и добавим функцию настройки коэффициента фильтрации после функции BME280_SetStandby

 

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

  void BME280_SetFilter(uint8_t filter) {

  uint8_t reg;

  reg = BME280_ReadReg(BME280_REG_CONFIG) & ~BME280_FILTER_MSK;

  reg |= filter & BME280_FILTER_MSK;

  BME280_WriteReg(BME280_REG_CONFIG,reg);

}

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

 

Вызовем данную функцию в функции инициализации датчика, настроив средний коэффициент фильтрации показаний

 

BME280_SetStandby(BME280_STBY_1000);

BME280_SetFilter(BME280_FILTER_4);

 

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

Настройки оверсемплингов (передискретизации) хранятся в двух регистрах.

В одном регистре — ctrl_meas — хранятся настройки оверсемплингов температуры и давления

 

 

Вот это настройки оверсемплинга показаний температуры

 

 

А вот настройки оверсемплинга показаний давления

 

 

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

А для настройки оверсемплинга влажности существует отдельный регистр

 

 

Вот сами настройки

 

 

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

 

#define BME280_SOFTRESET_VALUE 0xB6 //BME280 SOFT RESET VALUE

#define BME280_REG_CTRL_HUM 0xF2 // Humidity measure control register

#define BME280_REGISTER_STATUS 0XF3 //BME280 STATUS REGISTER

#define BME280_REG_CTRL_MEAS 0xF4 // Control register pressure and temperature measure

 

 

Также добавим макросы для масок и настроек битов оверсемплингов

 

#define BME280_FILTER_16 0x10

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

#define BME280_OSRS_T_MSK 0xE0

#define BME280_OSRS_T_SKIP 0x00

#define BME280_OSRS_T_x1 0x20

#define BME280_OSRS_T_x2 0x40

#define BME280_OSRS_T_x4 0x60

#define BME280_OSRS_T_x8 0x80

#define BME280_OSRS_T_x16 0xA0

#define BME280_OSRS_P_MSK 0x1C

#define BME280_OSRS_P_SKIP 0x00

#define BME280_OSRS_P_x1 0x04

#define BME280_OSRS_P_x2 0x08

#define BME280_OSRS_P_x4 0x0C

#define BME280_OSRS_P_x8 0x10

#define BME280_OSRS_P_x16 0x14

#define BME280_OSRS_H_MSK 0x07

#define BME280_OSRS_H_SKIP 0x00

#define BME280_OSRS_H_x1 0x01

#define BME280_OSRS_H_x2 0x02

#define BME280_OSRS_H_x4 0x03

#define BME280_OSRS_H_x8 0x04

#define BME280_OSRS_H_x16 0x05

 

Вернёмся в файл BME280.c и добавим функции настройки оверсемплингов для различных показаний после функции BME280_SetFilter

 

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

void BME280_SetOversamplingTemper(uint8_t osrs)

{

  uint8_t reg;

  reg = BME280_ReadReg(BME280_REG_CTRL_MEAS) & ~BME280_OSRS_T_MSK;

  reg |= osrs & BME280_OSRS_T_MSK;

  BME280_WriteReg(BME280_REG_CTRL_MEAS,reg);

}

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

void BME280_SetOversamplingPressure(uint8_t osrs)

{

  uint8_t reg;

  reg = BME280_ReadReg(BME280_REG_CTRL_MEAS) & ~BME280_OSRS_P_MSK;

  reg |= osrs & BME280_OSRS_P_MSK;

  BME280_WriteReg(BME280_REG_CTRL_MEAS,reg);

}

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

void BME280_SetOversamplingHum(uint8_t osrs)

{

  uint8_t reg;

  reg = BME280_ReadReg(BME280_REG_CTRL_HUM) & ~BME280_OSRS_H_MSK;

  reg |= osrs & BME280_OSRS_H_MSK;

  BME280_WriteReg(BME280_REG_CTRL_HUM,reg);

  //The 'ctrl_hum' register needs to be written

  //after changing 'ctrl_hum' for the changes to become effwctive.

  reg = BME280_ReadReg(BME280_REG_CTRL_MEAS);

  BME280_WriteReg(BME280_REG_CTRL_MEAS,reg);

}

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

 

 

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

 

 

Настроим оверсемплинги, вызвав данные функции с желаемыми настройками в функции инициализации

 

BME280_SetFilter(BME280_FILTER_4);

BME280_SetOversamplingTemper(BME280_OSRS_T_x4);

BME280_SetOversamplingPressure(BME280_OSRS_P_x2);

BME280_SetOversamplingHum(BME280_OSRS_H_x1);

 

Считаем настройки и посмотрим их в терминальной программе

 

BME280_SetOversamplingHum(BME280_OSRS_H_x1);

value32 = BME280_ReadReg(BME280_REG_CTRL_MEAS);

value32 |= BME280_ReadReg(BME280_REG_CTRL_HUM) << 8;

sprintf(str1, "Measurements status: %04Xrn", value32);

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

sprintf(str1, "Temperature: %srnPressure: %srnHumidity: %srn",

  (value32 & BME280_OSRS_T_MSK) ? "ON" : "OFF",

  (value32 & BME280_OSRS_P_MSK) ? "ON" : "OFF",

  ((value32 >> 8) & BME280_OSRS_H_MSK) ? "ON" : "OFF");

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

 

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

 

 

 

Осталось для полной инициализации датчика нам только включить режим NORMAL.

Как мы помним, режимы настраиваются в определённых битах регистра ctrl_meas

 

 

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

 

#define BME280_OSRS_H_x16 0x05

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

#define BME280_MODE_MSK 0x03

#define BME280_MODE_SLEEP 0x00

#define BME280_MODE_FORCED 0x01

#define BME280_MODE_NORMAL 0x03

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

 

Вернёмся в файл BME280.c и добавим функцию установки текущего режима после функции BME280_SetOversamplingHum

 

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

void BME280_SetMode(uint8_t mode) {

  uint8_t reg;

  reg = BME280_ReadReg(BME280_REG_CTRL_MEAS) & ~BME280_MODE_MSK;

  reg |= mode & BME280_MODE_MSK;

  BME280_WriteReg(BME280_REG_CTRL_MEAS,reg);

}

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

 

Включим режим NORMAL в функции инициализации датчика

 

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

  BME280_SetMode(BME280_MODE_NORMAL);

}

 

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

Осталась самая малость — считать показания величин. Ну ничего. Благодаря примерам кода в технической документации, мы всё же, надеюсь, с этим справимся.

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

 

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

float BME280_ReadTemperature(void)

{

  float temper_float = 0.0f;

  return temper_float;

}

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

float BME280_ReadPressure(void)

{

  float press_float = 0.0f;

  return press_float;

}

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

float BME280_ReadHumidity(void)

{

  float hum_float = 0.0f;

  return hum_float;

}

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

float BME280_ReadAltitude(float seaLevel)

{

  float att = 0.0f;

  return att;

}

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

 

Добавим для данных функций прототипы в заголовочном файле.

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

 

#define BME280_REG_CONFIG 0xF5 // Configuration register

#define BME280_REGISTER_PRESSUREDATA 0xF7

#define BME280_REGISTER_TEMPDATA 0xFA

#define BME280_REGISTER_HUMIDDATA 0xFD

 

Начнём с температуры.

Первоначальное «сырое» значение температуры непосредственно с сенсора после обработки блоком ADC записывается в следующий регистр

 

 

Получается, что показания температуры у нас записываются в 20 бит.

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

Давайте пока просто считаем данный регистр без переворота.

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

 

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

void BME280_ReadReg_U24(uint8_t Reg, uint32_t *Value)

{

  I2Cx_ReadData24(BME280_ADDRESS,Reg,Value);

  *(uint32_t *) Value &= 0x00FFFFFF;

}

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

 

Так как у нас не бывает 24-битных типов, то мы используем 32-битный, соответственно, поэтому мы лишний старший байт очищаем.

Прочитаем регистр значений температуры в теле функции BME280_ReadTemperature

 

float temper_float = 0.0f;

uint32_t temper_raw;

BME280_ReadReg_U24(BME280_REGISTER_TEMPDATA,&temper_raw);

 

В функции main() файла main.c добавим локальные переменные для хранения данных различных показателей датчика

 

/* USER CODE BEGIN 1 */

float tf = 0.0f, pf = 0.0f, af = 0.0f, hf = 0.0f;

/* USER CODE END 1 */

 

Перейдём в бесконечный цикл и вызовем функцию чтения температуры, после чего добавим задержку на 1000 милисекунд, что и предусмотрено установленным нами таймингом режима STANDBY после чтения показаний датчика

 

  /* USER CODE BEGIN 3 */

  tf = BME280_ReadTemperature();

  sprintf(str1, "Temperature: %.3f *Crn", tf);

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

  HAL_Delay(1000);

}

 

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

Вот наши принятые байты

 

 

Байты считываются, это уже хорошо.

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

 

BME280_ReadReg_U24(BME280_REGISTER_TEMPDATA,&temper_raw);

sprintf(str1, "Temperature RAW: 0x%08Xrn", temper_raw);

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

 

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

 

 

По изменениям данных мы видим, что они у нас перевёрнуты, то есть меняется старший байт, не может же температура скакать на порядок. Ну, в принципе, мы так и ожидали, так как байты в регистре и расположены наоборот, судя по даташиту, старшим байтом вперёд (формат big endian). Поэтому нам надо как-то перевернуть всё это дело. Мы с таким явлением уже встречались, когда программировали микросхему ENC28J60.

Перейдём в заголовочный файл BME280.h и добавим макросы перестановки байтов, заодно также и для 16-битных величин, они тоже нам будут нужны

 

#define LED_TGL HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN)

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

#define be16toword(a) ((((a)>>8)&0xff)|(((a)<<8)&0xff00))

#define be24toword(a) ((((a)>>16)&0x000000ff)|((a)&0x0000ff00)|(((a)<<16)&0x00ff0000))

 

Вернёмся в файл BME280.c и добавим функцию считывания 24-битного регистра в формате Big Endian после функции BME280_ReadReg_U24

 

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

void BME280_ReadReg_BE_U24(uint8_t Reg, uint32_t *Value)

{

  I2Cx_ReadData24(BME280_ADDRESS,Reg,Value);

  *(uint32_t *) Value = be24toword(*(uint32_t *) Value) & 0x00FFFFFF;

}

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

 

Вернёмся в функцию считывания и обработки температуры BME280_ReadTemperature и поменяем там функцию

 

BME280_ReadReg_BE_U24(BME280_REGISTER_TEMPDATA,&temper_raw);

 

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

 

 

Совсем другое дело!

Теперь сдвинем всё это на 4 бита вправо, так как именно так у нас хранится сырое значение температуры и четыре самых младших бита не участвуют в показаниях

 

BME280_ReadReg_BE_U24(BME280_REGISTER_TEMPDATA,&temper_raw);

temper_raw >>= 4;

 

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

 

 

Всё отлично передвинулось.

Теперь добавим глобальную переменную для временного хранения показаний температуры

 

BME280_CalibData CalibData;

int32_t temper_int;

 

Вернёмся в функцию BME280_ReadTemperature и добавим ещё некоторые локальные переменные для промежуточных расчётов

 

uint32_t temper_raw;

int32_t val1, val2;

 

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

 

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

val1 = ((((temper_raw>>3) - ((int32_t)CalibData.dig_T1 <<1))) *

((int32_t)CalibData.dig_T2)) >> 11;

val2 = (((((temper_raw>>4) - ((int32_t)CalibData.dig_T1)) *

((temper_raw>>4) - ((int32_t)CalibData.dig_T1))) >> 12) *

((int32_t)CalibData.dig_T3)) >> 14;

temper_int = val1 + val2;

 

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

 

temper_int = val1 + val2;

temper_float = ((temper_int * 5 + 128) >> 8);

temper_float /= 100.0f;

 

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

 

 

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

 

sprintf(str1, "Temperature RAW: 0x%08Xrn", temper_raw);

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

 

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

 

 

 

 

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

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

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

Дисплей LCD 20×4

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

 

 

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

STM Name

 

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

STM Name

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

  1. Добрый день.
    Можете прояснить, почему у меня не отрабатывает в процедуре закомментированная строка?

    float BME280_ReadTemperature(void)
    {
    float temper_float = 0.0f;
    uint32_t temper_raw;
    int32_t val1, val2;
    BME280_ReadReg_BE_U24(BME280_REGISTER_TEMPDATA,&temper_raw);
    temper_raw >>= 4;
    val1 = ((((temper_raw>>3) — ((int32_t)CalibData.dig_T1 <> 11;
    val2 = (((((temper_raw>>4) — ((int32_t)CalibData.dig_T1)) *
    ((temper_raw>>4) — ((int32_t)CalibData.dig_T1))) >> 12) *
    ((int32_t)CalibData.dig_T3)) >> 14;
    temper_int = val1 + val2;
    temper_float = ((temper_int * 5 + 128) >> 8);
    //temper_float = temper_float/100.0f;
    return temper_float;
    }

    т.е. и при отсутствии комментирующих слэшей и с ними результат в терминале температура умноженная на 100. Причем в main.c напротив вызова этой функции стоит предупреждение: implicit declaration of function 'BME280_ReadTemperature' is invalid in C99.
    Если в настройках во вкладке С/С++ убрать галочку с «C99 mode», то компилируется вообще с кучей ошибок..

  2. Добрый день. Ну вобщем то мне это на электрониксе объяснили и действительно декларирование этой функции чудесным образом помогло. А как же у вас ваш код отрабатывает без её декларирования? Я же кодом из вашего урока полностью пользуюсь

  3. И ещё одна странность у меня выявилась.
    Подцепил датчик на кабель длиной 10 метров, в помещении вроде бы всё норм, показывает довольно точно температуру, вынес за окно и вот что имеем:

    Temperature: 1.810 *C
    Pressure: 1022383.813 Pa; 1022.384 hPa; 766.851 mmHg
    Altitude: -75.769 m
    Humidity: 27.641 %
    Temperature: 1.490 *C
    Pressure: 1022411.688 Pa; 1022.412 hPa; 766.872 mmHg
    Altitude: -76.000 m
    Humidity: 28.019 %
    Temperature: 1.150 *C
    Pressure: 1022431.063 Pa; 1022.431 hPa; 766.886 mmHg
    Altitude: -76.160 m
    Humidity: 28.752 %
    Temperature: 0.470 *C
    Pressure: 1022432.188 Pa; 1022.432 hPa; 766.887 mmHg
    Altitude: -76.170 m
    Humidity: 29.136 %
    Temperature: 0.110 *C
    Pressure: 1022446.563 Pa; 1022.447 hPa; 766.898 mmHg
    Altitude: -76.288 m
    Humidity: 29.575 %
    Temperature: 409.340 *C
    Pressure: 1540857.625 Pa; 1540.858 hPa; 1155.738 mmHg
    Altitude: -3681.034 m
    Humidity: 0.000 %
    Temperature: 408.950 *C
    Pressure: 1541868.000 Pa; 1541.868 hPa; 1156.496 mmHg
    Altitude: -3687.024 m
    Humidity: 0.263 %
    Temperature: 408.600 *C
    Pressure: 1542814.125 Pa; 1542.814 hPa; 1157.206 mmHg
    Altitude: -3692.630 m
    Humidity: 1.371 %
    Temperature: 408.270 *C
    Pressure: 1543696.125 Pa; 1543.696 hPa; 1157.867 mmHg
    Altitude: -3697.853 m
    Humidity: 2.366 %
    Temperature: 407.940 *C
    Pressure: 1544553.250 Pa; 1544.553 hPa; 1158.510 mmHg
    Altitude: -3702.927 m
    Humidity: 3.341 %

    т.е. когда температура упала в минус, попёрли странные цифры 400 градусов.
    Вы не пробовали измерять отрицательную температуру?

    Вот занёс в тепло обратно и видно, что когда нагрелся до 0+ цифры стали нормальными:

    Temperature: 409.240 *C
    Pressure: 1541205.500 Pa; 1541.205 hPa; 1155.999 mmHg
    Altitude: -3683.097 m
    Humidity: 42.121 %
    Temperature: 409.450 *C
    Pressure: 1540698.375 Pa; 1540.698 hPa; 1155.619 mmHg
    Altitude: -3680.091 m
    Humidity: 41.276 %
    Temperature: 0.040 *C
    Pressure: 1022530.563 Pa; 1022.531 hPa; 766.961 mmHg
    Altitude: -76.982 m
    Humidity: 50.817 %
    Temperature: 0.230 *C
    Pressure: 1022504.500 Pa; 1022.505 hPa; 766.941 mmHg
    Altitude: -76.766 m
    Humidity: 50.436 %
    Temperature: 0.420 *C
    Pressure: 1022497.750 Pa; 1022.498 hPa; 766.936 mmHg
    Altitude: -76.711 m
    Humidity: 50.062 %

  4. Функция расчёта val2 написана неправильно. В ней участвует беззнаковая переменная temper_raw в результате переменная полученная из CalibData.dig_T3 теряет знак. Надо хотя бы так:
    val2 = (int32_t)(((((temper_raw>>4) — ((int32_t)CalibData.dig_T1)) *
    ((temper_raw>>4) — ((int32_t)CalibData.dig_T1))) >> 12) *
    ((int32_t)CalibData.dig_T3)) >> 14;

  5. Для корректного отображения отрицательных температур нужно изменить тип temper_raw на знаковый — int32_t и добавить беззнаковую переменную — uint32_t temper_raw_unsigned.
    //В выражении:
    BME280_ReadReg_BE_U24(BME280_REGISTER_TEMPDATA,&temper_raw);
    //подставить — temper_raw_unsigned, как и требует прототип функции, т.е. будет:
    BME280_ReadReg_BE_U24(BME280_REGISTER_TEMPDATA,&temper_raw_unsigned);
    //а далее сразу же привести полученное значение к знаковому типу
    temper_raw=(int32_t)temper_raw_add;
    //и будет вам счастье)

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

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

*