Jump to content

STM32 и BME280


Tibman
 Share

Recommended Posts

Posted (edited)

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

Осваиваю работу с датчиком 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

Химические элементы FANSO EVE Energy для питания беспроводных датчиков
Литиевые батарейки различного химического состава и разных типоразмеров широко используются в беспроводных датчиках систем сбора данных, промышленной автоматики и систем умного дома. И в любом из многочисленных вариантов использования беспроводных датчиков основными требованиями к их работе являются автономность и бесперебойность функционирования.
Главным условием, гарантирующим такую работу, является правильный выбор элемента питания для датчика.

Подробнее >>

Сравнительное тестирование алкалиновых батареек POWER FLASH 

В потребительском и промышленном сегментах российского рынка химических источников тока имеется множество щелочных (алкалиновых) батареек различных производителей и ценовых категорий. Но велика ли разница в их качестве?

Провели небольшой сравнительный тест, чтобы понять, могут ли источники тока POWER FLASH эффективно заменить продукцию таких известных производителей, как Duracell и GP, вычислить, чему равна стоимость одного часа работы батареек, а также сравнить полученные данные со значениями, указанными в технической документации.  Подробнее>>

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

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

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

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

Подробнее >>

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

Posted (edited)

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

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

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.
  • Сообщения

    • @солар, к чему концовка Вашего сообщения? Из текста топикстартера очень похоже, что он ищет халяву, а не предлагает хорошо оплачиваемую работу. Особенно на это указывают фразы про кнопку и датчик за 700 рублей. Классическое "там только проводок припаять". Хотел бы автор помочь бабушке: приехал бы сам или оплатил мастера из АСЦ LG.
    • Вам знаком этот журнал? @Yurec66 , ошибок в схемах много. Даже во времена Союза в журнале "Радио" (его инженеры и называли "Мурзилкой" - по доброму называли), в котором в редакционной коллегии были маститые спецы, проскакивали ошибки. Редко, но были. Ардуину не знаю. Предполагаю, что внешний источник нужен для поддержания системы в ждущем режиме. Если такой режим не нужен, то и питание можно заводить с одного источника. Но это всего лишь предположения. Почитайте внимательнее, что именно этой схемке нужно. Не разберётесь - тогда подключусь серьёзнее и будем решать вместе.
    • Впринципеможноеслиписатьправильноиначеневозможноразобратьчтоименнонужно
    • ...давно бы исправила на своём сайте грамматические ошибки:   А по "экспертному мнению с точки зрения электроники" ... вопросы возникают с первых же слов "описания". Как уже замечено, КПД реальных систем не бывает больше 100%. Если эта супертехнологичная начинка получает питание при изгибе лыжи, то сколько энергии останется, чтобы гнуть лыжу для "стабилизации"? Навскидку, чтобы хоть как-то влияло на изгиб лыжи, требуется энергия как минимум порядка единиц джоулей (1 Дж = 1 Н·м=1 кг·м²/с²). Электрически 1 Дж - это ток в 1 ампер, текущий 1 секунду при напряжении в 1 вольт. Ничего даже близко подобной мощности в данной "начинке" не наблюдается. Как только в этой распиленной лыже найдут эти кондесаторы, по их ёмкости можно будет говорить о мощности, имеющейся для "стабилизации".
    • Осталось только этот провод присоединить к корпусу котла. И проверить результат. Даже плохое заземление в данном случае лучше, чем никакое. 
    • @Dr. West , да, очередная чушь собачья. @tolyan_8 , любая система регулирования подразумевает в себе датчик процесса, схему обработки сигнала от датчика и сравнение его показаний с неким заданным значением, систему воздействия на элементы, регулирующие процесс. Датчик кое-как описан в приведенном Вами описании. Других устройств из системы регулирования я не обнаружил. Предположения о способах регулирования. 1. Лыжи за счет пьезоэлектриков накапливают заряд, который через электрод, вторкнутый в зад, при неправильном использовании лыж жжёт задницу катающегося. 2. Купив такие инновационные лыжи по инновационной цене, лыжник никак не сможет себе позволить неправильное их использование. Здесь работает правило "Жаба душит". Вот моё Вам, @tolyan_8 , "экспертное" мнение.
×
×
  • Create New...