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 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Александр
Александр
1 год назад

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другое дело 👍

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