BMP085 - Датчик давления

15 Марта 2013 К комментариям

BMP085 - цифровой интегральный датчик атмосферного давления.

Датчик выпускается в корпусе LCC8, его габариты 5х5х1,2мм.

Контактные площадки располагаются снизу корпуса:

1

Мне не хотелось самому возиться с мелким корпусом, поэтому я купил готовую платку на ebay (86 рублей): DPP_20130313

DPP_20130314

Краткие характеристики:

  • Диапазон измерения атмосферного давления: 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):

2

Диапазон питающих напряжений датчика от 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 смотреть


Дополнительная информация:

BMP085 Справочный листок


Categories: Проекты Tags: BMP085

comments powered by Disqus