Перейти к содержанию

STM32 и BME280


Tibman

Рекомендуемые сообщения

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

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

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

 

Изменено пользователем Tibman
Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

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

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

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, или вывода его в терминал. 

 

 

Ссылка на комментарий
Поделиться на другие сайты

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

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

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

Ссылка на комментарий
Поделиться на другие сайты

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

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

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 сразу перевести в десятичную систему, а потом умножать и делить, то результат очень похож на правду.

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

Ссылка на комментарий
Поделиться на другие сайты

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

Спойлер

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;
}

 

 

Ссылка на комментарий
Поделиться на другие сайты

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

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-тичное значение, да ещё и дробное.

Ссылка на комментарий
Поделиться на другие сайты

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

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

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

Ссылка на комментарий
Поделиться на другие сайты

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

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

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);

 

Изменено пользователем Tibman
Ссылка на комментарий
Поделиться на другие сайты

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

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

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

Ссылка на комментарий
Поделиться на другие сайты

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

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

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

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...