Attached to this article is a .zip file with PBP code that is able to do these calculations. It is setup to use SPI and for simplicity it uses the good old SHIFTOUT/SHIFTIN commands but for those inclined it should be easy enough to change to hardware SPI or I2C.
In order to use it you must have LONGs enabled and therefor you must use an 18F device. The code weighs in at around 6.5kB and uses 107 bytes of RAM. Running the compensation routines for all three values takes aproximately 2.2ms at 64MHz which equates to 35200 instruction cycles.
This is sort of a bare bones example of how to use it:
'******************************************************************** '* Name : BME280-Demo.pbp * '* Author : Henrik Olsson * '* Notice : Copyright (c) 2020 Henrik Olsson * '* : All Rights Reserved * '* Date : 2020-10-13 * '* Version : 1.0 * '* Notes : This program shows how to use the BME280.pbp include * '* : file to read (via SPI) and display values from the * '* : BME280 sensor. * '******************************************************************** ' [1] As always you need to configure and setup your particular device. ' I'm doing that in a separate file for my particular development board. INCLUDE "HWConfig.pbp" ' [2] The BME280.pbp file expects aliases for the following: BME280_CS VAR LATH.2 ' Chip select (output) BME280_CLK VAR PORTC.3 ' Clock pin (output) BME280_MISO VAR PORTC.4 ' PIC Data In (input) BME280_MOSI VAR PORTC.5 ' PIC Data Out (output) ' [3] Then we're ready to include the BME280.pbp file. INCLUDE "BME280.pbp" ' [4] Configure the BME280 sensor. ' See section 5.4.3 in the datasheet. BME280_Reg = $F2 ' Control humidity register BME280_Data = %00000011 ' 4x oversampling for humidity GOSUB BME280_Write ' See section 5.4.5 in the datasheet BME280_Reg = $F4 ' Control register BME280_Data = %01101111 ' 4x oversampling for temp and pressure, normal mode GOSUB BME280_Write ' See section 5.4.6 in the datasheet BME280_Reg = $F5 ' Config register BME280_Data = %11101000 ' 20ms standby time, IIR coefficient 4, 4 wire SPI GOSUB BME280_Write ' Read in all the compensation/calibration coefficients from the sensor. GOSUB BME280_GetTrimValues '--------------------------------------------------------------------------------- Main: GOSUB BME280_GetRawValues GOSUB BME280_CompensateValues ' At this point variables Temperature, Pressure and Humidity contains the properly compensated ' values and can, for example, be displayed as follows. HSEROUT2[SDEC Temperature/100,".", DEC Temperature//100, "°C ", DEC Pressure/100, ".",_ DEC Pressure//100, "hPa ", DEC Humidity/1000, ".", DEC Humidity //1000, "%RH",13] PAUSE 1000 Goto Main
Running the above code produces output like this:
The results of the PBP code has been compared to that of floating point code running on PC hardware and were found to be pretty accurate, here are a couple of datapoints:
' adc_T = 530386, adc_P = 345501, adc_h = 28680 ' Results of Pyhton code Results of PBP code ' t_Fine: 122508.9573 122505 ' Temperature: 23.9275 23.93 ' Pressure: 1011.1778 1011.20 ' Humidity: 46.3329 47.44 ' ' adc_t = 410521, adc_p = 290230, adc_h = 22123 ' Results of Pyhton code Results of PBP code ' t_Fine: -72370.21 -72372 ' Temperature: -14.13 -14.13 ' Pressure: 1038.4243 1041.76 ' Humidity: 11.819 12.10 ' ' adc_t = 380734 : adc_p = 414855 : adc_h = 32123 ' Results of Pyhton code Results of PBP code ' t_Fine: -120785.60 -120795 ' Temperature: -23.59 -23.58 ' Pressure: 828.2755 832.29 ' Humidity: 62.244 63.73
There's a forum thread where some details pertaining to this development was discussed. I suggest any questions, comments, suggestions, bug reports and so on is kept in that thread.
If you don't want to download a .zip file or if you're just curious, here's the code in the BME280.pbp file:
'**************************************************************************** '* Name : BME280.pbp * '* Author : Henrik Olsson * '* Notice : Copyright (c) 2020 Henrik Olsson * '* : All Rights Reserved * '* Date : 2020-10-13 * '* Version : 1.0 * '* Notes : This is an include file containing routines to read (via SPI) * '* temperature, pressure and humidity from the BME280 Digital * '* humidity, pressure and temperature sensor from BOSCH. * '* * '* : Uses 107 bytes of RAM and roughly 6.5kB of FLASH. * '* * '* History: v1.0 - 2020-10-13 * '* Initial version * '**************************************************************************** ' Code expects some aliases to be defined BEFORE including this file, namely: ' BME280_CS VAR LATH.2 ' BME280_CLK VAR PORTC.3 ' BME280_MISO VAR PORTC.4 ' BME280_MOSI VAR PORTC.5 ' ' Then you can include this file: ' INCLUDE "BME280.pbp" adc_T VAR LONG ' Raw temperature value as read from sensor. adc_P VAR LONG ' Raw pressure value as read from sensor. adc_H VAR WORD ' Raw humidity value as read from sensor. Temperature VAR LONG ' Final temperature value in units of 0.01 degrees C. Pressure VAR LONG ' Final pressure value in units of 1Pa. Humidity VAR LONG ' Final humidity value in units of 0.001%. ' Compensation / trimming parameters to be read from the BME280. ' Datatypes listed as comments are how they are defined in the datasheet. dig_T1 VAR WORD ' Unsigned short dig_T2 VAR LONG ' Signed short, we cast to LONG when read. dig_T3 VAR LONG ' Signed short, we cast to LONG when read. dig_P1 VAR WORD ' Unsigned short. dig_P2 VAR LONG ' Signed short, we cast to LONG when read. dig_P3 VAR LONG ' Signed short, we cast to LONG when read. dig_P4 VAR LONG ' Signed short, we cast to LONG when read. dig_P5 VAR LONG ' Signed short, we cast to LONG when read. dig_P6 VAR LONG ' Signed short, we cast to LONG when read. dig_P7 VAR LONG ' Signed short, we cast to LONG when read. dig_P8 VAR LONG ' Signed short, we cast to LONG when read. dig_P9 VAR LONG ' Signed short, we cast to LONG when read. dig_H1 VAR BYTE ' Unsigned char. dig_H2 VAR LONG ' Signed short, we cast to LONG when read. dig_H3 VAR BYTE ' Unsigned char. dig_H4 VAR WORD ' Signed short, we cast to LONG when read. dig_H5 VAR WORD ' Signed short, we cast to LONG when read. dig_H6 VAR LONG ' Signed char, we cast to LONG when read. ' These variables are used in all three compensation routines and needs to be included all the time. var1 VAR LONG var2 VAR LONG ' These are not needed if humidity is not going to be used. var3 VAR LONG var4 VAR LONG var5 VAR LONG ' This is used in all three compensation routines t_fine VAR LONG BME280_Reg VAR BYTE BME280_Data VAR BYTE tmp_B VAR BYTE ' Temporary variable. ' Actual code below this point, jump over as to not execute unless specifically called. GOTO OverBME280 '---------------------------------------------------------------------------------------------------------------------- BME280_Read: ' Basic read function. ' Set BME280_Reg to whatever address you want to read. ' Data at that address will be returned in BME280_Data BME280_Reg.7 = 0 BME280_CS = 0 SHIFTOUT BME280_MOSI, BME280_CLK, 5, [128 + BME280_Reg] SHIFTIN BME280_MISO, BME280_CLK, 6, [BME280_Data] BME280_CS = 1 RETURN '---------------------------------------------------------------------------------------------------------------------- '---------------------------------------------------------------------------------------------------------------------- BME280_Write: ' Basic write function. ' Set BME280_Reg to whatever address you want to write and ' BME280_Data to whatever value you want to write to that address. BME280_REg.7 = 0 BME280_CS = 0 SHIFTOUT BME280_MOSI, BME280_CLK, 5, [BME280_Reg, BME280_Data] BME280_CS = 1 RETURN '---------------------------------------------------------------------------------------------------------------------- '---------------------------------------------------------------------------------------------------------------------- BME280_Reset: ' This will perform a reset of the device, same as power on (according to datasheet). BME280_Reg = $E0 BME280_Data = $B6 GOSUB BME280_Write RETURN '---------------------------------------------------------------------------------------------------------------------- '---------------------------------------------------------------------------------------------------------------------- BME280_CompensateValues: ' The execution time for compensating all three values are measured ' to around 2190us with the PIC running at 64MHz '------------------------------------------------------------------ ' Optimized Temperature conversion, verified to work with negative temperatures ' Execution time at 64MHz is around 300us '------------------------------------------------------------------ var1 = (((adc_T >> 3) - (dig_T1 << 1)) * dig_T2) / 2048 var2 = (((( (adc_T >> 4) - dig_T1) * ((adc_T >> 4) - dig_T1)) / 4096) * dig_T3) / 16384 t_fine = var1 + var2 Temperature = (t_fine * 5 + 128) / 256 '------------------------------------------------------------------ '------------ Pressure compensation, working version -------------- '------------------------------------------------------------------ ' Execution time at 64MHz is around 1230us var1 = (t_fine / 2) - 64000 var2 = (((var1 / 4) * (var1 / 4)) / 2048) * dig_P6 var2 = var2 + (var1 * dig_P5 * 2) var2 = (var2 / 4) + (dig_P4 * 65536) var1 =((dig_P3 * ((var1 / 4) * var1 / 4) / 8192) / 8) + ((dig_P2 * var1) / 2) var1 = var1 / 262144 var1 = ((32768 + var1) * dig_P1) / 32768 IF var1 = 0 THEN Pressure = 0 RETURN ENDIF Pressure = ABS((1048576 - adc_P) - (var2 / 4096)) * 3125 IF Pressure < $80000000 THEN Pressure = (Pressure / 2) / (ABS(var1)) ELSE Pressure = (Pressure / ABS(var1)) * 2 ENDIF var1 = (dig_P9 * ( (Pressure >> 3) * (Pressure >> 3) / 8192)) / 4096 var2 = ((Pressure >> 2) * dig_P8) / 8192 Pressure = Pressure + ((var1 + var2 + dig_P7) / 16) '------------------------------------------------------------------ '----------- Humidity compensation, working version --------------- '------------------------------------------------------------------ ' Execution time at 64MHz is around 660us var1 = t_fine - 76800 var2 = adc_H << 14 ' adc_H is unsigned so we can use left shift instead of dived here var3 = dig_H4 * 1048576 var4 = dig_H5 * var1 var5 = (((var2 - var3) - var4) + 16384) / 32768 var2 = (var1 * dig_H6) / 1024 var3 = (var1 * dig_H3) / 2048 var4 = ((var2 * (var3 + 32768)) / 1024) + 2097152 var2 = ((var4 * dig_H2) + 8192) / 16384 var3 = var5 * var2 var4 = ((var3 / 32768) * (var3 / 32768)) / 128 var5 = var3 - ((var4 * dig_H1) / 16) IF Var5 < 0 THEN var5 = 0 ENDIF IF var5 > 419430400 THEN var5 = 419430400 ENDIF Humidity = var5 >> 12 IF Humidity > 102400 THEN Humidity = 102400 ENDIF RETURN '---------------------------------------------------------------------------------------------------------------------- '---------------------------------------------------------------------------------------------------------------------- BME280_GetRawValues: ' Make sure we don't have any "residue" in the highest significant byte of the variables ' since we're only shifting in 24 bits. adc_H is 16bits and we're shifting in 16bits so ' no need for any special attention there. adc_P.BYTE3 = 0 adc_T.BYTE3 = 0 BME280_Reg = $F7 BME280_Reg.7 = 0 BME280_CS = 0 SHIFTOUT BME280_MOSI, BME280_CLK, 5, [128 + BME280_Reg] SHIFTIN BME280_MISO, BME280_CLK, 6, [adc_P.BYTE2, adc_P.BYTE1, adc_P.BYTE0, adc_T.BYTE2, adc_T.BYTE1, adc_T.BYTE0, adc_H.BYTE1, adc_H.BYTE0] BME280_CS = 1 ' Temperature and pressure are left justified, needs to be shifted right four places. adc_P = adc_P >> 4 adc_T = adc_T >> 4 RETURN '---------------------------------------------------------------------------------------------------------------------- '---------------------------------------------------------------------------------------------------------------------- BME280_GetTrimValues: ' This routines reads the trim/compensation constants from the sensor. ' For some reason there's a gap in the memory map, first group of data are between 0x88 and 0xA1 (with 0xA0 skipped). ' and second group is from 0xE1 and 0xE7 with digits H4 and H5 spanning three bytes ' Since these values never change for the specific sensor it would be possible to ' read these once and hardcode them into their respective variables, or even better in ' that case, change the variables to constants. ' Most values (but not all) are 16bit signed as they are read from the sensor. ' But PBP does not do signed arithmetic on 16bit variables so we're storing them in LONGs. ' But for that to work properly we need "move" the sign bit (if the value) is negative. dig_T1 = 0 : dig_T2 = 0 : dig_T3 = 0 dig_P1 = 0 : dig_P2 = 0 : dig_P3 = 0 dig_P4 = 0 : dig_P5 = 0 : dig_P6 = 0 dig_P7 = 0 : dig_P8 = 0 : dig_P9 = 0 dig_H1 = 0 : dig_H2 = 0 : dig_H3 = 0 dig_H4 = 0 : dig_H5 = 0 : dig_H6 = 0 ' Read 0x88 thru 0xA1. 0xA0 is not in use so dump that in tmp_B BME280_Reg = $88 BME280_Reg.7 = 0 BME280_CS = 0 SHIFTOUT BME280_MOSI, BME280_CLK, 5, [128 + BME280_Reg] SHIFTIN BME280_MISO, BME280_CLK, 6, [dig_T1.BYTE0, dig_T1.BYTE1, dig_T2.BYTE0, dig_T2.BYTE1, dig_T3.BYTE0, dig_T3.BYTE1,_ dig_P1.BYTE0, dig_P1.BYTE1, dig_P2.BYTE0, dig_P2.BYTE1, dig_P3.BYTE0, dig_P3.BYTE1,_ dig_P4.BYTE0, dig_P4.BYTE1, dig_P5.BYTE0, dig_P5.BYTE1, dig_P6.BYTE0, dig_P6.BYTE1,_ dig_P7.BYTE0, dig_P7.BYTE1, dig_P8.BYTE0, dig_P8.BYTE1, dig_P9.BYTE0, dig_P9.BYTE1,_ tmp_B, dig_H1] IF dig_T2.15 = 1 THEN dig_T2 = -ABS(dig_T2.LOWWORD) ENDIF IF dig_T3.15 = 1 THEN dig_T3 = -ABS(dig_T3.LOWWORD) ENDIF IF dig_P2.15 = 1 THEN dig_P2 = -ABS(dig_P2.LOWWORD) ENDIF IF dig_P3.15 = 1 THEN dig_P3 = -ABS(dig_P3.LOWWORD) ENDIF IF dig_P4.15 = 1 THEN dig_P4 = -ABS(dig_P4.LOWWORD) ENDIF IF dig_P5.15 = 1 THEN dig_P5 = -ABS(dig_P5.LOWWORD) ENDIF IF dig_P6.15 = 1 THEN dig_P6 = -ABS(dig_P6.LOWWORD) ENDIF IF dig_P7.15 = 1 THEN dig_P7 = -ABS(dig_P7.LOWWORD) ENDIF IF dig_P8.15 = 1 THEN dig_P8 = -ABS(dig_P8.LOWWORD) ENDIF IF dig_P9.15 = 1 THEN dig_P9 = -ABS(dig_P9.LOWWORD) ENDIF BME280_CS = 1 BME280_Reg = $E1 BME280_Reg.7 = 0 BME280_CS = 0 SHIFTOUT BME280_MOSI, BME280_CLK, 5, [128 + BME280_Reg] SHIFTIN BME280_MISO, BME280_CLK, 6, [dig_H2.BYTE0, dig_H2.BYTE1, dig_H3, dig_H4.BYTE0, tmp_B, dig_H5.BYTE0, dig_H6] IF dig_H2.15 = 1 THEN dig_H2 = -ABS(dig_H2.LOWWORD) ENDIF dig_H4 = dig_H4 << 4 dig_H5 = dig_H5 << 4 ' Make room for lower 4 bits dig_H4 = dig_H4 + (tmp_B & %00001111) ' Extract lower 4 bits of dig_H4 tmp_B = tmp_B >> 4 ' Extract lower 4 bits of dig_H5 dig_H5 = dig_H5 + (tmp_B & %00001111) BME280_CS = 1 RETURN '*********************************************************************************************************************** '********* Optional debug code below this point. Will get included with #DEFINE BME280_DEBUG '*********************************************************************************************************************** #IFDEF BME280_DEBUG Testcase: adc_T = 530386 : adc_P = 345501 : adc_h = 28680 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_T = 486770 : adc_P = 330198 : adc_h = 35867 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_t = 468750 : adc_p = 324532 : adc_h = 27951 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_t = 455100 : adc_p = 320161 : adc_h = 29389 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_t = 438939 : adc_p = 314970 : adc_h = 31164 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_t = 427235 : adc_p = 311954 : adc_h = 38152 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_t = 410521 : adc_p = 290230 : adc_h = 22123 GOSUB BME280_CompensateValues : GOSUB PrintValues adc_t = 380734 : adc_p = 414855 : adc_h = 32123 GOSUB BME280_CompensateValues : GOSUB PrintValues RETURN PrintValues: HSEROUT2[DEC i, ": ", "Temp: ", SDEC Temperature, " P: ", SDEC Pressure, " RH: " , DEC Humidity, " t_fine: ", SDEC t_fine, 13] RETURN PrintTrimValues: HSEROUT2["dig_T1: ", DEC dig_T1, 13] HSEROUT2["dig_T2: ", SDEC dig_T2, 13] HSEROUT2["dig_T3: ", SDEC dig_T3, 13] HSEROUT2["dig_P1: ", DEC dig_P1, 13] HSEROUT2["dig_P2: ", SDEC dig_P2, 13] HSEROUT2["dig_P3: ", SDEC dig_P3, 13] HSEROUT2["dig_P4: ", SDEC dig_P4, 13] HSEROUT2["dig_P5: ", SDEC dig_P5, 13] HSEROUT2["dig_P6: ", SDEC dig_P6, 13] HSEROUT2["dig_P7: ", SDEC dig_P7, 13] HSEROUT2["dig_P8: ", SDEC dig_P8, 13] HSEROUT2["dig_P9: ", SDEC dig_P9, 13] HSEROUT2["dig_H1: ", DEC dig_H1, 13] HSEROUT2["dig_H2: ", SDEC dig_H2, 13] HSEROUT2["dig_H3: ", DEC dig_H3, 13] HSEROUT2["dig_H4: ", SDEC dig_H4, 13] HSEROUT2["dig_H5: ", SDEC dig_H5, 13] HSEROUT2["dig_H6: ", SDEC dig_H6, 13] RETURN #ENDIF OverBME280:
Re: CAN header and EXT asm question
Watch out for the slew rate control bit in PICS.
retepsnikrep Yesterday, 15:11It defaults to on in the PIC18F26K80!
This screwed me for hours as the SPI just kept falling over above 1mhz.
Not enough drive power for the...