Подключение датчика температуры и давления BMP-280 к микроконтроллеру.
Никаких Ардуин. Только хардкор. Ну а заодно делаем барометр.

Logo

Понадобилось мне измерять атмосферное давление. Ну что может пойти не так? Посмотрел я на разные датчики и модули на их основе и не сильно вникая в их работу выбрал BMP-280 от компании BOSCH. Взял пятивольтовый модуль с интегрированным стабилизатором на 3.3 вольта и интерфейсом I2C.

Сказать, что я намучился с его подключением, это ничего не сказать.


Итак. Для тестирования работы датчика я подключил его к своему универсальному модулю, стал настраивать их общение и помаленьку погружаться в даташит.

При первом взгляде на описание модуля я понял, что всё не так просто и однозначно. Выяснилось, что датчик имеет множество обязательных настроек, да еще в придачу 12 коэффициентов, которые необходимо извлечь и правильно обработать. И, да. Полученные данные температуры и давления являются набором цифр и требуют нетривиального расчета. Но, обо всём по порядку.


Для начала давайте разберемся с таблицей регистров и настройками датчика. Итак:

0xd0 – идентификационный номер датчика. Доступен для чтения и всегда возвращает 0x58. На мой взгляд полезен при инициализации. И если при чтении идентификационного номера возвращается другое число, значит что-то пошло не так.

0xe0 – сброс. При записи в регистр числа 0xb6 происходит полный сброс. Другие значения игнорируются, а при чтении возвращается ноль.

0xf3 – регистр контроля занятости датчика. Нулевой бит автоматически устанавливается в (1) при копировании данных в энергонезависимую память. По окончании копирования бит получает значение (0). Третий бит автоматически устанавливается в (1) при выполнении преобразования и возвращается в (0) по окончании процесса и передачи новых значений в регистры данных. Проще говоря, когда этот регистр при чтении возвращает ноль значит с датчиком можно проводить манипуляции.

0xf4 – регистр настроек получения данных.
Биты (7, 6, 5) контролируют избыточную выборку температурных данных. При запуске контроллера биты обнулены. Рекомендуемые параметры 001 и 010. Дальнейшее увеличении до 111 не дает значимого эффекта.
Биты (4, 3, 2) контролируют избыточную выборку данных давления. При запуске контроллера биты обнулены. Рекомендуемые параметры 001, 010, 011.
Биты (1, 0) управляют режимом питания датчика. 00 – датчик отключен (спящий режим). Режим устанавливается по умолчанию при запуске устройства. 01 – принудительный режим. В этом режиме происходит однократное измерение параметров после чего датчик возвращается в спящий режим, 11 – периодический режим. В этом режиме переодически происходит измерение параметров. Время ожидание между измерениями настраивается.

0xf5 – регистр конфигурации.
Биты (7, 6, 5) настраивают время ожидания при работе в периодическом режиме. Для ожидания 0,5 сек. записываем 100 и соответственно 101 для секундного ожидания. Другие параметры времени ожидания см. в даташите.
Биты (4, 3, 2) настраивают фильтр кратковременных резких изменений давления. При запуске датчика фильтр выключен. Рекомендуемые значения 001, 010.
Бит (0) переключает интерфейс. (0) – I2C, (1) – SPI. По умолчанию (0).

0xf7, 0xf8, 0xf9 – регистры данных давления.

0xfa, 0xfb, 0xfc – регистры данных температуры.

При запуске в регистрах 0xf7 и 0xfa записано число 0x80.

Регистры с 0х88 по 0х9f сдвоенные, в них хранятся те самые коэффициенты для расчета параметров температуры и давления. Необходимо отметить то, что данные в этих регистрах могут быть как беззнаковыми (unsigned int) так и знаковые (signed int). Соответственно знаковые величины легко могут быть отрицательными представленны в дополнительном коде.

С этими коэффициентами, по мимо того, что они могут быть отрицательными, есть еще одно затруднение. Дело в том, что при расчете мы будем оперировать знаковыми 32-х битными переменными типа (signed long int) и нам придется конвертировать 16-ти битный коэффициент в 32-х битное число. Нюанс заключается в том, что решение в лоб

signed long int dig_t2 = (signed long int) dig_T2; 

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

Регистры 0хa0 и 0хa1 пустые.


Ну вроде с регистрами разобрались. Теперь разберем нюансы передачи и приема данных по I2C.

Датчик BMP-280, в зависимости от настроек, может передавать данные по протоколам I2C и SPI. В моем случае модуль заточен для передачи данных только по I2C. Посему открыв даташит я, на первый взгляд, не увидел ничего особенного в диаграммах приема / передачи данных. Но почему-то подпрограммы, которые хорошо работают с другими устройствами, работали с BMP-280 не корректно. Причина в том, что алгоритм чтения данных для BMP-280 немного отличается от алгоритмов работы устройств, которые мне попадались ранее.

Дело в том, что для правильного приема данных необходимо учесть два нюанса. Первый, при передаче последнего подтверждающего бита необходимо установить единицу и только после этого передать условие (Stop). Второй, после того как переданы байты адреса устройства и регистра-указателя начала чтения, нельзя устанавливать условие (Stop), а необходимо, пропустив его, сразу начинать с условия (Start). В противном случае чтение будет происходить по другому адресу.

Но на этом трудности с получением информации из датчика не окончились. Оказывается, для получения корректной информации необходимо получение данных температуры и давления за один проход. В даташите указано, что параметры температуры и давления связаны и если читать их по отдельности, то возможна ошибка при расчетах.


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

Для получения полноценных данных температуры и давления необходимо провести хоть и несложные, но очень запутанные расчеты. Безусловно, все формулы представлены в даташите, а также есть пример расчета, но вот-же незадача. 8-ми битный контроллер не может прожевать расчетную формулу, если ее целиком поместить в программу. Ее приходится разбить на составные части. Даже в описание к датчику производитель указывает на то, что он не отвечает за правильность расчетов конкретно на вашем устройстве и предлагает приобрести специальный модуль для общения с датчиком.

Для примера давайте разберем формулу, определяющую промежуточное значения (var1) для расчёта текущей температуры. Это, к слову сказать, самая простая, на мой взгляд, представленная формула.

var1 = ((((adc_T>>3) – ((BMP280_S32_t) dig_T1<<1))) * ((BMP280_S32_t) dig_T2)) >> 11;

CV_AVR выдаст некорректный код если эту формулу поместить в программу как есть целиком. Для корректной работы необходимо разбить ее на составляющие и при этом не забыть правильно перевести коэффициенты из 16-ти бит в 32-х битные числа со знаком:

signed long int var_1 = CLEAN;   //переменная для промежуточных расчетов 

//var_1---------------
// dig_t1 коэффициент, переведенный в формат (unsigned long int)
// dig_t2 коэффициент, переведенный в формат (signed long int)
// *index_t_p – указатель на данные температуры из датчика (unsigned long int)

var_1 = *index_t_p >> 3;  	//1. Х = (adc_T>>3)	
var_1 -= dig_t1 << 1;      	//2. Y = (X – ((BMP280_S32_t) dig_T1<<1))
var_1 *= dig_t2;		//3. Z = ((Y) * ((BMP280_S32_t) dig_T2)) 
var_1 >>= 11;			//4. Q = Z >> 11

И таким образом надо разобрать все формулы.


Пару слов необходимо сказать о формате получаемых данных. И у меня, честно сказать, возникает по этому поводу вопрос, на который я для себя так и не ответил. Согласно даташита прибор может работать при температурах от -40 до +85 С. Но судя по формуле отрицательные числа он отобразить не может. Т.е. результат должен быть в пределе от 1 до 9999 С. Хотя я понимаю, что для расчета давления используется значение, которое скорей всего может каким-то образом указывать на отрицательные значения. Посему получается, что температура для отображения представлена четырехзначным числом (например, 2567). Для ее определения необходимо полученное число поделить на 100. Итог 25.67 гр.С. То есть, для отображения температуры достаточно просто поставить точку в нужном месте. Но как я уже писал, отрицательные значения температуры датчик представить для отображения не может. Ну, или я что-то не понял.

Давление получается в виде положительного числа, измеряемого в паскалях (Па). Среднее атмосферное давление около 101308 Па. Если кому нужно давление в мм. рт. ст., то необходимо давление в Па умножить на 100 и разделить на 1333. Полученное давление в мм. рт. ст. будет состоять из 4 цифр (например 7600) которое необходимо будет отобразить установив точку отделив десятую часть (760.0 мм. рт. ст.). Вот как-то так.

Ну и вишенка на тортик — из-за того, что получаемых параметров больше одного (температура в гр.С, давление в Па, а возможно и другие представления этих данных) приходится осуществлять их передачу в основную программу через указатели. Для начинающих программистов это не очень понятная технология. Она не сложная, но в ней легко запутаться. С другой стороны, если пренебречь значениям температуры, а использовать только полученное давление, то вполне можно обойтись без наворотов с указателями. Возвращая значения давления через (return).

Я очень надеюсь, что все сделал правильно и показания корректны. По крайней мере температура бьётся с бытовым термометром. Давление к сожалению проверить нечем.


В прилагаемых файлах схема, даташит и программа.


А на сегодня всё. Удачи.

20.11.2025


Если вдруг найдете в статье неточности или заблуждения. Напишите мне об этом. Я подправлю.

Приложения:
Схема, даташит и программа.