mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-01 21:47:46 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d1b0c39cc | |||
| 6cf07aff4c | |||
| af33129f41 | |||
| 100aadf585 | |||
| 4a9667ffef | |||
| f96a70915e | |||
| 3e64e754a2 | |||
| 2c2da5c7bb | |||
| 14994a3f31 | |||
| 47dfeb9e94 | |||
| f20d1759d3 | |||
| 6b79a1b386 | |||
| 7dd1067cc1 | |||
| 4edb771c5b | |||
| e95cfe66c1 | |||
| 4edb58c0c5 | |||
| f384e2db48 | |||
| 7c96387845 | |||
| 182d4c6ebb | |||
| dc2de4f9ed | |||
| 36a12dd7a2 | |||
| 00992756eb | |||
| 715c33d4b0 | |||
| fd21e6ac9b | |||
| 485ed702c8 | |||
| cfa50fd3c2 | |||
| 5888bb2ded | |||
| a9b36f8bd4 | |||
| 1e4545828f | |||
| cfa5103969 | |||
| a45227590e | |||
| 4f789fb556 | |||
| 996f1b047f | |||
| eef03917ab | |||
| 31538f1b2f | |||
| 64029612e0 | |||
| e6f82fad2e | |||
| e20c6d05f8 | |||
| 9c29529cbb | |||
| f6d399ec08 |
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/tinygo-org/tinygo-dev
|
||||
container: ghcr.io/tinygo-org/tinygo-dev:latest
|
||||
steps:
|
||||
- name: Work around CVE-2022-24765
|
||||
# We're not on a multi-user machine, so this is safe.
|
||||
|
||||
@@ -1,3 +1,40 @@
|
||||
0.26.0
|
||||
---
|
||||
- **core**
|
||||
- i2c iface refactor: Resolve #559
|
||||
- fix uses of legacy i2c WriteRegister calls
|
||||
- add correct Tx implementation for mock I2C interfaces
|
||||
- bump golang.org/x/net version
|
||||
|
||||
- **new devices**
|
||||
- **bma42x**
|
||||
- add new BMA421/BMA425 driver
|
||||
- **ndir**
|
||||
- add Sandbox Electronics NDIR CO2 sensor driver (#580)
|
||||
- **mpu9150**
|
||||
- implement driver for Mpu9150 (#596)
|
||||
- **sht4x**
|
||||
- implement driver for sht4x (#597)
|
||||
- **pcf8523**
|
||||
- implement driver for pcf8523 (#599)
|
||||
|
||||
- **enhancements**
|
||||
- **ssd1306**
|
||||
- improve bus error handling
|
||||
|
||||
- **bugfixes**
|
||||
- **st7789**
|
||||
- fix scrolling when rotated by 180°
|
||||
- **st7789**
|
||||
- fix incorrect Rotation configuration
|
||||
- fix SetScrollArea
|
||||
- **ili9341**
|
||||
- fix SetScrollArea
|
||||
|
||||
- **build**
|
||||
- use latest tag of tinygo-dev container for running tests
|
||||
|
||||
|
||||
0.25.0
|
||||
---
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
[](https://pkg.go.dev/tinygo.org/x/drivers) [](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
|
||||
|
||||
|
||||
This package provides a collection of hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
|
||||
|
||||
For the complete list, please see:
|
||||
https://tinygo.org/docs/reference/devices/
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -50,11 +53,6 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Supported devices
|
||||
|
||||
There are currently 96 devices supported. For the complete list, please see:
|
||||
https://tinygo.org/docs/reference/devices/
|
||||
|
||||
## Contributing
|
||||
|
||||
Your contributions are welcome!
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// Package adafruit4650 implements a driver for the Adafruit FeatherWing OLED - 128x64 OLED display.
|
||||
// The display is backed itself by a SH1107 driver chip.
|
||||
//
|
||||
// Store: https://www.adafruit.com/product/4650
|
||||
//
|
||||
// Documentation: https://learn.adafruit.com/adafruit-128x64-oled-featherwing
|
||||
package adafruit4650
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
const DefaultAddress = 0x3c
|
||||
|
||||
const (
|
||||
commandSetLowColumn = 0x00
|
||||
commandSetHighColumn = 0x10
|
||||
commandSetPage = 0xb0
|
||||
)
|
||||
|
||||
const (
|
||||
width = 128
|
||||
height = 64
|
||||
)
|
||||
|
||||
// Device represents an Adafruit 4650 device
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
buffer []byte
|
||||
width int16
|
||||
height int16
|
||||
}
|
||||
|
||||
// New creates a new device, not configuring anything yet.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: DefaultAddress,
|
||||
width: width,
|
||||
height: height,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure initializes the display with default configuration
|
||||
func (d *Device) Configure() error {
|
||||
|
||||
bufferSize := d.width * d.height / 8
|
||||
d.buffer = make([]byte, bufferSize)
|
||||
|
||||
// This sequence is an amalgamation of the datasheet, official Arduino driver, CircuitPython driver and other drivers
|
||||
initSequence := []byte{
|
||||
0xae, // display off, sleep mode
|
||||
//0xd5, 0x41, // set display clock divider (from original datasheet)
|
||||
0xd5, 0x51, // set display clock divider (from Adafruit driver)
|
||||
0xd9, 0x22, // pre-charge/dis-charge period mode: 2 DCLKs/2 DCLKs (POR)
|
||||
0x20, // memory mode
|
||||
0x81, 0x4f, // contrast setting = 0x4f
|
||||
0xad, 0x8a, // set dc/dc pump
|
||||
0xa0, // segment remap, flip-x
|
||||
0xc0, // common output scan direction
|
||||
0xdc, 0x00, // set display start line 0 (POR=0)
|
||||
0xa8, 0x3f, // multiplex ratio, height - 1 = 0x3f
|
||||
0xd3, 0x60, // set display offset mode = 0x60
|
||||
0xdb, 0x35, // VCOM deselect level = 0.770 (POR)
|
||||
0xa4, // entire display off, retain RAM, normal status (POR)
|
||||
0xa6, // normal (not reversed) display
|
||||
0xaf, // display on
|
||||
}
|
||||
|
||||
err := d.writeCommands(initSequence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// recommended in the datasheet, same in other drivers
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearDisplay clears the image buffer as well as the actual display
|
||||
func (d *Device) ClearDisplay() error {
|
||||
d.ClearBuffer()
|
||||
return d.Display()
|
||||
}
|
||||
|
||||
// ClearBuffer clears the buffer
|
||||
func (d *Device) ClearBuffer() {
|
||||
bzero(d.buffer)
|
||||
}
|
||||
|
||||
// SetPixel modifies the internal buffer. Since this display has a bit-depth of 1 bit any non-zero
|
||||
// color component will be treated as 'on', otherwise 'off'.
|
||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
if x < 0 || x >= d.width || y < 0 || y >= d.height {
|
||||
return
|
||||
}
|
||||
|
||||
// RAM layout
|
||||
// *-----> y
|
||||
// |
|
||||
// x| col0 col1 ... col63
|
||||
// v p0 a0 b0 ..
|
||||
// a1 b1 ..
|
||||
// .. .. ..
|
||||
// a7 b7 ..
|
||||
// p1 a0 b0
|
||||
// a1 b1
|
||||
//
|
||||
|
||||
//flip y - so the display orientation matches the silk screen labeling etc.
|
||||
y = d.height - y - 1
|
||||
|
||||
page := x / 8
|
||||
bytesPerPage := d.height
|
||||
byteIndex := y + bytesPerPage*page
|
||||
bit := x % 8
|
||||
if (c.R | c.G | c.B) != 0 {
|
||||
d.buffer[byteIndex] |= 1 << uint8(bit)
|
||||
} else {
|
||||
d.buffer[byteIndex] &^= 1 << uint8(bit)
|
||||
}
|
||||
}
|
||||
|
||||
// Display sends the whole buffer to the screen
|
||||
func (d *Device) Display() error {
|
||||
|
||||
bytesPerPage := d.height
|
||||
|
||||
pages := (d.width + 7) / 8
|
||||
for page := int16(0); page < pages; page++ {
|
||||
|
||||
err := d.setRAMPosition(uint8(page), 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset := page * bytesPerPage
|
||||
err = d.writeRAM(d.buffer[offset : offset+bytesPerPage])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setRAMPosition updates the device's current page and column position
|
||||
func (d *Device) setRAMPosition(page uint8, column uint8) error {
|
||||
if page > 15 {
|
||||
panic("page out of bounds")
|
||||
}
|
||||
if column > 127 {
|
||||
panic("column out of bounds")
|
||||
}
|
||||
setPage := commandSetPage | (page & 0xF)
|
||||
|
||||
lo := column & 0xF
|
||||
setLowColumn := commandSetLowColumn | lo
|
||||
|
||||
hi := (column >> 4) & 0x7
|
||||
setHighColumn := commandSetHighColumn | hi
|
||||
|
||||
cmds := []byte{
|
||||
setPage,
|
||||
setLowColumn,
|
||||
setHighColumn,
|
||||
}
|
||||
|
||||
return d.writeCommands(cmds)
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
func (d *Device) Size() (w, h int16) {
|
||||
return d.width, d.height
|
||||
}
|
||||
|
||||
func (d *Device) writeCommands(commands []byte) error {
|
||||
onlyCommandsFollowing := byte(0x00)
|
||||
return d.bus.Tx(uint16(d.Address), append([]byte{onlyCommandsFollowing}, commands...), nil)
|
||||
}
|
||||
|
||||
func (d *Device) writeRAM(data []byte) error {
|
||||
onlyRAMFollowing := byte(0x40)
|
||||
return d.bus.Tx(uint16(d.Address), append([]byte{onlyRAMFollowing}, data...), nil)
|
||||
}
|
||||
|
||||
func bzero(buf []byte) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package adafruit4650
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/tinyfont"
|
||||
"tinygo.org/x/tinyfont/freemono"
|
||||
)
|
||||
|
||||
//go:embed expected_hello_world.png
|
||||
var expectedHelloWorld []byte
|
||||
|
||||
// mockBus mocks a fake i2c device adafruit4650 display.
|
||||
// The memory layout assumes that clients set up the device in a particular way and always send complete
|
||||
// pages to the device buffer.
|
||||
type mockBus struct {
|
||||
img draw.Image
|
||||
line int
|
||||
addr uint8
|
||||
currentPage int
|
||||
currentColumn int
|
||||
}
|
||||
|
||||
func (m *mockBus) Tx(addr uint16, w, r []byte) error {
|
||||
if addr != uint16(m.addr) {
|
||||
panic("unexpected address")
|
||||
}
|
||||
if r != nil {
|
||||
panic("mock does not support reads")
|
||||
}
|
||||
|
||||
if w[0] == 0x00 {
|
||||
if w[1]&0xf0 == 0xb0 {
|
||||
m.currentPage = int(w[1] & 0x0f)
|
||||
|
||||
lo := w[2] & 0x0f
|
||||
hi := w[2] & 0x07
|
||||
m.currentColumn = int(hi<<4 | lo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if w[0] != 0x40 {
|
||||
panic("unexpected first byte: " + hex.EncodeToString(w[0:1]))
|
||||
}
|
||||
|
||||
return m.writeRAM(w[1:])
|
||||
}
|
||||
|
||||
func newMock() *mockBus {
|
||||
|
||||
m := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
return &mockBus{img: m, addr: DefaultAddress, currentPage: -1, currentColumn: -1}
|
||||
}
|
||||
|
||||
func (m *mockBus) writeRAM(data []byte) error {
|
||||
|
||||
// RAM layout
|
||||
// *-----> y
|
||||
// |
|
||||
// x| col0 col1 ... col63
|
||||
// v p0 a0 b0 ..
|
||||
// a1 b1 ..
|
||||
// .. .. ..
|
||||
// a7 b7 ..
|
||||
// p1 a0 b0
|
||||
// a1 b1
|
||||
//
|
||||
|
||||
fmt.Printf("writing page %d\n", m.currentPage)
|
||||
// assuming entire pages will be written
|
||||
for x := 0; x < 8; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
|
||||
col := data[y]
|
||||
|
||||
c := color.Black
|
||||
if col&(1<<x) != 0 {
|
||||
c = color.White
|
||||
}
|
||||
|
||||
m.img.Set(x+m.currentPage*8, height-y-1, c)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockBus) toImage() *image.RGBA {
|
||||
|
||||
container := image.NewRGBA(m.img.Bounds().Inset(-1))
|
||||
draw.Draw(container, container.Bounds(), image.NewUniform(color.RGBA{G: 255, A: 255}), image.Point{}, draw.Over)
|
||||
draw.Draw(container, m.img.Bounds(), m.img, image.Point{}, draw.Over)
|
||||
return container
|
||||
}
|
||||
|
||||
func TestDevice_Display(t *testing.T) {
|
||||
|
||||
bus := newMock()
|
||||
dev := New(bus)
|
||||
|
||||
dev.Configure()
|
||||
|
||||
drawPlus(&dev)
|
||||
drawHellowWorld(&dev)
|
||||
|
||||
//when
|
||||
dev.Display()
|
||||
|
||||
//then
|
||||
actual := bus.toImage()
|
||||
|
||||
expected, err := png.Decode(bytes.NewReader(expectedHelloWorld))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
assertEqualImages(t, actual, expected)
|
||||
}
|
||||
|
||||
func drawPlus(d drivers.Displayer) {
|
||||
for i := int16(0); i < 128; i++ {
|
||||
d.SetPixel(i, 32, color.RGBA{R: 1})
|
||||
}
|
||||
for i := int16(0); i < 64; i++ {
|
||||
d.SetPixel(64, i, color.RGBA{R: 1})
|
||||
}
|
||||
}
|
||||
|
||||
func drawHellowWorld(d drivers.Displayer) {
|
||||
tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
|
||||
}
|
||||
|
||||
func assertEqualImages(t testing.TB, actual, expected image.Image) {
|
||||
|
||||
if actual.Bounds().Dx() != expected.Bounds().Dx() || actual.Bounds().Dy() != expected.Bounds().Dy() {
|
||||
f := writeImage(actual)
|
||||
t.Fatalf("differing size: was %v, expected %v, saved actual to %s", actual.Bounds(), expected.Bounds(), f)
|
||||
}
|
||||
|
||||
bb := expected.Bounds()
|
||||
for x := bb.Min.X; x < bb.Max.X; x++ {
|
||||
for y := bb.Min.Y; y < bb.Max.Y; y++ {
|
||||
actualBB := actual.Bounds()
|
||||
if actual.At(x+actualBB.Min.X, y+actualBB.Min.Y) != expected.At(x, y) {
|
||||
f := writeImage(actual)
|
||||
t.Fatalf("different pixel at %d/%d: %v != %v, saved actual at %s", x, y, actual.At(x, y), expected.At(x, y), f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeImage(img image.Image) string {
|
||||
|
||||
fn := fmt.Sprintf("%d.png", time.Now().Unix())
|
||||
f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = png.Encode(f, img)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fn
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 449 B |
+4
-3
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
@@ -54,7 +55,7 @@ func (d *Device) Configure() (err error) {
|
||||
// Connected returns whether sensor has been found.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), RegID, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), RegID, data)
|
||||
return data[0]&0xF8 == 0xC8
|
||||
}
|
||||
|
||||
@@ -81,11 +82,11 @@ func (d *Device) writeByte(reg uint8, data byte) {
|
||||
}
|
||||
|
||||
func (d *Device) readByte(reg uint8) byte {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf)
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
|
||||
return d.buf[0]
|
||||
}
|
||||
|
||||
func (d *Device) readUint16(reg uint8) uint16 {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf)
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
|
||||
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
|
||||
}
|
||||
|
||||
+13
-10
@@ -5,7 +5,10 @@
|
||||
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
|
||||
package adxl345 // import "tinygo.org/x/drivers/adxl345"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Range uint8
|
||||
type Rate uint8
|
||||
@@ -68,21 +71,21 @@ func New(bus drivers.I2C) Device {
|
||||
|
||||
// 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()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
legacy.WriteRegister(d.bus, 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()})
|
||||
legacy.WriteRegister(d.bus, 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()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
@@ -103,7 +106,7 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
// from the adxl345.
|
||||
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
|
||||
data := []byte{0, 0, 0, 0, 0, 0}
|
||||
d.bus.ReadRegister(uint8(d.Address), REG_DATAX0, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
|
||||
|
||||
x = readIntLE(data[0], data[1])
|
||||
y = readIntLE(data[2], data[3])
|
||||
@@ -119,20 +122,20 @@ func (d *Device) UseLowPower(power bool) {
|
||||
} else {
|
||||
d.bwRate.lowPower = 0
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
|
||||
legacy.WriteRegister(d.bus, 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()})
|
||||
legacy.WriteRegister(d.bus, 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()})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+17
-16
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a AMG88xx device.
|
||||
@@ -48,7 +49,7 @@ func (d *Device) Configure(cfg Config) {
|
||||
|
||||
// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
|
||||
func (d *Device) ReadPixels(buffer *[64]int16) {
|
||||
d.bus.ReadRegister(uint8(d.Address), PIXEL_OFFSET, d.data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), PIXEL_OFFSET, d.data)
|
||||
for i := 0; i < 64; i++ {
|
||||
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
|
||||
if (buffer[i] & (1 << 11)) > 0 { // temperature negative
|
||||
@@ -61,17 +62,17 @@ func (d *Device) ReadPixels(buffer *[64]int16) {
|
||||
|
||||
// SetPCTL sets the PCTL
|
||||
func (d *Device) SetPCTL(pctl uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), PCTL, []byte{pctl})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), PCTL, []byte{pctl})
|
||||
}
|
||||
|
||||
// SetReset sets the reset value
|
||||
func (d *Device) SetReset(rst uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), RST, []byte{rst})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), RST, []byte{rst})
|
||||
}
|
||||
|
||||
// SetFrameRate configures the frame rate
|
||||
func (d *Device) SetFrameRate(framerate uint8) {
|
||||
d.bus.WriteRegister(uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), FPSC, []byte{framerate & 0x01})
|
||||
}
|
||||
|
||||
// SetMovingAverageMode sets the moving average mode
|
||||
@@ -80,7 +81,7 @@ func (d *Device) SetMovingAverageMode(mode bool) {
|
||||
if mode {
|
||||
value = 1
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), AVE, []byte{value << 5})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), AVE, []byte{value << 5})
|
||||
}
|
||||
|
||||
// SetInterruptLevels sets the interrupt levels
|
||||
@@ -97,8 +98,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
||||
if high > 4095 {
|
||||
high = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
|
||||
|
||||
low = low / PIXEL_TEMP_CONVERSION
|
||||
if low < -4095 {
|
||||
@@ -107,8 +108,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
||||
if low > 4095 {
|
||||
low = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
|
||||
|
||||
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
|
||||
if hysteresis < -4095 {
|
||||
@@ -117,32 +118,32 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
|
||||
if hysteresis > 4095 {
|
||||
hysteresis = 4095
|
||||
}
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
||||
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
|
||||
}
|
||||
|
||||
// EnableInterrupt enables the interrupt pin on the device
|
||||
func (d *Device) EnableInterrupt() {
|
||||
d.interruptEnable = 1
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// DisableInterrupt disables the interrupt pin on the device
|
||||
func (d *Device) DisableInterrupt() {
|
||||
d.interruptEnable = 0
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// SetInterruptMode sets the interrupt mode
|
||||
func (d *Device) SetInterruptMode(mode InterruptMode) {
|
||||
d.interruptMode = mode
|
||||
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
|
||||
}
|
||||
|
||||
// GetInterrupt reads the state of the triggered interrupts
|
||||
func (d *Device) GetInterrupt() []uint8 {
|
||||
data := make([]uint8, 8)
|
||||
d.bus.ReadRegister(uint8(d.Address), INT_OFFSET, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), INT_OFFSET, data)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -154,6 +155,6 @@ func (d *Device) ClearInterrupt() {
|
||||
// ReadThermistor reads the onboard thermistor
|
||||
func (d *Device) ReadThermistor() int16 {
|
||||
data := make([]uint8, 2)
|
||||
d.bus.ReadRegister(uint8(d.Address), TTHL, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), TTHL, data)
|
||||
return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
|
||||
}
|
||||
|
||||
+26
-25
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a APDS-9960 device.
|
||||
@@ -68,7 +69,7 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_ID_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data)
|
||||
return data[0] == 0xAB
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ func (d *Device) GetMode() uint8 {
|
||||
// DisableAll turns off the device and all functions
|
||||
func (d *Device) DisableAll() {
|
||||
d.enable(enableConfig{})
|
||||
d.bus.WriteRegister(d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00})
|
||||
d.mode = MODE_NONE
|
||||
d.gesture.detected = GESTURE_NONE
|
||||
}
|
||||
@@ -88,13 +89,13 @@ func (d *Device) DisableAll() {
|
||||
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetProximityPulse(length, count uint8) {
|
||||
d.bus.WriteRegister(d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
}
|
||||
|
||||
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
|
||||
// default: 16, 64
|
||||
func (d *Device) SetGesturePulse(length, count uint8) {
|
||||
d.bus.WriteRegister(d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
|
||||
}
|
||||
|
||||
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
|
||||
@@ -103,14 +104,14 @@ func (d *Device) SetADCIntegrationCycles(cycles uint16) {
|
||||
if cycles > 256 {
|
||||
cycles = 256
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
|
||||
}
|
||||
|
||||
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
|
||||
// default: 1, 1, 4
|
||||
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
|
||||
d.bus.WriteRegister(d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
||||
d.bus.WriteRegister(d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
|
||||
}
|
||||
|
||||
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
|
||||
@@ -127,7 +128,7 @@ func (d *Device) LEDBoost(percent uint16) {
|
||||
case 300:
|
||||
v = 3
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
|
||||
}
|
||||
|
||||
// Setthreshold sets threshold (0~255) for detecting gestures
|
||||
@@ -168,7 +169,7 @@ func (d *Device) ReadProximity() (proximity int32) {
|
||||
return 0
|
||||
}
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_PDATA_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data)
|
||||
return 255 - int32(data[0])
|
||||
}
|
||||
|
||||
@@ -195,14 +196,14 @@ func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
|
||||
return
|
||||
}
|
||||
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_CDATAL_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_CDATAH_REG, data[1:2])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_RDATAL_REG, data[2:3])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_RDATAH_REG, data[3:4])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GDATAL_REG, data[4:5])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GDATAH_REG, data[5:6])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_BDATAL_REG, data[6:7])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_BDATAH_REG, data[7:])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:])
|
||||
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
|
||||
r = int32(uint16(data[3])<<8 | uint16(data[2]))
|
||||
g = int32(uint16(data[5])<<8 | uint16(data[4]))
|
||||
@@ -234,13 +235,13 @@ func (d *Device) GestureAvailable() bool {
|
||||
data := []byte{0, 0, 0, 0}
|
||||
|
||||
// check GVALID
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GSTATUS_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1])
|
||||
if data[0]&0x01 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// get number of data sets available in FIFO
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFLVL_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1])
|
||||
availableDataSets := data[0]
|
||||
if availableDataSets == 0 {
|
||||
return false
|
||||
@@ -249,10 +250,10 @@ func (d *Device) GestureAvailable() bool {
|
||||
// read up, down, left and right proximity data from FIFO
|
||||
var dataSets [32][4]uint8
|
||||
for i := uint8(0); i < availableDataSets; i++ {
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
||||
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3])
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4])
|
||||
for j := uint8(0); j < 4; j++ {
|
||||
dataSets[i][j] = data[j]
|
||||
}
|
||||
@@ -385,7 +386,7 @@ func (d *Device) enable(cfg enableConfig) {
|
||||
}
|
||||
|
||||
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
|
||||
d.bus.WriteRegister(d.Address, APDS9960_ENABLE_REG, data)
|
||||
legacy.WriteRegister(d.bus, d.Address, APDS9960_ENABLE_REG, data)
|
||||
|
||||
if cfg.PON {
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
@@ -394,7 +395,7 @@ func (d *Device) enable(cfg enableConfig) {
|
||||
|
||||
func (d *Device) readStatus(param string) bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, APDS9960_STATUS_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
|
||||
|
||||
switch param {
|
||||
case "CPSAT":
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// registerAttributes is a bitfield of attributes for a register
|
||||
@@ -94,7 +95,7 @@ func (r *i2cRegister) readShiftAndMask(bus drivers.I2C, deviceAddress uint8, shi
|
||||
buf = buffer[:]
|
||||
}
|
||||
// Read the host register over I2C
|
||||
err := bus.ReadRegister(deviceAddress, r.host.address, buf)
|
||||
err := legacy.ReadRegister(bus, deviceAddress, r.host.address, buf)
|
||||
if nil != err {
|
||||
return 0, err
|
||||
}
|
||||
@@ -158,7 +159,7 @@ func (r *i2cRegister) write(bus drivers.I2C, deviceAddress uint8, value uint16)
|
||||
}
|
||||
|
||||
// Write the register from the buffer over I2C
|
||||
err := bus.WriteRegister(deviceAddress, r.host.address, buf)
|
||||
err := legacy.WriteRegister(bus, deviceAddress, r.host.address, buf)
|
||||
// after successful I2C write, cache this value if the host register (if also readable)
|
||||
// Note we cache the entire buffer without applying shift/mask
|
||||
if nil == err && r.host.attributes®_read != 0 {
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a DS3231 device.
|
||||
// Device wraps an I2C connection to an AT24CX device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ package axp192 // import "tinygo.org/x/drivers/axp192"
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Error uint8
|
||||
@@ -248,10 +249,10 @@ func (d *Device) SetLDOEnable(number uint8, state bool) {
|
||||
}
|
||||
|
||||
func (d *Device) write1Byte(reg, data uint8) {
|
||||
d.bus.WriteRegister(d.Address, reg, []byte{data})
|
||||
legacy.WriteRegister(d.bus, d.Address, reg, []byte{data})
|
||||
}
|
||||
|
||||
func (d *Device) read8bit(reg uint8) uint8 {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf[:1])
|
||||
return d.buf[0]
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,352 @@
|
||||
// Package bma42x provides a driver for the BMA421 and BMA425 accelerometer
|
||||
// chips.
|
||||
//
|
||||
// Here is a reasonably good datasheet:
|
||||
// https://datasheet.lcsc.com/lcsc/1912111437_Bosch-Sensortec-BMA425_C437656.pdf
|
||||
//
|
||||
// This driver was originally written for the PineTime, using the datasheet as a
|
||||
// guide. There is an open source C driver provided by Bosch, but unfortunately
|
||||
// it needs some small modifications to work with other chips (most importantly,
|
||||
// the "config file").
|
||||
// The InfiniTime and Wasp-OS drivers for this accelerometer have also been used
|
||||
// to figure out some driver details (especially step counting).
|
||||
package bma42x
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Driver for BMA421 and BMA425:
|
||||
// BMA421: https://files.pine64.org/doc/datasheet/pinetime/BST-BMA421-FL000.pdf
|
||||
// BMA425: https://datasheet.lcsc.com/lcsc/1912111437_Bosch-Sensortec-BMA425_C437656.pdf
|
||||
|
||||
// This is the BMA421 firmware from the Wasp-OS project.
|
||||
// It is identical to the so-called BMA423 firmware in InfiniTime, which I
|
||||
// suspect to be actually a BMA421 firmware. I don't know where this firmware
|
||||
// comes from or what the licensing status is.
|
||||
// It has the FEATURES_IN command prepended, so that it can be written directly
|
||||
// using I2C.Tx.
|
||||
// Source: https://github.com/wasp-os/bma42x-upy/blob/master/BMA42X-Sensor-API/bma421.h
|
||||
//
|
||||
//go:embed bma421-config-waspos.bin
|
||||
var bma421Firmware string
|
||||
|
||||
// Same as the BMA421 firmware, but for the BMA425.
|
||||
// Source: https://github.com/wasp-os/bma42x-upy/blob/master/BMA42X-Sensor-API/bma425.h
|
||||
//
|
||||
//go:embed bma425-config-waspos.bin
|
||||
var bma425Firmware string
|
||||
|
||||
var (
|
||||
errUnknownDevice = errors.New("bma42x: unknown device")
|
||||
errUnsupportedDevice = errors.New("bma42x: device not part of config")
|
||||
errConfigMismatch = errors.New("bma42x: config mismatch")
|
||||
errTimeout = errors.New("bma42x: timeout")
|
||||
errInitFailed = errors.New("bma42x: failed to initialize")
|
||||
)
|
||||
|
||||
const Address = 0x18 // BMA421/BMA425 address
|
||||
|
||||
type DeviceType uint8
|
||||
|
||||
const (
|
||||
DeviceBMA421 DeviceType = 1 << iota
|
||||
DeviceBMA425
|
||||
|
||||
AnyDevice = DeviceBMA421 | DeviceBMA425
|
||||
noDevice DeviceType = 0
|
||||
)
|
||||
|
||||
// Features to enable while configuring the accelerometer.
|
||||
type Features uint8
|
||||
|
||||
const (
|
||||
FeatureStepCounting = 1 << iota
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// Which devices to support (OR the device types together as needed).
|
||||
Device DeviceType
|
||||
|
||||
// Which features to enable. With Features == 0, only the accelerometer will
|
||||
// be enabled.
|
||||
Features Features
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
address uint8
|
||||
accelData [6]byte
|
||||
combinedTempSteps [5]uint8 // [0:3] steps, [4] temperature
|
||||
dataBuf [2]byte
|
||||
}
|
||||
|
||||
func NewI2C(i2c drivers.I2C, address uint8) *Device {
|
||||
return &Device{
|
||||
bus: i2c,
|
||||
address: address,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) Connected() bool {
|
||||
val, err := d.read1(_CHIP_ID)
|
||||
return err == nil && identifyChip(val) != noDevice
|
||||
}
|
||||
|
||||
func (d *Device) Configure(config Config) error {
|
||||
if config.Device == 0 {
|
||||
config.Device = AnyDevice
|
||||
}
|
||||
|
||||
// Check chip ID, to check the connection and to determine which BMA42x
|
||||
// device we're dealing with.
|
||||
chipID, err := d.read1(_CHIP_ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Determine which firmware (config file?) we'll be using.
|
||||
// There is an extra check for the device before using the given firmware.
|
||||
// This check will typically be optimized away if the given device is not
|
||||
// configured, so that the firmware (which is 6kB in size!) won't be linked
|
||||
// into the binary.
|
||||
var firmware string
|
||||
switch identifyChip(chipID) {
|
||||
case DeviceBMA421:
|
||||
if config.Device&DeviceBMA421 == 0 {
|
||||
return errUnsupportedDevice
|
||||
}
|
||||
firmware = bma421Firmware
|
||||
case DeviceBMA425:
|
||||
if config.Device&DeviceBMA425 == 0 {
|
||||
return errUnsupportedDevice
|
||||
}
|
||||
firmware = bma425Firmware
|
||||
default:
|
||||
return errUnknownDevice
|
||||
}
|
||||
|
||||
// Reset the chip, to be able to initialize it properly.
|
||||
// The datasheet says a delay is needed after a SoftReset, but it doesn't
|
||||
// say how long this delay should be. The bma423 driver however uses a 200ms
|
||||
// delay, so that's what we'll be using.
|
||||
err = d.write1(_CMD, cmdSoftReset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Disable power saving.
|
||||
err = d.write1(_PWR_CONF, 0x00)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(450 * time.Microsecond)
|
||||
|
||||
// Start initialization (because the datasheet says so).
|
||||
err = d.write1(_INIT_CTRL, 0x00)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write "config file" (actually a firmware, I think) to the chip.
|
||||
// To do this, unsafely cast the string to a byte slice to avoid putting it
|
||||
// in RAM. This is safe in this case because Tx won't write to the 'w'
|
||||
// slice.
|
||||
err = d.bus.Tx(uint16(d.address), unsafeStringToSlice(firmware), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the config data back.
|
||||
// We don't do that, as it slows down configuration and it probably isn't
|
||||
// _really_ necessary with a reasonably stable I2C bus.
|
||||
if false {
|
||||
data := make([]byte, len(firmware)-1)
|
||||
err = d.readn(_FEATURES_IN, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, c := range data {
|
||||
if firmware[i+1] != c {
|
||||
return errConfigMismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable sensors.
|
||||
err = d.write1(_INIT_CTRL, 0x01)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait until the device is initialized.
|
||||
start := time.Now()
|
||||
status := uint8(0) // busy
|
||||
for status == 0 {
|
||||
status, err = d.read1(_INTERNAL_STATUS)
|
||||
if err != nil {
|
||||
return err // I2C bus error.
|
||||
}
|
||||
if status > 1 {
|
||||
// Expected either 0 ("not_init") or 1 ("init_ok").
|
||||
return errInitFailed
|
||||
}
|
||||
if time.Since(start) >= 150*time.Millisecond {
|
||||
// The datasheet says initialization should not take longer than
|
||||
return errTimeout
|
||||
}
|
||||
// Don't bother the chip all the time while it's initializing.
|
||||
time.Sleep(50 * time.Microsecond)
|
||||
}
|
||||
|
||||
if config.Features&FeatureStepCounting != 0 {
|
||||
// Enable step counter.
|
||||
// TODO: support step counter parameters.
|
||||
var buf [71]byte
|
||||
buf[0] = _FEATURES_IN // prefix buf with the command
|
||||
data := buf[1:]
|
||||
err = d.readn(_FEATURES_IN, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data[0x3A+1] |= 0x10 // enable step counting by setting a magical bit
|
||||
err = d.bus.Tx(uint16(d.address), buf[:], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the accelerometer.
|
||||
err = d.write1(_PWR_CTRL, 0x04)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Configure accelerometer for low power usage:
|
||||
// acc_perf_mode=0 (power saving enabled)
|
||||
// acc_bwp=osr4_avg1 (no averaging)
|
||||
// acc_odr=50Hz (50Hz sampling interval, enough for the step counter)
|
||||
const accelConf = 0x00<<7 | 0x00<<4 | 0x07<<0
|
||||
err = d.write1(_ACC_CONF, accelConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reduce current consumption.
|
||||
// With power saving enabled (and the above ACC_CONF) the chip consumes only
|
||||
// 14µA.
|
||||
err = d.write1(_PWR_CONF, 0x03)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) Update(which drivers.Measurement) error {
|
||||
// TODO: combine temperature and step counter into a single read.
|
||||
if which&drivers.Temperature != 0 {
|
||||
val, err := d.read1(_TEMPERATURE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.combinedTempSteps[4] = val
|
||||
}
|
||||
if which&drivers.Acceleration != 0 {
|
||||
// The acceleration data is stored in DATA8 through DATA13 as 3 12-bit
|
||||
// values.
|
||||
err := d.readn(_DATA_8, d.accelData[:]) // ACC_X(LSB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.readn(_STEP_COUNTER_0, d.combinedTempSteps[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Temperature returns the last read temperature in celsius milli degrees (1°C
|
||||
// is 1000).
|
||||
func (d *Device) Temperature() int32 {
|
||||
// The temperature value is a two's complement number (meaning: signed) in
|
||||
// units of 1 kelvin, with 0 being 23°C.
|
||||
return (int32(int8(d.combinedTempSteps[4])) + 23) * 1000
|
||||
}
|
||||
|
||||
// Acceleration returns the last read acceleration in µg (micro-gravity).
|
||||
// When one of the axes is pointing straight to Earth and the sensor is not
|
||||
// moving the returned value will be around 1000000 or -1000000.
|
||||
func (d *Device) Acceleration() (x, y, z int32) {
|
||||
// Combine raw data from d.accelData (stored as 12-bit signed values) into a
|
||||
// number (0..4095):
|
||||
x = int32(d.accelData[0])>>4 | int32(d.accelData[1])<<4
|
||||
y = int32(d.accelData[2])>>4 | int32(d.accelData[3])<<4
|
||||
z = int32(d.accelData[4])>>4 | int32(d.accelData[5])<<4
|
||||
// Sign extend this number to -2048..2047:
|
||||
x = (x << 20) >> 20
|
||||
y = (y << 20) >> 20
|
||||
z = (z << 20) >> 20
|
||||
// Scale from -512..511 to -1000_000..998_046.
|
||||
// Or, at the maximum range (4g), from -2048..2047 to -2000_000..3998_046.
|
||||
// The formula derived as follows (where 512 is the expected value at 1g):
|
||||
// x = x * 1000_000 / 512
|
||||
// x = x * (1000_000/64) / (512/64)
|
||||
// x = x * 15625 / 8
|
||||
x = x * 15625 / 8
|
||||
y = y * 15625 / 8
|
||||
z = z * 15625 / 8
|
||||
return
|
||||
}
|
||||
|
||||
// Steps returns the number of steps counted since the BMA42x sensor was
|
||||
// initialized.
|
||||
func (d *Device) Steps() (steps uint32) {
|
||||
steps |= uint32(d.combinedTempSteps[0]) << 0
|
||||
steps |= uint32(d.combinedTempSteps[1]) << 8
|
||||
steps |= uint32(d.combinedTempSteps[2]) << 16
|
||||
steps |= uint32(d.combinedTempSteps[3]) << 24
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Device) read1(register uint8) (uint8, error) {
|
||||
d.dataBuf[0] = register
|
||||
err := d.bus.Tx(uint16(d.address), d.dataBuf[:1], d.dataBuf[1:2])
|
||||
return d.dataBuf[1], err
|
||||
}
|
||||
|
||||
func (d *Device) readn(register uint8, data []byte) error {
|
||||
d.dataBuf[0] = register
|
||||
return d.bus.Tx(uint16(d.address), d.dataBuf[:1], data)
|
||||
}
|
||||
|
||||
func (d *Device) write1(register uint8, data uint8) error {
|
||||
d.dataBuf[0] = register
|
||||
d.dataBuf[1] = data
|
||||
return d.bus.Tx(uint16(d.address), d.dataBuf[:2], nil)
|
||||
}
|
||||
|
||||
func unsafeStringToSlice(s string) []byte {
|
||||
// TODO: use unsafe.Slice(unsafe.StringData(...)) once we require Go 1.20.
|
||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(sh.Data)), len(s))
|
||||
}
|
||||
|
||||
func identifyChip(chipID uint8) DeviceType {
|
||||
switch chipID {
|
||||
case 0x11:
|
||||
return DeviceBMA421
|
||||
case 0x13:
|
||||
return DeviceBMA425
|
||||
default:
|
||||
return noDevice
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package bma42x
|
||||
|
||||
const (
|
||||
// I2C registers
|
||||
_CHIP_ID = 0x00
|
||||
_ERR_REG = 0x02
|
||||
_STATUS = 0x03
|
||||
_DATA_0 = 0x0A
|
||||
_DATA_1 = 0x0B
|
||||
_DATA_2 = 0x0C
|
||||
_DATA_3 = 0x0D
|
||||
_DATA_4 = 0x0E
|
||||
_DATA_5 = 0x0F
|
||||
_DATA_6 = 0x10
|
||||
_DATA_7 = 0x11
|
||||
_DATA_8 = 0x12
|
||||
_DATA_9 = 0x13
|
||||
_DATA_10 = 0x14
|
||||
_DATA_11 = 0x15
|
||||
_DATA_12 = 0x16
|
||||
_DATA_13 = 0x17
|
||||
_SENSORTIME_0 = 0x18
|
||||
_SENSORTIME_1 = 0x19
|
||||
_SENSORTIME_2 = 0x1A
|
||||
_EVENT = 0x1B
|
||||
_INT_STATUS_0 = 0x1C
|
||||
_INT_STATUS_1 = 0x1D
|
||||
_STEP_COUNTER_0 = 0x1E
|
||||
_STEP_COUNTER_1 = 0x1F
|
||||
_STEP_COUNTER_2 = 0x20
|
||||
_STEP_COUNTER_3 = 0x21
|
||||
_TEMPERATURE = 0x22
|
||||
_FIFO_LENGTH_0 = 0x24
|
||||
_FIFO_LENGTH_1 = 0x25
|
||||
_FIFO_DATA = 0x26
|
||||
_ACTIVITY_TYPE = 0x27
|
||||
_INTERNAL_STATUS = 0x2A
|
||||
_ACC_CONF = 0x40
|
||||
_ACC_RANGE = 0x41
|
||||
_AUX_CONF = 0x44
|
||||
_FIFO_DOWNS = 0x45
|
||||
_FIFO_WTM_0 = 0x46
|
||||
_FIFO_WTM_1 = 0x47
|
||||
_FIFO_CONFIG_0 = 0x48
|
||||
_FIFO_CONFIG_1 = 0x49
|
||||
_AUX_DEV_ID = 0x4B
|
||||
_AUX_IF_CONF = 0x4C
|
||||
_AUX_RD_ADDR = 0x4D
|
||||
_AUX_WR_ADDR = 0x4E
|
||||
_AUX_WR_DATA = 0x4F
|
||||
_INT1_IO_CTRL = 0x53
|
||||
_INT2_IO_CTRL = 0x54
|
||||
_INT_LATCH = 0x55
|
||||
_INT1_MAP = 0x56
|
||||
_INT2_MAP = 0x57
|
||||
_INT_MAP_DATA = 0x58
|
||||
_INIT_CTRL = 0x59
|
||||
_FEATURES_IN = 0x5E
|
||||
_INTERNAL_ERROR = 0x5F
|
||||
_NVM_CONF = 0x6A
|
||||
_IF_CONF = 0x6B
|
||||
_ACC_SELF_TEST = 0x6D
|
||||
_NV_CONF = 0x70
|
||||
_OFFSET_0 = 0x71
|
||||
_OFFSET_1 = 0x72
|
||||
_OFFSET_2 = 0x73
|
||||
_PWR_CONF = 0x7C
|
||||
_PWR_CTRL = 0x7D
|
||||
_CMD = 0x7E
|
||||
|
||||
// Commands send to regCommand.
|
||||
cmdSoftReset = 0xB6
|
||||
)
|
||||
+12
-11
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// calibrationCoefficients reads at startup and stores the calibration coefficients
|
||||
@@ -98,19 +99,19 @@ func (d *Device) ConfigureWithSettings(config Config) {
|
||||
}
|
||||
|
||||
var data [24]byte
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION, data[:])
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION, data[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var h1 [1]byte
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H1, h1[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var h2lsb [7]byte
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -137,12 +138,12 @@ func (d *Device) ConfigureWithSettings(config Config) {
|
||||
|
||||
d.Reset()
|
||||
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_CONFIG, []byte{byte(d.Config.Period<<5) | byte(d.Config.IIR<<2)})
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{byte(d.Config.Humidity)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_CONFIG, []byte{byte(d.Config.Period<<5) | byte(d.Config.IIR<<2)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{byte(d.Config.Humidity)})
|
||||
|
||||
// Normal mode, start measuring now
|
||||
if d.Config.Mode == ModeNormal {
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
||||
byte(d.Config.Temperature<<5) |
|
||||
byte(d.Config.Pressure<<2) |
|
||||
byte(d.Config.Mode)})
|
||||
@@ -153,13 +154,13 @@ func (d *Device) ConfigureWithSettings(config Config) {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == CHIP_ID
|
||||
}
|
||||
|
||||
// Reset the device
|
||||
func (d *Device) Reset() {
|
||||
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CMD_RESET, []byte{0xB6})
|
||||
}
|
||||
|
||||
// SetMode can set the device to Sleep, Normal or Forced mode
|
||||
@@ -170,7 +171,7 @@ func (d *Device) Reset() {
|
||||
func (d *Device) SetMode(mode Mode) {
|
||||
d.Config.Mode = mode
|
||||
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
||||
byte(d.Config.Temperature<<5) |
|
||||
byte(d.Config.Pressure<<2) |
|
||||
byte(d.Config.Mode)})
|
||||
@@ -252,7 +253,7 @@ func readIntLE(msb byte, lsb byte) int16 {
|
||||
func (d *Device) readData() (data [8]byte, err error) {
|
||||
if d.Config.Mode == ModeForced {
|
||||
// Write the CTRL_MEAS register to trigger a measurement
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
|
||||
byte(d.Config.Temperature<<5) |
|
||||
byte(d.Config.Pressure<<2) |
|
||||
byte(d.Config.Mode)})
|
||||
@@ -260,7 +261,7 @@ func (d *Device) readData() (data [8]byte, err error) {
|
||||
time.Sleep(d.measurementDelay())
|
||||
}
|
||||
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE, data[:])
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE, data[:])
|
||||
if err != nil {
|
||||
println(err)
|
||||
return
|
||||
|
||||
+7
-6
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// OversamplingMode is the oversampling ratio of the pressure measurement.
|
||||
@@ -55,7 +56,7 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == CHIP_ID
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ func (d *Device) Connected() bool {
|
||||
// read the calibration coefficients.
|
||||
func (d *Device) Configure() {
|
||||
data := make([]byte, 22)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), AC1_MSB, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), AC1_MSB, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -141,10 +142,10 @@ func (d *Device) ReadAltitude() (int32, error) {
|
||||
|
||||
// rawTemp returns the sensor's raw values of the temperature
|
||||
func (d *Device) rawTemp() (int32, error) {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
data := make([]byte, 2)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP_MSB, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP_MSB, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -160,10 +161,10 @@ func (d *Device) calculateB5(rawTemp int32) int32 {
|
||||
|
||||
// rawPressure returns the sensor's raw values of the pressure
|
||||
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
|
||||
time.Sleep(pauseForReading(mode))
|
||||
data := make([]byte, 3)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE_MSB, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE_MSB, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
+9
-8
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
|
||||
@@ -64,14 +65,14 @@ func New(bus drivers.I2C) Device {
|
||||
// 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)
|
||||
legacy.ReadRegister(d.bus, 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})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_RESET, []byte{CMD_RESET})
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication and
|
||||
@@ -85,15 +86,15 @@ func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pr
|
||||
|
||||
// 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)})
|
||||
legacy.WriteRegister(d.bus, 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)})
|
||||
legacy.WriteRegister(d.bus, 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)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALI, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -207,18 +208,18 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
|
||||
// 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)})
|
||||
legacy.WriteRegister(d.bus, 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:]) {
|
||||
for legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; legacy.ReadRegister(d.bus, 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[:])
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
||||
return data, err
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -240,10 +241,10 @@ func (d *Device) configurationError() bool {
|
||||
|
||||
func (d *Device) readRegister(register byte, len int) (data []byte, err error) {
|
||||
data = make([]byte, len)
|
||||
err = d.bus.ReadRegister(d.Address, register, data)
|
||||
err = legacy.ReadRegister(d.bus, d.Address, register, data)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Device) writeRegister(register byte, data byte) error {
|
||||
return d.bus.WriteRegister(d.Address, register, []byte{data})
|
||||
return legacy.WriteRegister(d.bus, d.Address, register, []byte{data})
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ func Sleep(duration time.Duration) {
|
||||
// * The CPU frequency is lower than 256MHz. If it is higher, long sleep
|
||||
// times (1-16ms) may not work correctly.
|
||||
cycles := uint32(duration) * (machine.CPUFrequency() / 1000_000) / 1000
|
||||
slept := C.tinygo_drivers_sleep(cycles)
|
||||
slept := C.tinygo_drivers_sleep(C.uint32_t(cycles))
|
||||
if !slept {
|
||||
// Fallback for platforms without inline assembly support.
|
||||
time.Sleep(duration)
|
||||
|
||||
+5
-4
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a DS1307 device.
|
||||
@@ -44,7 +45,7 @@ func (d *Device) SetTime(t time.Time) error {
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (time.Time, error) {
|
||||
data := make([]byte, 8)
|
||||
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
@@ -105,7 +106,7 @@ func (d *Device) Read(data []uint8) (n int, err error) {
|
||||
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
|
||||
return 0, errors.New("EOF")
|
||||
}
|
||||
err = d.bus.ReadRegister(d.Address, d.AddressSRAM, data)
|
||||
err = legacy.ReadRegister(d.bus, d.Address, d.AddressSRAM, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -124,7 +125,7 @@ func (d *Device) SetOscillatorFrequency(sqw uint8) error {
|
||||
// IsOscillatorRunning returns if the oscillator is running
|
||||
func (d *Device) IsOscillatorRunning() bool {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -134,7 +135,7 @@ func (d *Device) IsOscillatorRunning() bool {
|
||||
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
|
||||
func (d *Device) SetOscillatorRunning(running bool) error {
|
||||
data := make([]byte, 3)
|
||||
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
|
||||
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+10
-9
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type Mode uint8
|
||||
@@ -37,7 +38,7 @@ func (d *Device) Configure() bool {
|
||||
// IsTimeValid return true/false is the time in the device is valid
|
||||
func (d *Device) IsTimeValid() bool {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -47,7 +48,7 @@ func (d *Device) IsTimeValid() bool {
|
||||
// IsRunning returns if the oscillator is running
|
||||
func (d *Device) IsRunning() bool {
|
||||
data := []uint8{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -57,7 +58,7 @@ func (d *Device) IsRunning() bool {
|
||||
// SetRunning starts the internal oscillator
|
||||
func (d *Device) SetRunning(isRunning bool) error {
|
||||
data := []uint8{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,7 +67,7 @@ func (d *Device) SetRunning(isRunning bool) error {
|
||||
} else {
|
||||
data[0] |= 1 << EOSC
|
||||
}
|
||||
err = d.bus.WriteRegister(uint8(d.Address), REG_CONTROL, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -86,12 +87,12 @@ func (d *Device) SetRunning(isRunning bool) error {
|
||||
// instead of 2100-03-01.
|
||||
func (d *Device) SetTime(dt time.Time) error {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data[0] &^= 1 << OSF
|
||||
err = d.bus.WriteRegister(uint8(d.Address), REG_STATUS, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -117,7 +118,7 @@ func (d *Device) SetTime(dt time.Time) error {
|
||||
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
|
||||
data[6] = uint8ToBCD(year)
|
||||
|
||||
err = d.bus.WriteRegister(uint8(d.Address), REG_TIMEDATE, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -128,7 +129,7 @@ func (d *Device) SetTime(dt time.Time) error {
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (dt time.Time, err error) {
|
||||
data := make([]uint8, 7)
|
||||
err = d.bus.ReadRegister(uint8(d.Address), REG_TIMEDATE, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -150,7 +151,7 @@ func (d *Device) ReadTime() (dt time.Time, err error) {
|
||||
// ReadTemperature returns the temperature in millicelsius (mC)
|
||||
func (d *Device) ReadTemperature() (int32, error) {
|
||||
data := make([]uint8, 2)
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/adafruit4650"
|
||||
"tinygo.org/x/tinyfont"
|
||||
"tinygo.org/x/tinyfont/freemono"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
dev := adafruit4650.New(machine.I2C0)
|
||||
|
||||
err := dev.Configure()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
drawPlus(&dev)
|
||||
drawHelloWorld(&dev)
|
||||
|
||||
err = dev.Display()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func drawPlus(d drivers.Displayer) {
|
||||
for i := int16(0); i < 128; i++ {
|
||||
d.SetPixel(i, 32, color.RGBA{R: 1})
|
||||
}
|
||||
for i := int16(0); i < 64; i++ {
|
||||
d.SetPixel(64, i, color.RGBA{R: 1})
|
||||
}
|
||||
}
|
||||
|
||||
func drawHelloWorld(d drivers.Displayer) {
|
||||
tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
// Smoke test for the BMA421/BMA425 sensors.
|
||||
// Warning: this code has _not been tested_. It's only here as a smoke test.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/bma42x"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
i2cBus := machine.I2C1
|
||||
i2cBus.Configure(machine.I2CConfig{
|
||||
Frequency: 400 * machine.KHz,
|
||||
SDA: machine.SDA_PIN,
|
||||
SCL: machine.SCL_PIN,
|
||||
})
|
||||
|
||||
sensor := bma42x.NewI2C(i2cBus, bma42x.Address)
|
||||
err := sensor.Configure(bma42x.Config{
|
||||
Device: bma42x.DeviceBMA421 | bma42x.DeviceBMA425,
|
||||
Features: bma42x.FeatureStepCounting,
|
||||
})
|
||||
if err != nil {
|
||||
println("could not configure BMA421/BMA425:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !sensor.Connected() {
|
||||
println("BMA42x not connected")
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
err := sensor.Update(drivers.Acceleration | drivers.Temperature)
|
||||
if err != nil {
|
||||
println("Error reading sensor", err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("Temperature: %.2f °C\n", float32(sensor.Temperature())/1000)
|
||||
|
||||
accelX, accelY, accelZ := sensor.Acceleration()
|
||||
fmt.Printf("Acceleration: %.2fg %.2fg %.2fg\n", float32(accelX)/1e6, float32(accelY)/1e6, float32(accelZ)/1e6)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Connects to an MAG3110 I2C magnetometer.
|
||||
// Connects to an DS3231 I2C Real Time Clock (RTC).
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -8,15 +8,16 @@ import (
|
||||
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
||||
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
)
|
||||
|
||||
const (
|
||||
BGCOLOR = 0xAD75
|
||||
GRIDCOLOR = 0xA815
|
||||
BGSHADOW = 0x5285
|
||||
GRIDSHADOW = 0x600C
|
||||
RED = 0xF800
|
||||
WHITE = 0xFFFF
|
||||
BGCOLOR = pixel.RGB565BE(0x75AD)
|
||||
GRIDCOLOR = pixel.RGB565BE(0x15A8)
|
||||
BGSHADOW = pixel.RGB565BE(0x8552)
|
||||
GRIDSHADOW = pixel.RGB565BE(0x0C60)
|
||||
RED = pixel.RGB565BE(0x00F8)
|
||||
WHITE = pixel.RGB565BE(0xFFFF)
|
||||
|
||||
YBOTTOM = 123 // Ball Y coord at bottom
|
||||
YBOUNCE = -3.5 // Upward velocity on ball bounce
|
||||
@@ -25,7 +26,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8) * 2]uint8{}
|
||||
frameBuffer = pixel.NewImage[pixel.RGB565BE](graphics.BALLWIDTH+8, graphics.BALLHEIGHT+8)
|
||||
|
||||
startTime int64
|
||||
frame int64
|
||||
@@ -41,7 +42,7 @@ var (
|
||||
balloldy float32
|
||||
|
||||
// Color table for ball rotation effect
|
||||
palette [16]uint16
|
||||
palette [16]pixel.RGB565BE
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -108,6 +109,7 @@ func main() {
|
||||
|
||||
width = maxx - minx + 1
|
||||
height = maxy - miny + 1
|
||||
buffer := frameBuffer.Rescale(int(width), int(height))
|
||||
|
||||
// Ball animation frame # is incremented opposite the ball's X velocity
|
||||
ballframe -= ballvx * 0.5
|
||||
@@ -128,7 +130,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Only the changed rectangle is drawn into the 'renderbuf' array...
|
||||
var c uint16 //, *destPtr;
|
||||
var c pixel.RGB565BE //, *destPtr;
|
||||
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
|
||||
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
|
||||
bgx := minx // X relative to background bitmap (>= 0)
|
||||
@@ -149,19 +151,20 @@ func main() {
|
||||
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
|
||||
// Yes, do ball compositing math...
|
||||
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
|
||||
var nibble uint8
|
||||
if (bx1 & 1) != 0 {
|
||||
c = uint16(p & 0xF)
|
||||
nibble = p & 0xF
|
||||
} else {
|
||||
c = uint16(p >> 4)
|
||||
nibble = p >> 4
|
||||
} // Unpack high or low nybble
|
||||
if c == 0 { // Outside ball - just draw grid
|
||||
if nibble == 0 { // Outside ball - just draw grid
|
||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||
c = GRIDCOLOR
|
||||
} else {
|
||||
c = BGCOLOR
|
||||
}
|
||||
} else if c > 1 { // In ball area...
|
||||
c = palette[c]
|
||||
} else if nibble > 1 { // In ball area...
|
||||
c = palette[nibble]
|
||||
} else { // In shadow area...
|
||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||
c = GRIDSHADOW
|
||||
@@ -176,8 +179,7 @@ func main() {
|
||||
c = BGCOLOR
|
||||
}
|
||||
}
|
||||
frameBuffer[(y*int(width)+x)*2] = byte(c >> 8)
|
||||
frameBuffer[(y*int(width)+x)*2+1] = byte(c)
|
||||
buffer.Set(x, y, c)
|
||||
bx1++ // Increment bitmap position counters (X axis)
|
||||
bgx1++
|
||||
}
|
||||
@@ -188,7 +190,7 @@ func main() {
|
||||
bgy++
|
||||
}
|
||||
|
||||
display.DrawRGBBitmap8(minx, miny, frameBuffer[:width*height*2], width, height)
|
||||
display.DrawBitmap(minx, miny, buffer)
|
||||
|
||||
// Show approximate frame rate
|
||||
frame++
|
||||
@@ -205,6 +207,7 @@ func DrawBackground() {
|
||||
w, h := display.Size()
|
||||
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
|
||||
var b uint8
|
||||
buffer := frameBuffer.Rescale(int(w), 1)
|
||||
for j := int16(0); j < h; j++ {
|
||||
for k := int16(0); k < w; k++ {
|
||||
if k&7 > 0 {
|
||||
@@ -213,13 +216,11 @@ func DrawBackground() {
|
||||
b = graphics.Background[j*byteWidth+k/8]
|
||||
}
|
||||
if b&0x80 == 0 {
|
||||
frameBuffer[2*k] = byte(BGCOLOR >> 8)
|
||||
frameBuffer[2*k+1] = byte(BGCOLOR & 0xFF)
|
||||
buffer.Set(int(k), 0, BGCOLOR)
|
||||
} else {
|
||||
frameBuffer[2*k] = byte(GRIDCOLOR >> 8)
|
||||
frameBuffer[2*k+1] = byte(GRIDCOLOR & 0xFF)
|
||||
buffer.Set(int(k), 0, GRIDCOLOR)
|
||||
}
|
||||
}
|
||||
display.DrawRGBBitmap8(0, j, frameBuffer[0:w*2], w, 1)
|
||||
display.DrawBitmap(0, j, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,20 @@ Builds/flashes atcmd console application with simulator instead of actual LoRa r
|
||||
tinygo flash -target pico ./examples/lora/lorawan/atcmd/
|
||||
```
|
||||
|
||||
## PyBadge with LoRa Featherwing
|
||||
## PyBadge with LoRa Featherwing for EU868 region
|
||||
|
||||
Builds/flashes atcmd console application on PyBadge using LoRa Featherwing (RFM95/SX1276).
|
||||
|
||||
```
|
||||
tinygo flash -target pybadge -tags featherwing ./examples/lora/lorawan/atcmd/
|
||||
tinygo flash -target pybadge -tags featherwing -ldflags="-X main.reg=EU868" ./examples/lora/lorawan/atcmd/
|
||||
```
|
||||
|
||||
## LoRa-E5
|
||||
## LoRa-E5 for US915 region
|
||||
|
||||
Builds/flashes atcmd console application on Lora-E5 using onboard SX126x.
|
||||
|
||||
```
|
||||
tinygo flash -target lorae5 ./examples/lora/lorawan/atcmd/
|
||||
tinygo flash -target lorae5 -ldflags="-X main.reg=US915" ./examples/lora/lorawan/atcmd/
|
||||
```
|
||||
|
||||
## Joining a Public Lorawan Network
|
||||
|
||||
@@ -32,6 +32,8 @@ var (
|
||||
defaultTimeout uint32 = 1000
|
||||
)
|
||||
|
||||
var reg string
|
||||
|
||||
func main() {
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
|
||||
@@ -45,7 +47,16 @@ func main() {
|
||||
otaa = &lorawan.Otaa{}
|
||||
lorawan.UseRadio(radio)
|
||||
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
switch reg {
|
||||
case "AU915":
|
||||
lorawan.UseRegionSettings(region.AU915())
|
||||
case "EU868":
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
case "US915":
|
||||
lorawan.UseRegionSettings(region.US915())
|
||||
default:
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
}
|
||||
|
||||
for {
|
||||
if uart.Buffered() > 0 {
|
||||
|
||||
@@ -21,16 +21,16 @@ loraConnect: Connected !
|
||||
tinygo flash -target pico ./examples/lora/lorawan/basic-demo
|
||||
```
|
||||
|
||||
## PyBadge with LoRa Featherwing
|
||||
## PyBadge with LoRa Featherwing for EU868 region
|
||||
|
||||
```
|
||||
tinygo flash -target pybadge -tags featherwing ./examples/lora/lorawan/basic-demo
|
||||
tinygo flash -target pybadge -tags featherwing -ldflags="-X main.reg=EU868" ./examples/lora/lorawan/basic-demo
|
||||
```
|
||||
|
||||
## LoRa-E5
|
||||
## LoRa-E5 for US915 region
|
||||
|
||||
```
|
||||
tinygo flash -target lorae5 ./examples/lora/lorawan/basic-demo
|
||||
tinygo flash -target lorae5 -ldflags="-X main.reg=US915" ./examples/lora/lorawan/basic-demo
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
"tinygo.org/x/drivers/lora/lorawan/region"
|
||||
)
|
||||
|
||||
var debug string
|
||||
var (
|
||||
reg string
|
||||
debug string
|
||||
)
|
||||
|
||||
const (
|
||||
LORAWAN_JOIN_TIMEOUT_SEC = 180
|
||||
@@ -67,8 +70,16 @@ func main() {
|
||||
|
||||
// Connect the lorawan with the Lora Radio device.
|
||||
lorawan.UseRadio(radio)
|
||||
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
switch reg {
|
||||
case "AU915":
|
||||
lorawan.UseRegionSettings(region.AU915())
|
||||
case "EU868":
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
case "US915":
|
||||
lorawan.UseRegionSettings(region.US915())
|
||||
default:
|
||||
lorawan.UseRegionSettings(region.EU868())
|
||||
}
|
||||
|
||||
// Configure AppEUI, DevEUI, APPKey, and public/private Lorawan Network
|
||||
setLorawanKeys()
|
||||
|
||||
@@ -23,13 +23,18 @@ func (sr *SimLoraRadio) Rx(timeoutMs uint32) ([]uint8, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
|
||||
func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
|
||||
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
|
||||
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
|
||||
func (sr *SimLoraRadio) SetCrc(enable bool) {}
|
||||
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
|
||||
func (sr *SimLoraRadio) LoraConfig(cnf lora.Config) {}
|
||||
func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
|
||||
func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
|
||||
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
|
||||
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
|
||||
func (sr *SimLoraRadio) SetCrc(enable bool) {}
|
||||
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
|
||||
func (sr *SimLoraRadio) SetHeaderType(headerType uint8) {}
|
||||
func (sr *SimLoraRadio) SetPreambleLength(pLen uint16) {}
|
||||
func (sr *SimLoraRadio) SetPublicNetwork(enabled bool) {}
|
||||
func (sr *SimLoraRadio) SetSyncWord(syncWord uint16) {}
|
||||
func (sr *SimLoraRadio) SetTxPower(txPower int8) {}
|
||||
func (sr *SimLoraRadio) LoraConfig(cnf lora.Config) {}
|
||||
|
||||
func FirmwareVersion() string {
|
||||
return "simulator " + CurrentVersion()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Connects to an MPU9150 I2C accelerometer/gyroscope.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/mpu9150"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
accel := mpu9150.New(machine.I2C0)
|
||||
accel.Configure()
|
||||
|
||||
for {
|
||||
x, y, z := accel.ReadAcceleration(mpu9150.ACCEL_XOUT_H)
|
||||
println(x, y, z)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/ndir"
|
||||
)
|
||||
|
||||
var (
|
||||
ndirBus = machine.I2C0
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := ndirBus.Configure(machine.I2CConfig{
|
||||
Frequency: 100_000,
|
||||
})
|
||||
if err != nil {
|
||||
panic("i2c config fail:" + err.Error())
|
||||
}
|
||||
// Set the address based on how the resistors are soldered.
|
||||
// True means the left and middle pads are joined.
|
||||
ndirAddr := ndir.Addr(true, false)
|
||||
dev := ndir.NewDevI2C(ndirBus, ndirAddr)
|
||||
err = dev.Init()
|
||||
if err != nil {
|
||||
panic("ndir init fail:" + err.Error())
|
||||
}
|
||||
// Datasheet tells us to wait 12 seconds before reading from the sensor.
|
||||
time.Sleep(12 * time.Second)
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
err := dev.Update(drivers.AllMeasurements)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
continue
|
||||
}
|
||||
println("PPM:", dev.PPMCO2())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/pcf8523"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
dev := pcf8523.New(machine.I2C0)
|
||||
|
||||
// make sure the battery takes over if power is lost
|
||||
err := dev.SetPowerManagement(pcf8523.PowerManagement_SwitchOver_ModeStandard)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set RTC once, i.e. from `date -u +"%Y-%m-%dT%H:%M:%SZ"`
|
||||
now, _ := time.Parse(time.RFC3339, "2023-09-18T20:31:38Z")
|
||||
err = dev.SetTime(now)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
ts, err := dev.ReadTime()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
println("tick-tock, it's: " + ts.String())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sht4x"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
sensor := sht4x.New(machine.I2C0)
|
||||
|
||||
for {
|
||||
temp, humidity, _ := sensor.ReadTemperatureHumidity()
|
||||
t := fmt.Sprintf("%.2f", float32(temp)/1000)
|
||||
h := fmt.Sprintf("%.2f", float32(humidity)/100)
|
||||
println("Temperature: ", t, "°C")
|
||||
println("Humidity: ", h, "%")
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -8,6 +8,7 @@ import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/touch"
|
||||
)
|
||||
|
||||
@@ -98,10 +99,10 @@ func (d *Device) Touched() bool {
|
||||
}
|
||||
|
||||
func (d *Device) write1Byte(reg, data uint8) {
|
||||
d.bus.WriteRegister(d.Address, reg, []byte{data})
|
||||
legacy.WriteRegister(d.bus, d.Address, reg, []byte{data})
|
||||
}
|
||||
|
||||
func (d *Device) read8bit(reg uint8) uint8 {
|
||||
d.bus.ReadRegister(d.Address, reg, d.buf[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, reg, d.buf[:1])
|
||||
return d.buf[0]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Package gbadisplay implements a simple driver for the GameBoy Advance
|
||||
// display.
|
||||
package gbadisplay
|
||||
|
||||
import (
|
||||
"device/gba"
|
||||
"errors"
|
||||
"image/color"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
)
|
||||
|
||||
// Image buffer type used by the GameBoy Advance.
|
||||
type Image = pixel.Image[pixel.RGB555]
|
||||
|
||||
const (
|
||||
displayWidth = 240
|
||||
displayHeight = 160
|
||||
)
|
||||
|
||||
var (
|
||||
errOutOfBounds = errors.New("rectangle coordinates outside display area")
|
||||
)
|
||||
|
||||
type Device struct{}
|
||||
|
||||
// New returns a new GameBoy Advance display object.
|
||||
func New() Device {
|
||||
return Device{}
|
||||
}
|
||||
|
||||
var displayFrameBuffer = (*[160 * 240]volatile.Register16)(unsafe.Pointer(uintptr(gba.MEM_VRAM)))
|
||||
|
||||
type Config struct {
|
||||
// TODO: add more display modes here.
|
||||
}
|
||||
|
||||
// Configure the display as a regular 15bpp framebuffer.
|
||||
func (d Device) Configure(config Config) {
|
||||
// Use video mode 3 (in BG2, a 16bpp bitmap in VRAM) and Enable BG2.
|
||||
gba.DISP.DISPCNT.Set(gba.DISPCNT_BGMODE_3<<gba.DISPCNT_BGMODE_Pos |
|
||||
gba.DISPCNT_SCREENDISPLAY_BG2_ENABLE<<gba.DISPCNT_SCREENDISPLAY_BG2_Pos)
|
||||
}
|
||||
|
||||
// Size returns the fixed size of this display.
|
||||
func (d Device) Size() (x, y int16) {
|
||||
return displayWidth, displayHeight
|
||||
}
|
||||
|
||||
// Display is a no-op: the display framebuffer is modified directly.
|
||||
func (d Device) Display() error {
|
||||
// Nothing to do here.
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPixel changes the pixel at (x, y) to the given color.
|
||||
func (d Device) SetPixel(x, y int16, c color.RGBA) {
|
||||
if x < 0 || y < 0 || x >= displayWidth || y > displayHeight {
|
||||
// Out of bounds, so ignore.
|
||||
return
|
||||
}
|
||||
val := pixel.NewColor[pixel.RGB555](c.R, c.G, c.B)
|
||||
displayFrameBuffer[(int(y))*240+int(x)].Set(uint16(val))
|
||||
}
|
||||
|
||||
// DrawBitmap updates the rectangle at (x, y) to the image stored in buf.
|
||||
func (d Device) DrawBitmap(x, y int16, buf Image) error {
|
||||
width, height := buf.Size()
|
||||
if x < 0 || y < 0 || int(x)+width > displayWidth || int(y)+height > displayHeight {
|
||||
return errOutOfBounds
|
||||
}
|
||||
|
||||
// TODO: try to do a 4-byte memcpy if possible. That should significantly
|
||||
// speed up the copying of this image.
|
||||
for bufY := 0; bufY < int(height); bufY++ {
|
||||
for bufX := 0; bufX < int(width); bufX++ {
|
||||
val := buf.Get(bufX, bufY)
|
||||
displayFrameBuffer[(int(y)+bufY)*240+int(x)+bufX].Set(uint16(val))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,12 +1,20 @@
|
||||
module tinygo.org/x/drivers
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/frankban/quicktest v1.10.2
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/net v0.7.0
|
||||
tinygo.org/x/tinyfont v0.3.0
|
||||
tinygo.org/x/tinyterm v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.2 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.1.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
)
|
||||
|
||||
@@ -7,32 +7,22 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/hajimehoshi/go-jisx0208 v1.0.0/go.mod h1:yYxEStHL7lt9uL+AbdWgW9gBumwieDoZCiB1f/0X0as=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/sago35/go-bdf v0.0.0-20200313142241-6c17821c91c4/go.mod h1:rOebXGuMLsXhZAC6mF/TjxONsm45498ZyzVhel++6KM=
|
||||
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI=
|
||||
tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM=
|
||||
tinygo.org/x/tinyfont v0.3.0 h1:HIRLQoI3oc+2CMhPcfv+Ig88EcTImE/5npjqOnMD4lM=
|
||||
tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk=
|
||||
tinygo.org/x/tinyfs v0.1.0/go.mod h1:ysc8Y92iHfhTXeyEM9+c7zviUQ4fN9UCFgSOFfMWv20=
|
||||
tinygo.org/x/tinyterm v0.1.0 h1:80i+j+KWoxCFa/Xfp6pWbh79x+8zUdMXC1vaKj2QhkY=
|
||||
tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4=
|
||||
|
||||
+25
-24
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a HTS221 device.
|
||||
@@ -32,7 +33,7 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, HTS221_WHO_AM_I_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_WHO_AM_I_REG, data)
|
||||
return data[0] == 0xBC
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ func (d *Device) Power(status bool) {
|
||||
if status {
|
||||
data[0] = 0x84
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, HTS221_CTRL1_REG, data)
|
||||
legacy.WriteRegister(d.bus, d.Address, HTS221_CTRL1_REG, data)
|
||||
}
|
||||
|
||||
// ReadHumidity returns the relative humidity in percent * 100.
|
||||
@@ -55,8 +56,8 @@ func (d *Device) ReadHumidity() (humidity int32, err error) {
|
||||
|
||||
// read data and calibrate
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG+1, data[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_HUMID_OUT_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_HUMID_OUT_REG+1, data[1:])
|
||||
hValue := readInt(data[1], data[0])
|
||||
hValueCalib := float32(hValue)*d.humiditySlope + d.humidityZero
|
||||
|
||||
@@ -73,8 +74,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
|
||||
// read data and calibrate
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG+1, data[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_TEMP_OUT_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_TEMP_OUT_REG+1, data[1:])
|
||||
tValue := readInt(data[1], data[0])
|
||||
tValueCalib := float32(tValue)*d.temperatureSlope + d.temperatureZero
|
||||
|
||||
@@ -91,7 +92,7 @@ func (d *Device) Resolution(h uint8, t uint8) {
|
||||
if t > 7 {
|
||||
t = 3 // default
|
||||
}
|
||||
d.bus.WriteRegister(d.Address, HTS221_AV_CONF_REG, []byte{h<<3 | t})
|
||||
legacy.WriteRegister(d.bus, d.Address, HTS221_AV_CONF_REG, []byte{h<<3 | t})
|
||||
}
|
||||
|
||||
// private functions
|
||||
@@ -104,19 +105,19 @@ func (d *Device) calibration() {
|
||||
h0t0Out, h1t0Out := []byte{0, 0}, []byte{0, 0}
|
||||
t0Out, t1Out := []byte{0, 0}, []byte{0, 0}
|
||||
|
||||
d.bus.ReadRegister(d.Address, HTS221_H0_rH_x2_REG, h0rH)
|
||||
d.bus.ReadRegister(d.Address, HTS221_H1_rH_x2_REG, h1rH)
|
||||
d.bus.ReadRegister(d.Address, HTS221_T0_degC_x8_REG, t0degC)
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_degC_x8_REG, t1degC)
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_T0_MSB_REG, t1t0msb)
|
||||
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:])
|
||||
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG, t0Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG+1, t0Out[1:])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG, t1Out[:1])
|
||||
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG+1, t1Out[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_H0_rH_x2_REG, h0rH)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_H1_rH_x2_REG, h1rH)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T0_degC_x8_REG, t0degC)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T1_degC_x8_REG, t1degC)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T1_T0_MSB_REG, t1t0msb)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T0_OUT_REG, t0Out[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T0_OUT_REG+1, t0Out[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T1_OUT_REG, t1Out[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_T1_OUT_REG+1, t1Out[1:])
|
||||
|
||||
h0rH_v := float32(h0rH[0]) / 2.0
|
||||
h1rH_v := float32(h1rH[0]) / 2.0
|
||||
@@ -138,7 +139,7 @@ func (d *Device) waitForOneShot(filter uint8) error {
|
||||
data := []byte{0}
|
||||
|
||||
// check if the device is on
|
||||
d.bus.ReadRegister(d.Address, HTS221_CTRL1_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_CTRL1_REG, data)
|
||||
if data[0]&0x80 == 0 {
|
||||
return errors.New("device is off, unable to query")
|
||||
}
|
||||
@@ -146,19 +147,19 @@ func (d *Device) waitForOneShot(filter uint8) error {
|
||||
// wait until one shot (one conversion) is ready to go
|
||||
data[0] = 1
|
||||
for {
|
||||
d.bus.ReadRegister(d.Address, HTS221_CTRL2_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_CTRL2_REG, data)
|
||||
if data[0]&0x01 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// trigger one shot
|
||||
d.bus.WriteRegister(d.Address, HTS221_CTRL2_REG, []byte{0x01})
|
||||
legacy.WriteRegister(d.bus, d.Address, HTS221_CTRL2_REG, []byte{0x01})
|
||||
|
||||
// wait until conversion completed
|
||||
data[0] = 0
|
||||
for {
|
||||
d.bus.ReadRegister(d.Address, HTS221_STATUS_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, HTS221_STATUS_REG, data)
|
||||
if data[0]&filter == filter {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -3,7 +3,15 @@ package drivers
|
||||
// I2C represents an I2C bus. It is notably implemented by the
|
||||
// machine.I2C type.
|
||||
type I2C interface {
|
||||
ReadRegister(addr uint8, r uint8, buf []byte) error
|
||||
WriteRegister(addr uint8, r uint8, buf []byte) error
|
||||
// Tx performs a [I²C] transaction with address addr.
|
||||
// Most I2C peripherals have some sort of register mapping scheme to allow
|
||||
// users to interact with them:
|
||||
//
|
||||
// bus.Tx(addr, []byte{reg}, buf) // Reads register reg into buf.
|
||||
// bus.Tx(addr, append([]byte{reg}, buf...), nil) // Writes buf into register reg.
|
||||
//
|
||||
// The semantics of most I2C transactions require that the w write buffer be non-empty.
|
||||
//
|
||||
// [I²C]: https://en.wikipedia.org/wiki/I%C2%B2C
|
||||
Tx(addr uint16, w, r []byte) error
|
||||
}
|
||||
|
||||
+23
-2
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -31,6 +32,9 @@ type Device struct {
|
||||
rd machine.Pin
|
||||
}
|
||||
|
||||
// Image buffer type used in the ili9341.
|
||||
type Image = pixel.Image[pixel.RGB565BE]
|
||||
|
||||
var cmdBuf [6]byte
|
||||
|
||||
var initCmd = []byte{
|
||||
@@ -173,6 +177,8 @@ func (d *Device) EnableTEOutput(on bool) {
|
||||
}
|
||||
|
||||
// DrawRGBBitmap copies an RGB bitmap to the internal buffer at given coordinates
|
||||
//
|
||||
// Deprecated: use DrawBitmap instead.
|
||||
func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
|
||||
@@ -187,6 +193,8 @@ func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
|
||||
}
|
||||
|
||||
// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates
|
||||
//
|
||||
// Deprecated: use DrawBitmap instead.
|
||||
func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
|
||||
@@ -200,6 +208,13 @@ func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawBitmap copies the bitmap to the internal buffer on the screen at the
|
||||
// given coordinates. It returns once the image data has been sent completely.
|
||||
func (d *Device) DrawBitmap(x, y int16, bitmap Image) error {
|
||||
width, height := bitmap.Size()
|
||||
return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height))
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at given coordinates with a color
|
||||
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
k, i := d.Size()
|
||||
@@ -320,10 +335,16 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||
// SetScrollArea sets an area to scroll with fixed top/bottom or left/right parts of the display
|
||||
// Rotation affects scroll direction
|
||||
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
if d.height < 320 {
|
||||
// The screen doesn't use the full 320 pixel height.
|
||||
// Enlarge the bottom fixed area to fill the 320 pixel height, so that
|
||||
// bottomFixedArea starts from the visible bottom of the screen.
|
||||
bottomFixedArea += 320 - d.height
|
||||
}
|
||||
cmdBuf[0] = uint8(topFixedArea >> 8)
|
||||
cmdBuf[1] = uint8(topFixedArea)
|
||||
cmdBuf[2] = uint8(d.height - topFixedArea - bottomFixedArea>>8)
|
||||
cmdBuf[3] = uint8(d.height - topFixedArea - bottomFixedArea)
|
||||
cmdBuf[2] = uint8((320 - topFixedArea - bottomFixedArea) >> 8)
|
||||
cmdBuf[3] = uint8(320 - topFixedArea - bottomFixedArea)
|
||||
cmdBuf[4] = uint8(bottomFixedArea >> 8)
|
||||
cmdBuf[5] = uint8(bottomFixedArea)
|
||||
d.sendCommand(VSCRDEF, cmdBuf[:6])
|
||||
|
||||
+6
-3
@@ -1,6 +1,9 @@
|
||||
package ina260
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to an INA260 device.
|
||||
type Device struct {
|
||||
@@ -97,7 +100,7 @@ func (d *Device) Power() int32 {
|
||||
// Read a register
|
||||
func (d *Device) ReadRegister(reg uint8) uint16 {
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(uint8(d.Address), reg, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), reg, data)
|
||||
return (uint16(data[0]) << 8) | uint16(data[1])
|
||||
}
|
||||
|
||||
@@ -107,5 +110,5 @@ func (d *Device) WriteRegister(reg uint8, v uint16) {
|
||||
data[0] = byte(v >> 8)
|
||||
data[1] = byte(v & 0xff)
|
||||
|
||||
d.bus.WriteRegister(uint8(d.Address), reg, data)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), reg, data)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package legacy
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
func ReadRegister(bus drivers.I2C, addr uint8, reg uint8, data []byte) error {
|
||||
return bus.Tx(uint16(addr), []byte{reg}, data)
|
||||
}
|
||||
|
||||
func WriteRegister(bus drivers.I2C, addr uint8, reg uint8, data []byte) error {
|
||||
buf := make([]uint8, len(data)+1)
|
||||
buf[0] = reg
|
||||
copy(buf[1:], data)
|
||||
return bus.Tx(uint16(addr), buf, nil)
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device implements TinyGo driver for Lumissil IS31FL3731 matrix LED driver
|
||||
@@ -92,7 +93,7 @@ func (d *Device) Configure() (err error) {
|
||||
func (d *Device) selectCommand(command uint8) (err error) {
|
||||
if command != d.selectedCommand {
|
||||
d.selectedCommand = command
|
||||
return d.bus.WriteRegister(d.Address, COMMAND, []byte{command})
|
||||
return legacy.WriteRegister(d.bus, d.Address, COMMAND, []byte{command})
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -105,7 +106,7 @@ func (d *Device) writeFunctionRegister(operation uint8, data []byte) (err error)
|
||||
return err
|
||||
}
|
||||
|
||||
return d.bus.WriteRegister(d.Address, operation, data)
|
||||
return legacy.WriteRegister(d.bus, d.Address, operation, data)
|
||||
}
|
||||
|
||||
// enableLEDs enables only LEDs that are soldered on the set board. Enabled
|
||||
@@ -119,7 +120,7 @@ func (d *Device) enableLEDs() (err error) {
|
||||
|
||||
// Enable every LED (16 columns x 9 rows)
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
err = d.bus.WriteRegister(d.Address, i, []byte{0xFF})
|
||||
err = legacy.WriteRegister(d.bus, d.Address, i, []byte{0xFF})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -136,7 +137,7 @@ func (d *Device) setPixelPWD(frame, n, value uint8) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.bus.WriteRegister(d.Address, LED_PWM_OFFSET+n, []byte{value})
|
||||
return legacy.WriteRegister(d.bus, d.Address, LED_PWM_OFFSET+n, []byte{value})
|
||||
}
|
||||
|
||||
// SetActiveFrame sets frame to display with LEDs
|
||||
@@ -165,7 +166,7 @@ func (d *Device) Fill(frame, value uint8) (err error) {
|
||||
}
|
||||
|
||||
for i := uint8(0); i < 6; i++ {
|
||||
err = d.bus.WriteRegister(d.Address, LED_PWM_OFFSET+i*24, data)
|
||||
err = legacy.WriteRegister(d.bus, d.Address, LED_PWM_OFFSET+i*24, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// DeviceAdafruitCharlieWing15x7 implements TinyGo driver for Lumissil
|
||||
@@ -47,20 +48,20 @@ func (d *DeviceAdafruitCharlieWing15x7) enableLEDs() (err error) {
|
||||
|
||||
// Enable left half
|
||||
for i := uint8(0); i < 16; i += 2 {
|
||||
err = d.bus.WriteRegister(d.Address, i, []byte{0b11111110})
|
||||
err = legacy.WriteRegister(d.bus, d.Address, i, []byte{0b11111110})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Enable right half
|
||||
for i := uint8(3); i < 16; i += 2 {
|
||||
err = d.bus.WriteRegister(d.Address, i, []byte{0b01111111})
|
||||
err = legacy.WriteRegister(d.bus, d.Address, i, []byte{0b01111111})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Disable invisible column on the right side
|
||||
err = d.bus.WriteRegister(d.Address, 1, []byte{0b00000000})
|
||||
err = legacy.WriteRegister(d.bus, d.Address, 1, []byte{0b00000000})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+6
-5
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -87,15 +88,15 @@ func (d *DevI2C) Configure(cfg Config) error {
|
||||
}
|
||||
|
||||
func (d *DevI2C) Update() error {
|
||||
err := d.bus.ReadRegister(d.addr, OUT_X_L, d.databuf[:2])
|
||||
err := legacy.ReadRegister(d.bus, d.addr, OUT_X_L, d.databuf[:2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.bus.ReadRegister(d.addr, OUT_Y_L, d.databuf[2:4])
|
||||
err = legacy.ReadRegister(d.bus, d.addr, OUT_Y_L, d.databuf[2:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.bus.ReadRegister(d.addr, OUT_Z_L, d.databuf[4:6])
|
||||
err = legacy.ReadRegister(d.bus, d.addr, OUT_Z_L, d.databuf[4:6])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -131,11 +132,11 @@ func (d *DevI2C) AngularVelocity() (x, y, z int32) {
|
||||
// func (d DevI2C) Update(measurement)
|
||||
|
||||
func (d DevI2C) read8(reg uint8) (byte, error) {
|
||||
err := d.bus.ReadRegister(d.addr, reg, d.buf[:1])
|
||||
err := legacy.ReadRegister(d.bus, d.addr, reg, d.buf[:1])
|
||||
return d.buf[0], err
|
||||
}
|
||||
|
||||
func (d DevI2C) write8(reg uint8, val byte) error {
|
||||
d.buf[0] = val
|
||||
return d.bus.WriteRegister(d.addr, reg, d.buf[:1])
|
||||
return legacy.WriteRegister(d.bus, d.addr, reg, d.buf[:1])
|
||||
}
|
||||
|
||||
+9
-8
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a LIS2MDL device.
|
||||
@@ -38,7 +39,7 @@ func New(bus drivers.I2C) Device {
|
||||
// Connected returns whether LIS2MDL sensor has been found.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x40
|
||||
}
|
||||
|
||||
@@ -66,25 +67,25 @@ func (d *Device) Configure(cfg Configuration) {
|
||||
|
||||
// reset
|
||||
cmd[0] = byte(1 << 5)
|
||||
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// reboot
|
||||
cmd[0] = byte(1 << 6)
|
||||
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// bdu
|
||||
cmd[0] = byte(1 << 4)
|
||||
d.bus.WriteRegister(uint8(d.Address), CFG_REG_C, cmd)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_C, cmd)
|
||||
|
||||
// Temperature compensation is on for magnetic sensor (0x80)
|
||||
cmd[0] = byte(0x80)
|
||||
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
|
||||
|
||||
// speed
|
||||
cmd[0] = byte(0x80 | d.DataRate)
|
||||
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
|
||||
}
|
||||
|
||||
// ReadMagneticField reads the current magnetic field from the device and returns
|
||||
@@ -93,11 +94,11 @@ func (d *Device) ReadMagneticField() (x int32, y int32, z int32) {
|
||||
// turn back on read mode, even though it is supposed to be continuous?
|
||||
cmd := []byte{0}
|
||||
cmd[0] = byte(0x80 | d.PowerMode<<4 | d.DataRate<<2 | d.SystemMode)
|
||||
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
data := make([]byte, 6)
|
||||
d.bus.ReadRegister(uint8(d.Address), OUTX_L_REG, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_REG, data)
|
||||
|
||||
x = int32(int16((uint16(data[0]) << 8) | uint16(data[1])))
|
||||
y = int32(int16((uint16(data[2]) << 8) | uint16(data[3])))
|
||||
|
||||
+13
-10
@@ -3,7 +3,10 @@
|
||||
// Datasheet: https://www.st.com/resource/en/datasheet/lis3dh.pdf
|
||||
package lis3dh // import "tinygo.org/x/drivers/lis3dh"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a LIS3DH device.
|
||||
type Device struct {
|
||||
@@ -22,13 +25,13 @@ func New(bus drivers.I2C) Device {
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure() {
|
||||
// enable all axes, normal mode
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL1, []byte{0x07})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL1, []byte{0x07})
|
||||
|
||||
// 400Hz rate
|
||||
d.SetDataRate(DATARATE_400_HZ)
|
||||
|
||||
// High res & BDU enabled
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL4, []byte{0x88})
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL4, []byte{0x88})
|
||||
|
||||
// get current range
|
||||
d.r = d.ReadRange()
|
||||
@@ -38,7 +41,7 @@ func (d *Device) Configure() {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -48,27 +51,27 @@ func (d *Device) Connected() bool {
|
||||
// SetDataRate sets the speed of data collected by the LIS3DH.
|
||||
func (d *Device) SetDataRate(rate DataRate) {
|
||||
ctl1 := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CTRL1, ctl1)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL1, ctl1)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
// mask off bits
|
||||
ctl1[0] &^= 0xf0
|
||||
ctl1[0] |= (byte(rate) << 4)
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL1, ctl1)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL1, ctl1)
|
||||
}
|
||||
|
||||
// SetRange sets the G range for LIS3DH.
|
||||
func (d *Device) SetRange(r Range) {
|
||||
ctl := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CTRL4, ctl)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
// mask off bits
|
||||
ctl[0] &^= 0x30
|
||||
ctl[0] |= (byte(r) << 4)
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_CTRL4, ctl)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||
|
||||
// store the new range
|
||||
d.r = r
|
||||
@@ -77,7 +80,7 @@ func (d *Device) SetRange(r Range) {
|
||||
// ReadRange returns the current G range for LIS3DH.
|
||||
func (d *Device) ReadRange() (r Range) {
|
||||
ctl := []byte{0}
|
||||
err := d.bus.ReadRegister(uint8(d.Address), REG_CTRL4, ctl)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
@@ -111,7 +114,7 @@ func (d *Device) ReadAcceleration() (int32, int32, int32, error) {
|
||||
|
||||
// ReadRawAcceleration returns the raw x, y and z axis from the LIS3DH
|
||||
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||
d.bus.WriteRegister(uint8(d.Address), REG_OUT_X_L|0x80, nil)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_OUT_X_L|0x80, nil)
|
||||
|
||||
data := []byte{0, 0, 0, 0, 0, 0}
|
||||
d.bus.Tx(d.Address, nil, data)
|
||||
|
||||
@@ -80,6 +80,9 @@ const (
|
||||
const (
|
||||
MHz_868_1 = 868100000
|
||||
MHz_868_5 = 868500000
|
||||
MHz_902_3 = 902300000
|
||||
Mhz_903_0 = 903000000
|
||||
MHZ_915_0 = 915000000
|
||||
MHz_916_8 = 916800000
|
||||
MHz_923_3 = 923300000
|
||||
)
|
||||
|
||||
+31
-25
@@ -30,11 +30,11 @@ const (
|
||||
var (
|
||||
ActiveRadio lora.Radio
|
||||
Retries = 15
|
||||
regionSettings region.RegionSettings
|
||||
regionSettings region.Settings
|
||||
)
|
||||
|
||||
// UseRegionSettings sets current Lorawan Regional parameters
|
||||
func UseRegionSettings(rs region.RegionSettings) {
|
||||
func UseRegionSettings(rs region.Settings) {
|
||||
regionSettings = rs
|
||||
}
|
||||
|
||||
@@ -52,13 +52,13 @@ func SetPublicNetwork(enabled bool) {
|
||||
}
|
||||
|
||||
// ApplyChannelConfig sets current Lora modulation according to current regional settings
|
||||
func applyChannelConfig(ch *region.Channel) {
|
||||
ActiveRadio.SetFrequency(ch.Frequency)
|
||||
ActiveRadio.SetBandwidth(ch.Bandwidth)
|
||||
ActiveRadio.SetCodingRate(ch.CodingRate)
|
||||
ActiveRadio.SetSpreadingFactor(ch.SpreadingFactor)
|
||||
ActiveRadio.SetPreambleLength(ch.PreambleLength)
|
||||
ActiveRadio.SetTxPower(ch.TxPowerDBm)
|
||||
func applyChannelConfig(ch region.Channel) {
|
||||
ActiveRadio.SetFrequency(ch.Frequency())
|
||||
ActiveRadio.SetBandwidth(ch.Bandwidth())
|
||||
ActiveRadio.SetCodingRate(ch.CodingRate())
|
||||
ActiveRadio.SetSpreadingFactor(ch.SpreadingFactor())
|
||||
ActiveRadio.SetPreambleLength(ch.PreambleLength())
|
||||
ActiveRadio.SetTxPower(ch.TxPowerDBm())
|
||||
// Lorawan defaults to explicit headers
|
||||
ActiveRadio.SetHeaderType(lora.HeaderExplicit)
|
||||
ActiveRadio.SetCrc(true)
|
||||
@@ -84,24 +84,30 @@ func Join(otaa *Otaa, session *Session) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare radio for Join Tx
|
||||
applyChannelConfig(regionSettings.JoinRequestChannel())
|
||||
ActiveRadio.SetIqMode(lora.IQStandard)
|
||||
ActiveRadio.Tx(payload, LORA_TX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
joinRequestChannel := regionSettings.JoinRequestChannel()
|
||||
joinAcceptChannel := regionSettings.JoinAcceptChannel()
|
||||
|
||||
// Wait for JoinAccept
|
||||
applyChannelConfig(regionSettings.JoinAcceptChannel())
|
||||
ActiveRadio.SetIqMode(lora.IQInverted)
|
||||
resp, err = ActiveRadio.Rx(LORA_RX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Prepare radio for Join Tx
|
||||
applyChannelConfig(joinRequestChannel)
|
||||
ActiveRadio.SetIqMode(lora.IQStandard)
|
||||
ActiveRadio.Tx(payload, LORA_TX_TIMEOUT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return ErrNoJoinAcceptReceived
|
||||
// Wait for JoinAccept
|
||||
if joinAcceptChannel.Frequency() != 0 {
|
||||
applyChannelConfig(joinAcceptChannel)
|
||||
}
|
||||
ActiveRadio.SetIqMode(lora.IQInverted)
|
||||
resp, err = ActiveRadio.Rx(LORA_RX_TIMEOUT)
|
||||
if err == nil && resp != nil {
|
||||
break
|
||||
}
|
||||
if !joinAcceptChannel.Next() {
|
||||
return ErrNoJoinAcceptReceived
|
||||
}
|
||||
}
|
||||
|
||||
err = otaa.DecodeJoinAccept(resp, session)
|
||||
|
||||
@@ -7,43 +7,41 @@ const (
|
||||
AU915_DEFAULT_TX_POWER_DBM = 20
|
||||
)
|
||||
|
||||
type RegionSettingsAU915 struct {
|
||||
joinRequestChannel *Channel
|
||||
joinAcceptChannel *Channel
|
||||
uplinkChannel *Channel
|
||||
type ChannelAU struct {
|
||||
channel
|
||||
}
|
||||
|
||||
func AU915() *RegionSettingsAU915 {
|
||||
return &RegionSettingsAU915{
|
||||
joinRequestChannel: &Channel{lora.MHz_916_8,
|
||||
func (c *ChannelAU) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type SettingsAU915 struct {
|
||||
settings
|
||||
}
|
||||
|
||||
func AU915() *SettingsAU915 {
|
||||
return &SettingsAU915{settings: settings{
|
||||
joinRequestChannel: &ChannelAU{channel: channel{lora.MHz_916_8,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_5,
|
||||
AU915_DEFAULT_PREAMBLE_LEN,
|
||||
AU915_DEFAULT_TX_POWER_DBM},
|
||||
joinAcceptChannel: &Channel{lora.MHz_923_3,
|
||||
AU915_DEFAULT_TX_POWER_DBM}},
|
||||
joinAcceptChannel: &ChannelAU{channel: channel{lora.MHz_923_3,
|
||||
lora.Bandwidth_500_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_5,
|
||||
AU915_DEFAULT_PREAMBLE_LEN,
|
||||
AU915_DEFAULT_TX_POWER_DBM},
|
||||
uplinkChannel: &Channel{lora.MHz_916_8,
|
||||
AU915_DEFAULT_TX_POWER_DBM}},
|
||||
uplinkChannel: &ChannelAU{channel: channel{lora.MHz_916_8,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_5,
|
||||
AU915_DEFAULT_PREAMBLE_LEN,
|
||||
AU915_DEFAULT_TX_POWER_DBM},
|
||||
}
|
||||
AU915_DEFAULT_TX_POWER_DBM}},
|
||||
}}
|
||||
}
|
||||
|
||||
func (r *RegionSettingsAU915) JoinRequestChannel() *Channel {
|
||||
return r.joinRequestChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsAU915) JoinAcceptChannel() *Channel {
|
||||
return r.joinAcceptChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsAU915) UplinkChannel() *Channel {
|
||||
return r.uplinkChannel
|
||||
func Next(c *ChannelAU) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package region
|
||||
|
||||
type Channel interface {
|
||||
Next() bool
|
||||
Frequency() uint32
|
||||
Bandwidth() uint8
|
||||
SpreadingFactor() uint8
|
||||
CodingRate() uint8
|
||||
PreambleLength() uint16
|
||||
TxPowerDBm() int8
|
||||
SetFrequency(v uint32)
|
||||
SetBandwidth(v uint8)
|
||||
SetSpreadingFactor(v uint8)
|
||||
SetCodingRate(v uint8)
|
||||
SetPreambleLength(v uint16)
|
||||
SetTxPowerDBm(v int8)
|
||||
}
|
||||
|
||||
type channel struct {
|
||||
frequency uint32
|
||||
bandwidth uint8
|
||||
spreadingFactor uint8
|
||||
codingRate uint8
|
||||
preambleLength uint16
|
||||
txPowerDBm int8
|
||||
}
|
||||
|
||||
// Getter functions
|
||||
func (c *channel) Frequency() uint32 { return c.frequency }
|
||||
func (c *channel) Bandwidth() uint8 { return c.bandwidth }
|
||||
func (c *channel) SpreadingFactor() uint8 { return c.spreadingFactor }
|
||||
func (c *channel) CodingRate() uint8 { return c.codingRate }
|
||||
func (c *channel) PreambleLength() uint16 { return c.preambleLength }
|
||||
func (c *channel) TxPowerDBm() int8 { return c.txPowerDBm }
|
||||
|
||||
// Set functions
|
||||
func (c *channel) SetFrequency(v uint32) { c.frequency = v }
|
||||
func (c *channel) SetBandwidth(v uint8) { c.bandwidth = v }
|
||||
func (c *channel) SetSpreadingFactor(v uint8) { c.spreadingFactor = v }
|
||||
func (c *channel) SetCodingRate(v uint8) { c.codingRate = v }
|
||||
func (c *channel) SetPreambleLength(v uint16) { c.preambleLength = v }
|
||||
func (c *channel) SetTxPowerDBm(v int8) { c.txPowerDBm = v }
|
||||
@@ -7,43 +7,37 @@ const (
|
||||
EU868_DEFAULT_TX_POWER_DBM = 20
|
||||
)
|
||||
|
||||
type RegionSettingsEU868 struct {
|
||||
joinRequestChannel *Channel
|
||||
joinAcceptChannel *Channel
|
||||
uplinkChannel *Channel
|
||||
type ChannelEU struct {
|
||||
channel
|
||||
}
|
||||
|
||||
func EU868() *RegionSettingsEU868 {
|
||||
return &RegionSettingsEU868{
|
||||
joinRequestChannel: &Channel{lora.MHz_868_1,
|
||||
func (c *ChannelEU) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type SettingsEU868 struct {
|
||||
settings
|
||||
}
|
||||
|
||||
func EU868() *SettingsEU868 {
|
||||
return &SettingsEU868{settings: settings{
|
||||
joinRequestChannel: &ChannelEU{channel: channel{lora.MHz_868_1,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
EU868_DEFAULT_PREAMBLE_LEN,
|
||||
EU868_DEFAULT_TX_POWER_DBM},
|
||||
joinAcceptChannel: &Channel{lora.MHz_868_1,
|
||||
EU868_DEFAULT_TX_POWER_DBM}},
|
||||
joinAcceptChannel: &ChannelEU{channel: channel{lora.MHz_868_1,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
EU868_DEFAULT_PREAMBLE_LEN,
|
||||
EU868_DEFAULT_TX_POWER_DBM},
|
||||
uplinkChannel: &Channel{lora.MHz_868_1,
|
||||
EU868_DEFAULT_TX_POWER_DBM}},
|
||||
uplinkChannel: &ChannelEU{channel: channel{lora.MHz_868_1,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_7,
|
||||
EU868_DEFAULT_PREAMBLE_LEN,
|
||||
EU868_DEFAULT_TX_POWER_DBM},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RegionSettingsEU868) JoinRequestChannel() *Channel {
|
||||
return r.joinRequestChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsEU868) JoinAcceptChannel() *Channel {
|
||||
return r.joinAcceptChannel
|
||||
}
|
||||
|
||||
func (r *RegionSettingsEU868) UplinkChannel() *Channel {
|
||||
return r.uplinkChannel
|
||||
EU868_DEFAULT_TX_POWER_DBM}},
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package region
|
||||
|
||||
type Channel struct {
|
||||
Frequency uint32
|
||||
Bandwidth uint8
|
||||
SpreadingFactor uint8
|
||||
CodingRate uint8
|
||||
PreambleLength uint16
|
||||
TxPowerDBm int8
|
||||
}
|
||||
|
||||
type RegionSettings interface {
|
||||
JoinRequestChannel() *Channel
|
||||
JoinAcceptChannel() *Channel
|
||||
UplinkChannel() *Channel
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package region
|
||||
|
||||
type Settings interface {
|
||||
JoinRequestChannel() Channel
|
||||
JoinAcceptChannel() Channel
|
||||
UplinkChannel() Channel
|
||||
}
|
||||
|
||||
type settings struct {
|
||||
joinRequestChannel Channel
|
||||
joinAcceptChannel Channel
|
||||
uplinkChannel Channel
|
||||
}
|
||||
|
||||
func (r *settings) JoinRequestChannel() Channel {
|
||||
return r.joinRequestChannel
|
||||
}
|
||||
|
||||
func (r *settings) JoinAcceptChannel() Channel {
|
||||
return r.joinAcceptChannel
|
||||
}
|
||||
|
||||
func (r *settings) UplinkChannel() Channel {
|
||||
return r.uplinkChannel
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package region
|
||||
|
||||
import "tinygo.org/x/drivers/lora"
|
||||
|
||||
const (
|
||||
US915_DEFAULT_PREAMBLE_LEN = 8
|
||||
US915_DEFAULT_TX_POWER_DBM = 20
|
||||
US915_FREQUENCY_INCREMENT_DR_0 = 200000 // only for 125 kHz Bandwidth
|
||||
US915_FREQUENCY_INCREMENT_DR_4 = 1600000 // only for 500 kHz Bandwidth
|
||||
)
|
||||
|
||||
type ChannelUS struct {
|
||||
channel
|
||||
}
|
||||
|
||||
func (c *ChannelUS) Next() bool {
|
||||
switch c.Bandwidth() {
|
||||
case lora.Bandwidth_125_0:
|
||||
freq, ok := stepFrequency125(c.frequency)
|
||||
if ok {
|
||||
c.frequency = freq
|
||||
} else {
|
||||
c.frequency = lora.Mhz_903_0
|
||||
c.bandwidth = lora.Bandwidth_500_0
|
||||
}
|
||||
case lora.Bandwidth_500_0:
|
||||
freq, ok := stepFrequency500(c.frequency)
|
||||
if ok {
|
||||
c.frequency = freq
|
||||
} else {
|
||||
// there are no more frequencies to check after sweeping all 8 500 kHz channels
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func stepFrequency125(freq uint32) (uint32, bool) {
|
||||
f := freq + US915_FREQUENCY_INCREMENT_DR_0
|
||||
if f >= lora.MHZ_915_0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return f, true
|
||||
}
|
||||
|
||||
func stepFrequency500(freq uint32) (uint32, bool) {
|
||||
f := freq + US915_FREQUENCY_INCREMENT_DR_4
|
||||
if f >= lora.MHZ_915_0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return f, true
|
||||
}
|
||||
|
||||
type SettingsUS915 struct {
|
||||
settings
|
||||
}
|
||||
|
||||
func US915() *SettingsUS915 {
|
||||
return &SettingsUS915{settings: settings{
|
||||
joinRequestChannel: &ChannelUS{channel: channel{lora.MHz_902_3,
|
||||
lora.Bandwidth_125_0,
|
||||
lora.SpreadingFactor10,
|
||||
lora.CodingRate4_5,
|
||||
US915_DEFAULT_PREAMBLE_LEN,
|
||||
US915_DEFAULT_TX_POWER_DBM}},
|
||||
joinAcceptChannel: &ChannelUS{channel: channel{0,
|
||||
lora.Bandwidth_500_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_5,
|
||||
US915_DEFAULT_PREAMBLE_LEN,
|
||||
US915_DEFAULT_TX_POWER_DBM}},
|
||||
uplinkChannel: &ChannelUS{channel: channel{lora.Mhz_903_0,
|
||||
lora.Bandwidth_500_0,
|
||||
lora.SpreadingFactor9,
|
||||
lora.CodingRate4_5,
|
||||
US915_DEFAULT_PREAMBLE_LEN,
|
||||
US915_DEFAULT_TX_POWER_DBM}},
|
||||
}}
|
||||
}
|
||||
+9
-8
@@ -5,6 +5,7 @@ package lps22hb
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a HTS221 device.
|
||||
@@ -27,9 +28,9 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
|
||||
|
||||
// read data
|
||||
data := []byte{0, 0, 0}
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_PRESS_OUT_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_PRESS_OUT_REG+1, data[1:2])
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_PRESS_OUT_REG+2, data[2:])
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_PRESS_OUT_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_PRESS_OUT_REG+1, data[1:2])
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_PRESS_OUT_REG+2, data[2:])
|
||||
pValue := float32(uint32(data[2])<<16|uint32(data[1])<<8|uint32(data[0])) / 4096.0
|
||||
|
||||
return int32(pValue * 1000), nil
|
||||
@@ -39,7 +40,7 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_WHO_AM_I_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_WHO_AM_I_REG, data)
|
||||
return data[0] == 0xB1
|
||||
}
|
||||
|
||||
@@ -49,8 +50,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
|
||||
// read data
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_TEMP_OUT_REG, data[:1])
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_TEMP_OUT_REG+1, data[1:])
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_TEMP_OUT_REG, data[:1])
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_TEMP_OUT_REG+1, data[1:])
|
||||
tValue := float32(int16(uint16(data[1])<<8|uint16(data[0]))) / 100.0
|
||||
|
||||
return int32(tValue * 1000), nil
|
||||
@@ -61,12 +62,12 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
// wait and trigger one shot in block update
|
||||
func (d *Device) waitForOneShot() {
|
||||
// trigger one shot
|
||||
d.bus.WriteRegister(d.Address, LPS22HB_CTRL2_REG, []byte{0x01})
|
||||
legacy.WriteRegister(d.bus, d.Address, LPS22HB_CTRL2_REG, []byte{0x01})
|
||||
|
||||
// wait until one shot is cleared
|
||||
data := []byte{1}
|
||||
for {
|
||||
d.bus.ReadRegister(d.Address, LPS22HB_CTRL2_REG, data)
|
||||
legacy.ReadRegister(d.bus, d.Address, LPS22HB_CTRL2_REG, data)
|
||||
if data[0]&0x01 == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
package lps22hb
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import "tinygo.org/x/drivers/internal/legacy"
|
||||
|
||||
// Configure sets up the LPS22HB device for communication.
|
||||
func (d *Device) Configure() {
|
||||
// set to block update mode
|
||||
d.bus.WriteRegister(d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
|
||||
legacy.WriteRegister(d.bus, d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ package lps22hb
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Configure sets up the LPS22HB device for communication.
|
||||
@@ -18,5 +20,5 @@ func (d *Device) Configure() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// set to block update mode
|
||||
d.bus.WriteRegister(d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
|
||||
legacy.WriteRegister(d.bus, d.Address, LPS22HB_CTRL1_REG, []byte{0x02})
|
||||
}
|
||||
|
||||
+11
-10
@@ -9,6 +9,7 @@ import (
|
||||
"math"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a LSM303AGR device.
|
||||
@@ -52,8 +53,8 @@ func New(bus drivers.I2C) *Device {
|
||||
// It does two "who am I" requests and checks the responses.
|
||||
func (d *Device) Connected() bool {
|
||||
data1, data2 := []byte{0}, []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_WHO_AM_I, data1)
|
||||
d.bus.ReadRegister(uint8(d.MagAddress), MAG_WHO_AM_I, data2)
|
||||
legacy.ReadRegister(d.bus, uint8(d.AccelAddress), ACCEL_WHO_AM_I, data1)
|
||||
legacy.ReadRegister(d.bus, uint8(d.MagAddress), MAG_WHO_AM_I, data2)
|
||||
return data1[0] == 0x33 && data2[0] == 0x40
|
||||
}
|
||||
|
||||
@@ -104,26 +105,26 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
data := d.buf[:1]
|
||||
|
||||
data[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
|
||||
err = d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data[0] = byte(0x80 | d.AccelRange<<4)
|
||||
err = d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data[0] = byte(0xC0)
|
||||
err = d.bus.WriteRegister(uint8(d.AccelAddress), TEMP_CFG_REG_A, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), TEMP_CFG_REG_A, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Temperature compensation is on for magnetic sensor
|
||||
data[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
|
||||
err = d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -137,7 +138,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_AUTO_INC, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), ACCEL_OUT_AUTO_INC, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -183,14 +184,14 @@ func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||
if d.MagSystemMode == MAG_SYSTEM_SINGLE {
|
||||
cmd := d.buf[:1]
|
||||
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
|
||||
err = d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, cmd)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, cmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := d.buf[0:6]
|
||||
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_AUTO_INC, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.MagAddress), MAG_OUT_AUTO_INC, data)
|
||||
|
||||
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
|
||||
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
|
||||
@@ -219,7 +220,7 @@ func (d *Device) ReadCompass() (h int32, err error) {
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
|
||||
data := d.buf[:2]
|
||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_AUTO_INC, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_TEMP_AUTO_INC, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+13
-12
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -95,7 +96,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
if cfg.IsPedometer { // CONFIGURE AS PEDOMETER
|
||||
// Configure accelerometer: 2G + 26Hz
|
||||
data[0] = uint8(ACCEL_2G) | uint8(ACCEL_SR_26)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -105,40 +106,40 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
if cfg.ResetStepCounter {
|
||||
data[0] |= 0x02
|
||||
}
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL10_C, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL10_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Enable pedometer
|
||||
data[0] = 0x40
|
||||
err = d.bus.WriteRegister(uint8(d.Address), TAP_CFG, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), TAP_CFG, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else { // NORMAL USE
|
||||
// Configure accelerometer
|
||||
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate) | uint8(d.accelBandWidth)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set ODR bit
|
||||
err = d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
|
||||
data[0] |= BW_SCAL_ODR_ENABLED
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure gyroscope
|
||||
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -151,7 +152,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := d.buf[:1]
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x69
|
||||
}
|
||||
|
||||
@@ -161,7 +162,7 @@ func (d *Device) Connected() bool {
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_XL, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -186,7 +187,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
// you would get a value close to 360000000.
|
||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -210,7 +211,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -223,7 +224,7 @@ func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
// ReadSteps returns the steps of the pedometer
|
||||
func (d *Device) ReadSteps() (s int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), STEP_COUNTER_L, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), STEP_COUNTER_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -87,26 +88,26 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
|
||||
// Configure accelerometer
|
||||
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set ODR bit
|
||||
err = d.bus.ReadRegister(uint8(d.Address), CTRL4_C, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
|
||||
data[0] |= BW_SCAL_ODR_ENABLED
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL4_C, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure gyroscope
|
||||
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -118,7 +119,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := d.buf[:1]
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x6A
|
||||
}
|
||||
|
||||
@@ -128,7 +129,7 @@ func (d *Device) Connected() bool {
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_XL, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -153,7 +154,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
// you would get a value close to 360000000.
|
||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -177,7 +178,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -78,13 +79,13 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
data := d.buf[:1]
|
||||
// Configure accelerometer
|
||||
data[0] = uint8(cfg.AccelRange) | uint8(cfg.AccelSampleRate)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL1_XL, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Configure gyroscope
|
||||
data[0] = uint8(cfg.GyroRange) | uint8(cfg.GyroSampleRate)
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL2_G, data)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -96,7 +97,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := d.buf[:1]
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x6C
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ func (d *Device) Connected() bool {
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_A, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_A, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -122,7 +123,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
// you would get a value close to 360000000.
|
||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUTX_L_G, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -135,7 +136,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUT_TEMP_L, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+13
-12
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -61,8 +62,8 @@ func New(bus drivers.I2C) *Device {
|
||||
// but "who am I" responses have unexpected values.
|
||||
func (d *Device) Connected() bool {
|
||||
data1, data2 := d.buf[:1], d.buf[1:2]
|
||||
d.bus.ReadRegister(d.AccelAddress, WHO_AM_I, data1)
|
||||
d.bus.ReadRegister(d.MagAddress, WHO_AM_I_M, data2)
|
||||
legacy.ReadRegister(d.bus, d.AccelAddress, WHO_AM_I, data1)
|
||||
legacy.ReadRegister(d.bus, d.MagAddress, WHO_AM_I_M, data2)
|
||||
return data1[0] == 0x68 && data2[0] == 0x3D
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ func (d *Device) Connected() bool {
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_X_L_XL, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -88,7 +89,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
// you would get a value close to 360000000.
|
||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_X_L_G, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -102,7 +103,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
// it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
|
||||
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = d.bus.ReadRegister(uint8(d.MagAddress), OUT_X_L_M, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), OUT_X_L_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -115,7 +116,7 @@ func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_TEMP_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -171,7 +172,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// Configure accelerometer
|
||||
// Sample rate & measurement range
|
||||
data[0] = uint8(cfg.AccelSampleRate)<<5 | uint8(cfg.AccelRange)<<3
|
||||
err = d.bus.WriteRegister(d.AccelAddress, CTRL_REG6_XL, data)
|
||||
err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG6_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -179,7 +180,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// Configure gyroscope
|
||||
// Sample rate & measurement range
|
||||
data[0] = uint8(cfg.GyroSampleRate)<<5 | uint8(cfg.GyroRange)<<3
|
||||
err = d.bus.WriteRegister(d.AccelAddress, CTRL_REG1_G, data)
|
||||
err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG1_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -190,14 +191,14 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// High-performance mode XY axis
|
||||
// Sample rate
|
||||
data[0] = 0b10000000 | 0b01000000 | uint8(cfg.MagSampleRate)<<2
|
||||
err = d.bus.WriteRegister(d.MagAddress, CTRL_REG1_M, data)
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG1_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Measurement range
|
||||
data[0] = uint8(cfg.MagRange) << 5
|
||||
err = d.bus.WriteRegister(d.MagAddress, CTRL_REG2_M, data)
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG2_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -205,14 +206,14 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// Continuous-conversion mode
|
||||
// https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode
|
||||
data[0] = 0b00000000
|
||||
err = d.bus.WriteRegister(d.MagAddress, CTRL_REG3_M, data)
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG3_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// High-performance mode Z axis
|
||||
data[0] = 0b00001000
|
||||
err = d.bus.WriteRegister(d.MagAddress, CTRL_REG4_M, data)
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG4_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+9
-6
@@ -4,7 +4,10 @@
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf
|
||||
package mag3110 // import "tinygo.org/x/drivers/mag3110"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a MAG3110 device.
|
||||
type Device struct {
|
||||
@@ -24,22 +27,22 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0xC4
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d Device) Configure() {
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_REG2, []uint8{0x80}) // Power down when not used
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_REG2, []uint8{0x80}) // Power down when not used
|
||||
}
|
||||
|
||||
// ReadMagnetic reads the vectors of the magnetic field of the device and
|
||||
// returns it.
|
||||
func (d Device) ReadMagnetic() (x int16, y int16, z int16) {
|
||||
d.bus.WriteRegister(uint8(d.Address), CTRL_REG1, []uint8{0x1a}) // Request a measurement
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_REG1, []uint8{0x1a}) // Request a measurement
|
||||
|
||||
data := make([]byte, 6)
|
||||
d.bus.ReadRegister(uint8(d.Address), OUT_X_MSB, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), OUT_X_MSB, data)
|
||||
x = int16((uint16(data[0]) << 8) | uint16(data[1]))
|
||||
y = int16((uint16(data[2]) << 8) | uint16(data[3]))
|
||||
z = int16((uint16(data[4]) << 8) | uint16(data[5]))
|
||||
@@ -50,6 +53,6 @@ func (d Device) ReadMagnetic() (x int16, y int16, z int16) {
|
||||
// celsius milli degrees (°C/1000).
|
||||
func (d Device) ReadTemperature() (int32, error) {
|
||||
data := make([]byte, 1)
|
||||
d.bus.ReadRegister(uint8(d.Address), DIE_TEMP, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), DIE_TEMP, data)
|
||||
return int32(data[0]) * 1000, nil
|
||||
}
|
||||
|
||||
+7
-11
@@ -8,6 +8,9 @@ package mcp23017
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -75,19 +78,12 @@ const (
|
||||
// address pins).
|
||||
var ErrInvalidHWAddress = errors.New("invalid hardware address")
|
||||
|
||||
// I2C represents an I2C bus. It is notably implemented by the
|
||||
// machine.I2C type.
|
||||
type I2C interface {
|
||||
ReadRegister(addr uint8, r uint8, buf []byte) error
|
||||
WriteRegister(addr uint8, r uint8, buf []byte) error
|
||||
}
|
||||
|
||||
// New returns a new MCP23017 device at the given I2C address
|
||||
// on the given bus.
|
||||
// It returns ErrInvalidHWAddress if the address isn't possible for the device.
|
||||
//
|
||||
// By default all pins are configured as inputs.
|
||||
func NewI2C(bus I2C, address uint8) (*Device, error) {
|
||||
func NewI2C(bus drivers.I2C, address uint8) (*Device, error) {
|
||||
if address&hwAddressMask != hwAddress {
|
||||
return nil, ErrInvalidHWAddress
|
||||
}
|
||||
@@ -115,7 +111,7 @@ type Device struct {
|
||||
|
||||
// bus holds the reference the I2C bus that the device lives on.
|
||||
// It's an interface so that we can write tests for it.
|
||||
bus I2C
|
||||
bus drivers.I2C
|
||||
addr uint8
|
||||
// pins caches the most recent pin values that have been set.
|
||||
// This enables us to change individual pin values without
|
||||
@@ -259,7 +255,7 @@ func (d *Device) writeRegisterAB(r register, val Pins) error {
|
||||
// and the fact that registers alternate between A and B
|
||||
// to write both ports in a single operation.
|
||||
buf := [2]byte{uint8(val), uint8(val >> 8)}
|
||||
return d.bus.WriteRegister(d.addr, uint8(r&^portB), buf[:])
|
||||
return legacy.WriteRegister(d.bus, d.addr, uint8(r&^portB), buf[:])
|
||||
}
|
||||
|
||||
func (d *Device) readRegisterAB(r register) (Pins, error) {
|
||||
@@ -267,7 +263,7 @@ func (d *Device) readRegisterAB(r register) (Pins, error) {
|
||||
// and the fact that registers alternate between A and B
|
||||
// to read both ports in a single operation.
|
||||
var buf [2]byte
|
||||
if err := d.bus.ReadRegister(d.addr, uint8(r), buf[:]); err != nil {
|
||||
if err := legacy.ReadRegister(d.bus, d.addr, uint8(r), buf[:]); err != nil {
|
||||
return Pins(0), err
|
||||
}
|
||||
return Pins(buf[0]) | (Pins(buf[1]) << 8), nil
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package mcp23017
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
// All is a convenience value that represents all pins high (or all mask bits one).
|
||||
var All = PinSlice{0xffff}
|
||||
|
||||
@@ -12,7 +14,7 @@ type Devices []*Device
|
||||
// NewI2CDevices returns a Devices slice holding the Device values
|
||||
// for all the given addresses on the given bus.
|
||||
// When more than one bus is in use, create the slice yourself.
|
||||
func NewI2CDevices(bus I2C, addrs ...uint8) (Devices, error) {
|
||||
func NewI2CDevices(bus drivers.I2C, addrs ...uint8) (Devices, error) {
|
||||
devs := make(Devices, len(addrs))
|
||||
for i, addr := range addrs {
|
||||
dev, err := NewI2C(bus, addr)
|
||||
|
||||
+9
-6
@@ -5,7 +5,10 @@
|
||||
// https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf
|
||||
package mma8653 // import "tinygo.org/x/drivers/mma8653"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a MMA8653 device.
|
||||
type Device struct {
|
||||
@@ -26,27 +29,27 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x5A
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d *Device) Configure(speed DataRate, sensitivity Sensitivity) error {
|
||||
// Set mode to STANDBY to be able to change the sensitivity.
|
||||
err := d.bus.WriteRegister(uint8(d.Address), CTRL_REG1, []uint8{0})
|
||||
err := legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_REG1, []uint8{0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set sensitivity (2G, 4G, 8G).
|
||||
err = d.bus.WriteRegister(uint8(d.Address), XYZ_DATA_CFG, []uint8{uint8(sensitivity)})
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), XYZ_DATA_CFG, []uint8{uint8(sensitivity)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.sensitivity = sensitivity
|
||||
|
||||
// Set mode to ACTIVE and set the data rate.
|
||||
err = d.bus.WriteRegister(uint8(d.Address), CTRL_REG1, []uint8{(uint8(speed) << 3) | 1})
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_REG1, []uint8{(uint8(speed) << 3) | 1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,7 +62,7 @@ func (d *Device) Configure(speed DataRate, sensitivity Sensitivity) error {
|
||||
// -1000000.
|
||||
func (d Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
data := make([]byte, 6)
|
||||
err = d.bus.ReadRegister(uint8(d.Address), OUT_X_MSB, data)
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_X_MSB, data)
|
||||
shift := uint32(8)
|
||||
switch d.sensitivity {
|
||||
case Sensitivity4G:
|
||||
|
||||
+10
-7
@@ -6,7 +6,10 @@
|
||||
// https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
|
||||
package mpu6050 // import "tinygo.org/x/drivers/mpu6050"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a MPU6050 device.
|
||||
type Device struct {
|
||||
@@ -26,7 +29,7 @@ func New(bus drivers.I2C) Device {
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x68
|
||||
}
|
||||
|
||||
@@ -41,7 +44,7 @@ func (d Device) Configure() error {
|
||||
// -1000000.
|
||||
func (d Device) ReadAcceleration() (x int32, y int32, z int32) {
|
||||
data := make([]byte, 6)
|
||||
d.bus.ReadRegister(uint8(d.Address), ACCEL_XOUT_H, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), ACCEL_XOUT_H, data)
|
||||
// Now do two things:
|
||||
// 1. merge the two values to a 16-bit number (and cast to a 32-bit integer)
|
||||
// 2. scale the value to bring it in the -1000000..1000000 range.
|
||||
@@ -62,7 +65,7 @@ func (d Device) ReadAcceleration() (x int32, y int32, z int32) {
|
||||
// you would get a value close to 360000000.
|
||||
func (d Device) ReadRotation() (x int32, y int32, z int32) {
|
||||
data := make([]byte, 6)
|
||||
d.bus.ReadRegister(uint8(d.Address), GYRO_XOUT_H, data)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), GYRO_XOUT_H, data)
|
||||
// First the value is converted from a pair of bytes to a signed 16-bit
|
||||
// value and then to a signed 32-bit value to avoid integer overflow.
|
||||
// Then the value is scaled to µ°/s (micro-degrees per second).
|
||||
@@ -81,15 +84,15 @@ func (d Device) ReadRotation() (x int32, y int32, z int32) {
|
||||
|
||||
// SetClockSource allows the user to configure the clock source.
|
||||
func (d Device) SetClockSource(source uint8) error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{source})
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), PWR_MGMT_1, []uint8{source})
|
||||
}
|
||||
|
||||
// SetFullScaleGyroRange allows the user to configure the scale range for the gyroscope.
|
||||
func (d Device) SetFullScaleGyroRange(rng uint8) error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), GYRO_CONFIG, []uint8{rng})
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), GYRO_CONFIG, []uint8{rng})
|
||||
}
|
||||
|
||||
// SetFullScaleAccelRange allows the user to configure the scale range for the accelerometer.
|
||||
func (d Device) SetFullScaleAccelRange(rng uint8) error {
|
||||
return d.bus.WriteRegister(uint8(d.Address), ACCEL_CONFIG, []uint8{rng})
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), ACCEL_CONFIG, []uint8{rng})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Package mpu9150 provides a driver for the MPU9150 accelerometer and gyroscope
|
||||
// made by InvenSense.
|
||||
//
|
||||
// Datasheets:
|
||||
// https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-9150-Datasheet.pdf
|
||||
// https://inertialelements.com/documents/resources_page/MPU9150-register-manual.pdf
|
||||
|
||||
package mpu9150
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a MPU9150 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
}
|
||||
|
||||
// New creates a new MPU9150 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{bus, Address}
|
||||
}
|
||||
|
||||
// Connected returns whether a MPU9150 has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x68 // 4.32 Register 117 – Who Am I (MPU-9150 Register Map and Descriptions)
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d Device) Configure() error {
|
||||
return d.SetClockSource(CLOCK_INTERNAL)
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
// it in µg (micro-gravity). When one of the axes is pointing straight to Earth
|
||||
// and the sensor is not moving the returned value will be around 1000000 or
|
||||
// -1000000.
|
||||
func (d Device) ReadAcceleration(accel_axis byte) (x int32, y int32, z int32) {
|
||||
data := make([]byte, 6)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), accel_axis, data)
|
||||
// Now do two things:
|
||||
// 1. merge the two values to a 16-bit number (and cast to a 32-bit integer)
|
||||
// 2. scale the value to bring it in the -1000000..1000000 range.
|
||||
// This is done with a trick. What we do here is essentially multiply by
|
||||
// 1000000 and divide by 16384 to get the original scale, but to avoid
|
||||
// overflow we do it at 1/64 of the value:
|
||||
// 1000000 / 64 = 15625
|
||||
// 16384 / 64 = 256
|
||||
x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / 256
|
||||
y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / 256
|
||||
z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 256
|
||||
return
|
||||
}
|
||||
|
||||
// ReadRotation reads the current rotation from the device and returns it in
|
||||
// µ°/s (micro-degrees/sec). This means that if you were to do a complete
|
||||
// rotation along one axis and while doing so integrate all values over time,
|
||||
// you would get a value close to 360000000.
|
||||
func (d Device) ReadRotation(gyro_axis byte) (x int32, y int32, z int32) {
|
||||
data := make([]byte, 6)
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), gyro_axis, data)
|
||||
// First the value is converted from a pair of bytes to a signed 16-bit
|
||||
// value and then to a signed 32-bit value to avoid integer overflow.
|
||||
// Then the value is scaled to µ°/s (micro-degrees per second).
|
||||
// This is done in the following steps:
|
||||
// 1. Multiply by 250 * 1000_000
|
||||
// 2. Divide by 32768
|
||||
// The following calculation (x * 15625 / 2048 * 1000) is essentially the
|
||||
// same but avoids overflow. First both operations are divided by 16 leading
|
||||
// to multiply by 15625000 and divide by 2048, and then part of the multiply
|
||||
// is done after the divide instead of before.
|
||||
x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / 2048 * 1000
|
||||
y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / 2048 * 1000
|
||||
z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 2048 * 1000
|
||||
return
|
||||
}
|
||||
|
||||
// SetClockSource allows the user to configure the clock source.
|
||||
func (d Device) SetClockSource(source uint8) error {
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), PWR_MGMT_1, []uint8{source})
|
||||
}
|
||||
|
||||
// SetFullScaleGyroRange allows the user to configure the scale range for the gyroscope.
|
||||
func (d Device) SetFullScaleGyroRange(rng uint8) error {
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), GYRO_CONFIG, []uint8{rng})
|
||||
}
|
||||
|
||||
// SetFullScaleAccelRange allows the user to configure the scale range for the accelerometer.
|
||||
func (d Device) SetFullScaleAccelRange(rng uint8) error {
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), ACCEL_CONFIG, []uint8{rng})
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package mpu9150
|
||||
|
||||
// Constants/addresses used for I2C.
|
||||
|
||||
// The I2C address which this device listens to.
|
||||
const Address = 0x68
|
||||
|
||||
// Registers. Names, addresses and comments copied from the datasheet.
|
||||
const (
|
||||
// Self test registers
|
||||
SELF_TEST_X = 0x0D
|
||||
SELF_TEST_Y = 0x0E
|
||||
SELF_TEST_Z = 0x0F
|
||||
SELF_TEST_A = 0x10
|
||||
|
||||
SMPLRT_DIV = 0x19 // Sample rate divider
|
||||
CONFIG = 0x1A // Configuration
|
||||
GYRO_CONFIG = 0x1B // Gyroscope configuration
|
||||
ACCEL_CONFIG = 0x1C // Accelerometer configuration
|
||||
FIFO_EN = 0x23 // FIFO enable
|
||||
|
||||
// I2C pass-through configuration
|
||||
I2C_MST_CTRL = 0x24
|
||||
I2C_SLV0_ADDR = 0x25
|
||||
I2C_SLV0_REG = 0x26
|
||||
I2C_SLV0_CTRL = 0x27
|
||||
I2C_SLV1_ADDR = 0x28
|
||||
I2C_SLV1_REG = 0x29
|
||||
I2C_SLV1_CTRL = 0x2A
|
||||
I2C_SLV2_ADDR = 0x2B
|
||||
I2C_SLV2_REG = 0x2C
|
||||
I2C_SLV2_CTRL = 0x2D
|
||||
I2C_SLV3_ADDR = 0x2E
|
||||
I2C_SLV3_REG = 0x2F
|
||||
I2C_SLV3_CTRL = 0x30
|
||||
I2C_SLV4_ADDR = 0x31
|
||||
I2C_SLV4_REG = 0x32
|
||||
I2C_SLV4_DO = 0x33
|
||||
I2C_SLV4_CTRL = 0x34
|
||||
I2C_SLV4_DI = 0x35
|
||||
I2C_MST_STATUS = 0x36
|
||||
|
||||
// Interrupt configuration
|
||||
INT_PIN_CFG = 0x37 // Interrupt pin/bypass enable configuration
|
||||
INT_ENABLE = 0x38 // Interrupt enable
|
||||
INT_STATUS = 0x3A // Interrupt status
|
||||
|
||||
// Accelerometer measurements
|
||||
ACCEL_XOUT_H = 0x3B
|
||||
ACCEL_XOUT_L = 0x3C
|
||||
ACCEL_YOUT_H = 0x3D
|
||||
ACCEL_YOUT_L = 0x3E
|
||||
ACCEL_ZOUT_H = 0x3F
|
||||
ACCEL_ZOUT_L = 0x40
|
||||
|
||||
// Temperature measurement
|
||||
TEMP_OUT_H = 0x41
|
||||
TEMP_OUT_L = 0x42
|
||||
|
||||
// Gyroscope measurements
|
||||
GYRO_XOUT_H = 0x43
|
||||
GYRO_XOUT_L = 0x44
|
||||
GYRO_YOUT_H = 0x45
|
||||
GYRO_YOUT_L = 0x46
|
||||
GYRO_ZOUT_H = 0x47
|
||||
GYRO_ZOUT_L = 0x48
|
||||
|
||||
// External sensor data
|
||||
EXT_SENS_DATA_00 = 0x49
|
||||
EXT_SENS_DATA_01 = 0x4A
|
||||
EXT_SENS_DATA_02 = 0x4B
|
||||
EXT_SENS_DATA_03 = 0x4C
|
||||
EXT_SENS_DATA_04 = 0x4D
|
||||
EXT_SENS_DATA_05 = 0x4E
|
||||
EXT_SENS_DATA_06 = 0x4F
|
||||
EXT_SENS_DATA_07 = 0x50
|
||||
EXT_SENS_DATA_08 = 0x51
|
||||
EXT_SENS_DATA_09 = 0x52
|
||||
EXT_SENS_DATA_10 = 0x53
|
||||
EXT_SENS_DATA_11 = 0x54
|
||||
EXT_SENS_DATA_12 = 0x55
|
||||
EXT_SENS_DATA_13 = 0x56
|
||||
EXT_SENS_DATA_14 = 0x57
|
||||
EXT_SENS_DATA_15 = 0x58
|
||||
EXT_SENS_DATA_16 = 0x59
|
||||
EXT_SENS_DATA_17 = 0x5A
|
||||
EXT_SENS_DATA_18 = 0x5B
|
||||
EXT_SENS_DATA_19 = 0x5C
|
||||
EXT_SENS_DATA_20 = 0x5D
|
||||
EXT_SENS_DATA_21 = 0x5E
|
||||
EXT_SENS_DATA_22 = 0x5F
|
||||
EXT_SENS_DATA_23 = 0x60
|
||||
|
||||
// I2C peripheral data out
|
||||
I2C_SLV0_DO = 0x63
|
||||
I2C_SLV1_DO = 0x64
|
||||
I2C_SLV2_DO = 0x65
|
||||
I2C_SLV3_DO = 0x66
|
||||
I2C_MST_DELAY_CTRL = 0x67
|
||||
SIGNAL_PATH_RESET = 0x68
|
||||
|
||||
USER_CTRL = 0x6A // User control
|
||||
PWR_MGMT_1 = 0x6B // Power Management 1
|
||||
PWR_MGMT_2 = 0x6C // Power Management 2
|
||||
FIFO_COUNTH = 0x72 // FIFO count registers (high bits)
|
||||
FIFO_COUNTL = 0x73 // FIFO count registers (low bits)
|
||||
FIFO_R_W = 0x74 // FIFO read/write
|
||||
WHO_AM_I = 0x75 // Who am I
|
||||
|
||||
// Clock settings (4.28 Register 107 – Power Management 1)
|
||||
CLOCK_INTERNAL = 0x00
|
||||
CLOCK_PLL_XGYRO = 0x01
|
||||
CLOCK_PLL_YGYRO = 0x02
|
||||
CLOCK_PLL_ZGYRO = 0x03
|
||||
CLOCK_PLL_EXTERNAL_32_768_KZ = 0x04
|
||||
CLOCK_PLL_EXTERNAL_19_2_MHZ = 0x05
|
||||
CLOCK_RESERVED = 0x06
|
||||
CLOCK_STOP = 0x07
|
||||
|
||||
// Gyroscope settings (4.4 Register 27 – Gyroscope Configuration)
|
||||
FS_RANGE_250 = 0x00
|
||||
FS_RANGE_500 = 0x01
|
||||
FS_RANGE_1000 = 0x02
|
||||
FS_RANGE_2000 = 0x03
|
||||
|
||||
// Accelerometer settings (4.5 Register 28 – Accelerometer Configuration)
|
||||
AFS_RANGE_2G = 0x00
|
||||
AFS_RANGE_4G = 0x01
|
||||
AFS_RANGE_8G = 0x02
|
||||
AFS_RANGE_16G = 0x03
|
||||
)
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
package ndir
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Addr returns the I2C address given the solder pad configuration on the Sandbox Electronics i2c/uart converter.
|
||||
// When the resistor is connected between the left and middle pads the bit is said to be set
|
||||
// and a0 or a1 should be passed in as true.
|
||||
func Addr(a0, a1 bool) uint8 {
|
||||
return 0b1001000 | b2u8(a0) | b2u8(a1)<<2
|
||||
}
|
||||
|
||||
func b2u8(b bool) uint8 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// See https://github.com/SandboxElectronics/NDIR/blob/master/NDIR_I2C/NDIR_I2C.cpp
|
||||
|
||||
// General Registers
|
||||
const (
|
||||
addrRHR = 0x00
|
||||
addrTHR = 0x00
|
||||
addrIER = 0x01
|
||||
addrFCR = 0x02
|
||||
addrIIR = 0x02
|
||||
addrLCR = 0x03
|
||||
addrMCR = 0x04
|
||||
addrLSR = 0x05
|
||||
addrMSR = 0x06
|
||||
addrSPR = 0x07
|
||||
addrTCR = 0x06
|
||||
addrTLR = 0x07
|
||||
addrTXLVL = 0x08
|
||||
addrRXLVL = 0x09
|
||||
addrIODIR = 0x0A
|
||||
addrIOSTATE = 0x0B
|
||||
addrIOINTENA = 0x0C
|
||||
addrIOCONTROL = 0x0E // This addr fails on write of 0x08?
|
||||
addrEFCR = 0x0F
|
||||
)
|
||||
|
||||
// Special registers
|
||||
const (
|
||||
addrDLL = 0x00
|
||||
addrDLH = 1
|
||||
)
|
||||
|
||||
const (
|
||||
shortTxCooldown = time.Millisecond
|
||||
longTxCooldown = 10 * time.Millisecond
|
||||
rxTimeout = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
var (
|
||||
cmd_readCO2 = [...]byte{0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}
|
||||
cmd_measure = [...]byte{0xFF, 0x01, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63}
|
||||
cmd_calibrateZero = [...]byte{0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78}
|
||||
cmd_enableAutoCalibration = [...]byte{0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}
|
||||
cmd_disableAutoCalibration = [...]byte{0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}
|
||||
)
|
||||
|
||||
// DevI2C is a handle to a MH-Z16 NDIR CO2 Sensor using the I2C interface.
|
||||
type DevI2C struct {
|
||||
bus drivers.I2C
|
||||
addr uint8
|
||||
nextAvail time.Time
|
||||
initTime time.Time
|
||||
lastMeasurement int32
|
||||
}
|
||||
|
||||
// NewDevI2C returns a new NDIR device ready for use. It performs no I/O.
|
||||
func NewDevI2C(bus drivers.I2C, addr uint8) *DevI2C {
|
||||
return &DevI2C{
|
||||
bus: bus,
|
||||
addr: addr,
|
||||
lastMeasurement: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// PPM returns the CO2 parts per million read in the last Update call.
|
||||
func (d *DevI2C) PPMCO2() int32 {
|
||||
return d.lastMeasurement
|
||||
}
|
||||
|
||||
var errInitWait = errors.New("ndir: must wait 12 seconds after init before reading concentration")
|
||||
|
||||
// Update reads the CO2 concentration from the NDIR and stores it ready for the
|
||||
// PPM() method.
|
||||
func (d *DevI2C) Update(which drivers.Measurement) (err error) {
|
||||
if which&drivers.Concentration == 0 {
|
||||
return nil // NDIR only measures concentration, so nothing to do here.
|
||||
}
|
||||
if time.Since(d.initTime) < 12*time.Second {
|
||||
// Wait 12 seconds before performing first read.
|
||||
return nil
|
||||
}
|
||||
err = d.writeRegister(addrFCR, 0x07)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.send(cmd_measure[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("sending cmd_measure: %w", err)
|
||||
}
|
||||
time.Sleep(11 * time.Millisecond)
|
||||
var buf [9]byte
|
||||
buf, err = d.receive()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("receiving during measure: %w", err)
|
||||
}
|
||||
if buf[0] != 0xff && buf[1] != 0x9c {
|
||||
return fmt.Errorf("buffer rx bad values: %q", string(buf[:]))
|
||||
}
|
||||
var sum uint16
|
||||
for i := 0; i < len(buf); i++ {
|
||||
sum += uint16(buf[i])
|
||||
}
|
||||
mod := sum % 256
|
||||
if mod != 0xff {
|
||||
return fmt.Errorf("ndir checksum modulus got %#x, expected 0xff", mod)
|
||||
}
|
||||
ppm := uint32(buf[2])<<24 | uint32(buf[3])<<16 | uint32(buf[4])<<8 | uint32(buf[5])
|
||||
d.lastMeasurement = int32(ppm)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DevI2C) Init() (err error) {
|
||||
// AddrIOCONTROL write is always NACKed so ignore
|
||||
// error here.
|
||||
d.writeRegister(addrIOCONTROL, 0x08)
|
||||
|
||||
err = d.writeRegister(addrFCR, 0x07)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.writeRegister(addrLCR, 0x83)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.writeRegister(addrDLL, 0x60)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.writeRegister(addrDLH, 0x00)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.writeRegister(addrLCR, 0x03)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.initTime = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalibrateZero calibrates the NDIR to around 412ppm.
|
||||
func (d *DevI2C) CalibrateZero() error {
|
||||
return d.enactCommand(cmd_calibrateZero[:])
|
||||
}
|
||||
|
||||
// SetAutoCalibration can enable or disable the NDIR's auto calibration mode.
|
||||
func (d *DevI2C) SetAutoCalibration(enable bool) (err error) {
|
||||
if enable {
|
||||
err = d.enactCommand(cmd_enableAutoCalibration[:])
|
||||
} else {
|
||||
err = d.enactCommand(cmd_disableAutoCalibration[:])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DevI2C) send(cmd []byte) error {
|
||||
txlvl, err := d.ReadRegister(addrTXLVL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if int(txlvl) < len(cmd) {
|
||||
return fmt.Errorf("txlvl=%d less than length of command %d", txlvl, len(cmd))
|
||||
}
|
||||
return d.tx(append([]byte{addrTHR}, cmd...), nil)
|
||||
}
|
||||
|
||||
func (d *DevI2C) receive() (cmd [9]byte, err error) {
|
||||
start := time.Now()
|
||||
n := uint8(9)
|
||||
for n > 0 {
|
||||
if time.Since(start) > rxTimeout {
|
||||
return [9]byte{}, errors.New("NDIR rx timeout")
|
||||
}
|
||||
rxlvl, err := d.ReadRegister(addrRXLVL)
|
||||
if err != nil {
|
||||
return [9]byte{}, err
|
||||
}
|
||||
if rxlvl > n {
|
||||
rxlvl = n
|
||||
}
|
||||
ptr := 9 - n
|
||||
err = d.tx([]byte{addrRHR << 3}, cmd[ptr:ptr+rxlvl])
|
||||
n -= rxlvl
|
||||
if err != nil {
|
||||
return [9]byte{}, err
|
||||
}
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (d *DevI2C) enactCommand(cmd []byte) error {
|
||||
if len(cmd) > 31 {
|
||||
return errors.New("ndir: command too long")
|
||||
}
|
||||
// Most commands always start with the same FCR write here.
|
||||
err := d.writeRegister(addrFCR, 0x07)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(longTxCooldown)
|
||||
|
||||
// C++ send method begins here.
|
||||
got, err := d.ReadRegister(addrTXLVL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if got < uint8(len(cmd)) {
|
||||
return fmt.Errorf("ndir: txlevel=%d too low for command of length %d", got, len(cmd))
|
||||
}
|
||||
var buf [32]byte
|
||||
buf[0] = addrTHR
|
||||
n := 1 + copy(buf[1:], cmd)
|
||||
err = d.tx(buf[:n], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.nextAvail.Add(longTxCooldown) // add some extra time.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DevI2C) writeRegister(addr, val uint8) (err error) {
|
||||
return d.WriteRegisters(addr, []byte{val})
|
||||
}
|
||||
|
||||
func (d *DevI2C) WriteRegisters(addr uint8, vals []byte) (err error) {
|
||||
var buf [32]byte
|
||||
if len(vals) > 31 {
|
||||
return errors.New("can only write up to 31 bytes")
|
||||
}
|
||||
buf[0] = addr << 3
|
||||
n := copy(buf[1:], vals)
|
||||
err = d.tx(buf[:n+1], nil)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("NDIR write %#x (%d) to %#x: %w", buf[1], len(vals), buf[0], err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DevI2C) ReadRegister(addr uint8) (uint8, error) {
|
||||
var buf [2]byte
|
||||
buf[0] = addr << 3
|
||||
err := d.tx(buf[:1], buf[1:2])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("NDIR read from %#x: %w", buf[0], err)
|
||||
}
|
||||
return buf[1], err
|
||||
}
|
||||
|
||||
func (d *DevI2C) tx(w, r []byte) error {
|
||||
wait := time.Until(d.nextAvail)
|
||||
if wait > 0 {
|
||||
// Try yielding process first, maybe there's a short time to wait and a schedule call is enough delay.
|
||||
runtime.Gosched()
|
||||
wait = time.Until(d.nextAvail)
|
||||
if wait > 0 {
|
||||
// If yielding did not work then perform sleep
|
||||
time.Sleep(wait)
|
||||
}
|
||||
}
|
||||
err := d.bus.Tx(uint16(d.addr), w, r)
|
||||
d.nextAvail = time.Now().Add(shortTxCooldown)
|
||||
return err
|
||||
}
|
||||
+4
-2
@@ -1,9 +1,11 @@
|
||||
package pca9685
|
||||
|
||||
import "tinygo.org/x/drivers/internal/legacy"
|
||||
|
||||
func (d *Dev) readReg(reg uint8, data []byte) error {
|
||||
return d.bus.ReadRegister(d.addr, reg, data)
|
||||
return legacy.ReadRegister(d.bus, d.addr, reg, data)
|
||||
}
|
||||
|
||||
func (d *Dev) writeReg(reg uint8, data []byte) error {
|
||||
return d.bus.WriteRegister(d.addr, reg, data)
|
||||
return legacy.WriteRegister(d.bus, d.addr, reg, data)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Package pcf8523 implements a driver for the PCF8523 CMOS Real-Time Clock (RTC)
|
||||
//
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8523.pdf
|
||||
package pcf8523
|
||||
|
||||
import (
|
||||
"time"
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
}
|
||||
|
||||
func New(i2c drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: i2c,
|
||||
Address: DefaultAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the device according to the datasheet section 8.3
|
||||
// This does not wipe the time registers, but resets control registers.
|
||||
func (d *Device) Reset() (err error) {
|
||||
return d.bus.Tx(uint16(d.Address), []byte{rControl1, 0x58}, nil)
|
||||
}
|
||||
|
||||
// SetPowerManagement configures how the device makes use of the backup battery, see
|
||||
// datasheet section 8.5
|
||||
func (d *Device) SetPowerManagement(b PowerManagement) error {
|
||||
return d.setRegister(rControl3, byte(b)<<5, 0xE0)
|
||||
}
|
||||
|
||||
func (d *Device) setRegister(reg uint8, value, mask uint8) error {
|
||||
var buf [1]byte
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{reg}, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf[0] = (value & mask) | (buf[0] & (^mask))
|
||||
return d.bus.Tx(uint16(d.Address), []byte{reg, buf[0]}, nil)
|
||||
}
|
||||
|
||||
// SetTime sets the time and date
|
||||
func (d *Device) SetTime(t time.Time) error {
|
||||
buf := []byte{
|
||||
rSeconds,
|
||||
bin2bcd(t.Second()),
|
||||
bin2bcd(t.Minute()),
|
||||
bin2bcd(t.Hour()),
|
||||
bin2bcd(t.Day()),
|
||||
bin2bcd(int(t.Weekday())),
|
||||
bin2bcd(int(t.Month())),
|
||||
bin2bcd(t.Year() - 2000),
|
||||
}
|
||||
|
||||
return d.bus.Tx(uint16(d.Address), buf, nil)
|
||||
}
|
||||
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (time.Time, error) {
|
||||
buf := make([]byte, 9)
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{rSeconds}, buf)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
seconds := bcd2bin(buf[0] & 0x7F)
|
||||
minute := bcd2bin(buf[1] & 0x7F)
|
||||
hour := bcd2bin(buf[2] & 0x3F)
|
||||
day := bcd2bin(buf[3] & 0x3F)
|
||||
//skipping weekday buf[4]
|
||||
month := time.Month(bcd2bin(buf[5] & 0x1F))
|
||||
year := int(bcd2bin(buf[6])) + 2000
|
||||
|
||||
t := time.Date(year, month, day, hour, minute, seconds, 0, time.UTC)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// bin2bcd converts binary to BCD
|
||||
func bin2bcd(dec int) uint8 {
|
||||
return uint8(dec + 6*(dec/10))
|
||||
}
|
||||
|
||||
// bcd2bin converts BCD to binary
|
||||
func bcd2bin(bcd uint8) int {
|
||||
return int(bcd - 6*(bcd>>4))
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package pcf8523
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
"time"
|
||||
"tinygo.org/x/drivers/tester"
|
||||
)
|
||||
|
||||
func TestDecToBcd_RoundTrip(t *testing.T) {
|
||||
|
||||
for i := 0; i < 60; i++ {
|
||||
a := bcd2bin(bin2bcd(i))
|
||||
if a != i {
|
||||
t.Logf("not equal: %d != %d", a, i)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevice_Reset(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
err := dev.Reset()
|
||||
assertNoError(t, err)
|
||||
|
||||
assertEquals(t, fake.Registers[rControl1], 0x58)
|
||||
}
|
||||
|
||||
func TestDevice_SetPowerManagement(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
err := dev.SetPowerManagement(PowerManagement_SwitchOver_ModeStandard)
|
||||
assertNoError(t, err)
|
||||
|
||||
assertEquals(t, fake.Registers[rControl3], 0b100<<5)
|
||||
}
|
||||
|
||||
func TestDevice_SetTime(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
pointInTime, _ := time.Parse(time.RFC3339, "2023-09-12T22:35:50Z")
|
||||
err := dev.SetTime(pointInTime)
|
||||
assertNoError(t, err)
|
||||
|
||||
actual := hex.EncodeToString(fake.Registers[rSeconds : rSeconds+7])
|
||||
expected := "50352212020923"
|
||||
assertEquals(t, actual, expected)
|
||||
}
|
||||
|
||||
func TestDevice_ReadTime(t *testing.T) {
|
||||
bus := tester.NewI2CBus(t)
|
||||
fake := bus.NewDevice(DefaultAddress)
|
||||
|
||||
expectedPointInTime := time.Date(2023, 9, 12, 17, 55, 42, 0, time.UTC)
|
||||
fake.Registers[rSeconds] = 0x42
|
||||
fake.Registers[rMinutes] = 0x55
|
||||
fake.Registers[rHours] = 0x17
|
||||
fake.Registers[rDays] = 0x12
|
||||
fake.Registers[rMonths] = 0x9
|
||||
fake.Registers[rYears] = 0x23
|
||||
|
||||
dev := New(bus)
|
||||
|
||||
//when
|
||||
actualPointInTime, err := dev.ReadTime()
|
||||
|
||||
//then
|
||||
assertNoError(t, err)
|
||||
assertEquals(t, actualPointInTime, expectedPointInTime)
|
||||
}
|
||||
|
||||
func assertNoError(t testing.TB, e error) {
|
||||
if e != nil {
|
||||
t.Fatalf("unexpected error: %v", e)
|
||||
}
|
||||
}
|
||||
func assertEquals[T comparable](t testing.TB, a, b T) {
|
||||
if a != b {
|
||||
t.Fatalf("%v != %v", a, b)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package pcf8523
|
||||
|
||||
const DefaultAddress = 0x68
|
||||
|
||||
// datasheet 8.5 Power management functions, table 11
|
||||
type PowerManagement byte
|
||||
|
||||
const (
|
||||
PowerManagement_SwitchOver_ModeStandard_LowDetection PowerManagement = 0b000
|
||||
PowerManagement_SwitchOver_ModeDirect_LowDetection PowerManagement = 0b001
|
||||
PowerManagement_VddOnly_LowDetection PowerManagement = 0b010
|
||||
PowerManagement_SwitchOver_ModeStandard PowerManagement = 0b100
|
||||
PowerManagement_SwitchOver_ModeDirect PowerManagement = 0b101
|
||||
PowerManagement_VddOnly PowerManagement = 0b101
|
||||
)
|
||||
|
||||
// constants for all internal registers
|
||||
const (
|
||||
rControl1 = 0x00 // Control_1
|
||||
rControl2 = 0x01 // Control_2
|
||||
rControl3 = 0x02 // Control_3
|
||||
rSeconds = 0x03 // Seconds
|
||||
rMinutes = 0x04 // Minutes
|
||||
rHours = 0x05 // Hours
|
||||
rDays = 0x06 // Days
|
||||
rWeekdays = 0x07 // Weekdays
|
||||
rMonths = 0x08 // Months
|
||||
rYears = 0x09 // Years
|
||||
rMinuteAlarm = 0x0A // Minute_alarm
|
||||
rHourAlarm = 0x0B // Hour_alarm
|
||||
rDayAlarm = 0x0C // Day_alarm
|
||||
rWeekdayAlarm = 0x0D // Weekday_alarm
|
||||
rOffset = 0x0E // Offset
|
||||
rTimerClkoutControl = 0x0F // Tmr_CLKOUT_ctrl
|
||||
rTimerAFrequencyControl = 0x10 // Tmr_A_freq_ctrl
|
||||
rTimerARegister = 0x11 // Tmr_A_reg
|
||||
rTimerBFrequencyControl = 0x12 // Tmr_B_freq_ctrl
|
||||
rTimerBRegister = 0x13 // Tmr_B_reg
|
||||
)
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package pixel
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Image buffer, used for working with the native image format of various
|
||||
// displays. It works a lot like a slice: it can be rescaled while reusing the
|
||||
// underlying buffer and should be passed around by value.
|
||||
type Image[T Color] struct {
|
||||
width int16
|
||||
height int16
|
||||
data unsafe.Pointer
|
||||
}
|
||||
|
||||
// NewImage creates a new image of the given size.
|
||||
func NewImage[T Color](width, height int) Image[T] {
|
||||
if width < 0 || height < 0 || int(int16(width)) != width || int(int16(height)) != height {
|
||||
// The width/height are stored as 16-bit integers and should never be
|
||||
// negative.
|
||||
panic("NewImage: width/height out of bounds")
|
||||
}
|
||||
var zeroColor T
|
||||
var data unsafe.Pointer
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
// Typical formats like RGB888 and RGB565.
|
||||
// Each color starts at a whole byte offset from the start.
|
||||
buf := make([]T, width*height)
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
// Formats like RGB444 that have 12 bits per pixel.
|
||||
// We access these as bytes, so allocate the buffer as a byte slice.
|
||||
bufBits := width * height * zeroColor.BitsPerPixel()
|
||||
bufBytes := (bufBits + 7) / 8
|
||||
buf := make([]byte, bufBytes)
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
}
|
||||
return Image[T]{
|
||||
width: int16(width),
|
||||
height: int16(height),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Rescale returns a new Image buffer based on the img buffer.
|
||||
// The contents is undefined after the Rescale operation, and any modification
|
||||
// to the returned image will overwrite the underlying image buffer in undefined
|
||||
// ways. It will panic if width*height is larger than img.Len().
|
||||
func (img Image[T]) Rescale(width, height int) Image[T] {
|
||||
if width*height > img.Len() {
|
||||
panic("Image.Rescale size out of bounds")
|
||||
}
|
||||
return Image[T]{
|
||||
width: int16(width),
|
||||
height: int16(height),
|
||||
data: img.data,
|
||||
}
|
||||
}
|
||||
|
||||
// LimitHeight returns a subimage with the bottom part cut off, as specified by
|
||||
// height.
|
||||
func (img Image[T]) LimitHeight(height int) Image[T] {
|
||||
if height < 0 || height > int(img.height) {
|
||||
panic("Image.LimitHeight: out of bounds")
|
||||
}
|
||||
return Image[T]{
|
||||
width: img.width,
|
||||
height: int16(height),
|
||||
data: img.data,
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of pixels in this image buffer.
|
||||
func (img Image[T]) Len() int {
|
||||
return int(img.width) * int(img.height)
|
||||
}
|
||||
|
||||
// RawBuffer returns a byte slice that can be written directly to the screen
|
||||
// using DrawRGBBitmap8.
|
||||
func (img Image[T]) RawBuffer() []uint8 {
|
||||
var zeroColor T
|
||||
var numBytes int
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
// Each color starts at a whole byte offset.
|
||||
numBytes = int(unsafe.Sizeof(zeroColor)) * int(img.width) * int(img.height)
|
||||
} else {
|
||||
// Formats like RGB444 that aren't a whole number of bytes.
|
||||
numBits := zeroColor.BitsPerPixel() * int(img.width) * int(img.height)
|
||||
numBytes = (numBits + 7) / 8 // round up (see NewImage)
|
||||
}
|
||||
return unsafe.Slice((*byte)(img.data), numBytes)
|
||||
}
|
||||
|
||||
// Size returns the image size.
|
||||
func (img Image[T]) Size() (int, int) {
|
||||
return int(img.width), int(img.height)
|
||||
}
|
||||
|
||||
func (img Image[T]) setPixel(index int, c T) {
|
||||
var zeroColor T
|
||||
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
// Each color starts at a whole byte offset.
|
||||
// This is the easy case.
|
||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||
ptr := unsafe.Add(img.data, offset)
|
||||
*((*T)(ptr)) = c
|
||||
return
|
||||
}
|
||||
|
||||
if c, ok := any(c).(RGB444BE); ok {
|
||||
// Special case for RGB444.
|
||||
bitIndex := index * zeroColor.BitsPerPixel()
|
||||
if bitIndex%8 == 0 {
|
||||
byteOffset := bitIndex / 8
|
||||
ptr := (*[2]byte)(unsafe.Add(img.data, byteOffset))
|
||||
ptr[0] = uint8(c >> 4)
|
||||
ptr[1] = ptr[1]&0x0f | uint8(c)<<4 // change top bits
|
||||
} else {
|
||||
byteOffset := bitIndex / 8
|
||||
ptr := (*[2]byte)(unsafe.Add(img.data, byteOffset))
|
||||
ptr[0] = ptr[0]&0xf0 | uint8(c>>8) // change bottom bits
|
||||
ptr[1] = uint8(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: the code for RGB444 should be generalized to support any bit size.
|
||||
panic("todo: setPixel for odd bits per pixel")
|
||||
}
|
||||
|
||||
// Set sets the pixel at x, y to the given color.
|
||||
// Use FillSolidColor to efficiently fill the entire image buffer.
|
||||
func (img Image[T]) Set(x, y int, c T) {
|
||||
if uint(x) >= uint(int(img.width)) || uint(y) >= uint(int(img.height)) {
|
||||
panic("Image.Set: out of bounds")
|
||||
}
|
||||
index := y*int(img.width) + x
|
||||
img.setPixel(index, c)
|
||||
}
|
||||
|
||||
// Get returns the color at the given index.
|
||||
func (img Image[T]) Get(x, y int) T {
|
||||
if uint(x) >= uint(int(img.width)) || uint(y) >= uint(int(img.height)) {
|
||||
panic("Image.Get: out of bounds")
|
||||
}
|
||||
var zeroColor T
|
||||
index := y*int(img.width) + x // index into img.data
|
||||
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
// Colors like RGB565, RGB888, etc.
|
||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||
ptr := unsafe.Add(img.data, offset)
|
||||
return *((*T)(ptr))
|
||||
}
|
||||
|
||||
if _, ok := any(zeroColor).(RGB444BE); ok {
|
||||
// Special case for RGB444 that isn't stored in a neat byte multiple.
|
||||
bitIndex := index * zeroColor.BitsPerPixel()
|
||||
var c RGB444BE
|
||||
if bitIndex%8 == 0 {
|
||||
byteOffset := bitIndex / 8
|
||||
ptr := (*[2]byte)(unsafe.Add(img.data, byteOffset))
|
||||
c |= RGB444BE(ptr[0]) << 4
|
||||
c |= RGB444BE(ptr[1] >> 4) // load top bits
|
||||
} else {
|
||||
byteOffset := bitIndex / 8
|
||||
ptr := (*[2]byte)(unsafe.Add(img.data, byteOffset))
|
||||
c |= RGB444BE(ptr[0]&0x0f) << 8 // load bottom bits
|
||||
c |= RGB444BE(ptr[1])
|
||||
}
|
||||
return any(c).(T)
|
||||
}
|
||||
|
||||
// TODO: generalize the above code.
|
||||
panic("todo: Image.Get for odd bits per pixel")
|
||||
}
|
||||
|
||||
// FillSolidColor fills the entire image with the given color.
|
||||
// This may be faster than setting individual pixels.
|
||||
func (img Image[T]) FillSolidColor(color T) {
|
||||
var zeroColor T
|
||||
|
||||
// Fast pass for colors of 8, 16, 24, etc bytes in size.
|
||||
if zeroColor.BitsPerPixel()%8 == 0 {
|
||||
ptr := img.data
|
||||
for i := 0; i < img.Len(); i++ {
|
||||
// TODO: this can be optimized a lot.
|
||||
// - The store can be done as a 32-bit integer, after checking for
|
||||
// alignment.
|
||||
// - Perhaps the loop can be unrolled to improve copy performance.
|
||||
*(*T)(ptr) = color
|
||||
ptr = unsafe.Add(ptr, unsafe.Sizeof(zeroColor))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Special case for RGB444.
|
||||
if c, ok := any(color).(RGB444BE); ok {
|
||||
// RGB444 can be stored in a more optimized way, by storing two colors
|
||||
// at a time instead of setting each color individually. This avoids
|
||||
// loading and masking the old color bits for the half-bytes.
|
||||
var buf [3]uint8
|
||||
buf[0] = uint8(c >> 4)
|
||||
buf[1] = uint8(c)<<4 | uint8(c>>8)
|
||||
buf[2] = uint8(c)
|
||||
rawBuf := unsafe.Slice((*[3]byte)(img.data), img.Len()/2)
|
||||
for i := 0; i < len(rawBuf); i++ {
|
||||
rawBuf[i] = buf
|
||||
}
|
||||
if img.Len()%2 != 0 {
|
||||
// The image contains an uneven number of pixels.
|
||||
// This is uncommon, but it can happen and we have to handle it.
|
||||
img.setPixel(img.Len()-1, color)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback for other color formats.
|
||||
for i := 0; i < img.Len(); i++ {
|
||||
img.setPixel(i, color)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package pixel_test
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
)
|
||||
|
||||
func TestImageRGB565BE(t *testing.T) {
|
||||
image := pixel.NewImage[pixel.RGB565BE](5, 3)
|
||||
if width, height := image.Size(); width != 5 && height != 3 {
|
||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
||||
}
|
||||
for _, c := range []color.RGBA{
|
||||
{R: 0xff, A: 0xff},
|
||||
{G: 0xff, A: 0xff},
|
||||
{B: 0xff, A: 0xff},
|
||||
{R: 0x10, A: 0xff},
|
||||
{G: 0x10, A: 0xff},
|
||||
{B: 0x10, A: 0xff},
|
||||
} {
|
||||
image.Set(4, 2, pixel.NewColor[pixel.RGB565BE](c.R, c.G, c.B))
|
||||
c2 := image.Get(4, 2).RGBA()
|
||||
if c2 != c {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageRGB444BE(t *testing.T) {
|
||||
image := pixel.NewImage[pixel.RGB444BE](5, 3)
|
||||
if width, height := image.Size(); width != 5 && height != 3 {
|
||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
||||
}
|
||||
for _, c := range []color.RGBA{
|
||||
{R: 0xff, A: 0xff},
|
||||
{G: 0xff, A: 0xff},
|
||||
{B: 0xff, A: 0xff},
|
||||
{R: 0x11, A: 0xff},
|
||||
{G: 0x11, A: 0xff},
|
||||
{B: 0x11, A: 0xff},
|
||||
} {
|
||||
encoded := pixel.NewColor[pixel.RGB444BE](c.R, c.G, c.B)
|
||||
image.Set(0, 0, encoded)
|
||||
image.Set(0, 1, encoded)
|
||||
encoded2 := image.Get(0, 0)
|
||||
encoded3 := image.Get(0, 1)
|
||||
if encoded != encoded2 {
|
||||
t.Errorf("failed to roundtrip color %v: expected %d but got %d", c, encoded, encoded2)
|
||||
}
|
||||
if encoded != encoded3 {
|
||||
t.Errorf("failed to roundtrip color %v: expected %d but got %d", c, encoded, encoded3)
|
||||
}
|
||||
c2 := encoded2.RGBA()
|
||||
if c2 != c {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c2)
|
||||
}
|
||||
c3 := encoded3.RGBA()
|
||||
if c3 != c {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c3)
|
||||
}
|
||||
}
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
// Package pixel contains pixel format definitions used in various displays and
|
||||
// fast operations on them.
|
||||
//
|
||||
// This package is just a base for pixel operations, it is _not_ a graphics
|
||||
// library. It doesn't define circles, lines, etc - just the bare minimum
|
||||
// graphics operations needed plus the ones that need to be specialized per
|
||||
// pixel format.
|
||||
package pixel
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Pixel with a particular color, matching the underlying hardware of a
|
||||
// particular display. Each pixel is at least 1 byte in size.
|
||||
// The color format is sRGB (or close to it) in all cases.
|
||||
type Color interface {
|
||||
RGB888 | RGB565BE | RGB555 | RGB444BE
|
||||
|
||||
BaseColor
|
||||
}
|
||||
|
||||
// BaseColor contains all the methods needed in a color format. This can be used
|
||||
// in display drivers that want to define their own Color type with just the
|
||||
// pixel formats the display supports.
|
||||
type BaseColor interface {
|
||||
// The number of bits when stored.
|
||||
// This means for example that RGB555 (which is still stored as a 16-bit
|
||||
// integer) returns 16, while RGB444 returns 12.
|
||||
BitsPerPixel() int
|
||||
|
||||
// Return the given color in color.RGBA format, which is always sRGB. The
|
||||
// alpha channel is always 255.
|
||||
RGBA() color.RGBA
|
||||
}
|
||||
|
||||
// NewColor returns the given color based on the RGB values passed in the
|
||||
// parameters. The input value is assumed to be in sRGB color space.
|
||||
func NewColor[T Color](r, g, b uint8) T {
|
||||
// Ugly cast from color.RGBA to T. The type switch and interface casts are
|
||||
// trivially optimized away after instantiation.
|
||||
var value T
|
||||
switch any(value).(type) {
|
||||
case RGB888:
|
||||
return any(NewRGB888(r, g, b)).(T)
|
||||
case RGB565BE:
|
||||
return any(NewRGB565BE(r, g, b)).(T)
|
||||
case RGB555:
|
||||
return any(NewRGB555(r, g, b)).(T)
|
||||
case RGB444BE:
|
||||
return any(NewRGB444BE(r, g, b)).(T)
|
||||
default:
|
||||
panic("unknown color format")
|
||||
}
|
||||
}
|
||||
|
||||
// NewLinearColor returns the given color based on the linear RGB values passed
|
||||
// in the parameters. Use this if the RGB values are actually linear colors
|
||||
// (like those that are used in most RGB LEDs) and not when it is in the usual
|
||||
// sRGB color space (which is not linear).
|
||||
//
|
||||
// The input is assumed to be in the linear sRGB color space.
|
||||
func NewLinearColor[T Color](r, g, b uint8) T {
|
||||
r = gammaEncodeTable[r]
|
||||
g = gammaEncodeTable[g]
|
||||
b = gammaEncodeTable[b]
|
||||
return NewColor[T](r, g, b)
|
||||
}
|
||||
|
||||
// RGB888 format, more commonly used in other places (desktop PC displays, CSS,
|
||||
// etc). Less commonly used on embedded displays due to the higher memory usage.
|
||||
type RGB888 struct {
|
||||
R, G, B uint8
|
||||
}
|
||||
|
||||
func NewRGB888(r, g, b uint8) RGB888 {
|
||||
return RGB888{r, g, b}
|
||||
}
|
||||
|
||||
func (c RGB888) BitsPerPixel() int {
|
||||
return 24
|
||||
}
|
||||
|
||||
func (c RGB888) RGBA() color.RGBA {
|
||||
return color.RGBA{
|
||||
R: c.R,
|
||||
G: c.G,
|
||||
B: c.B,
|
||||
A: 255,
|
||||
}
|
||||
}
|
||||
|
||||
// RGB565 as used in many SPI displays. Stored as a big endian value.
|
||||
//
|
||||
// The color format in integer form is gggbbbbb_rrrrrggg on little endian
|
||||
// systems, which is the standard RGB565 format but with the top and bottom
|
||||
// bytes swapped.
|
||||
//
|
||||
// There are a few alternatives to this weird big-endian format, but they're not
|
||||
// great:
|
||||
// - Storing the value in two 8-bit stores (to make the code endian-agnostic)
|
||||
// incurs too much of a performance penalty.
|
||||
// - Swapping the upper and lower bits just before storing. This is still less
|
||||
// efficient than it could be, since colors are usually constructed once and
|
||||
// then reused in many store operations. Doing the swap once instead of many
|
||||
// times for each store is a performance win.
|
||||
type RGB565BE uint16
|
||||
|
||||
func NewRGB565BE(r, g, b uint8) RGB565BE {
|
||||
val := uint16(r&0xF8)<<8 +
|
||||
uint16(g&0xFC)<<3 +
|
||||
uint16(b&0xF8)>>3
|
||||
// Swap endianness (make big endian).
|
||||
// This is done using a single instruction on ARM (rev16).
|
||||
// TODO: this should only be done on little endian systems, but TinyGo
|
||||
// doesn't currently (2023) support big endian systems so it's difficult to
|
||||
// test. Also, big endian systems don't seem fasionable these days.
|
||||
val = bits.ReverseBytes16(val)
|
||||
return RGB565BE(val)
|
||||
}
|
||||
|
||||
func (c RGB565BE) BitsPerPixel() int {
|
||||
return 16
|
||||
}
|
||||
|
||||
func (c RGB565BE) RGBA() color.RGBA {
|
||||
// Note: on ARM, the compiler uses a rev instruction instead of a rev16
|
||||
// instruction. I wonder whether this can be optimized further to use rev16
|
||||
// instead?
|
||||
c = c<<8 | c>>8
|
||||
color := color.RGBA{
|
||||
R: uint8(c>>11) << 3,
|
||||
G: uint8(c>>5) << 2,
|
||||
B: uint8(c) << 3,
|
||||
A: 255,
|
||||
}
|
||||
// Correct color rounding, so that 0xff roundtrips back to 0xff.
|
||||
color.R |= color.R >> 5
|
||||
color.G |= color.G >> 6
|
||||
color.B |= color.B >> 5
|
||||
return color
|
||||
}
|
||||
|
||||
// Color format used on the GameBoy Advance among others.
|
||||
//
|
||||
// Colors are stored as native endian values, with bits 0bbbbbgg_gggrrrrr (red
|
||||
// is least significant, blue is most significant).
|
||||
type RGB555 uint16
|
||||
|
||||
func NewRGB555(r, g, b uint8) RGB555 {
|
||||
return RGB555(r)>>3 | (RGB555(g)>>3)<<5 | (RGB555(b)>>3)<<10
|
||||
}
|
||||
|
||||
func (c RGB555) BitsPerPixel() int {
|
||||
// 15 bits per pixel, but there are 16 bits when stored
|
||||
return 16
|
||||
}
|
||||
|
||||
func (c RGB555) RGBA() color.RGBA {
|
||||
color := color.RGBA{
|
||||
R: uint8(c>>10) << 3,
|
||||
G: uint8(c>>5) << 3,
|
||||
B: uint8(c) << 3,
|
||||
A: 255,
|
||||
}
|
||||
// Correct color rounding, so that 0xff roundtrips back to 0xff.
|
||||
color.R |= color.R >> 5
|
||||
color.G |= color.G >> 5
|
||||
color.B |= color.B >> 5
|
||||
return color
|
||||
}
|
||||
|
||||
// Color format that is supported by the ST7789 for example.
|
||||
// It may be a bit faster to use than RGB565BE on very slow SPI buses.
|
||||
//
|
||||
// The color format is native endian as a uint16 (0000rrrr_ggggbbbb), not big
|
||||
// endian which you might expect. I tried swapping the bytes, but it didn't have
|
||||
// much of a performance impact and made the code harder to read. It is stored
|
||||
// as a 12-bit big endian value in Image[RGB444BE] though.
|
||||
type RGB444BE uint16
|
||||
|
||||
func NewRGB444BE(r, g, b uint8) RGB444BE {
|
||||
return RGB444BE(r>>4)<<8 | RGB444BE(g>>4)<<4 | RGB444BE(b>>4)
|
||||
}
|
||||
|
||||
func (c RGB444BE) BitsPerPixel() int {
|
||||
return 12
|
||||
}
|
||||
|
||||
func (c RGB444BE) RGBA() color.RGBA {
|
||||
color := color.RGBA{
|
||||
R: uint8(c>>8) << 4,
|
||||
G: uint8(c>>4) << 4,
|
||||
B: uint8(c>>0) << 4,
|
||||
A: 255,
|
||||
}
|
||||
// Correct color rounding, so that 0xff roundtrips back to 0xff.
|
||||
color.R |= color.R >> 4
|
||||
color.G |= color.G >> 4
|
||||
color.B |= color.B >> 4
|
||||
return color
|
||||
}
|
||||
|
||||
// Gamma brightness lookup table:
|
||||
// https://victornpb.github.io/gamma-table-generator
|
||||
// gamma = 0.45 steps = 256 range = 0-255
|
||||
var gammaEncodeTable = [256]uint8{
|
||||
0, 21, 28, 34, 39, 43, 46, 50, 53, 56, 59, 61, 64, 66, 68, 70,
|
||||
72, 74, 76, 78, 80, 82, 84, 85, 87, 89, 90, 92, 93, 95, 96, 98,
|
||||
99, 101, 102, 103, 105, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118,
|
||||
119, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
|
||||
136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150,
|
||||
151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 160, 160, 161, 162, 163,
|
||||
164, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175,
|
||||
175, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 184, 185, 186,
|
||||
186, 187, 188, 188, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 195, 196,
|
||||
197, 197, 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206,
|
||||
206, 207, 207, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215,
|
||||
215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224,
|
||||
224, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232,
|
||||
232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239, 240,
|
||||
240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248,
|
||||
248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255,
|
||||
}
|
||||
@@ -5,7 +5,10 @@
|
||||
// https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf
|
||||
package qmi8656c
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps the I2C connection to the QMIC8658 sensor
|
||||
type Device struct {
|
||||
@@ -179,12 +182,12 @@ func (d *Device) ReadTemperature() (int32, error) {
|
||||
|
||||
// Convenience method to read the register and avoid repetition.
|
||||
func (d *Device) ReadRegister(reg uint8, buf []byte) error {
|
||||
return d.bus.ReadRegister(uint8(d.Address), reg, buf)
|
||||
return legacy.ReadRegister(d.bus, uint8(d.Address), reg, buf)
|
||||
}
|
||||
|
||||
// Convenience method to write the register and avoid repetition.
|
||||
func (d *Device) WriteRegister(reg uint8, v uint16) error {
|
||||
data := []byte{byte(v)}
|
||||
err := d.bus.WriteRegister(uint8(d.Address), reg, data)
|
||||
err := legacy.WriteRegister(d.bus, uint8(d.Address), reg, data)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ const (
|
||||
MagneticField
|
||||
Luminosity
|
||||
Time
|
||||
// Gas or liquid concentration, usually measured in ppm (parts per million).
|
||||
Concentration
|
||||
// Add Measurements above AllMeasurements.
|
||||
|
||||
// AllMeasurements is the OR of all Measurement values. It ensures all measurements are done.
|
||||
|
||||
+5
-4
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an SPI connection.
|
||||
@@ -51,7 +52,7 @@ type Buser interface {
|
||||
|
||||
type VccMode uint8
|
||||
|
||||
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
|
||||
// NewI2C creates a new SH1106 connection. The I2C wire must already be configured.
|
||||
func NewI2C(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: &I2CBus{
|
||||
@@ -61,7 +62,7 @@ func NewI2C(bus drivers.I2C) Device {
|
||||
}
|
||||
}
|
||||
|
||||
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
|
||||
// NewSPI creates a new SH1106 connection. The SPI wire must already be configured.
|
||||
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
@@ -283,9 +284,9 @@ func (d *Device) Tx(data []byte, isCommand bool) {
|
||||
// tx sends data to the display (I2CBus implementation)
|
||||
func (b *I2CBus) tx(data []byte, isCommand bool) {
|
||||
if isCommand {
|
||||
b.wire.WriteRegister(uint8(b.Address), 0x00, data)
|
||||
legacy.WriteRegister(b.wire, uint8(b.Address), 0x00, data)
|
||||
} else {
|
||||
b.wire.WriteRegister(uint8(b.Address), 0x40, data)
|
||||
legacy.WriteRegister(b.wire, uint8(b.Address), 0x40, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Package sht4x provides a driver for the SHT4x digital humidity sensor series by Sensirion.
|
||||
// Datasheet: https://www.sensirion.com/media/documents/33FD6951/64D3B030/Sensirion_Datasheet_SHT4x.pdf
|
||||
package sht4x
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
const DefaultAddress = 0x44
|
||||
|
||||
const (
|
||||
// single-shot, high-repeatability measurement
|
||||
commandMeasurement = 0xfd
|
||||
)
|
||||
|
||||
// Device represents a SHT4x sensor
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint8
|
||||
}
|
||||
|
||||
// New creates a new SHT4x connection. The I2C bus must already be
|
||||
// configured.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: DefaultAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTemperatureHumidity starts a measurement and then reads out the results. This function blocks
|
||||
// while the measurement is in progress.
|
||||
//
|
||||
// Temperature is returned in [degree Celsius], multiplied by 1000,
|
||||
// and relative humidity in [percent relative humidity], multiplied by 1000.
|
||||
func (d *Device) ReadTemperatureHumidity() (temperatureMilliCelsius int32, relativeHumidityMilliPercent int32, err error) {
|
||||
rawTemp, rawHum, err := d.rawReadings()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// from the reference driver: https://github.com/Sensirion/embedded-sht/blob/fcc8a523210cc1241a2750899ff6b0f68f3ed212/sht4x/sht4x.c#L81
|
||||
temperatureMilliCelsius = ((21875 * int32(rawTemp)) >> 13) - 45000
|
||||
relativeHumidityMilliPercent = ((15625 * int32(rawHum)) >> 13) - 6000
|
||||
|
||||
return temperatureMilliCelsius, relativeHumidityMilliPercent, err
|
||||
}
|
||||
|
||||
// rawReadings returns the sensor's raw values of the temperature and humidity
|
||||
func (d *Device) rawReadings() (uint16, uint16, error) {
|
||||
err := d.bus.Tx(uint16(d.Address), []byte{commandMeasurement}, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// max time for measurement according to datasheet
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
var data [6]byte
|
||||
err = d.bus.Tx(uint16(d.Address), nil, data[:])
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
tTicks := readUint(data[0], data[1])
|
||||
rhTicks := readUint(data[3], data[4])
|
||||
|
||||
return tTicks, rhTicks, nil
|
||||
}
|
||||
|
||||
// readUint converts two bytes to uint16
|
||||
func readUint(msb byte, lsb byte) uint16 {
|
||||
return (uint16(msb) << 8) | uint16(lsb)
|
||||
}
|
||||
+1
-1
@@ -133,7 +133,7 @@ func runSmokeTest(filename string) error {
|
||||
result := <-job.resultChan
|
||||
os.Stdout.Write(job.output.Bytes())
|
||||
if result != nil {
|
||||
return err
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-1
@@ -4,6 +4,8 @@
|
||||
# avoid a race condition between writing the output and reading the result to
|
||||
# get an md5sum).
|
||||
|
||||
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/adafruit4650
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx
|
||||
@@ -13,6 +15,7 @@ tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=pinetime ./examples/bma42x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
|
||||
@@ -61,6 +64,7 @@ tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
|
||||
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht4x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
|
||||
@@ -100,6 +104,7 @@ tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examp
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
||||
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8523/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||
@@ -126,4 +131,9 @@ tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/d
|
||||
tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/lora/lorawan/atcmd/
|
||||
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/as560x/main.go
|
||||
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu6886/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ttp229/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ttp229/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/ndir/main_ndir.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/main_ndir.go
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go
|
||||
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
|
||||
|
||||
+25
-17
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps I2C or SPI connection.
|
||||
@@ -44,9 +45,9 @@ type SPIBus struct {
|
||||
}
|
||||
|
||||
type Buser interface {
|
||||
configure()
|
||||
tx(data []byte, isCommand bool)
|
||||
setAddress(address uint16)
|
||||
configure() error
|
||||
tx(data []byte, isCommand bool) error
|
||||
setAddress(address uint16) error
|
||||
}
|
||||
|
||||
type VccMode uint8
|
||||
@@ -192,8 +193,7 @@ func (d *Device) Display() error {
|
||||
d.Command(uint8(d.height/8) - 1)
|
||||
}
|
||||
|
||||
d.Tx(d.buffer, false)
|
||||
return nil
|
||||
return d.Tx(d.buffer, false)
|
||||
}
|
||||
|
||||
// SetPixel enables or disables a pixel in the buffer
|
||||
@@ -243,21 +243,23 @@ func (d *Device) Command(command uint8) {
|
||||
}
|
||||
|
||||
// setAddress sets the address to the I2C bus
|
||||
func (b *I2CBus) setAddress(address uint16) {
|
||||
func (b *I2CBus) setAddress(address uint16) error {
|
||||
b.Address = address
|
||||
return nil
|
||||
}
|
||||
|
||||
// setAddress does nothing, but it's required to avoid reflection
|
||||
func (b *SPIBus) setAddress(address uint16) {
|
||||
func (b *SPIBus) setAddress(address uint16) error {
|
||||
// do nothing
|
||||
println("trying to Configure an address on a SPI device")
|
||||
return nil
|
||||
}
|
||||
|
||||
// configure does nothing, but it's required to avoid reflection
|
||||
func (b *I2CBus) configure() {}
|
||||
func (b *I2CBus) configure() error { return nil }
|
||||
|
||||
// configure configures some pins with the SPI bus
|
||||
func (b *SPIBus) configure() {
|
||||
func (b *SPIBus) configure() error {
|
||||
b.csPin.Low()
|
||||
b.dcPin.Low()
|
||||
b.resetPin.Low()
|
||||
@@ -267,31 +269,35 @@ func (b *SPIBus) configure() {
|
||||
b.resetPin.Low()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
b.resetPin.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx sends data to the display
|
||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||
d.bus.tx(data, isCommand)
|
||||
func (d *Device) Tx(data []byte, isCommand bool) error {
|
||||
return d.bus.tx(data, isCommand)
|
||||
}
|
||||
|
||||
// tx sends data to the display (I2CBus implementation)
|
||||
func (b *I2CBus) tx(data []byte, isCommand bool) {
|
||||
func (b *I2CBus) tx(data []byte, isCommand bool) error {
|
||||
if isCommand {
|
||||
b.wire.WriteRegister(uint8(b.Address), 0x00, data)
|
||||
return legacy.WriteRegister(b.wire, uint8(b.Address), 0x00, data)
|
||||
} else {
|
||||
b.wire.WriteRegister(uint8(b.Address), 0x40, data)
|
||||
return legacy.WriteRegister(b.wire, uint8(b.Address), 0x40, data)
|
||||
}
|
||||
}
|
||||
|
||||
// tx sends data to the display (SPIBus implementation)
|
||||
func (b *SPIBus) tx(data []byte, isCommand bool) {
|
||||
func (b *SPIBus) tx(data []byte, isCommand bool) error {
|
||||
var err error
|
||||
|
||||
if isCommand {
|
||||
b.csPin.High()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
b.dcPin.Low()
|
||||
b.csPin.Low()
|
||||
|
||||
b.wire.Tx(data, nil)
|
||||
err = b.wire.Tx(data, nil)
|
||||
b.csPin.High()
|
||||
} else {
|
||||
b.csPin.High()
|
||||
@@ -299,9 +305,11 @@ func (b *SPIBus) tx(data []byte, isCommand bool) {
|
||||
b.dcPin.High()
|
||||
b.csPin.Low()
|
||||
|
||||
b.wire.Tx(data, nil)
|
||||
err = b.wire.Tx(data, nil)
|
||||
b.csPin.High()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
|
||||
+72
-52
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
)
|
||||
|
||||
type Model uint8
|
||||
@@ -20,12 +21,23 @@ type Model uint8
|
||||
// Deprecated: use drivers.Rotation instead.
|
||||
type Rotation = drivers.Rotation
|
||||
|
||||
// Pixel formats supported by the st7735 driver.
|
||||
type Color interface {
|
||||
pixel.RGB444BE | pixel.RGB565BE
|
||||
|
||||
pixel.BaseColor
|
||||
}
|
||||
|
||||
var (
|
||||
errOutOfBounds = errors.New("rectangle coordinates outside display area")
|
||||
)
|
||||
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
type Device = DeviceOf[pixel.RGB565BE]
|
||||
|
||||
// DeviceOf is a generic version of Device, which supports different pixel
|
||||
// formats.
|
||||
type DeviceOf[T Color] struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
@@ -39,7 +51,7 @@ type Device struct {
|
||||
batchLength int16
|
||||
model Model
|
||||
isBGR bool
|
||||
batchData []uint8
|
||||
batchData pixel.Image[T] // "image" with width, height of (batchLength, 1)
|
||||
}
|
||||
|
||||
// Config is the configuration for the display
|
||||
@@ -54,11 +66,17 @@ type Config struct {
|
||||
|
||||
// New creates a new ST7735 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||
}
|
||||
|
||||
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI
|
||||
// wire must already be configured.
|
||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
return Device{
|
||||
return DeviceOf[T]{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
resetPin: resetPin,
|
||||
@@ -68,7 +86,7 @@ func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
}
|
||||
|
||||
// Configure initializes the display with default configuration
|
||||
func (d *Device) Configure(cfg Config) {
|
||||
func (d *DeviceOf[T]) Configure(cfg Config) {
|
||||
d.model = cfg.Model
|
||||
if cfg.Width != 0 {
|
||||
d.width = cfg.Width
|
||||
@@ -93,7 +111,7 @@ func (d *Device) Configure(cfg Config) {
|
||||
d.batchLength = d.height
|
||||
}
|
||||
d.batchLength += d.batchLength & 1
|
||||
d.batchData = make([]uint8, d.batchLength*2)
|
||||
d.batchData = pixel.NewImage[T](int(d.batchLength), 1)
|
||||
|
||||
// reset the device
|
||||
d.resetPin.High()
|
||||
@@ -142,8 +160,16 @@ func (d *Device) Configure(cfg Config) {
|
||||
d.Data(0xEE)
|
||||
d.Command(VMCTR1)
|
||||
d.Data(0x0E)
|
||||
|
||||
// Set the color format depending on the generic type.
|
||||
d.Command(COLMOD)
|
||||
d.Data(0x05)
|
||||
var zeroColor T
|
||||
switch any(zeroColor).(type) {
|
||||
case pixel.RGB444BE:
|
||||
d.Data(0x03) // 12 bits per pixel
|
||||
default:
|
||||
d.Data(0x05) // 16 bits per pixel
|
||||
}
|
||||
|
||||
if d.model == GREENTAB {
|
||||
d.InvertColors(false)
|
||||
@@ -204,12 +230,12 @@ func (d *Device) Configure(cfg Config) {
|
||||
}
|
||||
|
||||
// Display does nothing, there's no buffer as it might be too big for some boards
|
||||
func (d *Device) Display() error {
|
||||
func (d *DeviceOf[T]) Display() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPixel sets a pixel in the screen
|
||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
func (d *DeviceOf[T]) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
w, h := d.Size()
|
||||
if x < 0 || y < 0 || x >= w || y >= h {
|
||||
return
|
||||
@@ -218,7 +244,7 @@ func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
}
|
||||
|
||||
// setWindow prepares the screen to be modified at a given rectangle
|
||||
func (d *Device) setWindow(x, y, w, h int16) {
|
||||
func (d *DeviceOf[T]) setWindow(x, y, w, h int16) {
|
||||
if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
|
||||
x += d.columnOffset
|
||||
y += d.rowOffset
|
||||
@@ -234,7 +260,9 @@ func (d *Device) setWindow(x, y, w, h int16) {
|
||||
}
|
||||
|
||||
// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display
|
||||
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
func (d *DeviceOf[T]) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
// TODO: this code is broken, see the st7789 and ili9341 implementations for
|
||||
// how to do this correctly.
|
||||
d.Command(VSCRDEF)
|
||||
d.Tx([]uint8{
|
||||
uint8(topFixedArea >> 8), uint8(topFixedArea),
|
||||
@@ -244,38 +272,32 @@ func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
}
|
||||
|
||||
// SetScroll sets the vertical scroll address of the display.
|
||||
func (d *Device) SetScroll(line int16) {
|
||||
func (d *DeviceOf[T]) SetScroll(line int16) {
|
||||
d.Command(VSCRSADD)
|
||||
d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false)
|
||||
}
|
||||
|
||||
// SpotScroll returns the display to its normal state
|
||||
func (d *Device) StopScroll() {
|
||||
func (d *DeviceOf[T]) StopScroll() {
|
||||
d.Command(NORON)
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at a given coordinates with a color
|
||||
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
func (d *DeviceOf[T]) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
|
||||
x >= k || (x+width) > k || y >= i || (y+height) > i {
|
||||
return errors.New("rectangle coordinates outside display area")
|
||||
}
|
||||
d.setWindow(x, y, width, height)
|
||||
c565 := RGBATo565(c)
|
||||
c1 := uint8(c565 >> 8)
|
||||
c2 := uint8(c565)
|
||||
|
||||
for i = 0; i < d.batchLength; i++ {
|
||||
d.batchData[i*2] = c1
|
||||
d.batchData[i*2+1] = c2
|
||||
}
|
||||
d.batchData.FillSolidColor(pixel.NewColor[T](c.R, c.G, c.B))
|
||||
i = width * height
|
||||
for i > 0 {
|
||||
if i >= d.batchLength {
|
||||
d.Tx(d.batchData, false)
|
||||
d.Tx(d.batchData.RawBuffer(), false)
|
||||
} else {
|
||||
d.Tx(d.batchData[:i*2], false)
|
||||
d.Tx(d.batchData.Rescale(int(i), 1).RawBuffer(), false)
|
||||
}
|
||||
i -= d.batchLength
|
||||
}
|
||||
@@ -283,7 +305,9 @@ func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
}
|
||||
|
||||
// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates
|
||||
func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
//
|
||||
// Deprecated: use DrawBitmap instead.
|
||||
func (d *DeviceOf[T]) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
|
||||
x >= k || (x+w) > k || y >= i || (y+h) > i {
|
||||
@@ -294,8 +318,15 @@ func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawBitmap copies the bitmap to the internal buffer on the screen at the
|
||||
// given coordinates. It returns once the image data has been sent completely.
|
||||
func (d *DeviceOf[T]) DrawBitmap(x, y int16, bitmap pixel.Image[T]) error {
|
||||
width, height := bitmap.Size()
|
||||
return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height))
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at a given coordinates with a buffer
|
||||
func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
|
||||
func (d *DeviceOf[T]) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
|
||||
k, l := d.Size()
|
||||
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
|
||||
x >= k || (x+width) > k || y >= l || (y+height) > l {
|
||||
@@ -313,17 +344,14 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
|
||||
for k > 0 {
|
||||
for i := int16(0); i < d.batchLength; i++ {
|
||||
if offset+i < l {
|
||||
c565 := RGBATo565(buffer[offset+i])
|
||||
c1 := uint8(c565 >> 8)
|
||||
c2 := uint8(c565)
|
||||
d.batchData[i*2] = c1
|
||||
d.batchData[i*2+1] = c2
|
||||
c := buffer[offset+i]
|
||||
d.batchData.Set(int(i), 0, pixel.NewColor[T](c.R, c.G, c.B))
|
||||
}
|
||||
}
|
||||
if k >= d.batchLength {
|
||||
d.Tx(d.batchData, false)
|
||||
d.Tx(d.batchData.RawBuffer(), false)
|
||||
} else {
|
||||
d.Tx(d.batchData[:k*2], false)
|
||||
d.Tx(d.batchData.Rescale(int(k), 1).RawBuffer(), false)
|
||||
}
|
||||
k -= d.batchLength
|
||||
offset += d.batchLength
|
||||
@@ -332,7 +360,7 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
|
||||
}
|
||||
|
||||
// DrawFastVLine draws a vertical line faster than using SetPixel
|
||||
func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
|
||||
func (d *DeviceOf[T]) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
|
||||
if y0 > y1 {
|
||||
y0, y1 = y1, y0
|
||||
}
|
||||
@@ -340,7 +368,7 @@ func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
|
||||
}
|
||||
|
||||
// DrawFastHLine draws a horizontal line faster than using SetPixel
|
||||
func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
|
||||
func (d *DeviceOf[T]) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
|
||||
if x0 > x1 {
|
||||
x0, x1 = x1, x0
|
||||
}
|
||||
@@ -348,7 +376,7 @@ func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
|
||||
}
|
||||
|
||||
// FillScreen fills the screen with a given color
|
||||
func (d *Device) FillScreen(c color.RGBA) {
|
||||
func (d *DeviceOf[T]) FillScreen(c color.RGBA) {
|
||||
if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
|
||||
d.FillRectangle(0, 0, d.width, d.height, c)
|
||||
} else {
|
||||
@@ -357,12 +385,12 @@ func (d *Device) FillScreen(c color.RGBA) {
|
||||
}
|
||||
|
||||
// Rotation returns the currently configured rotation.
|
||||
func (d *Device) Rotation() drivers.Rotation {
|
||||
func (d *DeviceOf[T]) Rotation() drivers.Rotation {
|
||||
return d.rotation
|
||||
}
|
||||
|
||||
// SetRotation changes the rotation of the device (clock-wise)
|
||||
func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||
func (d *DeviceOf[T]) SetRotation(rotation drivers.Rotation) error {
|
||||
d.rotation = rotation
|
||||
madctl := uint8(0)
|
||||
switch rotation % 4 {
|
||||
@@ -384,23 +412,23 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||
}
|
||||
|
||||
// Command sends a command to the display
|
||||
func (d *Device) Command(command uint8) {
|
||||
func (d *DeviceOf[T]) Command(command uint8) {
|
||||
d.Tx([]byte{command}, true)
|
||||
}
|
||||
|
||||
// Command sends a data to the display
|
||||
func (d *Device) Data(data uint8) {
|
||||
func (d *DeviceOf[T]) Data(data uint8) {
|
||||
d.Tx([]byte{data}, false)
|
||||
}
|
||||
|
||||
// Tx sends data to the display
|
||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||
func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) {
|
||||
d.dcPin.Set(!isCommand)
|
||||
d.bus.Tx(data, nil)
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
func (d *Device) Size() (w, h int16) {
|
||||
func (d *DeviceOf[T]) Size() (w, h int16) {
|
||||
if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
|
||||
return d.width, d.height
|
||||
}
|
||||
@@ -408,7 +436,7 @@ func (d *Device) Size() (w, h int16) {
|
||||
}
|
||||
|
||||
// EnableBacklight enables or disables the backlight
|
||||
func (d *Device) EnableBacklight(enable bool) {
|
||||
func (d *DeviceOf[T]) EnableBacklight(enable bool) {
|
||||
if enable {
|
||||
d.blPin.High()
|
||||
} else {
|
||||
@@ -419,7 +447,7 @@ func (d *Device) EnableBacklight(enable bool) {
|
||||
// Set the sleep mode for this LCD panel. When sleeping, the panel uses a lot
|
||||
// less power. The LCD won't display an image anymore, but the memory contents
|
||||
// will be kept.
|
||||
func (d *Device) Sleep(sleepEnabled bool) error {
|
||||
func (d *DeviceOf[T]) Sleep(sleepEnabled bool) error {
|
||||
if sleepEnabled {
|
||||
// Shut down LCD panel.
|
||||
d.Command(SLPIN)
|
||||
@@ -435,7 +463,7 @@ func (d *Device) Sleep(sleepEnabled bool) error {
|
||||
}
|
||||
|
||||
// InverColors inverts the colors of the screen
|
||||
func (d *Device) InvertColors(invert bool) {
|
||||
func (d *DeviceOf[T]) InvertColors(invert bool) {
|
||||
if invert {
|
||||
d.Command(INVON)
|
||||
} else {
|
||||
@@ -444,14 +472,6 @@ func (d *Device) InvertColors(invert bool) {
|
||||
}
|
||||
|
||||
// IsBGR changes the color mode (RGB/BGR)
|
||||
func (d *Device) IsBGR(bgr bool) {
|
||||
func (d *DeviceOf[T]) IsBGR(bgr bool) {
|
||||
d.isBGR = bgr
|
||||
}
|
||||
|
||||
// RGBATo565 converts a color.RGBA to uint16 used in the display
|
||||
func RGBATo565(c color.RGBA) uint16 {
|
||||
r, g, b, _ := c.RGBA()
|
||||
return uint16((r & 0xF800) +
|
||||
((g & 0xFC00) >> 5) +
|
||||
((b & 0xF800) >> 11))
|
||||
}
|
||||
|
||||
+127
-82
@@ -14,6 +14,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/pixel"
|
||||
)
|
||||
|
||||
// Rotation controls the rotation used by the display.
|
||||
@@ -24,6 +25,13 @@ type Rotation = drivers.Rotation
|
||||
// The color format used on the display, like RGB565, RGB666, and RGB444.
|
||||
type ColorFormat uint8
|
||||
|
||||
// Pixel formats supported by the st7789 driver.
|
||||
type Color interface {
|
||||
pixel.RGB444BE | pixel.RGB565BE
|
||||
|
||||
pixel.BaseColor
|
||||
}
|
||||
|
||||
// FrameRate controls the frame rate used by the display.
|
||||
type FrameRate uint8
|
||||
|
||||
@@ -32,7 +40,11 @@ var (
|
||||
)
|
||||
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
type Device = DeviceOf[pixel.RGB565BE]
|
||||
|
||||
// DeviceOf is a generic version of Device. It supports multiple different pixel
|
||||
// formats.
|
||||
type DeviceOf[T Color] struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
@@ -47,6 +59,7 @@ type Device struct {
|
||||
rotation drivers.Rotation
|
||||
frameRate FrameRate
|
||||
batchLength int32
|
||||
batchData pixel.Image[T] // "image" with (width, height) of (batchLength, 1)
|
||||
isBGR bool
|
||||
vSyncLines int16
|
||||
cmdBuf [1]byte
|
||||
@@ -71,11 +84,17 @@ type Config struct {
|
||||
|
||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||
}
|
||||
|
||||
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
|
||||
// wire must already be configured.
|
||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
return Device{
|
||||
return DeviceOf[T]{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
resetPin: resetPin,
|
||||
@@ -85,7 +104,7 @@ func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
}
|
||||
|
||||
// Configure initializes the display with default configuration
|
||||
func (d *Device) Configure(cfg Config) {
|
||||
func (d *DeviceOf[T]) Configure(cfg Config) {
|
||||
if cfg.Width != 0 {
|
||||
d.width = cfg.Width
|
||||
} else {
|
||||
@@ -137,7 +156,14 @@ func (d *Device) Configure(cfg Config) {
|
||||
d.sendCommand(SLPOUT, nil) // Exit sleep mode
|
||||
|
||||
// Memory initialization
|
||||
d.setColorFormat(ColorRGB565) // Set color mode to 16-bit color
|
||||
var zeroColor T
|
||||
switch any(zeroColor).(type) {
|
||||
case pixel.RGB444BE:
|
||||
d.setColorFormat(ColorRGB444) // 12 bits per pixel
|
||||
default:
|
||||
// Use default RGB565 color format.
|
||||
d.setColorFormat(ColorRGB565) // 16 bits per pixel
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
d.setRotation(d.rotation) // Memory orientation
|
||||
@@ -189,7 +215,7 @@ func (d *Device) Configure(cfg Config) {
|
||||
// Send a command with data to the display. It does not change the chip select
|
||||
// pin (it must be low when calling). The DC pin is left high after return,
|
||||
// meaning that data can be sent right away.
|
||||
func (d *Device) sendCommand(command uint8, data []byte) error {
|
||||
func (d *DeviceOf[T]) sendCommand(command uint8, data []byte) error {
|
||||
d.cmdBuf[0] = command
|
||||
d.dcPin.Low()
|
||||
err := d.bus.Tx(d.cmdBuf[:1], nil)
|
||||
@@ -202,7 +228,7 @@ func (d *Device) sendCommand(command uint8, data []byte) error {
|
||||
|
||||
// startWrite must be called at the beginning of all exported methods to set the
|
||||
// chip select pin low.
|
||||
func (d *Device) startWrite() {
|
||||
func (d *DeviceOf[T]) startWrite() {
|
||||
if d.csPin != machine.NoPin {
|
||||
d.csPin.Low()
|
||||
}
|
||||
@@ -210,14 +236,23 @@ func (d *Device) startWrite() {
|
||||
|
||||
// endWrite must be called at the end of all exported methods to set the chip
|
||||
// select pin high.
|
||||
func (d *Device) endWrite() {
|
||||
func (d *DeviceOf[T]) endWrite() {
|
||||
if d.csPin != machine.NoPin {
|
||||
d.csPin.High()
|
||||
}
|
||||
}
|
||||
|
||||
// getBuffer returns the image buffer, that's always d.batchLength wide and 1
|
||||
// pixel high. It can be used as a temporary buffer to transmit image data.
|
||||
func (d *DeviceOf[T]) getBuffer() pixel.Image[T] {
|
||||
if d.batchData.Len() == 0 {
|
||||
d.batchData = pixel.NewImage[T](int(d.batchLength), 1)
|
||||
}
|
||||
return d.batchData
|
||||
}
|
||||
|
||||
// Sync waits for the display to hit the next VSYNC pause
|
||||
func (d *Device) Sync() {
|
||||
func (d *DeviceOf[T]) Sync() {
|
||||
d.SyncToScanLine(0)
|
||||
}
|
||||
|
||||
@@ -232,7 +267,7 @@ func (d *Device) Sync() {
|
||||
// NOTE: Use GetHighestScanLine and GetLowestScanLine to obtain the highest
|
||||
// and lowest useful values. Values are affected by front and back porch
|
||||
// vsync settings (derived from VSyncLines configuration option).
|
||||
func (d *Device) SyncToScanLine(scanline uint16) {
|
||||
func (d *DeviceOf[T]) SyncToScanLine(scanline uint16) {
|
||||
scan := d.GetScanLine()
|
||||
|
||||
// Sometimes GetScanLine returns erroneous 0 on first call after draw, so double check
|
||||
@@ -262,7 +297,7 @@ func (d *Device) SyncToScanLine(scanline uint16) {
|
||||
}
|
||||
|
||||
// GetScanLine reads the current scanline value from the display
|
||||
func (d *Device) GetScanLine() uint16 {
|
||||
func (d *DeviceOf[T]) GetScanLine() uint16 {
|
||||
d.startWrite()
|
||||
data := []uint8{0x00, 0x00}
|
||||
d.dcPin.Low()
|
||||
@@ -277,24 +312,24 @@ func (d *Device) GetScanLine() uint16 {
|
||||
}
|
||||
|
||||
// GetHighestScanLine calculates the last scanline id in the frame before VSYNC pause
|
||||
func (d *Device) GetHighestScanLine() uint16 {
|
||||
func (d *DeviceOf[T]) GetHighestScanLine() uint16 {
|
||||
// Last scanline id appears to be backporch/2 + 320/2
|
||||
return uint16(math.Ceil(float64(d.vSyncLines)/2)/2) + 160
|
||||
}
|
||||
|
||||
// GetLowestScanLine calculate the first scanline id to appear after VSYNC pause
|
||||
func (d *Device) GetLowestScanLine() uint16 {
|
||||
func (d *DeviceOf[T]) GetLowestScanLine() uint16 {
|
||||
// First scanline id appears to be backporch/2 + 1
|
||||
return uint16(math.Ceil(float64(d.vSyncLines)/2)/2) + 1
|
||||
}
|
||||
|
||||
// Display does nothing, there's no buffer as it might be too big for some boards
|
||||
func (d *Device) Display() error {
|
||||
func (d *DeviceOf[T]) Display() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPixel sets a pixel in the screen
|
||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
func (d *DeviceOf[T]) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
if x < 0 || y < 0 ||
|
||||
(((d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180) && (x >= d.width || y >= d.height)) ||
|
||||
((d.rotation == drivers.Rotation90 || d.rotation == drivers.Rotation270) && (x >= d.height || y >= d.width))) {
|
||||
@@ -304,7 +339,7 @@ func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
}
|
||||
|
||||
// setWindow prepares the screen to be modified at a given rectangle
|
||||
func (d *Device) setWindow(x, y, w, h int16) {
|
||||
func (d *DeviceOf[T]) setWindow(x, y, w, h int16) {
|
||||
x += d.columnOffset
|
||||
y += d.rowOffset
|
||||
copy(d.buf[:4], []uint8{uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)})
|
||||
@@ -315,45 +350,41 @@ func (d *Device) setWindow(x, y, w, h int16) {
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at a given coordinates with a color
|
||||
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
func (d *DeviceOf[T]) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
d.startWrite()
|
||||
err := d.fillRectangle(x, y, width, height, c)
|
||||
d.endWrite()
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) fillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
func (d *DeviceOf[T]) fillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
|
||||
x >= k || (x+width) > k || y >= i || (y+height) > i {
|
||||
return errors.New("rectangle coordinates outside display area")
|
||||
}
|
||||
d.setWindow(x, y, width, height)
|
||||
c565 := RGBATo565(c)
|
||||
c1 := uint8(c565 >> 8)
|
||||
c2 := uint8(c565)
|
||||
|
||||
data := make([]uint8, d.batchLength*2)
|
||||
for i := int32(0); i < d.batchLength; i++ {
|
||||
data[i*2] = c1
|
||||
data[i*2+1] = c2
|
||||
}
|
||||
j := int32(width) * int32(height)
|
||||
image := d.getBuffer()
|
||||
image.FillSolidColor(pixel.NewColor[T](c.R, c.G, c.B))
|
||||
j := int(width) * int(height)
|
||||
for j > 0 {
|
||||
// The DC pin is already set to data in the setWindow call, so we can
|
||||
// just write bytes on the SPI bus.
|
||||
if j >= d.batchLength {
|
||||
d.bus.Tx(data, nil)
|
||||
if j >= image.Len() {
|
||||
d.bus.Tx(image.RawBuffer(), nil)
|
||||
} else {
|
||||
d.bus.Tx(data[:j*2], nil)
|
||||
d.bus.Tx(image.Rescale(j, 1).RawBuffer(), nil)
|
||||
}
|
||||
j -= d.batchLength
|
||||
j -= image.Len()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates
|
||||
func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
//
|
||||
// Deprecated: use DrawBitmap instead.
|
||||
func (d *DeviceOf[T]) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
k, i := d.Size()
|
||||
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
|
||||
x >= k || (x+w) > k || y >= i || (y+h) > i {
|
||||
@@ -366,8 +397,15 @@ func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawBitmap copies the bitmap to the internal buffer on the screen at the
|
||||
// given coordinates. It returns once the image data has been sent completely.
|
||||
func (d *DeviceOf[T]) DrawBitmap(x, y int16, bitmap pixel.Image[T]) error {
|
||||
width, height := bitmap.Size()
|
||||
return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height))
|
||||
}
|
||||
|
||||
// FillRectangleWithBuffer fills buffer with a rectangle at a given coordinates.
|
||||
func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
|
||||
func (d *DeviceOf[T]) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
|
||||
i, j := d.Size()
|
||||
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
|
||||
x >= i || (x+width) > i || y >= j || (y+height) > j {
|
||||
@@ -379,35 +417,32 @@ func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []col
|
||||
d.startWrite()
|
||||
d.setWindow(x, y, width, height)
|
||||
|
||||
k := int32(width) * int32(height)
|
||||
data := make([]uint8, d.batchLength*2)
|
||||
offset := int32(0)
|
||||
k := int(width) * int(height)
|
||||
image := d.getBuffer()
|
||||
offset := 0
|
||||
for k > 0 {
|
||||
for i := int32(0); i < d.batchLength; i++ {
|
||||
if offset+i < int32(len(buffer)) {
|
||||
c565 := RGBATo565(buffer[offset+i])
|
||||
c1 := uint8(c565 >> 8)
|
||||
c2 := uint8(c565)
|
||||
data[i*2] = c1
|
||||
data[i*2+1] = c2
|
||||
for i := 0; i < image.Len(); i++ {
|
||||
if offset+i < len(buffer) {
|
||||
c := buffer[offset+i]
|
||||
image.Set(i, 0, pixel.NewColor[T](c.R, c.G, c.B))
|
||||
}
|
||||
}
|
||||
// The DC pin is already set to data in the setWindow call, so we don't
|
||||
// have to set it here.
|
||||
if k >= d.batchLength {
|
||||
d.bus.Tx(data, nil)
|
||||
if k >= image.Len() {
|
||||
d.bus.Tx(image.RawBuffer(), nil)
|
||||
} else {
|
||||
d.bus.Tx(data[:k*2], nil)
|
||||
d.bus.Tx(image.Rescale(k, 1).RawBuffer(), nil)
|
||||
}
|
||||
k -= d.batchLength
|
||||
offset += d.batchLength
|
||||
k -= image.Len()
|
||||
offset += image.Len()
|
||||
}
|
||||
d.endWrite()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawFastVLine draws a vertical line faster than using SetPixel
|
||||
func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
|
||||
func (d *DeviceOf[T]) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
|
||||
if y0 > y1 {
|
||||
y0, y1 = y1, y0
|
||||
}
|
||||
@@ -415,7 +450,7 @@ func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
|
||||
}
|
||||
|
||||
// DrawFastHLine draws a horizontal line faster than using SetPixel
|
||||
func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
|
||||
func (d *DeviceOf[T]) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
|
||||
if x0 > x1 {
|
||||
x0, x1 = x1, x0
|
||||
}
|
||||
@@ -423,13 +458,13 @@ func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
|
||||
}
|
||||
|
||||
// FillScreen fills the screen with a given color
|
||||
func (d *Device) FillScreen(c color.RGBA) {
|
||||
func (d *DeviceOf[T]) FillScreen(c color.RGBA) {
|
||||
d.startWrite()
|
||||
d.fillScreen(c)
|
||||
d.endWrite()
|
||||
}
|
||||
|
||||
func (d *Device) fillScreen(c color.RGBA) {
|
||||
func (d *DeviceOf[T]) fillScreen(c color.RGBA) {
|
||||
if d.rotation == NO_ROTATION || d.rotation == ROTATION_180 {
|
||||
d.fillRectangle(0, 0, d.width, d.height, c)
|
||||
} else {
|
||||
@@ -441,13 +476,13 @@ func (d *Device) fillScreen(c color.RGBA) {
|
||||
// The default is RGB565, setting it to any other value will break functions
|
||||
// like SetPixel, FillRectangle, etc. Instead, you can write color data in the
|
||||
// specified color format using DrawRGBBitmap8.
|
||||
func (d *Device) SetColorFormat(format ColorFormat) {
|
||||
func (d *DeviceOf[T]) SetColorFormat(format ColorFormat) {
|
||||
d.startWrite()
|
||||
d.setColorFormat(format)
|
||||
d.endWrite()
|
||||
}
|
||||
|
||||
func (d *Device) setColorFormat(format ColorFormat) {
|
||||
func (d *DeviceOf[T]) setColorFormat(format ColorFormat) {
|
||||
// Lower 4 bits set the color format used in SPI.
|
||||
// Upper 4 bits set the color format used in the direct RGB interface.
|
||||
// The RGB interface is not currently supported, so it is left at a
|
||||
@@ -457,12 +492,12 @@ func (d *Device) setColorFormat(format ColorFormat) {
|
||||
}
|
||||
|
||||
// Rotation returns the current rotation of the device.
|
||||
func (d *Device) Rotation() drivers.Rotation {
|
||||
func (d *DeviceOf[T]) Rotation() drivers.Rotation {
|
||||
return d.rotation
|
||||
}
|
||||
|
||||
// SetRotation changes the rotation of the device (clock-wise)
|
||||
func (d *Device) SetRotation(rotation Rotation) error {
|
||||
func (d *DeviceOf[T]) SetRotation(rotation Rotation) error {
|
||||
d.rotation = rotation
|
||||
d.startWrite()
|
||||
err := d.setRotation(rotation)
|
||||
@@ -470,24 +505,24 @@ func (d *Device) SetRotation(rotation Rotation) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) setRotation(rotation Rotation) error {
|
||||
func (d *DeviceOf[T]) setRotation(rotation Rotation) error {
|
||||
madctl := uint8(0)
|
||||
switch rotation % 4 {
|
||||
case drivers.Rotation0:
|
||||
madctl = MADCTL_MX | MADCTL_MY
|
||||
d.rowOffset = d.rowOffsetCfg
|
||||
d.columnOffset = d.columnOffsetCfg
|
||||
case drivers.Rotation90:
|
||||
madctl = MADCTL_MY | MADCTL_MV
|
||||
d.rowOffset = d.columnOffsetCfg
|
||||
d.columnOffset = d.rowOffsetCfg
|
||||
case drivers.Rotation180:
|
||||
d.rowOffset = 0
|
||||
d.columnOffset = 0
|
||||
case drivers.Rotation270:
|
||||
case drivers.Rotation90:
|
||||
madctl = MADCTL_MX | MADCTL_MV
|
||||
d.rowOffset = 0
|
||||
d.columnOffset = 0
|
||||
case drivers.Rotation180:
|
||||
madctl = MADCTL_MX | MADCTL_MY
|
||||
d.rowOffset = d.rowOffsetCfg
|
||||
d.columnOffset = d.columnOffsetCfg
|
||||
case drivers.Rotation270:
|
||||
madctl = MADCTL_MY | MADCTL_MV
|
||||
d.rowOffset = d.columnOffsetCfg
|
||||
d.columnOffset = d.rowOffsetCfg
|
||||
}
|
||||
if d.isBGR {
|
||||
madctl |= MADCTL_BGR
|
||||
@@ -496,7 +531,7 @@ func (d *Device) setRotation(rotation Rotation) error {
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
func (d *Device) Size() (w, h int16) {
|
||||
func (d *DeviceOf[T]) Size() (w, h int16) {
|
||||
if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
|
||||
return d.width, d.height
|
||||
}
|
||||
@@ -504,7 +539,7 @@ func (d *Device) Size() (w, h int16) {
|
||||
}
|
||||
|
||||
// EnableBacklight enables or disables the backlight
|
||||
func (d *Device) EnableBacklight(enable bool) {
|
||||
func (d *DeviceOf[T]) EnableBacklight(enable bool) {
|
||||
if enable {
|
||||
d.blPin.High()
|
||||
} else {
|
||||
@@ -515,7 +550,7 @@ func (d *Device) EnableBacklight(enable bool) {
|
||||
// Set the sleep mode for this LCD panel. When sleeping, the panel uses a lot
|
||||
// less power. The LCD won't display an image anymore, but the memory contents
|
||||
// will be kept.
|
||||
func (d *Device) Sleep(sleepEnabled bool) error {
|
||||
func (d *DeviceOf[T]) Sleep(sleepEnabled bool) error {
|
||||
if sleepEnabled {
|
||||
d.startWrite()
|
||||
d.sendCommand(SLPIN, nil)
|
||||
@@ -537,7 +572,7 @@ func (d *Device) Sleep(sleepEnabled bool) error {
|
||||
}
|
||||
|
||||
// InvertColors inverts the colors of the screen
|
||||
func (d *Device) InvertColors(invert bool) {
|
||||
func (d *DeviceOf[T]) InvertColors(invert bool) {
|
||||
d.startWrite()
|
||||
if invert {
|
||||
d.sendCommand(INVON, nil)
|
||||
@@ -548,15 +583,28 @@ func (d *Device) InvertColors(invert bool) {
|
||||
}
|
||||
|
||||
// IsBGR changes the color mode (RGB/BGR)
|
||||
func (d *Device) IsBGR(bgr bool) {
|
||||
func (d *DeviceOf[T]) IsBGR(bgr bool) {
|
||||
d.isBGR = bgr
|
||||
}
|
||||
|
||||
// SetScrollArea sets an area to scroll with fixed top and bottom parts of the display.
|
||||
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
func (d *DeviceOf[T]) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
if d.height < 320 {
|
||||
// The screen doesn't use the full 320 pixel height.
|
||||
// Enlarge the bottom fixed area to fill the 320 pixel height, so that
|
||||
// bottomFixedArea starts from the visible bottom of the screen.
|
||||
topFixedArea += d.rowOffset
|
||||
bottomFixedArea += (320 - d.height) - d.rowOffset
|
||||
}
|
||||
if d.rotation == drivers.Rotation180 {
|
||||
// The screen is rotated by 180°, so we have to switch the top and
|
||||
// bottom fixed area.
|
||||
topFixedArea, bottomFixedArea = bottomFixedArea, topFixedArea
|
||||
}
|
||||
verticalScrollArea := 320 - topFixedArea - bottomFixedArea
|
||||
copy(d.buf[:6], []uint8{
|
||||
uint8(topFixedArea >> 8), uint8(topFixedArea),
|
||||
uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea),
|
||||
uint8(verticalScrollArea >> 8), uint8(verticalScrollArea),
|
||||
uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)})
|
||||
d.startWrite()
|
||||
d.sendCommand(VSCRDEF, d.buf[:6])
|
||||
@@ -564,7 +612,12 @@ func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
|
||||
}
|
||||
|
||||
// SetScroll sets the vertical scroll address of the display.
|
||||
func (d *Device) SetScroll(line int16) {
|
||||
func (d *DeviceOf[T]) SetScroll(line int16) {
|
||||
if d.rotation == drivers.Rotation180 {
|
||||
// The screen is rotated by 180°, so we have to invert the scroll line
|
||||
// (taking care of the RowOffset).
|
||||
line = (319 - d.rowOffset) - line
|
||||
}
|
||||
d.buf[0] = uint8(line >> 8)
|
||||
d.buf[1] = uint8(line)
|
||||
d.startWrite()
|
||||
@@ -573,16 +626,8 @@ func (d *Device) SetScroll(line int16) {
|
||||
}
|
||||
|
||||
// StopScroll returns the display to its normal state.
|
||||
func (d *Device) StopScroll() {
|
||||
func (d *DeviceOf[T]) StopScroll() {
|
||||
d.startWrite()
|
||||
d.sendCommand(NORON, nil)
|
||||
d.endWrite()
|
||||
}
|
||||
|
||||
// RGBATo565 converts a color.RGBA to uint16 used in the display
|
||||
func RGBATo565(c color.RGBA) uint16 {
|
||||
r, g, b, _ := c.RGBA()
|
||||
return uint16((r & 0xF800) +
|
||||
((g & 0xFC00) >> 5) +
|
||||
((b & 0xF800) >> 11))
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,10 +5,10 @@ const MaxRegisters = 255
|
||||
|
||||
type I2CDevice interface {
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
ReadRegister(r uint8, buf []byte) error
|
||||
readRegister(r uint8, buf []byte) error
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
WriteRegister(r uint8, buf []byte) error
|
||||
writeRegister(r uint8, buf []byte) error
|
||||
|
||||
// Tx implements I2C.Tx
|
||||
Tx(w, r []byte) error
|
||||
|
||||
+14
-4
@@ -33,7 +33,7 @@ func (d *I2CDevice16) Addr() uint8 {
|
||||
}
|
||||
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
func (d *I2CDevice16) ReadRegister(r uint8, buf []byte) error {
|
||||
func (d *I2CDevice16) readRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func (d *I2CDevice16) ReadRegister(r uint8, buf []byte) error {
|
||||
}
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
func (d *I2CDevice16) WriteRegister(r uint8, buf []byte) error {
|
||||
func (d *I2CDevice16) writeRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
@@ -75,6 +75,16 @@ func (d *I2CDevice16) WriteRegister(r uint8, buf []byte) error {
|
||||
|
||||
// Tx implements I2C.Tx.
|
||||
func (bus *I2CDevice16) Tx(w, r []byte) error {
|
||||
// TODO: implement this
|
||||
return nil
|
||||
switch len(w) {
|
||||
case 0:
|
||||
bus.c.Fatalf("i2c mock: need a write byte")
|
||||
return nil
|
||||
case 1:
|
||||
return bus.readRegister(w[0], r)
|
||||
default:
|
||||
if len(r) > 0 || len(w) == 1 {
|
||||
bus.c.Fatalf("i2c mock: unsupported lengths in Tx(%d, %d)", len(w), len(r))
|
||||
}
|
||||
return bus.writeRegister(w[0], w[1:])
|
||||
}
|
||||
}
|
||||
|
||||
+17
-4
@@ -34,17 +34,20 @@ func (d *I2CDevice8) Addr() uint8 {
|
||||
}
|
||||
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
func (d *I2CDevice8) ReadRegister(r uint8, buf []byte) error {
|
||||
func (d *I2CDevice8) readRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
d.c.Fatalf("no register buffer to read into")
|
||||
}
|
||||
d.assertRegisterRange(r, buf)
|
||||
copy(buf, d.Registers[r:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
func (d *I2CDevice8) WriteRegister(r uint8, buf []byte) error {
|
||||
func (d *I2CDevice8) writeRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
@@ -55,8 +58,18 @@ func (d *I2CDevice8) WriteRegister(r uint8, buf []byte) error {
|
||||
|
||||
// Tx implements I2C.Tx.
|
||||
func (bus *I2CDevice8) Tx(w, r []byte) error {
|
||||
// TODO: implement this
|
||||
return nil
|
||||
switch len(w) {
|
||||
case 0:
|
||||
bus.c.Fatalf("i2c mock: need a write byte")
|
||||
return nil
|
||||
case 1:
|
||||
return bus.readRegister(w[0], r)
|
||||
default:
|
||||
if len(r) > 0 || len(w) == 1 {
|
||||
bus.c.Fatalf("i2c mock: unsupported lengths in Tx(%d, %d)", len(w), len(r))
|
||||
}
|
||||
return bus.writeRegister(w[0], w[1:])
|
||||
}
|
||||
}
|
||||
|
||||
// assertRegisterRange asserts that reading or writing the given
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ func (d *I2CDeviceCmd) Addr() uint8 {
|
||||
}
|
||||
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
func (d *I2CDeviceCmd) ReadRegister(r uint8, buf []byte) error {
|
||||
func (d *I2CDeviceCmd) readRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func (d *I2CDeviceCmd) ReadRegister(r uint8, buf []byte) error {
|
||||
}
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
func (d *I2CDeviceCmd) WriteRegister(r uint8, buf []byte) error {
|
||||
func (d *I2CDeviceCmd) writeRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
|
||||
+2
-2
@@ -38,12 +38,12 @@ func (bus *I2CBus) NewDevice(addr uint8) *I2CDevice8 {
|
||||
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
func (bus *I2CBus) ReadRegister(addr uint8, r uint8, buf []byte) error {
|
||||
return bus.FindDevice(addr).ReadRegister(r, buf)
|
||||
return bus.FindDevice(addr).readRegister(r, buf)
|
||||
}
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
func (bus *I2CBus) WriteRegister(addr uint8, r uint8, buf []byte) error {
|
||||
return bus.FindDevice(addr).WriteRegister(r, buf)
|
||||
return bus.FindDevice(addr).writeRegister(r, buf)
|
||||
}
|
||||
|
||||
// Tx implements I2C.Tx.
|
||||
|
||||
+6
-3
@@ -4,7 +4,10 @@
|
||||
|
||||
package tmp102 // import "tinygo.org/x/drivers/tmp102"
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device holds the already configured I2C bus and the address of the sensor.
|
||||
type Device struct {
|
||||
@@ -36,7 +39,7 @@ func (d *Device) Configure(cfg Config) {
|
||||
// Connected checks if the config register can be read and that the configuration is correct.
|
||||
func (d *Device) Connected() bool {
|
||||
configData := make([]byte, 2)
|
||||
err := d.bus.ReadRegister(d.address, RegConfiguration, configData)
|
||||
err := legacy.ReadRegister(d.bus, d.address, RegConfiguration, configData)
|
||||
// Check the reset configuration values.
|
||||
if err != nil || configData[0] != 0x60 || configData[1] != 0xA0 {
|
||||
return false
|
||||
@@ -50,7 +53,7 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
|
||||
tmpData := make([]byte, 2)
|
||||
|
||||
err = d.bus.ReadRegister(d.address, RegTemperature, tmpData)
|
||||
err = legacy.ReadRegister(d.bus, d.address, RegTemperature, tmpData)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ package drivers
|
||||
|
||||
// Version returns a user-readable string showing the version of the drivers package for support purposes.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.25.0"
|
||||
const Version = "0.26.0"
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
type Config struct {
|
||||
Width int16 // Width is the display resolution
|
||||
Height int16
|
||||
LogicalWidth int16 // LogicalWidth must be a multiple of 8 and same size or bigger than Width
|
||||
Rotation Rotation // Rotation is clock-wise
|
||||
LogicalWidth int16 // LogicalWidth must be a multiple of 8 and same size or bigger than Width
|
||||
Rotation drivers.Rotation
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
@@ -30,10 +30,11 @@ type Device struct {
|
||||
height int16
|
||||
buffer []uint8
|
||||
bufferLength uint32
|
||||
rotation Rotation
|
||||
rotation drivers.Rotation
|
||||
}
|
||||
|
||||
type Rotation uint8
|
||||
// Deprecated: use drivers.Rotation instead.
|
||||
type Rotation = drivers.Rotation
|
||||
|
||||
// Look up table for full updates
|
||||
var lutFullUpdate = [30]uint8{
|
||||
@@ -130,6 +131,17 @@ func (d *Device) DeepSleep() {
|
||||
d.WaitUntilIdle()
|
||||
}
|
||||
|
||||
// Set the sleep mode of the panel. The display will still show its contents,
|
||||
// but will go into a lower-power state.
|
||||
func (d *Device) Sleep(sleepEnabled bool) error {
|
||||
if sleepEnabled {
|
||||
d.DeepSleep()
|
||||
} else {
|
||||
d.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendCommand sends a command to the display
|
||||
func (d *Device) SendCommand(command uint8) {
|
||||
d.sendDataCommand(true, command)
|
||||
@@ -167,18 +179,22 @@ func (d *Device) SetLUT(fullUpdate bool) {
|
||||
}
|
||||
|
||||
// SetPixel modifies the internal buffer in a single pixel.
|
||||
// The display have 2 colors: black and white
|
||||
// We use RGBA(0,0,0, 255) as white (transparent)
|
||||
// Anything else as black
|
||||
// The display have 2 colors: black and white. We use a very simple cutoff to
|
||||
// determine whether a pixel is black or white (darker colors are black, lighter
|
||||
// colors are white).
|
||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||
x, y = d.xy(x, y)
|
||||
if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height {
|
||||
return
|
||||
}
|
||||
byteIndex := (x + y*d.logicalWidth) / 8
|
||||
if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
|
||||
// Very simle black/white split.
|
||||
// This isn't very accurate (especially for sRGB colors) but is close enough
|
||||
// to the truth that it probably doesn't matter much - especially on an
|
||||
// e-paper display.
|
||||
if int(c.R)+int(c.G)+int(c.B) > 128*3 { // light, convert to white
|
||||
d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
|
||||
} else { // WHITE / EMPTY
|
||||
} else { // dark, convert to black
|
||||
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
|
||||
}
|
||||
}
|
||||
@@ -209,13 +225,13 @@ func (d *Device) DisplayRect(x int16, y int16, width int16, height int16) error
|
||||
if x < 0 || y < 0 || x >= d.logicalWidth || y >= d.height || width < 0 || height < 0 {
|
||||
return errors.New("wrong rectangle")
|
||||
}
|
||||
if d.rotation == ROTATION_90 {
|
||||
if d.rotation == drivers.Rotation90 {
|
||||
width, height = height, width
|
||||
x -= width
|
||||
} else if d.rotation == ROTATION_180 {
|
||||
} else if d.rotation == drivers.Rotation180 {
|
||||
x -= width - 1
|
||||
y -= height - 1
|
||||
} else if d.rotation == ROTATION_270 {
|
||||
} else if d.rotation == drivers.Rotation270 {
|
||||
width, height = height, width
|
||||
y -= height
|
||||
}
|
||||
@@ -301,27 +317,33 @@ func (d *Device) ClearBuffer() {
|
||||
|
||||
// Size returns the current size of the display.
|
||||
func (d *Device) Size() (w, h int16) {
|
||||
if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
|
||||
if d.rotation == drivers.Rotation90 || d.rotation == drivers.Rotation270 {
|
||||
return d.height, d.logicalWidth
|
||||
}
|
||||
return d.logicalWidth, d.height
|
||||
}
|
||||
|
||||
// SetRotation changes the rotation (clock-wise) of the device
|
||||
func (d *Device) SetRotation(rotation Rotation) {
|
||||
// Rotation returns the current rotation of the device.
|
||||
func (d *Device) Rotation() drivers.Rotation {
|
||||
return d.rotation
|
||||
}
|
||||
|
||||
// SetRotation changes the rotation of the device.
|
||||
func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||
d.rotation = rotation
|
||||
return nil
|
||||
}
|
||||
|
||||
// xy chages the coordinates according to the rotation
|
||||
func (d *Device) xy(x, y int16) (int16, int16) {
|
||||
switch d.rotation {
|
||||
case NO_ROTATION:
|
||||
case drivers.Rotation0:
|
||||
return x, y
|
||||
case ROTATION_90:
|
||||
case drivers.Rotation90:
|
||||
return d.width - y - 1, x
|
||||
case ROTATION_180:
|
||||
case drivers.Rotation180:
|
||||
return d.width - x - 1, d.height - y - 1
|
||||
case ROTATION_270:
|
||||
case drivers.Rotation270:
|
||||
return y, d.height - x - 1
|
||||
}
|
||||
return x, y
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package epd2in13
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
// Registers
|
||||
const (
|
||||
DRIVER_OUTPUT_CONTROL = 0x01
|
||||
@@ -24,8 +26,8 @@ const (
|
||||
SET_RAM_Y_ADDRESS_COUNTER = 0x4F
|
||||
TERMINATE_FRAME_READ_WRITE = 0xFF
|
||||
|
||||
NO_ROTATION Rotation = 0
|
||||
ROTATION_90 Rotation = 1 // 90 degrees clock-wise rotation
|
||||
ROTATION_180 Rotation = 2
|
||||
ROTATION_270 Rotation = 3
|
||||
NO_ROTATION = drivers.Rotation0
|
||||
ROTATION_90 = drivers.Rotation90 // 90 degrees clock-wise rotation
|
||||
ROTATION_180 = drivers.Rotation180
|
||||
ROTATION_270 = drivers.Rotation270
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user