Top.Mail.Ru

Часть 14. STM32 и С++. Датчики BMP280 и BME280.

Сегодня рассмотрим подключение датчиков BMP280 и BME280. Описание и подключение уже рассматривалось здесь. Поэтому не будем долго толочь воду в ступе, а просто подключим и измерим.

Датчики, в основном, практически одинаковы, вся разница заключается в точности и пределах измерения и, соответственно, в цене. Может есть ещё отличия, но нам они не принципиальны. Основная фишка этих датчиков состоит в том, что они имеют таблицу корректировки, в которую прописываются данные на заводе-изготовителе. И наша задача - перед измерением считать эти параметры для перерасчёта данных, полученных с АЦП. Подключение будет производиться по I2C:

На картинке подключение по I2C, где вывод 4 - SCL, 3 - SDA. Не забываем подтягивающие резисторы (на схеме не показаны). Комбинируя способ подключения? можно заставить его работать по SPI (3 или 4 линии).

Мы рассмотрим только циклическое измерение. Возможно ещё держать датчик постоянно "спящим", в нужный момент заставить его измерить, и через некоторое время считать готовые данные. Такой режим используется, когда нам не нужно отслеживать параметры постоянно. Но и циклическое измерение имеет своё ограничение. Частота измерения 1 Герц.

Основных функций у нас четыре:

S_BME280();                                           // Создание объекта в классе BME280
uint8_t Init(I2C *I2C_Ptr);                           // Инициализация сенсора с адресом по умолчанию 0x76
uint8_t Init(I2C *I2C_Ptr, uint8_t address);          // Инициализация сенсора с нестандартным адресом
bool    isMeasuring(void);                            // Возвращает 'true' если производится операция измерения
float   readTemperature(void);                        // Чтение и вычисление температуры [float , *C]
float   readPressure(void);                           // Чтение и вычисление атмосферного давления [float , Pa]
float   readHumidity(void);                           // Чтение и вычисление влажности [float , %]
void    oneMeasurement(void);                         // Измерение и уход в сон [только  в FORCED_MODE]

Конструктор у нас практически ничего не делает, просто создаёт объект. Далее функции инициализации. Первая используется, если адрес датчика стандартный, вторая для нестандартного адреса, чего, в принципе, быть не должно, но пусть будет.

Далее функция isMeasuring(void) - может использоваться при единичных измерениях для определения окончания преобразования. Три функции для чтения температуры, давления и влажности. И функция единичного измерения oneMeasurement(void).

Функция инициализации:

uint8_t S_BME280::Init(I2C* I2C_Ptr, uint8_t address)
{
  _i2c_address  = address;
  _I2C_Ptr      = I2C_Ptr;
  /* === Проверяем, BME280 это или что-то другое === */
  if(!_reset()) return false;                           // Программный сброс и проверка ответа
  uint8_t ID = _readRegister(0xD0);                     // Читаем регистр ID
  if(ID != 0x60 && ID != 0x58) return false;            // Если нужный ID (bme/bmp280), то всё в порядке
  _readCalibrationData();                               // Читаем калибровочные данные

  /* === Загружаем настройки в BME280 === */
  _writeRegister(0xF2, _hum_oversampl);                                              	       
  _writeRegister(0xF2, _readRegister(0xF2));                             		       
  _writeRegister(0xF4, ((_temp_oversampl << 5) | (_press_oversampl << 2) | _operating_mode));  
  _writeRegister(0xF5, ((_standby_time << 5) | (_filter_coef << 2)));                	       
  return ID;
}

В первую очередь, производится программный сброс. Он делается простой записью в регистр 0x0E числа 0xB6, что приводит к сбросу аппаратной части датчика и обнулению всех настроек. Затем из регистра 0xD0 считывается идентификационный код, который для BME280 будет равен 0x60, для BMP280 0x58. Далее считываются калибровочные данные и запоминаются в структуре:

void S_BME280::_readCalibrationData(void)
{
  uint8_t Buff[30] = {0,};
  Buff[0] = 0x88;

  _I2C_Ptr->MasterWrite(_i2c_address, Buff, 1, 12);
  _I2C_Ptr->MasterRead(_i2c_address, Buff, 25, 12);

  CalibrationData._T1 = (Buff[0] | (Buff[1] << 8));
  CalibrationData._T2 = (Buff[2] | (Buff[3] << 8));
  CalibrationData._T3 = (Buff[4] | (Buff[5] << 8));
  CalibrationData._P1 = (Buff[6] | (Buff[7] << 8));
  CalibrationData._P2 = (Buff[8] | (Buff[9] << 8));
  CalibrationData._P3 = (Buff[10] | (Buff[11] << 8));
  CalibrationData._P4 = (Buff[12] | (Buff[13] << 8));
  CalibrationData._P5 = (Buff[14] | (Buff[15] << 8));
  CalibrationData._P6 = (Buff[16] | (Buff[17] << 8));
  CalibrationData._P7 = (Buff[18] | (Buff[19] << 8));
  CalibrationData._P8 = (Buff[20] | (Buff[21] << 8));
  CalibrationData._P9 = (Buff[22] | (Buff[23] << 8));
  CalibrationData._H1 = Buff[24];

  Buff[0] = 0xE1;
  _I2C_Ptr->MasterWrite(_i2c_address, Buff, 1, 12);
  _I2C_Ptr->MasterRead(_i2c_address, Buff, 8, 12);

  CalibrationData._H2 = (Buff[0] | (Buff[1] << 8));
  CalibrationData._H3 = Buff[2];
  CalibrationData._H4 = (Buff[3] << 4);
  uint8_t interVal = Buff[4];
  CalibrationData._H4 |= (interVal & 0xF);
  CalibrationData._H5 = (((interVal & 0xF0) >> 4) | (Buff[5] << 4));
  CalibrationData._H6 = Buff[6];
}

После чего пишем в датчик настройки, дающие нужную точность измерения. По умолчанию выставлены максимальные настройки. Если настройки не устраивают, перед вызовом Init() можно обратиться к функциям, изменяющим настройки по умолчанию:

void setFilter(uint8_t mode);                // Настройка фильтра при различных разрешениях в режимах отличных от стандартного [использовать перед Init()]
void setStandbyTime(uint8_t mode);           // Настройка "сна" перед измерением [только в NORMAL_MODE][использовать перед Init()]
void setHumOversampling(uint8_t mode);       // Установить разрешение или отключить модуль измерения влажности [использовать перед Init()]
void setTempOversampling(uint8_t mode);      // Установить разрешение или отключить модуль измерения теипературы [использовать перед Init()]
void setPressOversampling(uint8_t mode);     // Установить разрешение или отключить модуль измерения давления [использовать перед Init()]

 Сами измерения в циклическом режиме. До функции main() запуливаем конструктор:

S_BME280 myBME;

В функции main() инициализируем датчик:

/*
* Инициализируем BMP или BME
*/
myUART.println("BMP or BME Init");
Temp = myBME.Init(&myI2C);
if(Temp < 0)
{
    FlagBME = 0;
    sprintf(msg, "BME Read Error \n\r");
}
else if(Temp == 0x60)
{
    sprintf(msg, "BME ID - 0x%02X OK \n\r", Temp);
    FlagBME = 1;
}
myUART.println(msg);

В цикле делаем замеры:

while(1)
{
    /*
     * Замеры BME 280
     */
    if(FlagBME)
    {
        myUART.println("--- Measuring BME ---\n\r");
        sprintf(msg, "Temperature : %.2f *C\n\r", myBME.readTemperature());
        myUART.print(msg);

        sprintf(msg, "Humidity    : %.2f %%\n\r", myBME.readHumidity());
        myUART.print(msg);

        float pressure = myBME.readPressure();                  // Читаем давление в [Па]
        sprintf(msg, "Pressure    : %.2f hPa\n\r", pressure / 100.0F);
        myUART.print(msg);

        sprintf(msg, "Pressure    : %.2f mm Hg\n\r", _pressureToMmHg(pressure));
        myUART.println(msg);
    }
}

Как видите, ничего особенного нет. Как всегда весь материал на Яндекс Диске и архивом. Пример в каталоге WorkDevel\Developer\Tests MK\F407\F407_I2C_BMP280.

Тема на форуме - перейти.

Подписаться
Уведомить о
guest

16 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Александр
Александр
2 лет назад

Привет. Не получается с датчика BMP180 вывести данные на дисплей ST7735. Где можно примеры подглядеть?

Александр
Александр
Ответ на комментарий  Эдуард
2 лет назад

Да это то сделал, вопрос в том, что данные не могу на экран вывести. Просто строку с текстом получается, а данные нет. По i2c прикрутил, по spi ну ни как.

Дмитрий
Дмитрий
11 месяцев назад

Добрый день,
хочу подключить BMP280, связь по I2C налажена, получаю ID 0х58. А вот дальше не могу разобраться тк в архиве нет заголовочного файла(или не нашел...) и я не могу понять описание класса S_BME280 и его методов, также не понятна и структура I2C. Не могли бы помочь?

Дмитрий
Дмитрий
Ответ на комментарий  Эдуард
11 месяцев назад

сначала пытался Вашими примерами воспользоваться, установил соединение, проверил... Но, как и писал ранее, не нашел #include "STM_BME280.h" из main.cpp и на этом остановился.
Теперь пытаюсь самостоятельно портировать библиотеку Gyver-а для STM32 (stm32f411ceu6 у меня)

Дмитрий
Дмитрий
Ответ на комментарий  Эдуард
11 месяцев назад

Я не могу физически найти хидер файл. Его нет ни в прилагаемом архиве, ни на я-диске. Если сможете, вышлите на емейл.
Что касается f411, то i2c чудесно, как и spi, работает. У меня по spi подключен дисплей tft 240x320 на контроллере st7789v, туда и хочу выводить инфо с датчика.

Дмитрий
Дмитрий
Ответ на комментарий  Эдуард
11 месяцев назад

спасибо! не догадался посмотреть в либах... искал стандартно, в "Tests MK\F407\F407_I2C_BMP280\Core\Inc". Для дисплея использовал библиотеку VadRov https://github.com/vadrov/stm32-display-spi-dma
Понравилось, что с DMA и довольно просто и понятно написано и описано. Есть видео на ютуб.
ПС. не реклама)

Aveal
Администратор
Ответ на комментарий  Эдуард
11 месяцев назад

Реально по одному пикселю через DMA?

Aveal
Администратор
Ответ на комментарий  Эдуард
11 месяцев назад

Другое дело 👍

16
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x