mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-17 05:13:23 +00:00
Add Honeywell HSC TruStability SPI+I2C pressure sensor driver (#799)
* add honeywell pressure sensor * apply @aykevl suggestions
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/honeyhsc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Data taken from https://github.com/rodan/honeywell_hsc_ssc_i2c/blob/master/hsc_ssc_i2c.cpp
|
||||||
|
// these defaults are valid for the HSCMRNN030PA2A3 chip
|
||||||
|
const (
|
||||||
|
i2cAddress = 0x28
|
||||||
|
// 10%
|
||||||
|
outputMinimum = 0x666
|
||||||
|
// 90% of 2^14 - 1
|
||||||
|
outputMax = 0x399A
|
||||||
|
// min is 0 for sensors that give absolute values
|
||||||
|
pressureMin = 0
|
||||||
|
// 30psi (and we want results in millipascals)
|
||||||
|
// pressureMax = 206842.7
|
||||||
|
pressureMax = 206843 * 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
bus := machine.I2C0
|
||||||
|
err := bus.Configure(machine.I2CConfig{
|
||||||
|
Frequency: 400_000, // 100kHz minimum and 400kHz I2C maximum clock. 50 to 800 for SPI.
|
||||||
|
SDA: machine.I2C0_SDA_PIN,
|
||||||
|
SCL: machine.I2C0_SCL_PIN,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
sensor := honeyhsc.NewDevI2C(bus, i2cAddress, outputMinimum, outputMax, pressureMin, pressureMax)
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
const measuremask = drivers.Pressure | drivers.Temperature
|
||||||
|
err := sensor.Update(measuremask)
|
||||||
|
if err != nil {
|
||||||
|
println("error updating measurements:", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
P := sensor.Pressure()
|
||||||
|
T := sensor.Temperature()
|
||||||
|
println("pressure:", P, "temperature:", T)
|
||||||
|
}
|
||||||
|
}
|
||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
package honeyhsc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errSensorMissing = errors.New("hsc: not connected")
|
||||||
|
errDiagnostic = errors.New("hsc: diagnostic error")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
measuremask = drivers.Pressure | drivers.Temperature
|
||||||
|
statusMask = 0b1100_0000
|
||||||
|
statusOffset = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric
|
||||||
|
// analog or digital output for reading pressure over the specified full scale pressure span and temperature range.
|
||||||
|
type DevI2C struct {
|
||||||
|
bus drivers.I2C
|
||||||
|
dev
|
||||||
|
addr uint8
|
||||||
|
buf [6]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevI2C creates and returns a new DevI2C that communicates with an HSC device over the provided I2C bus.
|
||||||
|
// Parameters:
|
||||||
|
// - bus: the I2C bus to use.
|
||||||
|
// - addr: the 7-bit I2C address of the sensor.
|
||||||
|
// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model.
|
||||||
|
// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model.
|
||||||
|
//
|
||||||
|
// The returned DevI2C will use these calibration parameters to convert raw bridge counts to pressure.
|
||||||
|
func NewDevI2C(bus drivers.I2C, addr, outMin, outMax uint16, pMin, pMax int32) *DevI2C {
|
||||||
|
h := &DevI2C{
|
||||||
|
bus: bus,
|
||||||
|
addr: uint8(addr),
|
||||||
|
dev: dev{
|
||||||
|
cmin: outMin,
|
||||||
|
cmax: outMax,
|
||||||
|
pmin: pMin,
|
||||||
|
pmax: pMax,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the I2C-attached HSC device.
|
||||||
|
// It performs an Update internally to get the latest temperature value.
|
||||||
|
func (h *DevI2C) ReadTemperature() (int32, error) {
|
||||||
|
err := h.Update(drivers.Temperature)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return h.Temperature(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update reads both temperature and pressure data from the I2C-attached HSC device when
|
||||||
|
// the requested measurement mask includes pressure or temperature.
|
||||||
|
// If neither pressure nor temperature is requested, Update is a no-op.
|
||||||
|
func (d *DevI2C) Update(which drivers.Measurement) error {
|
||||||
|
// Update performs an I2C transaction to read 4 bytes, parses the status bits, 14-bit bridge data and
|
||||||
|
// temperature bits, and forwards them to the internal update routine. Any I2C transport error is returned,
|
||||||
|
// as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic).
|
||||||
|
if which&measuremask == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rbuf := d.buf[:4]
|
||||||
|
wbuf := d.buf[4:6]
|
||||||
|
const reg = 0
|
||||||
|
value := (d.addr << 1) | 1
|
||||||
|
wbuf[0] = reg
|
||||||
|
wbuf[1] = value
|
||||||
|
err := d.bus.Tx(uint16(d.addr), wbuf, rbuf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status := (rbuf[0] & statusMask) >> statusOffset
|
||||||
|
bridgeData := (uint16(rbuf[0]&^statusMask) << 8) | uint16(rbuf[1])
|
||||||
|
tempData := uint16(rbuf[2])<<8 | uint16(rbuf[3]&0xe0)>>5
|
||||||
|
return d.dev.update(status, bridgeData, tempData)
|
||||||
|
}
|
||||||
|
|
||||||
|
type pinout func(level bool)
|
||||||
|
|
||||||
|
// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric
|
||||||
|
// analog or digital output for reading pressure over the specified full scale pressure span and temperature range.
|
||||||
|
type DevSPI struct {
|
||||||
|
spi drivers.SPI
|
||||||
|
cs pinout
|
||||||
|
dev
|
||||||
|
buf [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevSPI creates and returns a new DevSPI that communicates with an HSC device over SPI.
|
||||||
|
// Parameters:
|
||||||
|
// - conn: the SPI connection to use.
|
||||||
|
// - cs: a chip-select function that drives the device select line low/high.
|
||||||
|
// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model.
|
||||||
|
// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model.
|
||||||
|
//
|
||||||
|
// The function returns the constructed DevSPI and an error value (currently always nil).
|
||||||
|
func NewDevSPI(conn drivers.SPI, cs pinout, outMin, outMax uint16, pMin, pMax int32) (*DevSPI, error) {
|
||||||
|
h := &DevSPI{
|
||||||
|
spi: conn,
|
||||||
|
cs: cs,
|
||||||
|
dev: dev{
|
||||||
|
cmin: outMin,
|
||||||
|
cmax: outMax,
|
||||||
|
pmin: pMin,
|
||||||
|
pmax: pMax,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the SPI-attached HSC device.
|
||||||
|
// It performs an Update internally to get the latest temperature value.
|
||||||
|
func (h *DevSPI) ReadTemperature() (int32, error) {
|
||||||
|
err := h.Update(drivers.Temperature)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return h.Temperature(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update reads pressure and temperature data from the SPI-attached HSC device when the requested measurement mask includes
|
||||||
|
// pressure or temperature. If neither pressure nor temperature is requested, Update is a no-op.
|
||||||
|
func (h *DevSPI) Update(which drivers.Measurement) error {
|
||||||
|
// It toggles the provided chip-select, performs an SPI transfer to read 4 bytes, parses the status bits,
|
||||||
|
// 14-bit bridge data and temperature bits, and forwards them to the internal update routine. Any SPI
|
||||||
|
// transport error is returned, as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic).
|
||||||
|
if which&measuremask == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
buf := &h.buf
|
||||||
|
h.cs(false)
|
||||||
|
err := h.spi.Tx(nil, buf[:4])
|
||||||
|
h.cs(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// First two bits are status bits.
|
||||||
|
status := (buf[0] & statusMask) >> statusOffset
|
||||||
|
bridgeData := (uint16(buf[0]&^statusMask) << 8) | uint16(buf[1])
|
||||||
|
|
||||||
|
tempData := uint16(buf[2])<<8 | uint16(buf[3]&0xe0)>>5
|
||||||
|
return h.dev.update(status, bridgeData, tempData)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dev struct {
|
||||||
|
pressure int32
|
||||||
|
temp int32
|
||||||
|
cmin, cmax uint16
|
||||||
|
pmin, pmax int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pressure returns the most recently computed pressure value in millipascals (mPa).
|
||||||
|
// The value is taken from the last successful Update.
|
||||||
|
func (d *dev) Pressure() int32 {
|
||||||
|
return d.pressure
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temperature returns the most recently read temperature value in milliKelvin (mC).
|
||||||
|
// The value is taken from the last successful Update.
|
||||||
|
func (d *dev) Temperature() int32 {
|
||||||
|
return d.temp + 273_150
|
||||||
|
}
|
||||||
|
|
||||||
|
// update interprets raw sensor fields (status, bridgeData, tempData) and updates the dev's stored
|
||||||
|
// pressure and temperature. It returns errSensorMissing when the temperature raw value indicates no sensor
|
||||||
|
// (tempData == math.MaxUint16), errDiagnostic when the status indicates a device diagnostic condition
|
||||||
|
// (status == 3), or nil on success. Pressure is computed with integer arithmetic using the configured
|
||||||
|
// cmin/cmax -> pmin/pmax linear mapping in order to avoid overflows.
|
||||||
|
func (d *dev) update(status uint8, bridgeData, tempData uint16) error {
|
||||||
|
if tempData == math.MaxUint16 {
|
||||||
|
return errSensorMissing
|
||||||
|
} else if status == 3 {
|
||||||
|
return errDiagnostic
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take care not to overflow here.
|
||||||
|
p := (int32(bridgeData)-int32(d.cmin))*(d.pmax-d.pmin)/int32(d.cmax-d.cmin) + d.pmin
|
||||||
|
d.temp = int32(tempData)
|
||||||
|
d.pressure = p
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user