Электронный барометр своими руками

В данном проекте мы рассмотрим подключения датчика давления по интерфейсу IC к контроллеру Arduino и снятие показаний. Проще говоря мы создадим электронный барометр своими руками.

В качестве датчика давления мы будем использовать BMP085 от фирмы Bosch. Описание и документацию на датчик можно найти здесь. Даташит: BMP085.
BMP085 представляет из себя высокоточный цифровой датчик атмосферного давления с ультранизким энергопотреблением. Также, датчик позволяет измерять температуру. Интерфейс подключения: IC. Применяется датчик в GPS-навигации, метеостанциях, приборах определения скорости подьема/спуска, в измерителях мощности воздушного потока и т.п.

Макетная плата с Arduino и BMP085

Информация, которая представлена фирмой Bosch по работе с данным датчиком очень скудная. Разобраться с BMP085 помог сайт Jeelabs

Аппаратная часть

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

К Arduino, датчик BMP085 подключается также, как и другие IC устройства: подключите VCC к VCC, GND к GND, SCL к аналоговому выводу 5, а SDA к аналоговому выводу 4. Поставьте подтягивающие (pull-up) резисторы (от 1к до 20к, например 4.7кОм) между SDA, SCL и VCC (на моей плате они уже присутствуют).

Напряжение питания датчика: 1.8-3.6 В. Также, датчик содержит выход EOC, который сигнализирует об окончании процесса измерения и обработки данных. Если EOC=1, то обработка завершена, если EOC=0, то в процессе. EOC подключен к аналоговому выводу 2 Arduino, но не использовался.

Программное обеспечение

К нашей радости, код от Jeenode содержит всю необходимую функциональность, единственное, что я добавил, так это возможность использования всех режимов передискретизации (oversampling). В BMP085 есть возможность задания 4 режимов передискретизации, каждый из которых затрачивает больше времени и энергии, чем предыдущий режим, но в то же время повышает точность измерения.

Итак, программа. Для начала, мы должны сделать возможность считывания всех 16-битных значений с регистров датчика:

 int read_int_register(unsigned char r) {   unsigned char msb, lsb;   Wire.beginTransmission(I2C_ADDRESS);   Wire.send(r);  // регистр для чтения   Wire.endTransmission();     Wire.requestFrom(I2C_ADDRESS, 2); // считываем 2 байта   while(!Wire.available()) {     // ожидание   }   msb = Wire.receive();   while(!Wire.available()) {     // ожидание   }   lsb = Wire.receive();   return (((int)msb<<8) | ((int)lsb)); } 

Далее, нам необходима функция, которая будет записывать значение в 8-ми битный регистр:

 char read_register(unsigned char r) {   unsigned char v;   Wire.beginTransmission(I2C_ADDRESS);   Wire.send(r);  // регистр для чтения   Wire.endTransmission();     Wire.requestFrom(I2C_ADDRESS, 1); // считываем байт   while(!Wire.available()) {     // ожидание   }   v = Wire.receive();   return v; } 

Затем, нужно определить несколько глобальных переменных для чтения калибровочных данных из Eeprom датчика:

 //взято с даташита BMP085 int ac1; int ac2; int ac3; unsigned int ac4; unsigned int ac5; unsigned int ac6; int b1; int b2; int mb; int mc; int md; 
 void  bmp085_get_cal_data() {   Serial.println("Reading Calibration Data");   ac1 = read_int_register(0xAA);   Serial.print("AC1: ");   Serial.println(ac1,DEC);   ac2 = read_int_register(0xAC);   Serial.print("AC2: ");   Serial.println(ac2,DEC);   ac3 = read_int_register(0xAE);   Serial.print("AC3: ");   Serial.println(ac3,DEC);   ac4 = read_int_register(0xB0);   Serial.print("AC4: ");   Serial.println(ac4,DEC);   ac5 = read_int_register(0xB2);   Serial.print("AC5: ");   Serial.println(ac5,DEC);   ac6 = read_int_register(0xB4);   Serial.print("AC6: ");   Serial.println(ac6,DEC);   b1 = read_int_register(0xB6);   Serial.print("B1: ");   Serial.println(b1,DEC);   b2 = read_int_register(0xB8);   Serial.print("B2: ");   Serial.println(b1,DEC);   mb = read_int_register(0xBA);   Serial.print("MB: ");   Serial.println(mb,DEC);   mc = read_int_register(0xBC);   Serial.print("MC: ");   Serial.println(mc,DEC);   md = read_int_register(0xBE);   Serial.print("MD: ");   Serial.println(md,DEC); } 

Снятие показаний всех значений с Eeprom может более эффективным, чем каждый раз записывать и считывать показания с каждого регистра. Т.о. достигается хороший выигрыш по времени.
Данные raw температуры(ut) и давления(up) могут быть считаны как 16 и 24 битные значения:

 unsigned int bmp085_read_ut() {   write_register(0xf4,0x2e);   delay(5); //дольше чем 4.5 мс   return read_int_register(0xf6); } 
 long bmp085_read_up() {   write_register(0xf4,0x34+(oversampling_setting<<6));    delay(pressure_waittime[oversampling_setting]);     unsigned char msb, lsb, xlsb;   Wire.beginTransmission(I2C_ADDRESS);   Wire.send(0xf6);   Wire.endTransmission();     Wire.requestFrom(I2C_ADDRESS, 3);   while(!Wire.available()) {     // ожидание   }   msb = Wire.receive();   while(!Wire.available()) {     // ожидание   }   lsb |= Wire.receive();   while(!Wire.available()) {     // ожидание   }   xlsb |= Wire.receive();   return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting); } 

Алгоритм преобразования температуры и давления из raw-данных датчика в реальную температуру (градусы Цельсия) и давление (Паскаль) взят из даташита (плюс некоторые дополнения от Jeenodes):

 void bmp085_read_temperature_and_pressure(int* temperature, long* pressure) {    int  ut= bmp085_read_ut();    long up = bmp085_read_up();    long x1, x2, x3, b3, b5, b6, p;    unsigned long b4, b7;      //расчет температуры    x1 = ((long)ut - ac6) * ac5 >> 15;    x2 = ((long) mc << 11) / (x1 + md);    b5 = x1 + x2;    *temperature = (b5 + 8) >> 4;       //расчет давления    b6 = b5 - 4000;    x1 = (b2 * (b6 * b6 >> 12)) >> 11;     x2 = ac2 * b6 >> 11;    x3 = x1 + x2;    b3 = (((int32_t) ac1 * 4 + x3)<> 2;    x1 = ac3 * b6 >> 13;    x2 = (b1 * (b6 * b6 >> 12)) >> 16;    x3 = ((x1 + x2) + 2) >> 2;    b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;    b7 = ((uint32_t) up - b3) * (50000 >> oversampling_setting);    p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;        x1 = (p >> 8) * (p >> 8);    x1 = (x1 * 3038) >> 16;    x2 = (-7357 * p) >> 16;    *pressure = p + ((x1 + x2 + 3791) >> 4);  } 

Данные температуры и давления рассчитываются в одно и то же время (значения температуры используются для расчета давления).