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 для удобства мониторинга показаний.

 

 

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

 

 

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

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

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

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

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

 

 

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

 

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. Сергей:

    Только меня смущает, что давление в Па раз эдак в 10 выше нормального?

  5. MartKot:

    Функция расчёта 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;

  6. MartKot:

    Или так:
    val2 = (int32_t)(((((temper_raw>>4) — CalibData.dig_T1) *
    ((temper_raw>>4) — CalibData.dig_T1)) >> 12) * CalibData.dig_T3) >> 14;

  7. HARRY_TATLLE:

    Для корректного отображения отрицательных температур нужно изменить тип 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;
    //и будет вам счастье)

  8. Igor:

    Dobriy Vecher Narod Stream,

    Ya poluchayu vot takie vot dannie
    1835.00 *C vmesto 18.35 *C. v chem moya oshibka?

    Spasibo

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

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

*