mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
1d09194bbc
* bmp280: Added support for the Bosch BMP280 temperature and pressure sensor.
244 lines
7.2 KiB
Go
244 lines
7.2 KiB
Go
package bmp280
|
|
|
|
import (
|
|
"machine"
|
|
"time"
|
|
)
|
|
|
|
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
|
|
type Oversampling uint
|
|
|
|
// Mode is the Power Mode.
|
|
type Mode uint
|
|
|
|
// Standby is the inactive period between the reads when the sensor is in normal power mode.
|
|
type Standby uint
|
|
|
|
// Filter unwanted changes in measurement caused by external (environmental) or internal changes (IC).
|
|
type Filter uint
|
|
|
|
// Device wraps an I2C connection to a BMP280 device.
|
|
type Device struct {
|
|
bus machine.I2C
|
|
Address uint16
|
|
cali calibrationCoefficients
|
|
Temperature Oversampling
|
|
Pressure Oversampling
|
|
Mode Mode
|
|
Standby Standby
|
|
Filter Filter
|
|
}
|
|
|
|
type calibrationCoefficients struct {
|
|
// Temperature compensation
|
|
t1 uint16
|
|
t2 int16
|
|
t3 int16
|
|
|
|
// Pressure compensation
|
|
p1 uint16
|
|
p2 int16
|
|
p3 int16
|
|
p4 int16
|
|
p5 int16
|
|
p6 int16
|
|
p7 int16
|
|
p8 int16
|
|
p9 int16
|
|
}
|
|
|
|
// New creates a new BMP280 connection. The I2C bus must already be
|
|
// configured.
|
|
//
|
|
// This function only creates the Device object, it does not initialize the device.
|
|
// You must call Configure() first in order to use the device itself.
|
|
func New(bus machine.I2C) Device {
|
|
return Device{
|
|
bus: bus,
|
|
Address: Address,
|
|
}
|
|
}
|
|
|
|
// Connected returns whether a BMP280 has been found.
|
|
// It does a "who am I" request and checks the response.
|
|
func (d *Device) Connected() bool {
|
|
data := make([]byte, 1)
|
|
d.bus.ReadRegister(uint8(d.Address), REG_ID, data)
|
|
return data[0] == CHIP_ID
|
|
}
|
|
|
|
// Reset preforms complete power-on-reset procedure.
|
|
// It is required to call Configure afterwards.
|
|
func (d *Device) Reset() {
|
|
d.bus.WriteRegister(uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
|
}
|
|
|
|
// Configure sets up the device for communication and
|
|
// read the calibration coefficients.
|
|
func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pres Oversampling, mode Mode) {
|
|
d.Standby = standby
|
|
d.Filter = filter
|
|
d.Temperature = temp
|
|
d.Pressure = pres
|
|
d.Mode = mode
|
|
|
|
// Write the configuration (standby, filter, spi 3 wire)
|
|
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
|
|
d.bus.WriteRegister(uint8(d.Address), REG_CONFIG, []byte{byte(config)})
|
|
|
|
// Write the control (temperature oversampling, pressure oversampling,
|
|
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
|
|
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
|
|
|
// Read Calibration data
|
|
data := make([]byte, 24)
|
|
err := d.bus.ReadRegister(uint8(d.Address), REG_CALI, data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Datasheet: 3.11.2 Trimming parameter readout
|
|
d.cali.t1 = readUintLE(data[0], data[1])
|
|
d.cali.t2 = readIntLE(data[2], data[3])
|
|
d.cali.t3 = readIntLE(data[4], data[5])
|
|
|
|
d.cali.p1 = readUintLE(data[6], data[7])
|
|
d.cali.p2 = readIntLE(data[8], data[9])
|
|
d.cali.p3 = readIntLE(data[10], data[11])
|
|
d.cali.p4 = readIntLE(data[12], data[13])
|
|
d.cali.p5 = readIntLE(data[14], data[15])
|
|
d.cali.p6 = readIntLE(data[16], data[17])
|
|
d.cali.p7 = readIntLE(data[18], data[19])
|
|
d.cali.p8 = readIntLE(data[20], data[21])
|
|
d.cali.p9 = readIntLE(data[22], data[23])
|
|
}
|
|
|
|
// PrintCali prints the Calibration information.
|
|
func (d *Device) PrintCali() {
|
|
println("T1:", d.cali.t1)
|
|
println("T2:", d.cali.t2)
|
|
println("T3:", d.cali.t3)
|
|
|
|
println("P1:", d.cali.p1)
|
|
println("P2:", d.cali.p2)
|
|
println("P3:", d.cali.p3)
|
|
println("P4:", d.cali.p4)
|
|
println("P5:", d.cali.p5)
|
|
println("P6:", d.cali.p6)
|
|
println("P7:", d.cali.p7)
|
|
println("P8:", d.cali.p8)
|
|
println("P9:", d.cali.p9, "\n")
|
|
}
|
|
|
|
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
|
|
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
|
data, err := d.readData(REG_TEMP, 3)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
rawTemp := convert3Bytes(data[0], data[1], data[2])
|
|
|
|
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
|
|
// Temperature compensation
|
|
var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
|
|
var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
|
|
int32(d.cali.t3) >> 14
|
|
|
|
tFine := var1 + var2
|
|
|
|
// Convert from degrees to milli degrees by multiplying by 10.
|
|
// Will output 30250 milli degrees celsius for 30.25 degrees celsius
|
|
temperature = 10 * ((tFine*5 + 128) >> 8)
|
|
return
|
|
}
|
|
|
|
// ReadPressure returns the pressure in milli pascals (mPa).
|
|
func (d *Device) ReadPressure() (pressure int32, err error) {
|
|
// First 3 bytes are Pressure, last 3 bytes are Temperature
|
|
data, err := d.readData(REG_PRES, 6)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
rawTemp := convert3Bytes(data[3], data[4], data[5])
|
|
|
|
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
|
|
// Calculate tFine (temperature), used for the Pressure compensation
|
|
var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
|
|
var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
|
|
int32(d.cali.t3) >> 14
|
|
|
|
tFine := var1 + var2
|
|
|
|
rawPres := convert3Bytes(data[0], data[1], data[2])
|
|
|
|
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
|
|
// Pressure compensation
|
|
var1 = (tFine >> 1) - 64000
|
|
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * int32(d.cali.p6)
|
|
var2 = var2 + ((var1 * int32(d.cali.p5)) << 1)
|
|
var2 = (var2 >> 2) + (int32(d.cali.p4) << 16)
|
|
var1 = (((int32(d.cali.p3) * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) +
|
|
((int32(d.cali.p2) * var1) >> 1)) >> 18
|
|
var1 = ((32768 + var1) * int32(d.cali.p1)) >> 15
|
|
|
|
if var1 == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
p := uint32(((1048576 - rawPres) - (var2 >> 12)) * 3125)
|
|
if p < 0x80000000 {
|
|
p = (p << 1) / uint32(var1)
|
|
} else {
|
|
p = (p / uint32(var1)) * 2
|
|
}
|
|
|
|
var1 = (int32(d.cali.p9) * int32(((p>>3)*(p>>3))>>13)) >> 12
|
|
var2 = (int32(p>>2) * int32(d.cali.p8)) >> 13
|
|
|
|
return 1000 * (int32(p) + ((var1 + var2 + int32(d.cali.p7)) >> 4)), nil
|
|
}
|
|
|
|
// readData reads n number of bytes of the specified register
|
|
func (d *Device) readData(register int, n int) ([]byte, error) {
|
|
// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
|
|
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
|
|
if d.Mode != MODE_NORMAL {
|
|
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
|
|
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
|
|
}
|
|
|
|
// Check STATUS register, wait if data is not available yet
|
|
status := make([]byte, 1)
|
|
for d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]) {
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
|
|
// Read the requested register
|
|
data := make([]byte, n)
|
|
err := d.bus.ReadRegister(uint8(d.Address), uint8(register), data[:])
|
|
return data, err
|
|
}
|
|
|
|
// convert3Bytes converts three bytes to int32
|
|
func convert3Bytes(msb byte, b1 byte, lsb byte) int32 {
|
|
return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4)
|
|
}
|
|
|
|
// readUint converts two bytes to uint16
|
|
func readUint(msb byte, lsb byte) uint16 {
|
|
return (uint16(msb) << 8) | uint16(lsb)
|
|
}
|
|
|
|
// readUintLE converts two little endian bytes to uint16
|
|
func readUintLE(msb byte, lsb byte) uint16 {
|
|
temp := readUint(msb, lsb)
|
|
return (temp >> 8) | (temp << 8)
|
|
}
|
|
|
|
// readIntLE converts two little endian bytes to int16
|
|
func readIntLE(msb byte, lsb byte) int16 {
|
|
return int16(readUintLE(msb, lsb))
|
|
}
|