BME280 – czujnik temperatury, wilgotności oraz ciśnienia (część 2)
Proces odczytywania danych z sensora
Kompletny proces odczytywania danych z sensora można podzielić na kilka etapów:
- Odczytanie danych kalibracji. Można to zrobić jednorazowo na początku programu.
- Odczytanie danych z rejestrów sensora BME280 za pomocą funkcji BME280_read_PTH.
- Złożenie danych o odpowiedniej długości z bajtów odczytanych z rejestrów.
- Obliczenie kompensacji wyników za pomocą specjalnych funkcji wykorzystujących dane kalibracji.
Do składania słowa pomiaru z 8-bitowych danych odczytanych z rejestrów służy funkcja BME280_assembleRawValue pokazana na listingu 10. Funkcja składa 16-bitową wartość z dwu pierwszych bajtów bufora p_data i jeżeli argument has_xlsb jest równy 1 to 16-bitowa wartość jest dodatkowo rozszerzana do 20-bitowej o 4 bity z trzeciego bajtu.
Listing 10. Funkcja składająca daną wielobitową z danych 8-bitowych
uint32_t BME280_assembleRawValue(uint8_t *p_data, uint8_t has_xlsb) { uint32_t value = p_data[0]; value <<= 8; value |= p_data[1]; if (has_xlsb!=0) { value <<= 4; value |= (p_data[2]>>4); } return (int32_t) value; }
Kompletna funkcja BME280_read odczytująca wyniki pomiarów z sensora, a także wyliczania dane z uwzględnieniem kalibracji została pokazana na listingu 11.
Listing 11. Funkcja odczytania i wyliczenia kompensacji pomiarów
/************************************* * odczytanie rejestrów wszystkich pomiarów * 8 bajtów od adresu BME280_MEASUREMENT_REGISTER = 0xB7 * obliczenie kompensacji pomiarów na podstawie odczytanych danych kompensacji */ void BME280_read(void) { // odczytanie wszystkich pomiarów w jednym cyklu uint8_t data[BME280_MEASUREMENT_SIZE]; BME280_read_PTH(BME280_MEASUREMENT_REGISTER,BME280_MEASUREMENT_SIZE,data); // składanie pomiarów int32_t p = BME280_assembleRawValue(&data[0],1); int32_t t = BME280_assembleRawValue(&data[3],1); int32_t h = BME280_assembleRawValue(&data[6],0); //kompensacja pomiarów _temperature = BME280compensateTemperature(t); // wyliczanie temperatury w pierwszej kolejności _pressure = BME280compensatePressure(p); // Uzycie wyliczonej temperatury do kompencacji cisnienia _humidity = BME280compensateHumidity(h); // Uzycie wyliczonej temperatury do kompencacji wilgotnosci }
Funkcje kompensacji pomiaru
Wyliczanie kompensacji pomiarów należy rozpocząć od temperatury, bo ten pomiar używa się do kompensowania pozostałych dwóch pomiarów: ciśnienia i wilgotności. W tym przypadku wartość temperatury używanej do kompensacji jest zapisywana w zmiennej globalnej _t_fine
Producent sensora podaje w swoich danych technicznych jak należy wyliczać pomiary na podstawie danych kalibracyjnych i mocno sugeruje żeby stosować gotowe procedury przygotowane przez BOSCH Sensortec. Firmowe procedury pokazano na listingach 12, 13 i 14.
Kompensacja pomiaru temperatury
Listing 12. Kompensacja pomiaru temperatury
//kompensacja temperatury temperature_t BME280compensateTemperature(int32_t adc_T) { double v_x1_u32; double v_x2_u32; double temperature; v_x1_u32 = (((double)adc_T) / 16384.0 - ((double)_dig_T1) / 1024.0) * ((double)_dig_T2); v_x2_u32 = ((((double)adc_T) / 131072.0 - ((double)_dig_T1) / 8192.0) * (((double)adc_T) / 131072.0 - ((double)_dig_T1) / 8192.0)) * ((double)_dig_T3); //_t_fine temperatura do kompensacji pozostałych pomiarów _t_fine = (int32_t)(v_x1_u32 + v_x2_u32); temperature = (v_x1_u32 + v_x2_u32) / 5120.0; return temperature; }
Kompensacja pomiaru ciśnienia
Listing 13. Kompensacja pomiaru ciśnienia
//kompensacja cisnienia pressure_t BME280compensatePressure(int32_t adc_P) { double v_x1_u32; double v_x2_u32; double pressure; v_x1_u32 = ((double)_t_fine / 2.0) - 64000.0; v_x2_u32 = v_x1_u32 * v_x1_u32 * ((double)_dig_P6) / 32768.0; v_x2_u32 = v_x2_u32 + v_x1_u32 * ((double)_dig_P5) * 2.0; v_x2_u32 = (v_x2_u32 / 4.0) + (((double)_dig_P4) * 65536.0); v_x1_u32 = (((double)_dig_P3) * v_x1_u32 * v_x1_u32 / 524288.0 + ((double)_dig_P2) * v_x1_u32) / 524288.0; v_x1_u32 = (1.0 + v_x1_u32 / 32768.0) * ((double)_dig_P1); pressure = 1048576.0 - (double)adc_P; // Avoid exception caused by division by zero. if (v_x1_u32 != 0) pressure = (pressure - (v_x2_u32 / 4096.0)) * 6250.0 / v_x1_u32; else return 0; v_x1_u32 = ((double)_dig_P9) * pressure * pressure / 2147483648.0; v_x2_u32 = pressure * ((double)_dig_P8) / 32768.0; pressure = pressure + (v_x1_u32 + v_x2_u32 + ((double)_dig_P7)) / 16.0; return pressure; }
Kompensacja pomiaru wilgotności
Listing 14. Kompensacja pomiaru wilgotności
//kompensacja wilgotnosci humidity_t BME280compensateHumidity(int32_t adc_H) { double var_h; var_h = (((double)_t_fine) - 76800.0); if (var_h != 0) { var_h = (adc_H - (((double)_dig_H4) * 64.0 + ((double)_dig_H5) / 16384.0 * var_h)) * (((double)_dig_H2) / 65536.0 * (1.0 + ((double) _dig_H6) / 67108864.0 * var_h * (1.0 + ((double)_dig_H3) / 67108864.0 * var_h))); } else return 0; var_h = var_h * (1.0 - ((double)_dig_H1)*var_h / 524288.0); if (var_h > 100.0) var_h = 100.0; else if (var_h < 0.0) var_h = 0.0; return var_h; }
Inicjalizacja czujnika BME280
Użycie funkcji obsługi sensora BME280 należy poprzedzić wywołaniem funkcji BME280_int, która zeruje zmienne wyniku temperatury, ciśnienia i wilgotności, otwiera interfejs I2C, zeruje zmienne z danymi kalibracji, odczytuje dane kalibracji, a także programuje rejestr konfiguracji o adresie 0xF5 pokazany na rysunku 9 – listing 15.
Listing 15. Inicjalizacja obsługi sensora BME280
uint8_t BME280_init(void) { ssp_err_t status; _temperature = 0; _pressure = 0; _humidity = 0; _t_fine = 0; Err = 0; status=BME280_BUS_Open();//otwarcie magistrali I2C //blad otwarcia magistrali if(status != SSP_SUCCESS) return (0xFF); //zerowanie zmiennych kalibracji BME280_clearCalibrationData(); //odczytanie danych kalibracji BME280_readCalibrationData(); BME280_writeConfigRegister(BME280_STANDBY_500_US,BME280_FILTER_OFF,0); return(Err); }
Pomiary w trybie Forced
Dla przykładowego pomiaru ciśnienia, wilgotności i temperatury w stacji pogodowej wykorzystamy tryb Forced. W związku z tym dane dotyczące czasu stanu Standby dla trybu NORMAL nie będą miały znaczenia. Wyłączymy filtr IIR, natomiast bit programujący ilość linii interfejsu SPI wyzerujemy (dla I2C nie ma znaczenia).
Pomiar w trybie Forced wymaga za każdym razem wyzwolenia przez wpisanie do rejestru Control (adres 0xF4) trybu Forced (rysunek 10). Ponieważ nie można zaprogramować tylko bitów trybu pracy, to trzeba wpisać do rejestru Control wszystkie dane sterujące pracą sensora. Dla pomiarów do celów pogodowych nie jest konieczna duża dokładność, a więc dla wszystkich czujników nadpróbkowanie wyłączamy (ustawiamy na wartość x1). Na listingu 16 zaprezentowano prostą nieskończoną pętlę pomiarowa, która:
- Wyzwala pomiar przez wpisanie trybu Forced do rejestru sterującego,
- Odczytuje wszystkie pomiary, a także wylicza kompensacje,
- Wyświetla wszystkie trzy pomiary,
- Odlicza opóźnienie 10 sekund.
Listing 16. Pętla wykonująca pomiary
while(1){ BME280_writeControlRegisters(BME280_OVERSAMPLING_1X,BME280_OVERSAMPLING_1X,BME280_OVERSAMPLING_1X,BME280_MODE_FORCED); R_BSP_SoftwareDelay(BSP_DELAY_UNITS_MILLISECONDS, 100); BME280_read(); BME280_disp_T(); BME280_disp_H (); BME280_disp_P(); R_BSP_SoftwareDelay(BSP_DELAY_UNITS_MILLISECONDS, 10000); }
Funkcje zapisujące rejestry
Na listingach 17 i 18 zaprezentowano funkcje zapisu rejestrów konfiguracji, a także rejestru kontrolnego.
Listing 17. Zapisanie rejestru Config
ssp_err_t BME280_writeConfigRegister(uint8_t t_sb, uint8_t filter, uint8_t spi) { ssp_err_t status; uint8_t data[2]; data[0] = BME280_CONFIG_REGISTER; data[1] = ((t_sb&0x07)<<5) | ((filter&0x07)<<2) | (spi&0x01); status = BME280_I2C_Write(data,2,false); return (status); }
Listing 18. Zapisanie rejestru sterującego
ssp_err_t BME280_writeControlRegisters(uint8_t osrs_t, uint8_t osrs_p, uint8_t osrs_h, uint8_t mode) { ssp_err_t status; uint8_t data[2]; data[0] = BME280_CTRL_HUM_REGISTER; data[1] = (osrs_h&0x07); status = BME280_I2C_Write(data,2,false); if(status != SSP_SUCCESS) return(status); data[0] = BME280_CTRL_MEAS_REGISTER; data[1] = ((osrs_t&0x07)<<5) | ((osrs_p&0x07)<<2) | (mode&0x03); status = BME280_I2C_Write(data,2,false); return(status); }
Program testowy wyświetla zmierzone wartości na wyświetlaczu dołączonym do płytki ewaluacyjnej. Pokazano to na rysunku 27.
Rysunek 27. Wyświetlanie pomiarów z czujnika BME280
Umieszczenie sensora BME280 na płytce ewaluacyjnej ma jedną wadę. Mierzona temperatura przewyższa temperaturę otoczenia, bo obok są źródła ciepła: mikrokontroler, układ debuggera, wyświetlacz z układem podświetlenia, stabilizatory układu zasilania itd. W przypadku mojej płytki z wyświetlaczem mierzona temperatura jest o ok. 4°C wyższa od temperatury otoczenia. W praktycznych realizacjach należałoby odsunąć sensor od płytki, a najlepiej umieścić go poza obudową, w pewnej odległości.
Kalibracja wyniku pomiaru ciśnienia
Wynik pomiaru ciśnienia jest podawany w Pascalach. Żeby go wyświetlać w hPa należy podzielić wynik przez 100. Wartość ciśnienia podawana jest wartościach bezwzględnych. Żeby miało wartość odnoszoną do poziomu morza, trzeba go skorygować. Ciśnienie rośnie na małych wysokościach do 1000 m w dużym przybliżeniu o około 11 Pa na każdy metr wysokości. W rzeczywistości ta zależność jest wykładnicza Jeżeli znamy wysokość pomiaru nad poziomem morza (h), to do zmierzonej wartości trzeba dodać 11*h.