Jump to content

STM32 и BME280


Tibman
 Share

Recommended Posts

Здравствуйте!

Осваиваю работу с датчиком BME280. Настраивал через Hal в CubeIDE, следуя примеру с одного из сайтов. Связь с датчиком есть, калибровочные коэффициенты считываются, и сырые данные тоже приходят. Но не могу корректно преобразовать эти результаты в адекватные значения температуры, а следовательно, и остальные параметры (влажность и давление) также рассчитываются (или отображаются) неправильно.

В кубе пришлось включить в настройках проекта "use newlib-nano to print floating numbers".

В терминале получаю следующие результаты:

Temperature RAW: 0x0007E071
Temperature: 1102373248.000 *C

Вот используемая функция для расчета температуры. Изначально в формуле были указаны переменные типа DIG_T1 и т. п. Я внёс эти прочитанные коэффициенты в формулу, так как датчик всё равно один. 

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;

    sprintf(str1, "Temperature RAW: 0x%08X\r\n", temper_raw);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

    val1 = ((((temper_raw>>3) - (27746 <<1)))*(26240)) >> 11;
    val2 = (((((temper_raw>>4) - (27746))*((temper_raw>>4) - (27746))) >> 12)*(50)) >> 14;
    temper_int = val1 + val2;
    temper_float = ((temper_int * 5 + 128) >> 8);
    temper_float = temper_float/100.0f;
    return temper_float;

  }

 

Код для передачи по UART  из файла main:

tf = BME280_ReadTemperature();
      sprintf(str1, "Temperature: %.3f *C\r\n", tf);
	  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

Вот константы, считанные с датчика:

DIG_T1: 27746
DIG_T2: 26240
DIG_T3: 50
DIG_P1: 35880
DIG_P2: -10656
DIG_P3: 3024
DIG_P4: 5317
DIG_P5: 139
DIG_P6: -7
DIG_P7: 9900
DIG_P8: -10230
DIG_P9: 4285
DIG_H1: 75
DIG_H2: 370
DIG_H3: 0
DIG_H4: 299
DIG_H5: 50
DIG_H6: 0

Прошу помочь разобраться, куда копать. У меня опыт небольшой. Датчик реагирует на изменения температуры. Явно какая-то ошибка уже на последнем этапе, при преобразовании сырого значения с датчика. Формулу сравнивал с даташитом. Либо всё же где-то в формуле что-то не так, что маловероятно, либо неправильно отправляю данные в терминал, точнее как-то неправильно их отображаю.

 

Edited by Tibman
Link to comment
Share on other sites

Выгодные LED-драйверы MOSO для индустриальных приложений

Компэл представляет выгодные светодиодные драйверы MOSO для промышленных решений с высоким классом защиты от внешних воздействующих факторов, хорошей устойчивостью к импульсным помехам и высокой надежностью. В ряде серий реализована возможность дистанционного контроля, кроме того, отдельные версии драйверов могут программироваться на работу в автономном режиме по заранее заданному сценарию. Рассмотрим подробнее их характеристики. 

Читать статью>>

Новые источники питания на DIN-рейку класса High End от MORNSUN
Компания MORNSUN разработала новую линейку ИП с креплением на DIN-рейку класса High End. Линейка состоит из двух семейств однофазных ИП, различающихся функционалом (LIMF и LIHF) и одного семейства на трехфазное напряжение (LITF). У всех этих ИП печатная плата с компонентами имеет лаковое покрытие. Продукция работоспособна в температурном диапазоне -40...85ºС (для однофазных) и -30...70ºС (для трехфазных). Кроме того, однофазные ИП соответствуют требованиям ATEX и могут использоваться во взрывоопасных зонах. Семейство LIMF имеет стандартный функционал (ККМ, сухой контакт реле, 150% перегрузочная способность), а семейство LIHF – максимальный функционал с доп. функциями селективной защиты (SFB) и возможностью дистанционного управления (может заменить серию QUINT от Phoenix Contact).
Подробнее >>

Благодарю за ссылку, интересная информация. 

Переписал формулу для расчета температуры следующим образом:

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;

    sprintf(str1, "Temperature RAW: 0x%08X\r\n", temper_raw);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

    val1 = temper_raw/ 8 - 27746*2;
    val1 = (val1 * 26240) / 2048;
    val2 = temper_raw / 16 - 27746;
    val2 = ((val2*val2) / 4096) * 50 / 16384;

    temper_int = val1 + val2;

    sprintf(str1, "Temperature predv: 0x%08X\r\n", temper_int);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

    temper_float = ((temper_int * 5 + 128) / 256);
    temper_float = temper_float/100.0f;
    return temper_float;

  }

Для интереса вывожу в терминал и переменную temper_int.

Вот такие результаты:
Temperature RAW: 0x0007DCDB
Temperature predv: 0x0001BE70
Temperature: 1102221184.000 *C

 

Берем это число 0x0001BE70, и переводим  в десятичный формат, получаем 114228.

Умножаем на 5, прибавляем 128, и всё это делим на 256. Результат = 2232.

Делим на 100, и получаем 22,32. Вроде по числам получается что-то вполне похожее на комнатную температуру.

Правильно ли я понимаю, что сама процедура пересчета работает вполне себе корректно, как минимум при расчете temper_int?

Но в терминале-то у меня отображается вовсе не такое значение, а какая-то ерунда. И похоже, проблема возникает на этапе расчета temper_float, или вывода его в терминал. 

 

 

Link to comment
Share on other sites

Новое семейство HRP/N3 от MEAN WELL – ИП с 350% перегрузкой для промышленных приложений

В промышленных устройствах и установках с электроприводом на двигателях постоянного тока в момент пуска требуется обеспечить повышенный ток. Для решения этой задачи MEAN WELL предлагает вместо ИП с повышенной избыточной мощностью, более оптимальное решение - источник питания с необходимой перегрузочной способностью семейства HRP/N3. 

Новое семейство, представленное в Компэл, экономичнее и расширяет уже существующее HRP/N в увеличении кратности перегрузки. Подробнее>>

2 минуты назад, Tibman сказал:

или вывода его в терминал

А вы попробуйте просто вывести в терминал какое-нибудь число, станет понятно где косяк. У меня есть рабочий код для BME280, но он на Бейсике. Но формулы то одинаковы. Если раэберётесь, могу выложить.

Link to comment
Share on other sites

Спасибо, действительно это отличный способ убедиться в правильности вывода в терминал.

Просто вывел через терминал дробное значение:

zf = 60.28;
sprintf(str1, "Test: %.3f *C\r\n", zf);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

Результат: Test: 60.280 *C

То есть эта часть тоже работает правильно. 

В таком случае получается, что вся "магия" творится в этой части кода:

temper_float = ((temper_int * 5 + 128) / 256);
    temper_float = temper_float/100.0f;

Причем, считая на калькуляторе, если temper_int сразу перевести в десятичную систему, а потом умножать и делить, то результат очень похож на правду.

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

Link to comment
Share on other sites

пропишите принудительно размерность констант в расчетах

Спойлер

typedef int32_t BME280_S32_t;

typedef struct {
    uint16_t    dig_T1;
    int16_t     dig_T2;
    int16_t     dig_T3;
    uint16_t    dig_P1;
    int16_t     dig_P2;
    int16_t     dig_P3;
    int16_t     dig_P4;
    int16_t     dig_P5;
    int16_t     dig_P6;
    int16_t     dig_P7;
    int16_t     dig_P8;
    int16_t     dig_P9;
    uint8_t     unused; //0xA0
    uint8_t     dig_H1;
}Blk1_t;



BME280_S32_t t_fine;
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of ?5123? equals 51.23 DegC.
// t_fine carries fine temperature as global value
BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) {
    BME280_S32_t var1, var2, T;
    var1 = ((((adc_T >> 3) - ((BME280_S32_t) blk1.dig_T1 << 1))) * ((BME280_S32_t) blk1.dig_T2)) >> 11;
    var2 = (((((adc_T >> 4) - ((BME280_S32_t) blk1.dig_T1)) * ((adc_T >> 4) - ((BME280_S32_t) blk1.dig_T1))) >> 12) *
            ((BME280_S32_t) blk1.dig_T3)) >> 14;
    t_fine = var1 + var2;
    T = (t_fine * 5 + 128) >> 8;
    return T;
}

 

 

Link to comment
Share on other sites

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

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;

    sprintf(str1, "Temperature RAW: 0x%08X\r\n", temper_raw);
    HAL_UART_Transmit(&huart1,(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;

    sprintf(str1, "Temperature predv1: 0x%08X\r\n", temper_int);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

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

    sprintf(str1, "Temperature predv2: 0x%08X\r\n", temper_int);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

    temper_float = temper_int/100;

    return temper_float;

  }

Результат:

Temperature RAW: 0x0007D472 - данные непосредственно с датчика
Temperature predv1: 0x0001B0F9 - это val1 + val2
Temperature predv2: 0x00000875 - это уже значение temp_int перед его делением на 100 и переводом во float.
Temperature: 1101529088.000 *C - полная ерунда в итоге.

Если перевести 0x00000875 в десятичный формат, то получается  2165, что после деления на 100 дало бы в результате 21.65 градуса Цельсия, то есть целочисленные расчеты производятся правильно. К датчику и его работе нет никаких претензий. Остаётся только правильно поделить на 100 и вывести в терминал.

Но почему-то возникает проблема со значением float. 

В main.c вывод значения в терминал:

tf = BME280_ReadTemperature();
      sprintf(str1, "Temperature: %.3f *C\r\n", tf);
	  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

Этот код нормально работает, если на вход подавать что-то вроде 20.36 или тому подобного. Но вот значение temper_float он явно воспринимает как-то не так.

Я так и не могу понять, как из 16-ричного значения датчика должно получиться 10-тичное значение, да ещё и дробное.

Link to comment
Share on other sites

3 минуты назад, Tibman сказал:

Но почему-то возникает проблема со значением float. 

естественно возникает... вы же целочисленное знаковое делите на целочисленную константу
temper_float = temper_int/100; результат деления очевиден или нет?
попробуйте почитать учебник:  https://cpp.com.ru/shildt_spr_po_c/02/0209.html

Link to comment
Share on other sites

Ой, точно, ошибся. Спасибо.

Теперь функция имеет следующий вид:

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;

    //sprintf(str1, "Temperature RAW: 0x%08X\r\n", temper_raw);
    //HAL_UART_Transmit(&huart1,(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 = ((temper_int * 5 + 128) >> 8);
    temper_float = temper_int/100.00f;

    sprintf(str1, "Temperature: %.2f *C\r\n", temper_float);
    HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

    return temper_float;

  }

Я сделал вывод данных в терминал непосредственно в этой функции. И в таком виде температура приходит правильная.

В результате такой вот результат:

Temperature: 21.68 *C
Temperature: 1101885568.00 *C

Верхнее значение выведено в терминал непосредственно из функции чтения температуры,  а нижнее - из main.c.

Но  тогда я не могу понять, почему тогда аналогичная конструкция, размещенная в main.c, выдаёт неправильные значения. Там же ведь значение temper_float, рассчитанное в функции, просто приравнивается к значению переменной tf. 

tf = BME280_ReadTemperature();
sprintf(str1, "Temperature: %.2f *C\r\n", tf);
HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

 

Edited by Tibman
Link to comment
Share on other sites

я правильно понял после
temper_float = temper_int/100.00f

переменная
temper_float = 21.68
а при возвращении
return temper_float;
имеем
temper_float=1101885568.00
???????

ЗЫ пересмотрел функцию float BME280_ReadTemperature(void)
но так и не понял, откуда вы взяли переменную temper_int ???

Link to comment
Share on other sites

Да, всё верно. Из функции отправляя temper_float в терминал, показывается правильное значение, temper_float = 21.68.

А возвращенное из функции через return аналогичное значение отображается через main как temper_float=1101885568.00.

temper_int объявлена ранее в файле с функцией.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...