diff --git a/README.md b/README.md index 4a475d1..11fad6b 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ func main() { | Device Name | Interface Type | |----------|-------------| +| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C | | [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI | | [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C | | [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C | diff --git a/adxl345/adxl345.go b/adxl345/adxl345.go new file mode 100644 index 0000000..27373a7 --- /dev/null +++ b/adxl345/adxl345.go @@ -0,0 +1,191 @@ +// Package provides a driver for the digital accelerometer ADXL345 +// +// Datasheet EN: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf +// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf +package adxl345 + +import ( + "machine" +) + +type Range uint8 +type Rate uint8 + +// Internal structure for the power configuration +type powerCtl struct { + link uint8 + autoSleep uint8 + measure uint8 + sleep uint8 + wakeUp uint8 +} + +// Internal structure for the sensor's data format configuration +type dataFormat struct { + selfTest uint8 + spi uint8 + intInvert uint8 + fullRes uint8 + justify uint8 + sensorRange Range +} + +// Internal structure for the sampling rate configuration +type bwRate struct { + lowPower uint8 + rate Rate +} + +// Device wraps an I2C connection to a BMP180 device. +type Device struct { + bus machine.I2C + Address uint16 + powerCtl powerCtl + dataFormat dataFormat + bwRate bwRate + x, y, z int32 + rawX, rawY, rawZ int32 +} + +// New creates a new BMP180 connection. The I2C bus must already be +// configured. +// +// This function only creates the Device object, it does not touch the device. +func New(bus machine.I2C) Device { + return Device{ + bus: bus, + powerCtl: powerCtl{ + measure: 1, + }, + dataFormat: dataFormat{ + sensorRange: RANGE_2G, + }, + bwRate: bwRate{ + lowPower: 1, + rate: RATE_100HZ, + }, + Address: AddressLow, + } +} + +// Configure sets up the device for communication +func (d *Device) Configure() { + d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()}) + d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()}) + d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()}) +} + +// Halt stops the sensor, values will not updated +func (d *Device) Halt() { + d.powerCtl.measure = 0 + d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()}) +} + +// Restart makes reading the sensor working again after a halt +func (d *Device) Restart() { + d.powerCtl.measure = 1 + d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()}) +} + +// Acceleration returns the adjusted x, y and z axis in µG +func (d *Device) Acceleration() (x int32, y int32, z int32) { + return d.x, d.y, d.z +} + +// XYZ returns the raw x, y and z axis from the adxl345 +func (d *Device) RawXYZ() (x int32, y int32, z int32) { + return d.rawX, d.rawY, d.rawZ +} + +// Update reads the sensor values and stores them in a buffer +func (d *Device) Update() { + data := []byte{0, 0, 0, 0, 0, 0} + d.bus.ReadRegister(uint8(d.Address), REG_DATAX0, data) + + d.rawX = readIntLE(data[0], data[1]) + d.rawY = readIntLE(data[2], data[3]) + d.rawZ = readIntLE(data[4], data[5]) + + d.x = d.dataFormat.convertToIS(d.rawX) + d.y = d.dataFormat.convertToIS(d.rawY) + d.z = d.dataFormat.convertToIS(d.rawZ) +} + +// SetRate change the current rate of the sensor +func (d *Device) UseLowPower(power bool) { + if power { + d.bwRate.lowPower = 1 + } else { + d.bwRate.lowPower = 0 + } + d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()}) +} + +// SetRate change the current rate of the sensor +func (d *Device) SetRate(rate Rate) bool { + d.bwRate.rate = rate & 0x0F + d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()}) + return true +} + +// SetRange change the current range of the sensor +func (d *Device) SetRange(sensorRange Range) bool { + d.dataFormat.sensorRange = sensorRange & 0x03 + d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()}) + return true +} + +// convertToIS adjusts the raw values from the adxl345 with the range configuration +func (d *dataFormat) convertToIS(rawValue int32) int32 { + switch d.sensorRange { + case RANGE_2G: + return rawValue * 4 // rawValue * 2 * 1000 / 512 + case RANGE_4G: + return rawValue * 8 // rawValue * 4 * 1000 / 512 + case RANGE_8G: + return rawValue * 16 // rawValue * 8 * 1000 / 512 + case RANGE_16G: + return rawValue * 32 // rawValue * 16 * 1000 / 512 + default: + return 0 + } +} + +// toByte returns a byte from the powerCtl configuration +func (p *powerCtl) toByte() (bits uint8) { + bits = 0x00 + bits = bits | (p.link << 5) + bits = bits | (p.autoSleep << 4) + bits = bits | (p.measure << 3) + bits = bits | (p.sleep << 2) + bits = bits | p.wakeUp + + return bits +} + +// toByte returns a byte from the dataFormat configuration +func (d *dataFormat) toByte() (bits uint8) { + bits = 0x00 + bits = bits | (d.selfTest << 7) + bits = bits | (d.spi << 6) + bits = bits | (d.intInvert << 5) + bits = bits | (d.fullRes << 3) + bits = bits | (d.justify << 2) + bits = bits | uint8(d.sensorRange) + + return bits +} + +// toByte returns a byte from the bwRate configuration +func (b *bwRate) toByte() (bits uint8) { + bits = 0x00 + bits = bits | (b.lowPower << 4) + bits = bits | uint8(b.rate) + + return bits +} + +// readInt converts two bytes to int16 +func readIntLE(msb byte, lsb byte) int32 { + return int32(uint16(msb) | uint16(lsb)<<8) +} diff --git a/adxl345/registers.go b/adxl345/registers.go new file mode 100644 index 0000000..cbbb9fc --- /dev/null +++ b/adxl345/registers.go @@ -0,0 +1,61 @@ +package adxl345 + +const AddressLow = 0x53 +const AddressHigh = 0x1D + +const ( + // Data rate + RATE_3200HZ Rate = 0x0F // 3200 Hz + RATE_1600HZ Rate = 0x0E // 1600 Hz + RATE_800HZ Rate = 0x0D // 800 Hz + RATE_400HZ Rate = 0x0C // 400 Hz + RATE_200HZ Rate = 0x0B // 200 Hz + RATE_100HZ Rate = 0x0A // 100 Hz + RATE_50HZ Rate = 0x09 // 50 Hz + RATE_25HZ Rate = 0x08 // 25 Hz + RATE_12_5HZ Rate = 0x07 // 12.5 Hz + RATE_6_25HZ Rate = 0x06 // 6.25 Hz + RATE_3_13HZ Rate = 0x05 // 3.13 Hz + RATE_1_56HZ Rate = 0x04 // 1.56 Hz + RATE_0_78HZ Rate = 0x03 // 0.78 Hz + RATE_0_39HZ Rate = 0x02 // 0.39 Hz + RATE_0_20HZ Rate = 0x01 // 0.20 Hz + RATE_0_10HZ Rate = 0x00 // 0.10 Hz + + // Data range + RANGE_2G Range = 0x00 // +-2 g + RANGE_4G Range = 0x01 // +-4 g + RANGE_8G Range = 0x02 // +-8 g + RANGE_16G Range = 0x03 // +-16 g) + + REG_DEVID = 0x00 // R, 11100101, Device ID + REG_THRESH_TAP = 0x1D // R/W, 00000000, Tap threshold + REG_OFSX = 0x1E // R/W, 00000000, X-axis offset + REG_OFSY = 0x1F // R/W, 00000000, Y-axis offset + REG_OFSZ = 0x20 // R/W, 00000000, Z-axis offset + REG_DUR = 0x21 // R/W, 00000000, Tap duration + REG_LATENT = 0x22 // R/W, 00000000, Tap latency + REG_WINDOW = 0x23 // R/W, 00000000, Tap window + REG_THRESH_ACT = 0x24 // R/W, 00000000, Activity threshold + REG_THRESH_INACT = 0x25 // R/W, 00000000, Inactivity threshold + REG_TIME_INACT = 0x26 // R/W, 00000000, Inactivity time + REG_ACT_INACT_CTL = 0x27 // R/W, 00000000, Axis enable control for activity and inactiv ity detection + REG_THRESH_FF = 0x28 // R/W, 00000000, Free-fall threshold + REG_TIME_FF = 0x29 // R/W, 00000000, Free-fall time + REG_TAP_AXES = 0x2A // R/W, 00000000, Axis control for single tap/double tap + REG_ACT_TAP_STATUS = 0x2B // R, 00000000, Source of single tap/double tap + REG_BW_RATE = 0x2C // R/W, 00001010, Data rate and power mode control + REG_POWER_CTL = 0x2D // R/W, 00000000, Power-saving features control + REG_INT_ENABLE = 0x2E // R/W, 00000000, Interrupt enable control + REG_INT_MAP = 0x2F // R/W, 00000000, Interrupt mapping control + REG_INT_SOUCE = 0x30 // R, 00000010, Source of interrupts + REG_DATA_FORMAT = 0x31 // R/W, 00000000, Data format control + REG_DATAX0 = 0x32 // R, 00000000, X-Axis Data 0 + REG_DATAX1 = 0x33 // R, 00000000, X-Axis Data 1 + REG_DATAY0 = 0x34 // R, 00000000, Y-Axis Data 0 + REG_DATAY1 = 0x35 // R, 00000000, Y-Axis Data 1 + REG_DATAZ0 = 0x36 // R, 00000000, Z-Axis Data 0 + REG_DATAZ1 = 0x37 // R, 00000000, Z-Axis Data 1 + REG_FIFO_CTL = 0x38 // R/W, 00000000, FIFO control + REG_FIFO_STATUS = 0x39 // R, 00000000, FIFO status +) diff --git a/examples/adxl345/main.go b/examples/adxl345/main.go new file mode 100644 index 0000000..3c6061d --- /dev/null +++ b/examples/adxl345/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "machine" + "time" + + "github.com/tinygo-org/drivers/adxl345" +) + +func main() { + machine.I2C0.Configure(machine.I2CConfig{}) + sensor := adxl345.New(machine.I2C0) + sensor.Configure() + + println("ADXL345 starts") + for { + sensor.Update() + x, y, z := sensor.Acceleration() + println("X:", x, "Y:", y, "Z:", z) + + rx, ry, rz := sensor.RawXYZ() + println("X (raw):", rx, "Y (raw):", ry, "Z (raw):", rz) + + time.Sleep(100 * time.Millisecond) + } +}