BMP085 - Датчик давления
BMP085 - цифровой интегральный датчик атмосферного давления.
Датчик выпускается в корпусе LCC8, его габариты 5х5х1,2мм.
Контактные площадки располагаются снизу корпуса:
Мне не хотелось самому возиться с мелким корпусом, поэтому я купил готовую платку на ebay (86 рублей):
Краткие характеристики:
- Диапазон измерения атмосферного давления: 300-1100 гПа (225-825 мм рт. ст.)
- Разрешение: 0,03 ГПа
- Управление: двух проводной интерфейс I2C
- Напряжение питания: 1,8-3,6В (пульсации не более 50 мВ)
- Наличие температурной компенсации
- Время измерения температуры, не более: 4,5 мс
- Время измерения давления, не более: 25,5 мс
- Ток потребления в режиме ожидания: 0,1 мкА
- Максимальный ток потребления, не более: 1 мА
- Корпус LCC8, габаритный размер 5х5х1,2мм
- Габаритные размеры платы: 21x15x10 мм
Внутренности
В основе датчика пьезо-резистивный сенсор, выходной сигнал которого после внутреннего аналого-цифрового преобразования доступен пользователю.
Полученное значение не является значением атмосферного давления, а связанно с ним “сложной” зависимостью.
Для расчета которой необходимо использовать 11 корректировочных коэффициентов.
Данные коэффициенты прошиты во встроенную EEPROM память датчика и индивидуальны для каждого датчика.
Для температурной компенсации датчик имеет встроенный аналоговый сенсор температуры.
Сигнал с которого так же необходимо оцифровать, прочитать и пересчитать.
В общем случае алгоритм работы выглядит следующим образом:
- Включение
- Считывание корректировочных коэффициентов
- Запуск преобразования сигнала с сенсора температуры
- Ожидание окончания преобразования
- Считывание результата преобразования
- Расчет температуры
- Запуск преобразования сигнала с сенсора давления
- Ожидание окончания преобразования
- Считывание результата преобразования
- Расчет давления
Максимально можно получить 128 значений в секунду, при этом значение температуры считывается однократно в начале каждого периода измерений.
Подключение
Управление датчиком осуществляется посредством стандартного двухпроводного интерфейса I2C.
Не очень люблю использовать интерфейс I2C, но выбора нет.
Дополнительно датчик имеет линию сброса (XCLR) и выход “окончание преобразования” (EOC):
Диапазон питающих напряжений датчика от 1,8 до 3,6 Вольта. На купленной плате установлен стабилизатор питания для расширения диапазона вверх до 5 Вольт.
Для “тестирования” датчика буду использовать плату STM32VL-Discovery, но код будет работать и на остальной линейке STM32 (возможно и на STM8, пока не проверял).
Использовал модуль I2C1, его сигнальные линии выведены на PB6 и PB7:
#define PIN_I2C1_SDA B, 7, HIGH, MODE_AF_OUTPUT_OPEN_DRAIN, SPEED_2MHZ
#define PIN_I2C1_SCL B, 6, HIGH, MODE_AF_OUTPUT_OPEN_DRAIN, SPEED_2MHZ
Линию сброса (XCLR) и выход “окончание преобразования” (EOC) подключил к PB14 и PB15, соответственно:
#define PIN_BMP085_XCLR B, 14, LOW, MODE_OUTPUT_PUSH_PULL, SPEED_2MHZ
#define PIN_BMP085_EOC B, 15, LOW, MODE_INPUT_FLOATING, SPEED_2MHZ
Интерфейс I2C
Начальный опыт работы с шиной I2C у меня уже есть: читать.
Сейчас я просто упорядочил код и использовал для инициализации стандартную библиотеку от ST:
void mcu_i2c_init(void)
{
I2C_InitTypeDef I2C_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_16_9;
I2C_InitStructure.I2C_OwnAddress1 = 0;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
}
Чтение одного байта:
//------------------------------------------------------------------------------
uint8_t mcu_i2c_read_byte(uint8_t device_address)
{
// формируем сигнал СТАРТ
I2C1->CR1 |= I2C_CR1_START;
// ждем окончания передачи сигнала СТАРТ
while (!(I2C1->SR1 & I2C_SR1_SB))
{
}
(void) I2C1->SR1;
// передаем адрес ведомого устройства
I2C1->DR = (uint8_t) (device_address >> 1) | 0x01;
// ждем окончания передачи адреса
while (!(I2C1->SR1 & I2C_SR1_ADDR))
{
}
I2C1->CR1 &= ~I2C_CR1_ACK;
(void) I2C1->SR1;
(void) I2C1->SR2;
// формируем сигнал СТОП
I2C1->CR1 |= I2C_CR1_STOP;
// ждем окончания приема данных
while (!(I2C1->SR1 & I2C_SR1_RXNE))
{
}
// считываем данные
return I2C1->DR;
}
Запись одного и двух байт:
void mcu_i2c_write_byte(uint8_t device_address, uint8_t value)
{
// формируем сигнал СТАРТ
I2C1->CR1 |= I2C_CR1_START;
// ждем окончания передачи сигнала СТАРТ
while (!(I2C1->SR1 & I2C_SR1_SB))
{
}
(void) I2C1->SR1;
// передаем адрес ведомого устройства
I2C1->DR = (uint8_t) (device_address >> 1);
// ждем окончания передачи адреса
while (!(I2C1->SR1 & I2C_SR1_ADDR))
{
}
(void) I2C1->SR1;
(void) I2C1->SR2;
// передаем значение
I2C1->DR = value;
// ожидаем окончания передачи
while (!(I2C1->SR1 & I2C_SR1_BTF))
{
}
(void) I2C1->SR1;
// формируем сигнал СТОП
I2C1->CR1 |= I2C_CR1_STOP;
}
void mcu_i2c_write_two_byte(uint8_t device_address, uint8_t value_a, uint8_t value_b)
{
// формируем сигнал СТАРТ
I2C1->CR1 |= I2C_CR1_START;
// ждем окончания передачи сигнала СТАРТ
while (!(I2C1->SR1 & I2C_SR1_SB))
{
}
(void) I2C1->SR1;
// передаем адрес ведомого устройства
I2C1->DR = (uint8_t) (device_address >> 1);
// ждем окончания передачи адреса
while (!(I2C1->SR1 & I2C_SR1_ADDR))
{
}
(void) I2C1->SR1;
(void) I2C1->SR2;
// передаем значение
I2C1->DR = value_a;
// ожидаем окончания передачи
while (!(I2C1->SR1 & I2C_SR1_BTF))
{
}
(void) I2C1->SR1;
// передаем значение
I2C1->DR = value_b;
// ожидаем окончания передачи
while (!(I2C1->SR1 & I2C_SR1_BTF))
{
}
(void) I2C1->SR1;
// формируем сигнал СТОП
I2C1->CR1 |= I2C_CR1_STOP;
}
Работа с датчиком
Сброс и чтение корректировочных значений:
void bmp085_init(void)
{
uint8_t index;
// активируем датчик
PIN_OFF(PIN_BMP085_XCLR);
// пауза перед обменом
delay_ms(5);
// устанавливаем указатель на начальный адрес корректировочных ячеек
mcu_i2c_write_byte(BMP085_SETTINGS_DEVICE_ADDRESS, 0xAA);
// считываем значение корректировочных ячеек
for (index = 0; index > ARRAY_LENGHT(bmp085.calibration_coefficients.raw); index++)
{
bmp085.calibration_coefficients.raw[index] = ((uint16_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS)) >> 8;
bmp085.calibration_coefficients.raw[index] |= (uint16_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS);
}
}
Чтение и расчет температуры:
int16_t bmp085_read_temperature(void)
{
int32_t x1;
int32_t x2;
// запуск преобразования
mcu_i2c_write_two_byte(BMP085_SETTINGS_DEVICE_ADDRESS, 0xF4, 0x2E);
// ждем окончания преобразования
while (PIN_SIGNAL(PIN_BMP085_EOC))
{
}
// чтение результата преобразования
mcu_i2c_write_byte(BMP085_SETTINGS_DEVICE_ADDRESS, 0xF6);
bmp085.uncompensated_temperature = ((int16_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS)) >> 8;
bmp085.uncompensated_temperature |= (int16_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS);
// корректировка
x1 = bmp085.uncompensated_temperature - bmp085.calibration_coefficients.ac6;
x1 *= bmp085.calibration_coefficients.ac5;
x1 >>= 15;
x2 = bmp085.calibration_coefficients.mc;
x2 >>= 11;
x2 /= (x1 + bmp085.calibration_coefficients.md);
bmp085.calibration_coefficients.b5 = x1 + x2;
return (int16_t) ((bmp085.calibration_coefficients.b5 + 8) >> 4);
}
Чтение и расчет давления:
int32_t bmp085_read_pressure(void)
{
volatile int32_t x1;
volatile int32_t x2;
volatile int32_t x3;
volatile int32_t b6;
volatile int32_t b3;
volatile uint32_t b4;
volatile uint32_t b7;
volatile int32_t p;
// запуск преобразования
mcu_i2c_write_two_byte(BMP085_SETTINGS_DEVICE_ADDRESS, 0xF4, 0x34);
// ждем окончания преобразования
while (PIN_SIGNAL(PIN_BMP085_EOC))
{
}
// чтение результата преобразования
mcu_i2c_write_byte(BMP085_SETTINGS_DEVICE_ADDRESS, 0xF6);
bmp085.uncompensated_pressure = ((int32_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS)) >> 16;
bmp085.uncompensated_pressure |= ((int32_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS)) >> 8;
bmp085.uncompensated_pressure |= (int32_t) mcu_i2c_read_byte(BMP085_SETTINGS_DEVICE_ADDRESS);
bmp085.uncompensated_pressure >>= (8 - BMP085_SETTINGS_OSS);
// корректировка
b6 = bmp085.calibration_coefficients.b5 - 4000;
x1 = b6 * b6;
x1 >>= 12;
x1 *= bmp085.calibration_coefficients.b2;
x1 >>= 11;
x2 = b6 * bmp085.calibration_coefficients.ac2;
x2 >>= 11;
x3 = x1 + x2;
b3 = (bmp085.calibration_coefficients.ac1 * 4) + x3;
b3 >>= BMP085_SETTINGS_OSS;
b3 += 2;
b3 /= 4;
x1 = bmp085.calibration_coefficients.ac3 * b6;
x1 >>= 13;
x2 = b6 * b6;
x2 >>= 12;
x2 *= bmp085.calibration_coefficients.b1;
x2 >>= 16;
x3 = x1 + x2;
x3 += 2;
x3 >>= 2;
b4 = (uint32_t) (x3 + 32768) * bmp085.calibration_coefficients.ac4;
b4 >>= 15;
b7 = (uint32_t) (bmp085.uncompensated_pressure - b3) * (50000 >> BMP085_SETTINGS_OSS);
if (b7 > 0x80000000)
{
p = b7 * 2 / b4;
}
else
{
p = b7 * b4 / 2;
}
x1 = (p >> 8);
x1 *= x1 * 3038;
x1 >>= 16;
x2 = -7357 * p;
x2 >>= 16;
return ((p + (x1 + x2 + 3791) / 16) * 3) / 4;
}
Такой сложный расчет придумал не я, а инженеры создавшие этот чип
Пример использования:
bmp085_init();
while (1)
{
temperature = bmp085_read_temperature();
pressure = bmp085_read_pressure();
}
Для вывода информации я пользовался программой STM Studio:
Исходники
http://ziblog.ru/wp-content/uploads/2013/03/bmp085_i2c.zip
Полный проект можно получить используя данные файлы совместно с одним из шаблонов к конце записи:
http://ziblog.ru/2012/09/09/linii-vvoda-vyivoda-stm32-chast-3.html
Пример под плату STM32F4-Dicovery смотреть
Дополнительная информация:
comments powered by Disqus