/* barometer.c */ // BMP280 Temperature/Pressure sensor. // Relative pressure resolution 0.12 hPa (1m altitude difference). // Temperature resolution 0.01 C (chip temperature). // Arduino UNO R3 // AVR_ATmega328P #define F_CPU 16000000UL #include // register names/addresses and bit names/numbers #include // for the nop instruction #include void _delay_ms ( double ); void _delay_us ( double ); void init_uart( void ); unsigned char getchar_uart( void ); void putchar_uart( unsigned char ); void string_out_uart( char * ); // uses strings stored in RAM void Decimal_out_uart( long unsigned int, unsigned char ); // 10 digits 2 decimals #define SPI_DDR DDRB #define SPI_PORT PORTB #define CS PINB2 #define MOSI PINB3 #define MISO PINB4 #define SCK PINB5 void SPI_init( void); void SPI_masterTransmitByte( unsigned char data ); unsigned char SPI_masterReceive(); void BMP_setup( void ); long signed int get_raw_temperature( void ); long signed int get_raw_pressure( void ); long unsigned int calculate_pressure( long signed int ); long signed int calculate_temperature( long signed int ); // individual sensor calibration constants stored in chip BMP280 // Run program "BMP280_calibration.c" to retrieve constants to paste here const unsigned int dig_T1 = 27391; const signed int dig_T2 = 26007; const signed int dig_T3 = 50; const unsigned int dig_P1 = 38504; const signed int dig_P2 = -10445; const signed int dig_P3 = 3024; const signed int dig_P4 = 7003; const signed int dig_P5 = -131; const signed int dig_P6 = -7; const signed int dig_P7 = 15500; const signed int dig_P8 = -14600; const signed int dig_P9 = 6000; long signed int t_fine; // Global variable "fine resolution temperature value" // value is changed by every temperature recalculation int main(void) { SPI_init(); init_uart( ); BMP_setup(); while(1) { // ctrl_meas 0x25 FORCED mode to make measurements SPI_PORT &= (~( 1 << CS )); // CS = 0 enable BMP280 SPI_masterTransmitByte( 0x74 ); // ctrl_meas register: 0x74 (0)1110100 write SPI_masterTransmitByte( 0x25 ); // 0x25 "00100101" FORCED mode selected SPI_PORT |= ( 1 << CS ); // CS = 1 disable BMP280, release the SPI bus _delay_ms (5000); // check every 5 seconds string_out_uart( "Temperature: " ); Decimal_out_uart( calculate_temperature( get_raw_temperature() ), 1); // temperature could be negative string_out_uart(" Degrees Celsius\r\n" ); string_out_uart( "Pressure: " ); Decimal_out_uart( calculate_pressure( get_raw_pressure() ), 0); // absolute pressure is always positive string_out_uart(" millibar (hPa)\r\n" ); } } /* *********************************** */ /* FUNCTIONS */ /* *********************************** */ void SPI_init() { // set CS, MOSI and SCK to output SPI_DDR |= (1 << CS) | (1 << MOSI) | (1 << SCK); SPI_PORT &= (~( 1 << CS )); // CS = 0 at startup for BMP280 to choose SPI instead of I2C _delay_ms(5); SPI_PORT |= ( 1 << CS ); // CS = 1 now disable BMP280 /* * SPCR control register * spie spe dord mstr cpol cpha spr1 spr0 "00000000" at reset * spie = 1 interrupt enable * spe = 1 SPI enable * dord = 0 MSB first, dord = 1 LSB first * mstr = 0 Slave, mstr = 1 Master * cpol = 0 clk_idle = 0, cpol = 1 clk_idle = 1 * cpha = 0 half cycle clock idle + half asserted, cpha = 1 half clock asserted half idle * * SPI-Mode is [cpol cpha] 00 01 10 11. BMP280 automatic recognizes mode 00 and 11. * * SPSR status register * spif wcol - - - - - spi2x "00-----0" at reset * * Clock frequency selection * SPI2X SPR1 SPR0 * 0 0 0 fosc/4 * 0 0 1 fosc/16 * 0 1 0 fosc/64 * 0 1 1 fosc/128 * 1 0 0 fosc/2 * 1 0 1 fosc/8 * 1 1 0 fosc/32 * 1 1 1 fosc/64 * */ // enable SPI, set as master, and clock to fosc/128 "01010011" // SPI-mode defaults to 00 SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); } void SPI_masterTransmitByte( unsigned char data ) { // load data into register SPDR = data; // Wait for transmission complete while(!(SPSR & (1 << SPIF))); } unsigned char SPI_masterReceive() { // transmit dummy byte SPDR = 0xFF; // Wait for reception complete while(!(SPSR & (1 << SPIF))); // return Data Register return SPDR; } void init_uart( void ) { // Bits in UCSR0B register: "RXCIE0 TXCIE0 UDRIE0 RXEN0 TXEN0 UCSZ02 RXB80 TXB80" UCSR0B |= (1<<(RXEN0)) | (1<<(TXEN0)); // "----1---" // Bits in UCSR0C register: "UMSEL01 UMSEL00 UPM01 UPM00 USBS0 UCSZ01/UCPHA0 UCSZ01/UCPHA0 UCPOL0" UCSR0C |= (1<<(UCSZ01)) | (1<<(UCSZ00)); // "-----11-" UBRR0 = 103; // Baud prescaler 103 -> 9600 Baud } void putchar_uart( unsigned char ch ) { // Bits in UCSR0A register: "RXC0 TXC0 UDRE0 FE0 DOR0 UPE0 U2X0 MPCM0" // wait until the port is ready to be written to while( ( UCSR0A & ( 1<<(UDRE0) ) ) == 0 ){} // write the byte to the serial port UDR0 = ch; } void string_out_uart( char * string ) // use for strings in RAM { while (* string != '\0') putchar_uart(*string ++ ); } void Decimal_out_uart( long unsigned int number, unsigned char signum) { unsigned long int divisor = 1000000000; unsigned char i; unsigned char s[10]; if ( signum == 1 ) // should be treated as signed number { if ((long signed int)number < 0) { number = -number; putchar_uart( '-' );} else putchar_uart( '+' ); } for ( i=0; i<10; i++ ) // populate array with 10 decimal digits { s[i]= (unsigned char)(number/divisor) + '0'; number %= divisor; divisor /= 10; } // find the leading zeros to skip i = 0; while ( (s[i] == '0') && (i < 7) ) i++ ; // print integer part for(i; i<8; i++) putchar_uart(s[i]); // insert decimal point at two decimals putchar_uart('.'); // print fractional part putchar_uart(s[8]); putchar_uart(s[9]); } void BMP_setup( void) { // Weather monitor. Oversampling * 1, SLEEP at setup. // (Measurements can then start by selecting FORCED mode.) // ctrl_meas 0x24 "00100100" at setup // ctrl_meas 0x25 "00100101" to make measurements // config 0x00 "---000-0" standby time irrelevant in FORCED mode, no Filtering. /* * * ctrl_meas * "osrs_t2 osrs_t1 osrs_t0 osrs_p2 osrs_p1 osrs_p0 mode1 mode0" * * osrs_t osrs_p mode * 000 NONE 000 NONE 00 SLEEP * 001 * 1 001 * 1 01 FORCED * 010 * 2 010 * 2 (10 FORCED) * 011 * 4 011 * 4 11 NORMAL * 100 * 8 100 * 8 * 101 * 16 101 * 16 * 110 - 110 - * 111 - 111 - * * Config will only be writeable in sleep mode * * config * "t_sb2 tsb_1 tsb0 filter2 filter1 filter0 - spi3w" * * tStandby filter spi3w * 000 0.5 ms 000 OFF 0 OFF (=spi4) * 001 62.5 ms 001 2 1 ON (=spi3) * 010 125 ms 010 4 * 011 250 ms 011 8 * 100 500 ms 100 16 * 101 1 s 101 - * 110 2 s 110 - * 111 4 s 111 - * */ SPI_PORT &= (~( 1 << CS )); // CS = 0 enable BMP280 SPI_masterTransmitByte( 0x74 ); // ctrl_meas register: 0x74 (0)1110100 write SPI_masterTransmitByte( 0x24 ); // 0x24 "00100100" SLEEP mode at setup SPI_PORT |= ( 1 << CS ); // CS = 1 disable BMP280, release the SPI bus _delay_ms(5); SPI_PORT &= (~( 1 << CS )); // CS = 0 enable BMP280 SPI_masterTransmitByte( 0x75 ); // config register: 0x75 (0)1110101 write SPI_masterTransmitByte( 0x00 ); // 0x00 "---000-0" SPI_PORT |= ( 1 << CS ); // CS = 1 disable BMP280, release the SPI bus } long signed int get_raw_temperature( void ) { long signed int adc_T; SPI_PORT &= (~( 1 << CS )); // CS = 0 enable BMP280 SPI_masterTransmitByte( 0xFA ); // start at temp_msb ( 0xFA), temp_lsb ( 0xFB), temp_xlsb ( 0xFC) adc_T = ((long unsigned int)SPI_masterReceive()) << 12; // MSB adc_T += ( ( (long unsigned int)SPI_masterReceive() ) << 4 ); // LSB adc_T += ( ( ( (long unsigned int)SPI_masterReceive() ) << 4 ) & 0x0F ); // XLSB SPI_PORT |= ( 1 << CS ); // CS = 1 disable BMP280, release the SPI bus return adc_T; } long signed int get_raw_pressure( void ) { long signed int adc_P; SPI_PORT &= (~( 1 << CS )); // CS = 0 enable BMP280 SPI_masterTransmitByte( 0xF7 ); // start at press_msb (0xF7), press_lsb (0xF8), press_xlsb (0xF9) adc_P = ((long unsigned int)SPI_masterReceive()) << 12; // MSB adc_P += ( ( (long unsigned int)SPI_masterReceive() ) << 4 ); // LSB adc_P += ( ( ( (long unsigned int)SPI_masterReceive() ) << 4 ) & 0x0F ); // XLSB SPI_PORT |= ( 1 << CS ); // CS = 1 disable BMP280, release the SPI bus return adc_P; } long signed int calculate_temperature( long signed int adc_T ) { // Temperature calculation in DegC, resolution is 0.01 DegC. // End Value of T of "5123" equals 51.23 DegC. // t_fine carries "fine temperature" as a global value long signed int var1, var2, T; var1 = ((( (adc_T >>3) - ( (long signed int)dig_T1<<1))) * ( (long signed int)dig_T2) ) >> 11; var2 = (((( (adc_T >>4) -( (long signed int)dig_T1)) * ((adc_T >>4) - ((long signed int)dig_T1))) >> 12) * ((long signed int)dig_T3) ) >> 14; t_fine = var1 + var2; // Global "fine resolution temperature value" is now recalculated T = (t_fine * 5 +128) >> 8; return T; } long unsigned int calculate_pressure( long signed int adc_P ) { // Pressure calculation in Pa as unsigned 32 bit int in Q24.8 format. // (24 integer bits and 8 fractional bits). // End value of p of "24674867" represents 24674867/256=96386.2 Pa = 963.86 hPa long long signed int var1, var2, p; // AVR long long int is 64 bit var1 = ((long long signed int)t_fine) - 128000; // t_fine is global variable var2 = var1 * var1 * (long long signed int)dig_P6; var2 = var2 + ((var1 * (long long signed int)dig_P5) << 17 ); var2 = var2 + (((long long signed int)dig_P4) << 35 ); var1 = ((var1 * var1 * (long long signed int)dig_P3) >> 8) + ((var1 * (long long signed int)dig_P2) << 12); var1 =(((((long long signed int)1)<<47) + var1)) * ((long long signed int)dig_P1) >> 33; p = 1048576 - adc_P; p = (( (p<<31) - var2) * 3125)/var1; var1 = (((long long signed int)dig_P9) *(p>>13) * (p>>13)) >> 25; var2 = (((long long signed int)dig_P8) * p) >> 19; p = ((p + var1 + var2) >> 8) + (((long long signed int)dig_P7 ) <<4); p = ( p + ( ( p & 0x80 ) << 1 ) >> 8 ); // round and divide by 256 return (long unsigned int)p; } /* *********************************** */ /* HARDWARE */ /* *********************************** */ /* Chip ATMega328 Arduino Uno R3 stackable header _______ Digital: _____/ \__ Analog: ______________ ______________ txd ->-|D00 >RXD A5|- | \/ | rxd -<-|D01 -(D00)-|02 PD0/RXD SDA/PC4 27|-(A4)- -|D03~ A2|- rxd -<-(D01)-|03 PD1/TXD PC3 26|-(A3)- -|D04 A1|- -(D02)-|04 PD2/INT0 PC2 25|-(A2)- -|D05~ A0|- -(D03)-|05 PD3/INT1/PWM PC1 24|-(A1)- -|D06~ | Power: -(D04)-|06 PD4 PC0 23|-(A0)- -|D07 Vin|- +5V ---|07 VCC GND 22|--- Gnd | GND|--- GND Gnd ---|08 GND AREF 21|--- Vin -|D08 GND|- Xtal |X|--|09 PB6/OSC1 AVCC 20|--- +5V -|D09~ +5V|--- +5V 16MHz |X|--|10 PB7/OSC2 SCK/PB5 19|-(D13)->- SCK CSB -<-|D10~ +3.3V|- -(D05)-|11 PD5/PWM MISO/PB4 18|-(D12)-<- SDO SDI -<-|D11~ Res|- -(D06)-|12 PD6/PWM PWM/MOSI/PB3 17|-(D11)->- SDI SDO ->-|D12 IOREF|- -(D07)-|13 PD7 PWM/SS'/PB2 16|-(D10)->- CSB SCK -<-|D13 LED --- | -(D08)-|14 PB0 PWM/PB1 15|-(D09)- -|GND | |______________________________| -|AREF | -|SCL | -|SDA | |________________| */ /* Bosch Barometer chip Adafruit BMP280 with 5/3 V level translators included __________ | BMP280 | | | +5V ---| 5V | -| 3V3 | GND --| GND | SCK ->-| SCK | MISO -<-| SDO | MOSI ->-| SDI | SS' ->-| CSB' | |__________| BMP280 Registers (Byte size): // Control and Status registers (R/-) 0xD0/- : id Chip identification (return = 0x58) (-/W) -/0x60 : reset (Write data = 0xB6 to reset) (R/-) 0xF3/- : status "---measuring---updating" (R/W) 0xF4/0x74 : ctrl_meas "osrs_t2 osrs_t1 osrs_t0 osrs_p2 osrs_p1 osrs_p0 mode1 mode0" (R/W) 0xF5/0x75 : config "t_sb2 tsb_1 tsb0 filter2 filter1 filter0 - spi3w" // Temperature and Pressure values from the AD-converter (R/-) 0xF7/- : press_msb (R/-) 0xF8/- : press_lsb (R/-) 0xF9/- : press_xlsb (R/-) 0xFA/- : temp_msb (R/-) 0xFB/- : temp_lsb (R/-) 0xFC/- : temp_xlsb // Indvidual Calibration Constants stored in chip (R/-) 0x88/- : calib00, dig_T1 LSB (R/-) 0x89/- : calib01, dig_T1 MSB (R/-) 0x8A/- : calib02, dig_T2 LSB (R/-) 0x8B/- : calib03, dig_T2 MSB (R/-) 0x8C/- : calib04, dig_T3 LSB (R/-) 0x8D/- : calib05, dig_T3 MSB (R/-) 0x8E/- : calib06, dig_P1 LSB (R/-) 0x8F/- : calib07, dig_P1 MSB (R/-) 0x90/- : calib08, dig_P2 LSB (R/-) 0x91/- : calib09, dig_P2 MSB (R/-) 0x92/- : calib10, dig_P3 LSB (R/-) 0x93/- : calib11, dig_P3 MSB (R/-) 0x94/- : calib12, dig_P4 LSB (R/-) 0x95/- : calib13, dig_P4 MSB (R/-) 0x96/- : calib14, dig_P5 LSB (R/-) 0x97/- : calib15, dig_P5 MSB (R/-) 0x98/- : calib16, dig_P6 LSB (R/-) 0x99/- : calib17, dig_P6 MSB (R/-) 0x9A/- : calib18, dig_P7 LSB (R/-) 0x9B/- : calib19, dig_P7 MSB (R/-) 0x9C/- : calib20, dig_P8 LSB (R/-) 0x9D/- : calib21, dig_P8 MSB (R/-) 0x9E/- : calib22, dig_P9 LSB (R/-) 0x9F/- : calib23, dig_P9 MSB */