Compare commits

..

40 Commits

Author SHA1 Message Date
Ayke van Laethem 5d1b0c39cc gbadisplay: add simple driver for the GameBoy Advance display
This only implements the 15 bits per pixel mode, not any of the other
possible display modes. This matches conventional SPI displays most
closely. Future additions might add more display modes.

Normally I'd argue interfacing with chip/board specific hardware should
be done in the machine package using a generic interface, but the GBA is
kind of special and I don't want the machine package to depend on the
tinygo.org/x/drivers/pixel package.
2023-11-23 22:39:33 +01:00
Ayke van Laethem 6cf07aff4c pyportal_boing: update to use DrawBitmap
Replace DrawRGBBitmap8 with DrawBitmap, following the change in the
previous commit.

This improves performance from 86fps to 100fps! I didn't investigate
why, but I suspect it's because it now needs only a single store instead
of two to update a pixel.
2023-11-23 17:27:00 +01:00
Ayke van Laethem af33129f41 ili9341: st7735: st7789: add DrawBitmap method
This adds a new DrawBitmap method, which is meant to replace
DrawRGBBitmap8.
2023-11-23 17:27:00 +01:00
Ayke van Laethem 100aadf585 st7789: make the display generic over RGB565 and RGB444
Same as for st7735 in the previous commit.

In addition, this avoids allocating a big chunk of memory on _every_
draw operation (even SetPixel) and instead reuses it across draw
operations. This makes the driver a whole lot more efficient.
2023-11-23 17:27:00 +01:00
Ayke van Laethem 4a9667ffef st7735: make the display generic over RGB565 and RGB444
Using RGB444 instead of RGB565 can speed up graphics operations by up to
25%, especially on slow screens. But for full support, all parts of the
driver need to be aware of the color format.

It's possible to do this using a regular configuration variable, but
it's unlikely to be very efficient. Hence the usage of generics.
2023-11-23 17:27:00 +01:00
Ayke van Laethem f96a70915e pixel: add package for efficiently working with raw pixel buffers
This has been optimized for working with SPI displays like the ST7789.
By working directly in the native color format of the display, graphics
operations can be much, _much_ faster.

Also, this makes it easier to use a different color format like RGB444
simply by changing the generic type.
2023-11-23 17:27:00 +01:00
Ayke van Laethem 3e64e754a2 epd2in13: use better black/white approximation
The previoius behavior was that entirely black pixels were treated as
white, and anything else as black. That's at least counter-intuitive.
This patch changes the behavior to actually look at the color values and
use a cutoff around medium gray: darker colors are treated as black, and
lighter colors are treated as white.

This is a backwards incompatible change, but I think this behavior makes
a lot more sense.
2023-11-09 10:07:19 +01:00
Ayke van Laethem 2c2da5c7bb epd2in13: add Sleep method like other displays
For details, see: https://github.com/tinygo-org/drivers/pull/548
2023-11-07 10:39:14 +01:00
Ayke van Laethem 14994a3f31 epd2in13: unify rotation configuration with other displays
This commit updates rotation behavior to match other displays.
For more information, see: https://github.com/tinygo-org/drivers/pull/550

This changes the signature of `SetRotation` but I don't think any
existing code will be affected by this change.
2023-11-07 10:39:14 +01:00
deadprogram 47dfeb9e94 examples/lora/lorawan: modify atcmd and basic demo to support choosing
any one of the supported regions at compile time by using ldflags.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-10-26 08:32:04 +02:00
deadprogram f20d1759d3 examples/lora/lorawan: add missing functions for simulated interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-10-26 08:32:04 +02:00
deadprogram 6b79a1b386 lora/lorawan: refactor shared functionality for ChannelUS, etc into embedded type
named channel, do the same for RegionSettings, and then change RegionSettings to
just Settings to avoid the redundant naming.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-10-26 08:32:04 +02:00
Emilio Garcia 7dd1067cc1 LoRa WAN add setter functions 2023-10-26 08:32:04 +02:00
Emilio Garcia 4edb771c5b fix lora us915 logic to step into 500 kHz range 2023-10-26 08:32:04 +02:00
Mirac Kara e95cfe66c1 LoRa WAN US915 Support
Adds a driver for us 915 protocol to the lora WAN drivers.
2023-10-26 08:32:04 +02:00
Thomas Richner 4edb58c0c5 fix-tests: fix broken testrunner 2023-10-25 09:57:40 +02:00
Thomas Richner f384e2db48 fix-tests: migrated legacy I2C 2023-10-25 09:57:40 +02:00
sago35 7c96387845 sh1106: fix I2C interface and add smoketest 2023-10-14 11:43:06 +02:00
Christian Ege 182d4c6ebb sh1106: fixed the description of the device struct
It looks like there was an copy paste left over. This is corrected to be
concise.

Signed-off-by: Christian Ege <ch@ege.io>
2023-10-09 08:59:44 +02:00
Thomas Richner dc2de4f9ed adafruit4650: support for Adafruite 4650 feather OLED 2023-10-06 09:03:32 +02:00
Christian Ege 36a12dd7a2 at24cx: fixed the description of the device struct
It looks like there was an copy paste left over. This is corrected to be
concise.

Signed-off-by: Christian Ege <ch@ege.io>
2023-10-06 08:44:12 +02:00
Christian Ege 00992756eb ds3231: fix the description in the example
It looks like there was a copy paste leftover. This got corrected to be
concise.

Signed-off-by: Christian Ege <ch@ege.io>
2023-10-06 08:44:12 +02:00
Ayke van Laethem 715c33d4b0 all: prepare for CGo changes in TinyGo
For details, see: https://github.com/tinygo-org/tinygo/pull/3927
This change just means we need to be more careful to use the right type,
now that types like C.uint32_t don't match to the Go equivalent (like
uint32).
2023-10-05 10:01:25 +02:00
deadprogram fd21e6ac9b all: update for drivers release 0.26.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-21 09:43:46 +02:00
Thomas Richner 485ed702c8 pcf8523: bump minimal go version to 1.18 2023-09-20 19:14:17 +02:00
Thomas Richner cfa50fd3c2 pcf8523: RTC driver 2023-09-20 19:14:17 +02:00
Thomas 5888bb2ded Sensirion Sht4x Support (#597)
sht4x: implemented driver
2023-09-19 08:31:43 +02:00
fchiesadoc a9b36f8bd4 Mpu9150 (#596)
mpu9150: add mpu9150 driver
2023-08-27 11:38:33 +02:00
deadprogram 1e4545828f build: use latest tag of tinygo-dev container for running tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-12 10:08:28 +02:00
dependabot[bot] cfa5103969 build(deps): bump golang.org/x/net
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20210614182718-04defd469f4e to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/commits/v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-11 14:39:57 +02:00
Patricio Whittingslow a45227590e add Sandbox Electronics NDIR CO2 sensor driver (2) (#580)
add sandbox electronics NDIR CO2 sensor
2023-08-11 11:58:28 +02:00
Kenneth Bell 4f789fb556 ssd1306: improve bus error handling 2023-08-11 11:40:45 +02:00
Kenneth Bell 996f1b047f fix uses of legacy i2c WriteRegister calls 2023-08-08 11:16:50 +02:00
Ayke van Laethem eef03917ab bma42x: add new BMA421/BMA425 driver
I wrote this for the PineTime, and all available sensors (accelerometer,
step counter, temperature sensor) do work.

This commit also includes two "configuration files", that actually
appear to be firmware files to run on the accelerometer for special
features like step counting.
I'm not sure where they originally came for, and I don't know the
copyright status of them. However, Bosch has open-sourced the BMA423
driver which includes a similar binary blob and the InfiniTime and
Wasp-OS projects have been shipping these blobs without issues, so I
think it's reasonably safe to include these binary blobs directly in the
source.
2023-08-08 10:36:23 +02:00
soypat 31538f1b2f fix requested changes by @aykevl 2023-07-02 18:30:11 +02:00
soypat 64029612e0 add correct Tx implementation for mock I2C interfaces 2023-07-02 18:30:11 +02:00
soypat e6f82fad2e i2c iface refactor: Resolve 559 2023-07-02 18:30:11 +02:00
Ayke van Laethem e20c6d05f8 st7789: fix scrolling when rotated by 180°
This fixes https://github.com/tinygo-org/drivers/issues/573.
It doesn't handle 90° or 270°, I guess it needs a fix for 270° but I
haven't tested that so didn't include it in the patch.
2023-06-20 17:49:47 +02:00
Ayke van Laethem 9c29529cbb st7789: fix incorrect Rotation configuration
The rotation as configured using st7789.Config was rotated 180°: 0° was
configured as 180°, 90° was configured as 270°, etc. Presumably with the
original test display, the ribbon cable was seen as the top of the
screen while if you look at product photos it is usually at the bottom.
Example:
https://www.buydisplay.com/wide-angle-1-3-inch-240x240-color-ips-tft-display-st7789-controller
Only Adafruit seems to sell these displays upside down:
https://www.adafruit.com/product/3787

This patch fixes this mistake. It should be noted that this is backwards
incompatible: all code that uses a st7789 will have to be modified to
use the correct rotation instead of the previous incorrect rotation.

If this is too big of a change, we could just keep things as-is and
pretend that all displays are upside down.
2023-06-20 10:44:49 +02:00
Ayke van Laethem f6d399ec08 ili9341: st7789: fix SetScrollArea
The existing code was broken in a few ways:

  - It didn't use the correct operator precedence for the VSCRDEF VSA
    variable: it needed some extra parentheses to be correct.
  - It used the configured height instead of the actual display height
    for calculating VSA, which is incorrect. TFA+VSA+BFA must always be
    exactly 320, even if a lower value is configured.
  - If a lower than 320 pixel height is configured, the bottomFixedArea
    parameter applied to the whole 320 pixel screen height. Because this
    seems counter intuitive (and relies on properties of any given
    screen), I've changed it to work from the actual visible bottom of
    the screen (which may be smaller than 320 pixels).
    TODO: this doesn't take RowOffset into account, while it probably
    should.

I haven't fixed the st7735 implementation, because I didn't have example
code on hand that would easily work on a st7735 screen. This is left as
a TODO for the future.
2023-06-20 10:27:08 +02:00
104 changed files with 3464 additions and 621 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/tinygo-org/tinygo-dev container: ghcr.io/tinygo-org/tinygo-dev:latest
steps: steps:
- name: Work around CVE-2022-24765 - name: Work around CVE-2022-24765
# We're not on a multi-user machine, so this is safe. # We're not on a multi-user machine, so this is safe.
+37
View File
@@ -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 0.25.0
--- ---
+4 -6
View File
@@ -3,7 +3,10 @@
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml) [![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](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 ## 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 ## Contributing
Your contributions are welcome! Your contributions are welcome!
+196
View File
@@ -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
}
}
+176
View File
@@ -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
View File
@@ -7,6 +7,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type Error uint8 type Error uint8
@@ -54,7 +55,7 @@ func (d *Device) Configure() (err error) {
// Connected returns whether sensor has been found. // Connected returns whether sensor has been found.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0]&0xF8 == 0xC8
} }
@@ -81,11 +82,11 @@ func (d *Device) writeByte(reg uint8, data byte) {
} }
func (d *Device) readByte(reg uint8) 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] return d.buf[0]
} }
func (d *Device) readUint16(reg uint8) uint16 { 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]) return uint16(d.buf[0])<<8 | uint16(d.buf[1])
} }
+13 -10
View File
@@ -5,7 +5,10 @@
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf // Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
package adxl345 // import "tinygo.org/x/drivers/adxl345" 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 Range uint8
type Rate uint8 type Rate uint8
@@ -68,21 +71,21 @@ func New(bus drivers.I2C) Device {
// Configure sets up the device for communication // Configure sets up the device for communication
func (d *Device) Configure() { func (d *Device) Configure() {
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()})
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()})
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()})
} }
// Halt stops the sensor, values will not updated // Halt stops the sensor, values will not updated
func (d *Device) Halt() { func (d *Device) Halt() {
d.powerCtl.measure = 0 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 // Restart makes reading the sensor working again after a halt
func (d *Device) Restart() { func (d *Device) Restart() {
d.powerCtl.measure = 1 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 // 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. // from the adxl345.
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) { func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
data := []byte{0, 0, 0, 0, 0, 0} 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]) x = readIntLE(data[0], data[1])
y = readIntLE(data[2], data[3]) y = readIntLE(data[2], data[3])
@@ -119,20 +122,20 @@ func (d *Device) UseLowPower(power bool) {
} else { } else {
d.bwRate.lowPower = 0 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 // SetRate change the current rate of the sensor
func (d *Device) SetRate(rate Rate) bool { func (d *Device) SetRate(rate Rate) bool {
d.bwRate.rate = rate & 0x0F 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 return true
} }
// SetRange change the current range of the sensor // SetRange change the current range of the sensor
func (d *Device) SetRange(sensorRange Range) bool { func (d *Device) SetRange(sensorRange Range) bool {
d.dataFormat.sensorRange = sensorRange & 0x03 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 return true
} }
+17 -16
View File
@@ -8,6 +8,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a AMG88xx device. // 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 // ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
func (d *Device) ReadPixels(buffer *[64]int16) { 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++ { for i := 0; i < 64; i++ {
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i])) buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
if (buffer[i] & (1 << 11)) > 0 { // temperature negative if (buffer[i] & (1 << 11)) > 0 { // temperature negative
@@ -61,17 +62,17 @@ func (d *Device) ReadPixels(buffer *[64]int16) {
// SetPCTL sets the PCTL // SetPCTL sets the PCTL
func (d *Device) SetPCTL(pctl uint8) { 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 // SetReset sets the reset value
func (d *Device) SetReset(rst uint8) { 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 // SetFrameRate configures the frame rate
func (d *Device) SetFrameRate(framerate uint8) { 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 // SetMovingAverageMode sets the moving average mode
@@ -80,7 +81,7 @@ func (d *Device) SetMovingAverageMode(mode bool) {
if mode { if mode {
value = 1 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 // SetInterruptLevels sets the interrupt levels
@@ -97,8 +98,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
if high > 4095 { if high > 4095 {
high = 4095 high = 4095
} }
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)}) legacy.WriteRegister(d.bus, 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) >> 4)})
low = low / PIXEL_TEMP_CONVERSION low = low / PIXEL_TEMP_CONVERSION
if low < -4095 { if low < -4095 {
@@ -107,8 +108,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
if low > 4095 { if low > 4095 {
low = 4095 low = 4095
} }
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)}) legacy.WriteRegister(d.bus, 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) >> 4)})
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
if hysteresis < -4095 { if hysteresis < -4095 {
@@ -117,32 +118,32 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
if hysteresis > 4095 { if hysteresis > 4095 {
hysteresis = 4095 hysteresis = 4095
} }
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)}) legacy.WriteRegister(d.bus, 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) >> 4)})
} }
// EnableInterrupt enables the interrupt pin on the device // EnableInterrupt enables the interrupt pin on the device
func (d *Device) EnableInterrupt() { func (d *Device) EnableInterrupt() {
d.interruptEnable = 1 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 // DisableInterrupt disables the interrupt pin on the device
func (d *Device) DisableInterrupt() { func (d *Device) DisableInterrupt() {
d.interruptEnable = 0 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 // SetInterruptMode sets the interrupt mode
func (d *Device) SetInterruptMode(mode InterruptMode) { func (d *Device) SetInterruptMode(mode InterruptMode) {
d.interruptMode = mode 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 // GetInterrupt reads the state of the triggered interrupts
func (d *Device) GetInterrupt() []uint8 { func (d *Device) GetInterrupt() []uint8 {
data := make([]uint8, 8) 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 return data
} }
@@ -154,6 +155,6 @@ func (d *Device) ClearInterrupt() {
// ReadThermistor reads the onboard thermistor // ReadThermistor reads the onboard thermistor
func (d *Device) ReadThermistor() int16 { func (d *Device) ReadThermistor() int16 {
data := make([]uint8, 2) 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 return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
} }
+26 -25
View File
@@ -8,6 +8,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a APDS-9960 device. // 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. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0xAB
} }
@@ -80,7 +81,7 @@ func (d *Device) GetMode() uint8 {
// DisableAll turns off the device and all functions // DisableAll turns off the device and all functions
func (d *Device) DisableAll() { func (d *Device) DisableAll() {
d.enable(enableConfig{}) 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.mode = MODE_NONE
d.gesture.detected = GESTURE_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) // SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
// default: 16, 64 // default: 16, 64
func (d *Device) SetProximityPulse(length, count uint8) { 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) // SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
// default: 16, 64 // default: 16, 64
func (d *Device) SetGesturePulse(length, count uint8) { 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) // 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 { if cycles > 256 {
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) // SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
// default: 1, 1, 4 // default: 1, 1, 4
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) { func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
d.bus.WriteRegister(d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)}) legacy.WriteRegister(d.bus, 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_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
} }
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%)) // LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
@@ -127,7 +128,7 @@ func (d *Device) LEDBoost(percent uint16) {
case 300: case 300:
v = 3 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 // Setthreshold sets threshold (0~255) for detecting gestures
@@ -168,7 +169,7 @@ func (d *Device) ReadProximity() (proximity int32) {
return 0 return 0
} }
data := []byte{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]) return 255 - int32(data[0])
} }
@@ -195,14 +196,14 @@ func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
return return
} }
data := []byte{0, 0, 0, 0, 0, 0, 0, 0} data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
d.bus.ReadRegister(d.Address, APDS9960_CDATAL_REG, data[:1]) legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1])
d.bus.ReadRegister(d.Address, APDS9960_CDATAH_REG, data[1:2]) legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2])
d.bus.ReadRegister(d.Address, APDS9960_RDATAL_REG, data[2:3]) legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3])
d.bus.ReadRegister(d.Address, APDS9960_RDATAH_REG, data[3:4]) legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4])
d.bus.ReadRegister(d.Address, APDS9960_GDATAL_REG, data[4:5]) legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5])
d.bus.ReadRegister(d.Address, APDS9960_GDATAH_REG, data[5:6]) legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6])
d.bus.ReadRegister(d.Address, APDS9960_BDATAL_REG, data[6:7]) legacy.ReadRegister(d.bus, 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_BDATAH_REG, data[7:])
clear = int32(uint16(data[1])<<8 | uint16(data[0])) clear = int32(uint16(data[1])<<8 | uint16(data[0]))
r = int32(uint16(data[3])<<8 | uint16(data[2])) r = int32(uint16(data[3])<<8 | uint16(data[2]))
g = int32(uint16(data[5])<<8 | uint16(data[4])) g = int32(uint16(data[5])<<8 | uint16(data[4]))
@@ -234,13 +235,13 @@ func (d *Device) GestureAvailable() bool {
data := []byte{0, 0, 0, 0} data := []byte{0, 0, 0, 0}
// check GVALID // 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 { if data[0]&0x01 == 0 {
return false return false
} }
// get number of data sets available in FIFO // 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] availableDataSets := data[0]
if availableDataSets == 0 { if availableDataSets == 0 {
return false return false
@@ -249,10 +250,10 @@ func (d *Device) GestureAvailable() bool {
// read up, down, left and right proximity data from FIFO // read up, down, left and right proximity data from FIFO
var dataSets [32][4]uint8 var dataSets [32][4]uint8
for i := uint8(0); i < availableDataSets; i++ { for i := uint8(0); i < availableDataSets; i++ {
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_U_REG, data[:1]) legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1])
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_D_REG, data[1:2]) legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2])
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_L_REG, data[2:3]) legacy.ReadRegister(d.bus, 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_R_REG, data[3:4])
for j := uint8(0); j < 4; j++ { for j := uint8(0); j < 4; j++ {
dataSets[i][j] = data[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} 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 { if cfg.PON {
time.Sleep(time.Millisecond * 10) time.Sleep(time.Millisecond * 10)
@@ -394,7 +395,7 @@ func (d *Device) enable(cfg enableConfig) {
func (d *Device) readStatus(param string) bool { func (d *Device) readStatus(param string) bool {
data := []byte{0} data := []byte{0}
d.bus.ReadRegister(d.Address, APDS9960_STATUS_REG, data) legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
switch param { switch param {
case "CPSAT": case "CPSAT":
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// registerAttributes is a bitfield of attributes for a register // 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[:] buf = buffer[:]
} }
// Read the host register over I2C // 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 { if nil != err {
return 0, 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 // 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) // after successful I2C write, cache this value if the host register (if also readable)
// Note we cache the entire buffer without applying shift/mask // Note we cache the entire buffer without applying shift/mask
if nil == err && r.host.attributes&reg_read != 0 { if nil == err && r.host.attributes&reg_read != 0 {
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"tinygo.org/x/drivers" "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 { type Device struct {
bus drivers.I2C bus drivers.I2C
Address uint16 Address uint16
+3 -2
View File
@@ -7,6 +7,7 @@ package axp192 // import "tinygo.org/x/drivers/axp192"
import ( import (
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type Error uint8 type Error uint8
@@ -248,10 +249,10 @@ func (d *Device) SetLDOEnable(number uint8, state bool) {
} }
func (d *Device) write1Byte(reg, data uint8) { 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 { 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] return d.buf[0]
} }
Binary file not shown.
Binary file not shown.
+352
View File
@@ -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
}
}
+73
View File
@@ -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
View File
@@ -10,6 +10,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// calibrationCoefficients reads at startup and stores the calibration coefficients // calibrationCoefficients reads at startup and stores the calibration coefficients
@@ -98,19 +99,19 @@ func (d *Device) ConfigureWithSettings(config Config) {
} }
var data [24]byte 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 { if err != nil {
return return
} }
var h1 [1]byte 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 { if err != nil {
return return
} }
var h2lsb [7]byte 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 { if err != nil {
return return
} }
@@ -137,12 +138,12 @@ func (d *Device) ConfigureWithSettings(config Config) {
d.Reset() d.Reset()
d.bus.WriteRegister(uint8(d.Address), CTRL_CONFIG, []byte{byte(d.Config.Period<<5) | byte(d.Config.IIR<<2)}) legacy.WriteRegister(d.bus, 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_HUMIDITY_ADDR, []byte{byte(d.Config.Humidity)})
// Normal mode, start measuring now // Normal mode, start measuring now
if d.Config.Mode == ModeNormal { 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.Temperature<<5) |
byte(d.Config.Pressure<<2) | byte(d.Config.Pressure<<2) |
byte(d.Config.Mode)}) 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. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0] == CHIP_ID
} }
// Reset the device // Reset the device
func (d *Device) Reset() { 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 // 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) { func (d *Device) SetMode(mode Mode) {
d.Config.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.Temperature<<5) |
byte(d.Config.Pressure<<2) | byte(d.Config.Pressure<<2) |
byte(d.Config.Mode)}) byte(d.Config.Mode)})
@@ -252,7 +253,7 @@ func readIntLE(msb byte, lsb byte) int16 {
func (d *Device) readData() (data [8]byte, err error) { func (d *Device) readData() (data [8]byte, err error) {
if d.Config.Mode == ModeForced { if d.Config.Mode == ModeForced {
// Write the CTRL_MEAS register to trigger a measurement // 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.Temperature<<5) |
byte(d.Config.Pressure<<2) | byte(d.Config.Pressure<<2) |
byte(d.Config.Mode)}) byte(d.Config.Mode)})
@@ -260,7 +261,7 @@ func (d *Device) readData() (data [8]byte, err error) {
time.Sleep(d.measurementDelay()) 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 { if err != nil {
println(err) println(err)
return return
+7 -6
View File
@@ -10,6 +10,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// OversamplingMode is the oversampling ratio of the pressure measurement. // 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. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0] == CHIP_ID
} }
@@ -63,7 +64,7 @@ func (d *Device) Connected() bool {
// read the calibration coefficients. // read the calibration coefficients.
func (d *Device) Configure() { func (d *Device) Configure() {
data := make([]byte, 22) 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 { if err != nil {
return return
} }
@@ -141,10 +142,10 @@ func (d *Device) ReadAltitude() (int32, error) {
// rawTemp returns the sensor's raw values of the temperature // rawTemp returns the sensor's raw values of the temperature
func (d *Device) rawTemp() (int32, error) { 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) time.Sleep(5 * time.Millisecond)
data := make([]byte, 2) 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 { if err != nil {
return 0, err return 0, err
} }
@@ -160,10 +161,10 @@ func (d *Device) calculateB5(rawTemp int32) int32 {
// rawPressure returns the sensor's raw values of the pressure // rawPressure returns the sensor's raw values of the pressure
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) { 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)) time.Sleep(pauseForReading(mode))
data := make([]byte, 3) 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 { if err != nil {
return 0, err return 0, err
} }
+9 -8
View File
@@ -4,6 +4,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement. // 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. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := make([]byte, 1) 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 return data[0] == CHIP_ID
} }
// Reset preforms complete power-on-reset procedure. // Reset preforms complete power-on-reset procedure.
// It is required to call Configure afterwards. // It is required to call Configure afterwards.
func (d *Device) Reset() { 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 // 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) // Write the configuration (standby, filter, spi 3 wire)
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00 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, // Write the control (temperature oversampling, pressure oversampling,
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode) 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 // Read Calibration data
data := make([]byte, 24) 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 { if err != nil {
return 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 // After the measurement in FORCED mode, the sensor will return to SLEEP mode
if d.Mode != MODE_NORMAL { if d.Mode != MODE_NORMAL {
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED) 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 // Check STATUS register, wait if data is not available yet
status := make([]byte, 1) 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) time.Sleep(time.Millisecond)
} }
// Read the requested register // Read the requested register
data := make([]byte, n) 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 return data, err
} }
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
var ( var (
@@ -240,10 +241,10 @@ func (d *Device) configurationError() bool {
func (d *Device) readRegister(register byte, len int) (data []byte, err error) { func (d *Device) readRegister(register byte, len int) (data []byte, err error) {
data = make([]byte, len) data = make([]byte, len)
err = d.bus.ReadRegister(d.Address, register, data) err = legacy.ReadRegister(d.bus, d.Address, register, data)
return return
} }
func (d *Device) writeRegister(register byte, data byte) error { 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
View File
@@ -49,7 +49,7 @@ func Sleep(duration time.Duration) {
// * The CPU frequency is lower than 256MHz. If it is higher, long sleep // * The CPU frequency is lower than 256MHz. If it is higher, long sleep
// times (1-16ms) may not work correctly. // times (1-16ms) may not work correctly.
cycles := uint32(duration) * (machine.CPUFrequency() / 1000_000) / 1000 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 { if !slept {
// Fallback for platforms without inline assembly support. // Fallback for platforms without inline assembly support.
time.Sleep(duration) time.Sleep(duration)
+5 -4
View File
@@ -9,6 +9,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a DS1307 device. // 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 // ReadTime returns the date and time
func (d *Device) ReadTime() (time.Time, error) { func (d *Device) ReadTime() (time.Time, error) {
data := make([]byte, 8) 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 { if err != nil {
return time.Time{}, err 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 { if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
return 0, errors.New("EOF") 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 { if err != nil {
return 0, err return 0, err
} }
@@ -124,7 +125,7 @@ func (d *Device) SetOscillatorFrequency(sqw uint8) error {
// IsOscillatorRunning returns if the oscillator is running // IsOscillatorRunning returns if the oscillator is running
func (d *Device) IsOscillatorRunning() bool { func (d *Device) IsOscillatorRunning() bool {
data := []byte{0} 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 { if err != nil {
return false return false
} }
@@ -134,7 +135,7 @@ func (d *Device) IsOscillatorRunning() bool {
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit // SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
func (d *Device) SetOscillatorRunning(running bool) error { func (d *Device) SetOscillatorRunning(running bool) error {
data := make([]byte, 3) 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 { if err != nil {
return err return err
} }
+10 -9
View File
@@ -8,6 +8,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type Mode uint8 type Mode uint8
@@ -37,7 +38,7 @@ func (d *Device) Configure() bool {
// IsTimeValid return true/false is the time in the device is valid // IsTimeValid return true/false is the time in the device is valid
func (d *Device) IsTimeValid() bool { func (d *Device) IsTimeValid() bool {
data := []byte{0} 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 { if err != nil {
return false return false
} }
@@ -47,7 +48,7 @@ func (d *Device) IsTimeValid() bool {
// IsRunning returns if the oscillator is running // IsRunning returns if the oscillator is running
func (d *Device) IsRunning() bool { func (d *Device) IsRunning() bool {
data := []uint8{0} 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 { if err != nil {
return false return false
} }
@@ -57,7 +58,7 @@ func (d *Device) IsRunning() bool {
// SetRunning starts the internal oscillator // SetRunning starts the internal oscillator
func (d *Device) SetRunning(isRunning bool) error { func (d *Device) SetRunning(isRunning bool) error {
data := []uint8{0} 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 { if err != nil {
return err return err
} }
@@ -66,7 +67,7 @@ func (d *Device) SetRunning(isRunning bool) error {
} else { } else {
data[0] |= 1 << EOSC 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 { if err != nil {
return err return err
} }
@@ -86,12 +87,12 @@ func (d *Device) SetRunning(isRunning bool) error {
// instead of 2100-03-01. // instead of 2100-03-01.
func (d *Device) SetTime(dt time.Time) error { func (d *Device) SetTime(dt time.Time) error {
data := []byte{0} 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 { if err != nil {
return err return err
} }
data[0] &^= 1 << OSF 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 { if err != nil {
return err return err
} }
@@ -117,7 +118,7 @@ func (d *Device) SetTime(dt time.Time) error {
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag) data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
data[6] = uint8ToBCD(year) 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 { if err != nil {
return err return err
} }
@@ -128,7 +129,7 @@ func (d *Device) SetTime(dt time.Time) error {
// ReadTime returns the date and time // ReadTime returns the date and time
func (d *Device) ReadTime() (dt time.Time, err error) { func (d *Device) ReadTime() (dt time.Time, err error) {
data := make([]uint8, 7) 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 { if err != nil {
return return
} }
@@ -150,7 +151,7 @@ func (d *Device) ReadTime() (dt time.Time, err error) {
// ReadTemperature returns the temperature in millicelsius (mC) // ReadTemperature returns the temperature in millicelsius (mC)
func (d *Device) ReadTemperature() (int32, error) { func (d *Device) ReadTemperature() (int32, error) {
data := make([]uint8, 2) 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 { if err != nil {
return 0, err return 0, err
} }
+42
View File
@@ -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})
}
+54
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
// Connects to an MAG3110 I2C magnetometer. // Connects to an DS3231 I2C Real Time Clock (RTC).
package main package main
import ( import (
+23 -22
View File
@@ -8,15 +8,16 @@ import (
"tinygo.org/x/drivers/examples/ili9341/initdisplay" "tinygo.org/x/drivers/examples/ili9341/initdisplay"
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics" "tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
"tinygo.org/x/drivers/ili9341" "tinygo.org/x/drivers/ili9341"
"tinygo.org/x/drivers/pixel"
) )
const ( const (
BGCOLOR = 0xAD75 BGCOLOR = pixel.RGB565BE(0x75AD)
GRIDCOLOR = 0xA815 GRIDCOLOR = pixel.RGB565BE(0x15A8)
BGSHADOW = 0x5285 BGSHADOW = pixel.RGB565BE(0x8552)
GRIDSHADOW = 0x600C GRIDSHADOW = pixel.RGB565BE(0x0C60)
RED = 0xF800 RED = pixel.RGB565BE(0x00F8)
WHITE = 0xFFFF WHITE = pixel.RGB565BE(0xFFFF)
YBOTTOM = 123 // Ball Y coord at bottom YBOTTOM = 123 // Ball Y coord at bottom
YBOUNCE = -3.5 // Upward velocity on ball bounce YBOUNCE = -3.5 // Upward velocity on ball bounce
@@ -25,7 +26,7 @@ const (
) )
var ( var (
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8) * 2]uint8{} frameBuffer = pixel.NewImage[pixel.RGB565BE](graphics.BALLWIDTH+8, graphics.BALLHEIGHT+8)
startTime int64 startTime int64
frame int64 frame int64
@@ -41,7 +42,7 @@ var (
balloldy float32 balloldy float32
// Color table for ball rotation effect // Color table for ball rotation effect
palette [16]uint16 palette [16]pixel.RGB565BE
) )
var ( var (
@@ -108,6 +109,7 @@ func main() {
width = maxx - minx + 1 width = maxx - minx + 1
height = maxy - miny + 1 height = maxy - miny + 1
buffer := frameBuffer.Rescale(int(width), int(height))
// Ball animation frame # is incremented opposite the ball's X velocity // Ball animation frame # is incremented opposite the ball's X velocity
ballframe -= ballvx * 0.5 ballframe -= ballvx * 0.5
@@ -128,7 +130,7 @@ func main() {
} }
// Only the changed rectangle is drawn into the 'renderbuf' array... // 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) bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
by := miny - int16(bally) // Y 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) bgx := minx // X relative to background bitmap (>= 0)
@@ -149,19 +151,20 @@ func main() {
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area? (by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
// Yes, do ball compositing math... // Yes, do ball compositing math...
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels) p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
var nibble uint8
if (bx1 & 1) != 0 { if (bx1 & 1) != 0 {
c = uint16(p & 0xF) nibble = p & 0xF
} else { } else {
c = uint16(p >> 4) nibble = p >> 4
} // Unpack high or low nybble } // 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 { if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDCOLOR c = GRIDCOLOR
} else { } else {
c = BGCOLOR c = BGCOLOR
} }
} else if c > 1 { // In ball area... } else if nibble > 1 { // In ball area...
c = palette[c] c = palette[nibble]
} else { // In shadow area... } else { // In shadow area...
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 { if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDSHADOW c = GRIDSHADOW
@@ -176,8 +179,7 @@ func main() {
c = BGCOLOR c = BGCOLOR
} }
} }
frameBuffer[(y*int(width)+x)*2] = byte(c >> 8) buffer.Set(x, y, c)
frameBuffer[(y*int(width)+x)*2+1] = byte(c)
bx1++ // Increment bitmap position counters (X axis) bx1++ // Increment bitmap position counters (X axis)
bgx1++ bgx1++
} }
@@ -188,7 +190,7 @@ func main() {
bgy++ bgy++
} }
display.DrawRGBBitmap8(minx, miny, frameBuffer[:width*height*2], width, height) display.DrawBitmap(minx, miny, buffer)
// Show approximate frame rate // Show approximate frame rate
frame++ frame++
@@ -205,6 +207,7 @@ func DrawBackground() {
w, h := display.Size() w, h := display.Size()
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
var b uint8 var b uint8
buffer := frameBuffer.Rescale(int(w), 1)
for j := int16(0); j < h; j++ { for j := int16(0); j < h; j++ {
for k := int16(0); k < w; k++ { for k := int16(0); k < w; k++ {
if k&7 > 0 { if k&7 > 0 {
@@ -213,13 +216,11 @@ func DrawBackground() {
b = graphics.Background[j*byteWidth+k/8] b = graphics.Background[j*byteWidth+k/8]
} }
if b&0x80 == 0 { if b&0x80 == 0 {
frameBuffer[2*k] = byte(BGCOLOR >> 8) buffer.Set(int(k), 0, BGCOLOR)
frameBuffer[2*k+1] = byte(BGCOLOR & 0xFF)
} else { } else {
frameBuffer[2*k] = byte(GRIDCOLOR >> 8) buffer.Set(int(k), 0, GRIDCOLOR)
frameBuffer[2*k+1] = byte(GRIDCOLOR & 0xFF)
} }
} }
display.DrawRGBBitmap8(0, j, frameBuffer[0:w*2], w, 1) display.DrawBitmap(0, j, buffer)
} }
} }
+4 -4
View File
@@ -23,20 +23,20 @@ Builds/flashes atcmd console application with simulator instead of actual LoRa r
tinygo flash -target pico ./examples/lora/lorawan/atcmd/ 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). 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. 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 ## Joining a Public Lorawan Network
+12 -1
View File
@@ -32,6 +32,8 @@ var (
defaultTimeout uint32 = 1000 defaultTimeout uint32 = 1000
) )
var reg string
func main() { func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx}) uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
@@ -45,7 +47,16 @@ func main() {
otaa = &lorawan.Otaa{} otaa = &lorawan.Otaa{}
lorawan.UseRadio(radio) 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 { for {
if uart.Buffered() > 0 { if uart.Buffered() > 0 {
+4 -4
View File
@@ -21,16 +21,16 @@ loraConnect: Connected !
tinygo flash -target pico ./examples/lora/lorawan/basic-demo 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
``` ```
+14 -3
View File
@@ -12,7 +12,10 @@ import (
"tinygo.org/x/drivers/lora/lorawan/region" "tinygo.org/x/drivers/lora/lorawan/region"
) )
var debug string var (
reg string
debug string
)
const ( const (
LORAWAN_JOIN_TIMEOUT_SEC = 180 LORAWAN_JOIN_TIMEOUT_SEC = 180
@@ -67,8 +70,16 @@ func main() {
// Connect the lorawan with the Lora Radio device. // Connect the lorawan with the Lora Radio device.
lorawan.UseRadio(radio) lorawan.UseRadio(radio)
switch reg {
lorawan.UseRegionSettings(region.EU868()) 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 // Configure AppEUI, DevEUI, APPKey, and public/private Lorawan Network
setLorawanKeys() setLorawanKeys()
+12 -7
View File
@@ -23,13 +23,18 @@ func (sr *SimLoraRadio) Rx(timeoutMs uint32) ([]uint8, error) {
return nil, nil return nil, nil
} }
func (sr *SimLoraRadio) SetFrequency(freq uint32) {} func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
func (sr *SimLoraRadio) SetIqMode(mode uint8) {} func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {} func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {} func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
func (sr *SimLoraRadio) SetCrc(enable bool) {} func (sr *SimLoraRadio) SetCrc(enable bool) {}
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {} func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
func (sr *SimLoraRadio) LoraConfig(cnf lora.Config) {} 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 { func FirmwareVersion() string {
return "simulator " + CurrentVersion() return "simulator " + CurrentVersion()
+22
View File
@@ -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)
}
}
+41
View File
@@ -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())
}
}
+34
View File
@@ -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)
}
}
+23
View File
@@ -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
View File
@@ -8,6 +8,7 @@ import (
"machine" "machine"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/touch" "tinygo.org/x/drivers/touch"
) )
@@ -98,10 +99,10 @@ func (d *Device) Touched() bool {
} }
func (d *Device) write1Byte(reg, data uint8) { 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 { 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] return d.buf[0]
} }
+85
View File
@@ -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
}
+10 -2
View File
@@ -1,12 +1,20 @@
module tinygo.org/x/drivers module tinygo.org/x/drivers
go 1.15 go 1.18
require ( require (
github.com/eclipse/paho.mqtt.golang v1.2.0 github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/frankban/quicktest v1.10.2 github.com/frankban/quicktest v1.10.2
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 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/tinyfont v0.3.0
tinygo.org/x/tinyterm v0.1.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
)
+4 -14
View File
@@ -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/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 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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/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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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= 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.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= 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.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.15.1/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.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 h1:HIRLQoI3oc+2CMhPcfv+Ig88EcTImE/5npjqOnMD4lM=
tinygo.org/x/tinyfont v0.3.0/go.mod h1:+TV5q0KpwSGRWnN+ITijsIhrWYJkoUCp9MYELjKpAXk= 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 h1:80i+j+KWoxCFa/Xfp6pWbh79x+8zUdMXC1vaKj2QhkY=
tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4= tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4=
+25 -24
View File
@@ -8,6 +8,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a HTS221 device. // 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. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0xBC
} }
@@ -42,7 +43,7 @@ func (d *Device) Power(status bool) {
if status { if status {
data[0] = 0x84 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. // ReadHumidity returns the relative humidity in percent * 100.
@@ -55,8 +56,8 @@ func (d *Device) ReadHumidity() (humidity int32, err error) {
// read data and calibrate // read data and calibrate
data := []byte{0, 0} data := []byte{0, 0}
d.bus.ReadRegister(d.Address, HTS221_HUMID_OUT_REG, data[:1]) legacy.ReadRegister(d.bus, 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+1, data[1:])
hValue := readInt(data[1], data[0]) hValue := readInt(data[1], data[0])
hValueCalib := float32(hValue)*d.humiditySlope + d.humidityZero hValueCalib := float32(hValue)*d.humiditySlope + d.humidityZero
@@ -73,8 +74,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
// read data and calibrate // read data and calibrate
data := []byte{0, 0} data := []byte{0, 0}
d.bus.ReadRegister(d.Address, HTS221_TEMP_OUT_REG, data[:1]) legacy.ReadRegister(d.bus, 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+1, data[1:])
tValue := readInt(data[1], data[0]) tValue := readInt(data[1], data[0])
tValueCalib := float32(tValue)*d.temperatureSlope + d.temperatureZero tValueCalib := float32(tValue)*d.temperatureSlope + d.temperatureZero
@@ -91,7 +92,7 @@ func (d *Device) Resolution(h uint8, t uint8) {
if t > 7 { if t > 7 {
t = 3 // default 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 // private functions
@@ -104,19 +105,19 @@ func (d *Device) calibration() {
h0t0Out, h1t0Out := []byte{0, 0}, []byte{0, 0} h0t0Out, h1t0Out := []byte{0, 0}, []byte{0, 0}
t0Out, t1Out := []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) legacy.ReadRegister(d.bus, d.Address, HTS221_H0_rH_x2_REG, h0rH)
d.bus.ReadRegister(d.Address, HTS221_H1_rH_x2_REG, h1rH) legacy.ReadRegister(d.bus, d.Address, HTS221_H1_rH_x2_REG, h1rH)
d.bus.ReadRegister(d.Address, HTS221_T0_degC_x8_REG, t0degC) legacy.ReadRegister(d.bus, d.Address, HTS221_T0_degC_x8_REG, t0degC)
d.bus.ReadRegister(d.Address, HTS221_T1_degC_x8_REG, t1degC) legacy.ReadRegister(d.bus, d.Address, HTS221_T1_degC_x8_REG, t1degC)
d.bus.ReadRegister(d.Address, HTS221_T1_T0_MSB_REG, t1t0msb) legacy.ReadRegister(d.bus, d.Address, HTS221_T1_T0_MSB_REG, t1t0msb)
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1]) legacy.ReadRegister(d.bus, d.Address, HTS221_H0_T0_OUT_REG, h0t0Out[:1])
d.bus.ReadRegister(d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:]) legacy.ReadRegister(d.bus, d.Address, HTS221_H0_T0_OUT_REG+1, h0t0Out[1:])
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1]) legacy.ReadRegister(d.bus, d.Address, HTS221_H1_T0_OUT_REG, h1t0Out[:1])
d.bus.ReadRegister(d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:]) legacy.ReadRegister(d.bus, d.Address, HTS221_H1_T0_OUT_REG+1, h1t0Out[1:])
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG, t0Out[:1]) legacy.ReadRegister(d.bus, d.Address, HTS221_T0_OUT_REG, t0Out[:1])
d.bus.ReadRegister(d.Address, HTS221_T0_OUT_REG+1, t0Out[1:]) legacy.ReadRegister(d.bus, d.Address, HTS221_T0_OUT_REG+1, t0Out[1:])
d.bus.ReadRegister(d.Address, HTS221_T1_OUT_REG, t1Out[:1]) legacy.ReadRegister(d.bus, 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_T1_OUT_REG+1, t1Out[1:])
h0rH_v := float32(h0rH[0]) / 2.0 h0rH_v := float32(h0rH[0]) / 2.0
h1rH_v := float32(h1rH[0]) / 2.0 h1rH_v := float32(h1rH[0]) / 2.0
@@ -138,7 +139,7 @@ func (d *Device) waitForOneShot(filter uint8) error {
data := []byte{0} data := []byte{0}
// check if the device is on // 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 { if data[0]&0x80 == 0 {
return errors.New("device is off, unable to query") 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 // wait until one shot (one conversion) is ready to go
data[0] = 1 data[0] = 1
for { 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 { if data[0]&0x01 == 0 {
break break
} }
} }
// trigger one shot // 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 // wait until conversion completed
data[0] = 0 data[0] = 0
for { 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 { if data[0]&filter == filter {
break break
} }
+10 -2
View File
@@ -3,7 +3,15 @@ package drivers
// I2C represents an I2C bus. It is notably implemented by the // I2C represents an I2C bus. It is notably implemented by the
// machine.I2C type. // machine.I2C type.
type I2C interface { type I2C interface {
ReadRegister(addr uint8, r uint8, buf []byte) error // Tx performs a [I²C] transaction with address addr.
WriteRegister(addr uint8, r uint8, buf []byte) error // 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 Tx(addr uint16, w, r []byte) error
} }
+23 -2
View File
@@ -7,6 +7,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/pixel"
) )
type Config struct { type Config struct {
@@ -31,6 +32,9 @@ type Device struct {
rd machine.Pin rd machine.Pin
} }
// Image buffer type used in the ili9341.
type Image = pixel.Image[pixel.RGB565BE]
var cmdBuf [6]byte var cmdBuf [6]byte
var initCmd = []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 // 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 { func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
k, i := d.Size() k, i := d.Size()
if x < 0 || y < 0 || w <= 0 || h <= 0 || 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 // 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 { func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error {
k, i := d.Size() k, i := d.Size()
if x < 0 || y < 0 || w <= 0 || h <= 0 || 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 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 // FillRectangle fills a rectangle at given coordinates with a color
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error { func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
k, i := d.Size() 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 // SetScrollArea sets an area to scroll with fixed top/bottom or left/right parts of the display
// Rotation affects scroll direction // Rotation affects scroll direction
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) { 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[0] = uint8(topFixedArea >> 8)
cmdBuf[1] = uint8(topFixedArea) cmdBuf[1] = uint8(topFixedArea)
cmdBuf[2] = uint8(d.height - topFixedArea - bottomFixedArea>>8) cmdBuf[2] = uint8((320 - topFixedArea - bottomFixedArea) >> 8)
cmdBuf[3] = uint8(d.height - topFixedArea - bottomFixedArea) cmdBuf[3] = uint8(320 - topFixedArea - bottomFixedArea)
cmdBuf[4] = uint8(bottomFixedArea >> 8) cmdBuf[4] = uint8(bottomFixedArea >> 8)
cmdBuf[5] = uint8(bottomFixedArea) cmdBuf[5] = uint8(bottomFixedArea)
d.sendCommand(VSCRDEF, cmdBuf[:6]) d.sendCommand(VSCRDEF, cmdBuf[:6])
+6 -3
View File
@@ -1,6 +1,9 @@
package ina260 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. // Device wraps an I2C connection to an INA260 device.
type Device struct { type Device struct {
@@ -97,7 +100,7 @@ func (d *Device) Power() int32 {
// Read a register // Read a register
func (d *Device) ReadRegister(reg uint8) uint16 { func (d *Device) ReadRegister(reg uint8) uint16 {
data := []byte{0, 0} 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]) 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[0] = byte(v >> 8)
data[1] = byte(v & 0xff) data[1] = byte(v & 0xff)
d.bus.WriteRegister(uint8(d.Address), reg, data) legacy.WriteRegister(d.bus, uint8(d.Address), reg, data)
} }
+14
View File
@@ -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)
}
+6 -5
View File
@@ -20,6 +20,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device implements TinyGo driver for Lumissil IS31FL3731 matrix LED driver // 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) { func (d *Device) selectCommand(command uint8) (err error) {
if command != d.selectedCommand { if command != d.selectedCommand {
d.selectedCommand = command d.selectedCommand = command
return d.bus.WriteRegister(d.Address, COMMAND, []byte{command}) return legacy.WriteRegister(d.bus, d.Address, COMMAND, []byte{command})
} }
return nil return nil
@@ -105,7 +106,7 @@ func (d *Device) writeFunctionRegister(operation uint8, data []byte) (err error)
return err 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 // 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) // Enable every LED (16 columns x 9 rows)
for i := uint8(0); i < 16; i++ { 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 { if err != nil {
return err return err
} }
@@ -136,7 +137,7 @@ func (d *Device) setPixelPWD(frame, n, value uint8) (err error) {
return err 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 // 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++ { 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 { if err != nil {
return err return err
} }
+4 -3
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// DeviceAdafruitCharlieWing15x7 implements TinyGo driver for Lumissil // DeviceAdafruitCharlieWing15x7 implements TinyGo driver for Lumissil
@@ -47,20 +48,20 @@ func (d *DeviceAdafruitCharlieWing15x7) enableLEDs() (err error) {
// Enable left half // Enable left half
for i := uint8(0); i < 16; i += 2 { 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 { if err != nil {
return err return err
} }
} }
// Enable right half // Enable right half
for i := uint8(3); i < 16; i += 2 { 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 { if err != nil {
return err return err
} }
} }
// Disable invisible column on the right side // 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 { if err != nil {
return err return err
} }
+6 -5
View File
@@ -5,6 +5,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
const ( const (
@@ -87,15 +88,15 @@ func (d *DevI2C) Configure(cfg Config) error {
} }
func (d *DevI2C) Update() 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 { if err != nil {
return err 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 { if err != nil {
return err 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 { if err != nil {
return err return err
} }
@@ -131,11 +132,11 @@ func (d *DevI2C) AngularVelocity() (x, y, z int32) {
// func (d DevI2C) Update(measurement) // func (d DevI2C) Update(measurement)
func (d DevI2C) read8(reg uint8) (byte, error) { 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 return d.buf[0], err
} }
func (d DevI2C) write8(reg uint8, val byte) error { func (d DevI2C) write8(reg uint8, val byte) error {
d.buf[0] = val 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
View File
@@ -9,6 +9,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a LIS2MDL device. // 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. // Connected returns whether LIS2MDL sensor has been found.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0x40
} }
@@ -66,25 +67,25 @@ func (d *Device) Configure(cfg Configuration) {
// reset // reset
cmd[0] = byte(1 << 5) 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) time.Sleep(100 * time.Millisecond)
// reboot // reboot
cmd[0] = byte(1 << 6) 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) time.Sleep(100 * time.Millisecond)
// bdu // bdu
cmd[0] = byte(1 << 4) 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) // Temperature compensation is on for magnetic sensor (0x80)
cmd[0] = byte(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 // speed
cmd[0] = byte(0x80 | d.DataRate) 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 // 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? // turn back on read mode, even though it is supposed to be continuous?
cmd := []byte{0} cmd := []byte{0}
cmd[0] = byte(0x80 | d.PowerMode<<4 | d.DataRate<<2 | d.SystemMode) 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) time.Sleep(10 * time.Millisecond)
data := make([]byte, 6) 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]))) x = int32(int16((uint16(data[0]) << 8) | uint16(data[1])))
y = int32(int16((uint16(data[2]) << 8) | uint16(data[3]))) y = int32(int16((uint16(data[2]) << 8) | uint16(data[3])))
+13 -10
View File
@@ -3,7 +3,10 @@
// Datasheet: https://www.st.com/resource/en/datasheet/lis3dh.pdf // Datasheet: https://www.st.com/resource/en/datasheet/lis3dh.pdf
package lis3dh // import "tinygo.org/x/drivers/lis3dh" 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. // Device wraps an I2C connection to a LIS3DH device.
type Device struct { type Device struct {
@@ -22,13 +25,13 @@ func New(bus drivers.I2C) Device {
// Configure sets up the device for communication // Configure sets up the device for communication
func (d *Device) Configure() { func (d *Device) Configure() {
// enable all axes, normal mode // 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 // 400Hz rate
d.SetDataRate(DATARATE_400_HZ) d.SetDataRate(DATARATE_400_HZ)
// High res & BDU enabled // 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 // get current range
d.r = d.ReadRange() d.r = d.ReadRange()
@@ -38,7 +41,7 @@ func (d *Device) Configure() {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 { if err != nil {
return false return false
} }
@@ -48,27 +51,27 @@ func (d *Device) Connected() bool {
// SetDataRate sets the speed of data collected by the LIS3DH. // SetDataRate sets the speed of data collected by the LIS3DH.
func (d *Device) SetDataRate(rate DataRate) { func (d *Device) SetDataRate(rate DataRate) {
ctl1 := []byte{0} 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 { if err != nil {
println(err.Error()) println(err.Error())
} }
// mask off bits // mask off bits
ctl1[0] &^= 0xf0 ctl1[0] &^= 0xf0
ctl1[0] |= (byte(rate) << 4) 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. // SetRange sets the G range for LIS3DH.
func (d *Device) SetRange(r Range) { func (d *Device) SetRange(r Range) {
ctl := []byte{0} 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 { if err != nil {
println(err.Error()) println(err.Error())
} }
// mask off bits // mask off bits
ctl[0] &^= 0x30 ctl[0] &^= 0x30
ctl[0] |= (byte(r) << 4) 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 // store the new range
d.r = r d.r = r
@@ -77,7 +80,7 @@ func (d *Device) SetRange(r Range) {
// ReadRange returns the current G range for LIS3DH. // ReadRange returns the current G range for LIS3DH.
func (d *Device) ReadRange() (r Range) { func (d *Device) ReadRange() (r Range) {
ctl := []byte{0} 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 { if err != nil {
println(err.Error()) 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 // ReadRawAcceleration returns the raw x, y and z axis from the LIS3DH
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) { 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} data := []byte{0, 0, 0, 0, 0, 0}
d.bus.Tx(d.Address, nil, data) d.bus.Tx(d.Address, nil, data)
+3
View File
@@ -80,6 +80,9 @@ const (
const ( const (
MHz_868_1 = 868100000 MHz_868_1 = 868100000
MHz_868_5 = 868500000 MHz_868_5 = 868500000
MHz_902_3 = 902300000
Mhz_903_0 = 903000000
MHZ_915_0 = 915000000
MHz_916_8 = 916800000 MHz_916_8 = 916800000
MHz_923_3 = 923300000 MHz_923_3 = 923300000
) )
+31 -25
View File
@@ -30,11 +30,11 @@ const (
var ( var (
ActiveRadio lora.Radio ActiveRadio lora.Radio
Retries = 15 Retries = 15
regionSettings region.RegionSettings regionSettings region.Settings
) )
// UseRegionSettings sets current Lorawan Regional parameters // UseRegionSettings sets current Lorawan Regional parameters
func UseRegionSettings(rs region.RegionSettings) { func UseRegionSettings(rs region.Settings) {
regionSettings = rs regionSettings = rs
} }
@@ -52,13 +52,13 @@ func SetPublicNetwork(enabled bool) {
} }
// ApplyChannelConfig sets current Lora modulation according to current regional settings // ApplyChannelConfig sets current Lora modulation according to current regional settings
func applyChannelConfig(ch *region.Channel) { func applyChannelConfig(ch region.Channel) {
ActiveRadio.SetFrequency(ch.Frequency) ActiveRadio.SetFrequency(ch.Frequency())
ActiveRadio.SetBandwidth(ch.Bandwidth) ActiveRadio.SetBandwidth(ch.Bandwidth())
ActiveRadio.SetCodingRate(ch.CodingRate) ActiveRadio.SetCodingRate(ch.CodingRate())
ActiveRadio.SetSpreadingFactor(ch.SpreadingFactor) ActiveRadio.SetSpreadingFactor(ch.SpreadingFactor())
ActiveRadio.SetPreambleLength(ch.PreambleLength) ActiveRadio.SetPreambleLength(ch.PreambleLength())
ActiveRadio.SetTxPower(ch.TxPowerDBm) ActiveRadio.SetTxPower(ch.TxPowerDBm())
// Lorawan defaults to explicit headers // Lorawan defaults to explicit headers
ActiveRadio.SetHeaderType(lora.HeaderExplicit) ActiveRadio.SetHeaderType(lora.HeaderExplicit)
ActiveRadio.SetCrc(true) ActiveRadio.SetCrc(true)
@@ -84,24 +84,30 @@ func Join(otaa *Otaa, session *Session) error {
return err return err
} }
// Prepare radio for Join Tx for {
applyChannelConfig(regionSettings.JoinRequestChannel()) joinRequestChannel := regionSettings.JoinRequestChannel()
ActiveRadio.SetIqMode(lora.IQStandard) joinAcceptChannel := regionSettings.JoinAcceptChannel()
ActiveRadio.Tx(payload, LORA_TX_TIMEOUT)
if err != nil {
return err
}
// Wait for JoinAccept // Prepare radio for Join Tx
applyChannelConfig(regionSettings.JoinAcceptChannel()) applyChannelConfig(joinRequestChannel)
ActiveRadio.SetIqMode(lora.IQInverted) ActiveRadio.SetIqMode(lora.IQStandard)
resp, err = ActiveRadio.Rx(LORA_RX_TIMEOUT) ActiveRadio.Tx(payload, LORA_TX_TIMEOUT)
if err != nil { if err != nil {
return err return err
} }
if resp == nil { // Wait for JoinAccept
return ErrNoJoinAcceptReceived 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) err = otaa.DecodeJoinAccept(resp, session)
+21 -23
View File
@@ -7,43 +7,41 @@ const (
AU915_DEFAULT_TX_POWER_DBM = 20 AU915_DEFAULT_TX_POWER_DBM = 20
) )
type RegionSettingsAU915 struct { type ChannelAU struct {
joinRequestChannel *Channel channel
joinAcceptChannel *Channel
uplinkChannel *Channel
} }
func AU915() *RegionSettingsAU915 { func (c *ChannelAU) Next() bool {
return &RegionSettingsAU915{ return false
joinRequestChannel: &Channel{lora.MHz_916_8, }
type SettingsAU915 struct {
settings
}
func AU915() *SettingsAU915 {
return &SettingsAU915{settings: settings{
joinRequestChannel: &ChannelAU{channel: channel{lora.MHz_916_8,
lora.Bandwidth_125_0, lora.Bandwidth_125_0,
lora.SpreadingFactor9, lora.SpreadingFactor9,
lora.CodingRate4_5, lora.CodingRate4_5,
AU915_DEFAULT_PREAMBLE_LEN, AU915_DEFAULT_PREAMBLE_LEN,
AU915_DEFAULT_TX_POWER_DBM}, AU915_DEFAULT_TX_POWER_DBM}},
joinAcceptChannel: &Channel{lora.MHz_923_3, joinAcceptChannel: &ChannelAU{channel: channel{lora.MHz_923_3,
lora.Bandwidth_500_0, lora.Bandwidth_500_0,
lora.SpreadingFactor9, lora.SpreadingFactor9,
lora.CodingRate4_5, lora.CodingRate4_5,
AU915_DEFAULT_PREAMBLE_LEN, AU915_DEFAULT_PREAMBLE_LEN,
AU915_DEFAULT_TX_POWER_DBM}, AU915_DEFAULT_TX_POWER_DBM}},
uplinkChannel: &Channel{lora.MHz_916_8, uplinkChannel: &ChannelAU{channel: channel{lora.MHz_916_8,
lora.Bandwidth_125_0, lora.Bandwidth_125_0,
lora.SpreadingFactor9, lora.SpreadingFactor9,
lora.CodingRate4_5, lora.CodingRate4_5,
AU915_DEFAULT_PREAMBLE_LEN, AU915_DEFAULT_PREAMBLE_LEN,
AU915_DEFAULT_TX_POWER_DBM}, AU915_DEFAULT_TX_POWER_DBM}},
} }}
} }
func (r *RegionSettingsAU915) JoinRequestChannel() *Channel { func Next(c *ChannelAU) bool {
return r.joinRequestChannel return false
}
func (r *RegionSettingsAU915) JoinAcceptChannel() *Channel {
return r.joinAcceptChannel
}
func (r *RegionSettingsAU915) UplinkChannel() *Channel {
return r.uplinkChannel
} }
+42
View File
@@ -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 }
+19 -25
View File
@@ -7,43 +7,37 @@ const (
EU868_DEFAULT_TX_POWER_DBM = 20 EU868_DEFAULT_TX_POWER_DBM = 20
) )
type RegionSettingsEU868 struct { type ChannelEU struct {
joinRequestChannel *Channel channel
joinAcceptChannel *Channel
uplinkChannel *Channel
} }
func EU868() *RegionSettingsEU868 { func (c *ChannelEU) Next() bool {
return &RegionSettingsEU868{ return false
joinRequestChannel: &Channel{lora.MHz_868_1, }
type SettingsEU868 struct {
settings
}
func EU868() *SettingsEU868 {
return &SettingsEU868{settings: settings{
joinRequestChannel: &ChannelEU{channel: channel{lora.MHz_868_1,
lora.Bandwidth_125_0, lora.Bandwidth_125_0,
lora.SpreadingFactor9, lora.SpreadingFactor9,
lora.CodingRate4_7, lora.CodingRate4_7,
EU868_DEFAULT_PREAMBLE_LEN, EU868_DEFAULT_PREAMBLE_LEN,
EU868_DEFAULT_TX_POWER_DBM}, EU868_DEFAULT_TX_POWER_DBM}},
joinAcceptChannel: &Channel{lora.MHz_868_1, joinAcceptChannel: &ChannelEU{channel: channel{lora.MHz_868_1,
lora.Bandwidth_125_0, lora.Bandwidth_125_0,
lora.SpreadingFactor9, lora.SpreadingFactor9,
lora.CodingRate4_7, lora.CodingRate4_7,
EU868_DEFAULT_PREAMBLE_LEN, EU868_DEFAULT_PREAMBLE_LEN,
EU868_DEFAULT_TX_POWER_DBM}, EU868_DEFAULT_TX_POWER_DBM}},
uplinkChannel: &Channel{lora.MHz_868_1, uplinkChannel: &ChannelEU{channel: channel{lora.MHz_868_1,
lora.Bandwidth_125_0, lora.Bandwidth_125_0,
lora.SpreadingFactor9, lora.SpreadingFactor9,
lora.CodingRate4_7, lora.CodingRate4_7,
EU868_DEFAULT_PREAMBLE_LEN, EU868_DEFAULT_PREAMBLE_LEN,
EU868_DEFAULT_TX_POWER_DBM}, 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
} }
-16
View File
@@ -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
}
+25
View File
@@ -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
}
+82
View File
@@ -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
View File
@@ -5,6 +5,7 @@ package lps22hb
import ( import (
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a HTS221 device. // Device wraps an I2C connection to a HTS221 device.
@@ -27,9 +28,9 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
// read data // read data
data := []byte{0, 0, 0} data := []byte{0, 0, 0}
d.bus.ReadRegister(d.Address, LPS22HB_PRESS_OUT_REG, data[:1]) legacy.ReadRegister(d.bus, d.Address, LPS22HB_PRESS_OUT_REG, data[:1])
d.bus.ReadRegister(d.Address, LPS22HB_PRESS_OUT_REG+1, data[1:2]) legacy.ReadRegister(d.bus, 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+2, data[2:])
pValue := float32(uint32(data[2])<<16|uint32(data[1])<<8|uint32(data[0])) / 4096.0 pValue := float32(uint32(data[2])<<16|uint32(data[1])<<8|uint32(data[0])) / 4096.0
return int32(pValue * 1000), nil 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. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0xB1
} }
@@ -49,8 +50,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
// read data // read data
data := []byte{0, 0} data := []byte{0, 0}
d.bus.ReadRegister(d.Address, LPS22HB_TEMP_OUT_REG, data[:1]) legacy.ReadRegister(d.bus, 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+1, data[1:])
tValue := float32(int16(uint16(data[1])<<8|uint16(data[0]))) / 100.0 tValue := float32(int16(uint16(data[1])<<8|uint16(data[0]))) / 100.0
return int32(tValue * 1000), nil 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 // wait and trigger one shot in block update
func (d *Device) waitForOneShot() { func (d *Device) waitForOneShot() {
// trigger one shot // 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 // wait until one shot is cleared
data := []byte{1} data := []byte{1}
for { 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 { if data[0]&0x01 == 0 {
break break
} }
+2 -2
View File
@@ -2,10 +2,10 @@
package lps22hb package lps22hb
import "tinygo.org/x/drivers" import "tinygo.org/x/drivers/internal/legacy"
// Configure sets up the LPS22HB device for communication. // Configure sets up the LPS22HB device for communication.
func (d *Device) Configure() { func (d *Device) Configure() {
// set to block update mode // 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})
} }
+3 -1
View File
@@ -5,6 +5,8 @@ package lps22hb
import ( import (
"machine" "machine"
"time" "time"
"tinygo.org/x/drivers/internal/legacy"
) )
// Configure sets up the LPS22HB device for communication. // Configure sets up the LPS22HB device for communication.
@@ -18,5 +20,5 @@ func (d *Device) Configure() {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
// set to block update mode // 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
View File
@@ -9,6 +9,7 @@ import (
"math" "math"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an I2C connection to a LSM303AGR device. // 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. // It does two "who am I" requests and checks the responses.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data1, data2 := []byte{0}, []byte{0} data1, data2 := []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_WHO_AM_I, data1) legacy.ReadRegister(d.bus, 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.MagAddress), MAG_WHO_AM_I, data2)
return data1[0] == 0x33 && data2[0] == 0x40 return data1[0] == 0x33 && data2[0] == 0x40
} }
@@ -104,26 +105,26 @@ func (d *Device) Configure(cfg Configuration) (err error) {
data := d.buf[:1] data := d.buf[:1]
data[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07) 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 { if err != nil {
return return
} }
data[0] = byte(0x80 | d.AccelRange<<4) 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 { if err != nil {
return return
} }
data[0] = byte(0xC0) 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 { if err != nil {
return return
} }
// Temperature compensation is on for magnetic sensor // Temperature compensation is on for magnetic sensor
data[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode) 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 { if err != nil {
return return
} }
@@ -137,7 +138,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
// -1000000. // -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) { func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return return
} }
@@ -183,14 +184,14 @@ func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
if d.MagSystemMode == MAG_SYSTEM_SINGLE { if d.MagSystemMode == MAG_SYSTEM_SINGLE {
cmd := d.buf[:1] cmd := d.buf[:1]
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode) 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 { if err != nil {
return return
} }
} }
data := d.buf[0:6] 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])))) x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
y = int32(int16((uint16(data[3])<<8 | uint16(data[2])))) 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) { func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2] 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 { if err != nil {
return return
} }
+13 -12
View File
@@ -8,6 +8,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type AccelRange uint8 type AccelRange uint8
@@ -95,7 +96,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
if cfg.IsPedometer { // CONFIGURE AS PEDOMETER if cfg.IsPedometer { // CONFIGURE AS PEDOMETER
// Configure accelerometer: 2G + 26Hz // Configure accelerometer: 2G + 26Hz
data[0] = uint8(ACCEL_2G) | uint8(ACCEL_SR_26) 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 { if err != nil {
return return
} }
@@ -105,40 +106,40 @@ func (d *Device) Configure(cfg Configuration) (err error) {
if cfg.ResetStepCounter { if cfg.ResetStepCounter {
data[0] |= 0x02 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 { if err != nil {
return return
} }
// Enable pedometer // Enable pedometer
data[0] = 0x40 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 { if err != nil {
return return
} }
} else { // NORMAL USE } else { // NORMAL USE
// Configure accelerometer // Configure accelerometer
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate) | uint8(d.accelBandWidth) 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 { if err != nil {
return return
} }
// Set ODR bit // 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 { if err != nil {
return return
} }
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
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 { if err != nil {
return return
} }
// Configure gyroscope // Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate) 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 { if err != nil {
return return
} }
@@ -151,7 +152,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := d.buf[:1] 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 return data[0] == 0x69
} }
@@ -161,7 +162,7 @@ func (d *Device) Connected() bool {
// -1000000. // -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) { func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return return
} }
@@ -186,7 +187,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
// you would get a value close to 360000000. // you would get a value close to 360000000.
func (d *Device) ReadRotation() (x, y, z int32, err error) { func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return 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) // ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) { func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2] 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 { if err != nil {
return return
} }
@@ -223,7 +224,7 @@ func (d *Device) ReadTemperature() (t int32, err error) {
// ReadSteps returns the steps of the pedometer // ReadSteps returns the steps of the pedometer
func (d *Device) ReadSteps() (s int32, err error) { func (d *Device) ReadSteps() (s int32, err error) {
data := d.buf[:2] 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 { if err != nil {
return return
} }
+9 -8
View File
@@ -8,6 +8,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type AccelRange uint8 type AccelRange uint8
@@ -87,26 +88,26 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// Configure accelerometer // Configure accelerometer
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate) 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 { if err != nil {
return return
} }
// Set ODR bit // 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 { if err != nil {
return return
} }
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
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 { if err != nil {
return return
} }
// Configure gyroscope // Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate) 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 { if err != nil {
return return
} }
@@ -118,7 +119,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := d.buf[:1] 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 return data[0] == 0x6A
} }
@@ -128,7 +129,7 @@ func (d *Device) Connected() bool {
// -1000000. // -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) { func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return return
} }
@@ -153,7 +154,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
// you would get a value close to 360000000. // you would get a value close to 360000000.
func (d *Device) ReadRotation() (x, y, z int32, err error) { func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return 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) // ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) { func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2] 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 { if err != nil {
return return
} }
+7 -6
View File
@@ -8,6 +8,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type AccelRange uint8 type AccelRange uint8
@@ -78,13 +79,13 @@ func (d *Device) Configure(cfg Configuration) (err error) {
data := d.buf[:1] data := d.buf[:1]
// Configure accelerometer // Configure accelerometer
data[0] = uint8(cfg.AccelRange) | uint8(cfg.AccelSampleRate) 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 { if err != nil {
return return
} }
// Configure gyroscope // Configure gyroscope
data[0] = uint8(cfg.GyroRange) | uint8(cfg.GyroSampleRate) 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 { if err != nil {
return return
} }
@@ -96,7 +97,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := d.buf[:1] 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 return data[0] == 0x6C
} }
@@ -106,7 +107,7 @@ func (d *Device) Connected() bool {
// -1000000. // -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) { func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return return
} }
@@ -122,7 +123,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
// you would get a value close to 360000000. // you would get a value close to 360000000.
func (d *Device) ReadRotation() (x, y, z int32, err error) { func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return 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) // ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) { func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2] 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 { if err != nil {
return return
} }
+13 -12
View File
@@ -7,6 +7,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
type AccelRange uint8 type AccelRange uint8
@@ -61,8 +62,8 @@ func New(bus drivers.I2C) *Device {
// but "who am I" responses have unexpected values. // but "who am I" responses have unexpected values.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data1, data2 := d.buf[:1], d.buf[1:2] data1, data2 := d.buf[:1], d.buf[1:2]
d.bus.ReadRegister(d.AccelAddress, WHO_AM_I, data1) legacy.ReadRegister(d.bus, d.AccelAddress, WHO_AM_I, data1)
d.bus.ReadRegister(d.MagAddress, WHO_AM_I_M, data2) legacy.ReadRegister(d.bus, d.MagAddress, WHO_AM_I_M, data2)
return data1[0] == 0x68 && data2[0] == 0x3D return data1[0] == 0x68 && data2[0] == 0x3D
} }
@@ -72,7 +73,7 @@ func (d *Device) Connected() bool {
// -1000000. // -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) { func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return return
} }
@@ -88,7 +89,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
// you would get a value close to 360000000. // you would get a value close to 360000000.
func (d *Device) ReadRotation() (x, y, z int32, err error) { func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return 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). // it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
func (d *Device) ReadMagneticField() (x, y, z int32, err error) { func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
data := d.buf[:6] 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 { if err != nil {
return 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) // ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) { func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2] 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 { if err != nil {
return return
} }
@@ -171,7 +172,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// Configure accelerometer // Configure accelerometer
// Sample rate & measurement range // Sample rate & measurement range
data[0] = uint8(cfg.AccelSampleRate)<<5 | uint8(cfg.AccelRange)<<3 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 { if err != nil {
return return
} }
@@ -179,7 +180,7 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// Configure gyroscope // Configure gyroscope
// Sample rate & measurement range // Sample rate & measurement range
data[0] = uint8(cfg.GyroSampleRate)<<5 | uint8(cfg.GyroRange)<<3 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 { if err != nil {
return return
} }
@@ -190,14 +191,14 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// High-performance mode XY axis // High-performance mode XY axis
// Sample rate // Sample rate
data[0] = 0b10000000 | 0b01000000 | uint8(cfg.MagSampleRate)<<2 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 { if err != nil {
return return
} }
// Measurement range // Measurement range
data[0] = uint8(cfg.MagRange) << 5 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 { if err != nil {
return return
} }
@@ -205,14 +206,14 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// Continuous-conversion mode // Continuous-conversion mode
// https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode // https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode
data[0] = 0b00000000 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 { if err != nil {
return return
} }
// High-performance mode Z axis // High-performance mode Z axis
data[0] = 0b00001000 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 { if err != nil {
return return
} }
+9 -6
View File
@@ -4,7 +4,10 @@
// Datasheet: https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf // Datasheet: https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf
package mag3110 // import "tinygo.org/x/drivers/mag3110" 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. // Device wraps an I2C connection to a MAG3110 device.
type Device struct { type Device struct {
@@ -24,22 +27,22 @@ func New(bus drivers.I2C) Device {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d Device) Connected() bool { func (d Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0xC4
} }
// Configure sets up the device for communication. // Configure sets up the device for communication.
func (d Device) Configure() { 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 // ReadMagnetic reads the vectors of the magnetic field of the device and
// returns it. // returns it.
func (d Device) ReadMagnetic() (x int16, y int16, z int16) { 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) 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])) x = int16((uint16(data[0]) << 8) | uint16(data[1]))
y = int16((uint16(data[2]) << 8) | uint16(data[3])) y = int16((uint16(data[2]) << 8) | uint16(data[3]))
z = int16((uint16(data[4]) << 8) | uint16(data[5])) 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). // celsius milli degrees (°C/1000).
func (d Device) ReadTemperature() (int32, error) { func (d Device) ReadTemperature() (int32, error) {
data := make([]byte, 1) 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 return int32(data[0]) * 1000, nil
} }
+7 -11
View File
@@ -8,6 +8,9 @@ package mcp23017
import ( import (
"errors" "errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
const ( const (
@@ -75,19 +78,12 @@ const (
// address pins). // address pins).
var ErrInvalidHWAddress = errors.New("invalid hardware address") 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 // New returns a new MCP23017 device at the given I2C address
// on the given bus. // on the given bus.
// It returns ErrInvalidHWAddress if the address isn't possible for the device. // It returns ErrInvalidHWAddress if the address isn't possible for the device.
// //
// By default all pins are configured as inputs. // 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 { if address&hwAddressMask != hwAddress {
return nil, ErrInvalidHWAddress return nil, ErrInvalidHWAddress
} }
@@ -115,7 +111,7 @@ type Device struct {
// bus holds the reference the I2C bus that the device lives on. // bus holds the reference the I2C bus that the device lives on.
// It's an interface so that we can write tests for it. // It's an interface so that we can write tests for it.
bus I2C bus drivers.I2C
addr uint8 addr uint8
// pins caches the most recent pin values that have been set. // pins caches the most recent pin values that have been set.
// This enables us to change individual pin values without // 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 // and the fact that registers alternate between A and B
// to write both ports in a single operation. // to write both ports in a single operation.
buf := [2]byte{uint8(val), uint8(val >> 8)} 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) { 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 // and the fact that registers alternate between A and B
// to read both ports in a single operation. // to read both ports in a single operation.
var buf [2]byte 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(0), err
} }
return Pins(buf[0]) | (Pins(buf[1]) << 8), nil return Pins(buf[0]) | (Pins(buf[1]) << 8), nil
+3 -1
View File
@@ -1,5 +1,7 @@
package mcp23017 package mcp23017
import "tinygo.org/x/drivers"
// All is a convenience value that represents all pins high (or all mask bits one). // All is a convenience value that represents all pins high (or all mask bits one).
var All = PinSlice{0xffff} var All = PinSlice{0xffff}
@@ -12,7 +14,7 @@ type Devices []*Device
// NewI2CDevices returns a Devices slice holding the Device values // NewI2CDevices returns a Devices slice holding the Device values
// for all the given addresses on the given bus. // for all the given addresses on the given bus.
// When more than one bus is in use, create the slice yourself. // 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)) devs := make(Devices, len(addrs))
for i, addr := range addrs { for i, addr := range addrs {
dev, err := NewI2C(bus, addr) dev, err := NewI2C(bus, addr)
+9 -6
View File
@@ -5,7 +5,10 @@
// https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf // https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf
package mma8653 // import "tinygo.org/x/drivers/mma8653" 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. // Device wraps an I2C connection to a MMA8653 device.
type Device struct { type Device struct {
@@ -26,27 +29,27 @@ func New(bus drivers.I2C) Device {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d Device) Connected() bool { func (d Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0x5A
} }
// Configure sets up the device for communication. // Configure sets up the device for communication.
func (d *Device) Configure(speed DataRate, sensitivity Sensitivity) error { func (d *Device) Configure(speed DataRate, sensitivity Sensitivity) error {
// Set mode to STANDBY to be able to change the sensitivity. // 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 { if err != nil {
return err return err
} }
// Set sensitivity (2G, 4G, 8G). // 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 { if err != nil {
return err return err
} }
d.sensitivity = sensitivity d.sensitivity = sensitivity
// Set mode to ACTIVE and set the data rate. // 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 { if err != nil {
return err return err
} }
@@ -59,7 +62,7 @@ func (d *Device) Configure(speed DataRate, sensitivity Sensitivity) error {
// -1000000. // -1000000.
func (d Device) ReadAcceleration() (x int32, y int32, z int32, err error) { func (d Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
data := make([]byte, 6) 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) shift := uint32(8)
switch d.sensitivity { switch d.sensitivity {
case Sensitivity4G: case Sensitivity4G:
+10 -7
View File
@@ -6,7 +6,10 @@
// https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf // https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf
package mpu6050 // import "tinygo.org/x/drivers/mpu6050" 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. // Device wraps an I2C connection to a MPU6050 device.
type Device struct { type Device struct {
@@ -26,7 +29,7 @@ func New(bus drivers.I2C) Device {
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d Device) Connected() bool { func (d Device) Connected() bool {
data := []byte{0} 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 return data[0] == 0x68
} }
@@ -41,7 +44,7 @@ func (d Device) Configure() error {
// -1000000. // -1000000.
func (d Device) ReadAcceleration() (x int32, y int32, z int32) { func (d Device) ReadAcceleration() (x int32, y int32, z int32) {
data := make([]byte, 6) 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: // Now do two things:
// 1. merge the two values to a 16-bit number (and cast to a 32-bit integer) // 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. // 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. // you would get a value close to 360000000.
func (d Device) ReadRotation() (x int32, y int32, z int32) { func (d Device) ReadRotation() (x int32, y int32, z int32) {
data := make([]byte, 6) 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 // 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. // value and then to a signed 32-bit value to avoid integer overflow.
// Then the value is scaled to µ°/s (micro-degrees per second). // 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. // SetClockSource allows the user to configure the clock source.
func (d Device) SetClockSource(source uint8) error { 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. // SetFullScaleGyroRange allows the user to configure the scale range for the gyroscope.
func (d Device) SetFullScaleGyroRange(rng uint8) error { 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. // SetFullScaleAccelRange allows the user to configure the scale range for the accelerometer.
func (d Device) SetFullScaleAccelRange(rng uint8) error { 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})
} }
+99
View File
@@ -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})
}
+131
View File
@@ -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
View File
@@ -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
View File
@@ -1,9 +1,11 @@
package pca9685 package pca9685
import "tinygo.org/x/drivers/internal/legacy"
func (d *Dev) readReg(reg uint8, data []byte) error { 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 { 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)
} }
+89
View File
@@ -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))
}
+91
View File
@@ -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)
}
}
+39
View File
@@ -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
View File
@@ -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)
}
}
+64
View File
@@ -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
View File
@@ -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,
}
+6 -3
View File
@@ -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 // 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 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 // Device wraps the I2C connection to the QMIC8658 sensor
type Device struct { type Device struct {
@@ -179,12 +182,12 @@ func (d *Device) ReadTemperature() (int32, error) {
// Convenience method to read the register and avoid repetition. // Convenience method to read the register and avoid repetition.
func (d *Device) ReadRegister(reg uint8, buf []byte) error { 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. // Convenience method to write the register and avoid repetition.
func (d *Device) WriteRegister(reg uint8, v uint16) error { func (d *Device) WriteRegister(reg uint8, v uint16) error {
data := []byte{byte(v)} 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 return err
} }
+2
View File
@@ -16,6 +16,8 @@ const (
MagneticField MagneticField
Luminosity Luminosity
Time Time
// Gas or liquid concentration, usually measured in ppm (parts per million).
Concentration
// Add Measurements above AllMeasurements. // Add Measurements above AllMeasurements.
// AllMeasurements is the OR of all Measurement values. It ensures all measurements are done. // AllMeasurements is the OR of all Measurement values. It ensures all measurements are done.
+5 -4
View File
@@ -10,6 +10,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps an SPI connection. // Device wraps an SPI connection.
@@ -51,7 +52,7 @@ type Buser interface {
type VccMode uint8 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 { func NewI2C(bus drivers.I2C) Device {
return Device{ return Device{
bus: &I2CBus{ 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 { func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.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) // tx sends data to the display (I2CBus implementation)
func (b *I2CBus) tx(data []byte, isCommand bool) { func (b *I2CBus) tx(data []byte, isCommand bool) {
if isCommand { if isCommand {
b.wire.WriteRegister(uint8(b.Address), 0x00, data) legacy.WriteRegister(b.wire, uint8(b.Address), 0x00, data)
} else { } else {
b.wire.WriteRegister(uint8(b.Address), 0x40, data) legacy.WriteRegister(b.wire, uint8(b.Address), 0x40, data)
} }
} }
+76
View File
@@ -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
View File
@@ -133,7 +133,7 @@ func runSmokeTest(filename string) error {
result := <-job.resultChan result := <-job.resultChan
os.Stdout.Write(job.output.Bytes()) os.Stdout.Write(job.output.Bytes())
if result != nil { if result != nil {
return err return result
} }
} }
+11 -1
View File
@@ -4,6 +4,8 @@
# avoid a race condition between writing the output and reading the result to # avoid a race condition between writing the output and reading the result to
# get an md5sum). # 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/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=itsybitsy-m0 ./examples/adxl345/main.go
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx 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=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/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=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/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/bmp180/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/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=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=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/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/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/i2c_128x32/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/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=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=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=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/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/clkout/
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/ 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.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/as560x/main.go
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu6886/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
View File
@@ -10,6 +10,7 @@ import (
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
) )
// Device wraps I2C or SPI connection. // Device wraps I2C or SPI connection.
@@ -44,9 +45,9 @@ type SPIBus struct {
} }
type Buser interface { type Buser interface {
configure() configure() error
tx(data []byte, isCommand bool) tx(data []byte, isCommand bool) error
setAddress(address uint16) setAddress(address uint16) error
} }
type VccMode uint8 type VccMode uint8
@@ -192,8 +193,7 @@ func (d *Device) Display() error {
d.Command(uint8(d.height/8) - 1) d.Command(uint8(d.height/8) - 1)
} }
d.Tx(d.buffer, false) return d.Tx(d.buffer, false)
return nil
} }
// SetPixel enables or disables a pixel in the buffer // 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 // setAddress sets the address to the I2C bus
func (b *I2CBus) setAddress(address uint16) { func (b *I2CBus) setAddress(address uint16) error {
b.Address = address b.Address = address
return nil
} }
// setAddress does nothing, but it's required to avoid reflection // 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 // do nothing
println("trying to Configure an address on a SPI device") println("trying to Configure an address on a SPI device")
return nil
} }
// configure does nothing, but it's required to avoid reflection // 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 // configure configures some pins with the SPI bus
func (b *SPIBus) configure() { func (b *SPIBus) configure() error {
b.csPin.Low() b.csPin.Low()
b.dcPin.Low() b.dcPin.Low()
b.resetPin.Low() b.resetPin.Low()
@@ -267,31 +269,35 @@ func (b *SPIBus) configure() {
b.resetPin.Low() b.resetPin.Low()
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
b.resetPin.High() b.resetPin.High()
return nil
} }
// Tx sends data to the display // Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) { func (d *Device) Tx(data []byte, isCommand bool) error {
d.bus.tx(data, isCommand) return d.bus.tx(data, isCommand)
} }
// tx sends data to the display (I2CBus implementation) // 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 { if isCommand {
b.wire.WriteRegister(uint8(b.Address), 0x00, data) return legacy.WriteRegister(b.wire, uint8(b.Address), 0x00, data)
} else { } 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) // 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 { if isCommand {
b.csPin.High() b.csPin.High()
time.Sleep(1 * time.Millisecond) time.Sleep(1 * time.Millisecond)
b.dcPin.Low() b.dcPin.Low()
b.csPin.Low() b.csPin.Low()
b.wire.Tx(data, nil) err = b.wire.Tx(data, nil)
b.csPin.High() b.csPin.High()
} else { } else {
b.csPin.High() b.csPin.High()
@@ -299,9 +305,11 @@ func (b *SPIBus) tx(data []byte, isCommand bool) {
b.dcPin.High() b.dcPin.High()
b.csPin.Low() b.csPin.Low()
b.wire.Tx(data, nil) err = b.wire.Tx(data, nil)
b.csPin.High() b.csPin.High()
} }
return err
} }
// Size returns the current size of the display. // Size returns the current size of the display.
+72 -52
View File
@@ -11,6 +11,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/pixel"
) )
type Model uint8 type Model uint8
@@ -20,12 +21,23 @@ type Model uint8
// Deprecated: use drivers.Rotation instead. // Deprecated: use drivers.Rotation instead.
type Rotation = drivers.Rotation type Rotation = drivers.Rotation
// Pixel formats supported by the st7735 driver.
type Color interface {
pixel.RGB444BE | pixel.RGB565BE
pixel.BaseColor
}
var ( var (
errOutOfBounds = errors.New("rectangle coordinates outside display area") errOutOfBounds = errors.New("rectangle coordinates outside display area")
) )
// Device wraps an SPI connection. // 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 bus drivers.SPI
dcPin machine.Pin dcPin machine.Pin
resetPin machine.Pin resetPin machine.Pin
@@ -39,7 +51,7 @@ type Device struct {
batchLength int16 batchLength int16
model Model model Model
isBGR bool isBGR bool
batchData []uint8 batchData pixel.Image[T] // "image" with width, height of (batchLength, 1)
} }
// Config is the configuration for the display // 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. // 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 { 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}) dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{ return DeviceOf[T]{
bus: bus, bus: bus,
dcPin: dcPin, dcPin: dcPin,
resetPin: resetPin, 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 // Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) { func (d *DeviceOf[T]) Configure(cfg Config) {
d.model = cfg.Model d.model = cfg.Model
if cfg.Width != 0 { if cfg.Width != 0 {
d.width = cfg.Width d.width = cfg.Width
@@ -93,7 +111,7 @@ func (d *Device) Configure(cfg Config) {
d.batchLength = d.height d.batchLength = d.height
} }
d.batchLength += d.batchLength & 1 d.batchLength += d.batchLength & 1
d.batchData = make([]uint8, d.batchLength*2) d.batchData = pixel.NewImage[T](int(d.batchLength), 1)
// reset the device // reset the device
d.resetPin.High() d.resetPin.High()
@@ -142,8 +160,16 @@ func (d *Device) Configure(cfg Config) {
d.Data(0xEE) d.Data(0xEE)
d.Command(VMCTR1) d.Command(VMCTR1)
d.Data(0x0E) d.Data(0x0E)
// Set the color format depending on the generic type.
d.Command(COLMOD) 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 { if d.model == GREENTAB {
d.InvertColors(false) 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 // 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 return nil
} }
// SetPixel sets a pixel in the screen // 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() w, h := d.Size()
if x < 0 || y < 0 || x >= w || y >= h { if x < 0 || y < 0 || x >= w || y >= h {
return 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 // 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 { if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
x += d.columnOffset x += d.columnOffset
y += d.rowOffset 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 // 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.Command(VSCRDEF)
d.Tx([]uint8{ d.Tx([]uint8{
uint8(topFixedArea >> 8), uint8(topFixedArea), 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. // 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.Command(VSCRSADD)
d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false) d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false)
} }
// SpotScroll returns the display to its normal state // SpotScroll returns the display to its normal state
func (d *Device) StopScroll() { func (d *DeviceOf[T]) StopScroll() {
d.Command(NORON) d.Command(NORON)
} }
// FillRectangle fills a rectangle at a given coordinates with a color // 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() k, i := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 || if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= k || (x+width) > k || y >= i || (y+height) > i { x >= k || (x+width) > k || y >= i || (y+height) > i {
return errors.New("rectangle coordinates outside display area") return errors.New("rectangle coordinates outside display area")
} }
d.setWindow(x, y, width, height) 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.FillSolidColor(pixel.NewColor[T](c.R, c.G, c.B))
d.batchData[i*2] = c1
d.batchData[i*2+1] = c2
}
i = width * height i = width * height
for i > 0 { for i > 0 {
if i >= d.batchLength { if i >= d.batchLength {
d.Tx(d.batchData, false) d.Tx(d.batchData.RawBuffer(), false)
} else { } else {
d.Tx(d.batchData[:i*2], false) d.Tx(d.batchData.Rescale(int(i), 1).RawBuffer(), false)
} }
i -= d.batchLength 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 // 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() k, i := d.Size()
if x < 0 || y < 0 || w <= 0 || h <= 0 || if x < 0 || y < 0 || w <= 0 || h <= 0 ||
x >= k || (x+w) > k || y >= i || (y+h) > i { 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 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 // 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() k, l := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 || if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= k || (x+width) > k || y >= l || (y+height) > l { 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 k > 0 {
for i := int16(0); i < d.batchLength; i++ { for i := int16(0); i < d.batchLength; i++ {
if offset+i < l { if offset+i < l {
c565 := RGBATo565(buffer[offset+i]) c := buffer[offset+i]
c1 := uint8(c565 >> 8) d.batchData.Set(int(i), 0, pixel.NewColor[T](c.R, c.G, c.B))
c2 := uint8(c565)
d.batchData[i*2] = c1
d.batchData[i*2+1] = c2
} }
} }
if k >= d.batchLength { if k >= d.batchLength {
d.Tx(d.batchData, false) d.Tx(d.batchData.RawBuffer(), false)
} else { } else {
d.Tx(d.batchData[:k*2], false) d.Tx(d.batchData.Rescale(int(k), 1).RawBuffer(), false)
} }
k -= d.batchLength k -= d.batchLength
offset += 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 // 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 { if y0 > y1 {
y0, y1 = y1, y0 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 // 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 { if x0 > x1 {
x0, x1 = x1, x0 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 // 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 { if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
d.FillRectangle(0, 0, d.width, d.height, c) d.FillRectangle(0, 0, d.width, d.height, c)
} else { } else {
@@ -357,12 +385,12 @@ func (d *Device) FillScreen(c color.RGBA) {
} }
// Rotation returns the currently configured rotation. // Rotation returns the currently configured rotation.
func (d *Device) Rotation() drivers.Rotation { func (d *DeviceOf[T]) Rotation() drivers.Rotation {
return d.rotation return d.rotation
} }
// SetRotation changes the rotation of the device (clock-wise) // 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 d.rotation = rotation
madctl := uint8(0) madctl := uint8(0)
switch rotation % 4 { switch rotation % 4 {
@@ -384,23 +412,23 @@ func (d *Device) SetRotation(rotation drivers.Rotation) error {
} }
// Command sends a command to the display // 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) d.Tx([]byte{command}, true)
} }
// Command sends a data to the display // 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) d.Tx([]byte{data}, false)
} }
// Tx sends data to the display // 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.dcPin.Set(!isCommand)
d.bus.Tx(data, nil) d.bus.Tx(data, nil)
} }
// Size returns the current size of the display. // 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 { if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
return d.width, d.height return d.width, d.height
} }
@@ -408,7 +436,7 @@ func (d *Device) Size() (w, h int16) {
} }
// EnableBacklight enables or disables the backlight // EnableBacklight enables or disables the backlight
func (d *Device) EnableBacklight(enable bool) { func (d *DeviceOf[T]) EnableBacklight(enable bool) {
if enable { if enable {
d.blPin.High() d.blPin.High()
} else { } 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 // 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 // less power. The LCD won't display an image anymore, but the memory contents
// will be kept. // will be kept.
func (d *Device) Sleep(sleepEnabled bool) error { func (d *DeviceOf[T]) Sleep(sleepEnabled bool) error {
if sleepEnabled { if sleepEnabled {
// Shut down LCD panel. // Shut down LCD panel.
d.Command(SLPIN) d.Command(SLPIN)
@@ -435,7 +463,7 @@ func (d *Device) Sleep(sleepEnabled bool) error {
} }
// InverColors inverts the colors of the screen // InverColors inverts the colors of the screen
func (d *Device) InvertColors(invert bool) { func (d *DeviceOf[T]) InvertColors(invert bool) {
if invert { if invert {
d.Command(INVON) d.Command(INVON)
} else { } else {
@@ -444,14 +472,6 @@ func (d *Device) InvertColors(invert bool) {
} }
// IsBGR changes the color mode (RGB/BGR) // IsBGR changes the color mode (RGB/BGR)
func (d *Device) IsBGR(bgr bool) { func (d *DeviceOf[T]) IsBGR(bgr bool) {
d.isBGR = bgr 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
View File
@@ -14,6 +14,7 @@ import (
"errors" "errors"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
"tinygo.org/x/drivers/pixel"
) )
// Rotation controls the rotation used by the display. // 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. // The color format used on the display, like RGB565, RGB666, and RGB444.
type ColorFormat uint8 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. // FrameRate controls the frame rate used by the display.
type FrameRate uint8 type FrameRate uint8
@@ -32,7 +40,11 @@ var (
) )
// Device wraps an SPI connection. // 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 bus drivers.SPI
dcPin machine.Pin dcPin machine.Pin
resetPin machine.Pin resetPin machine.Pin
@@ -47,6 +59,7 @@ type Device struct {
rotation drivers.Rotation rotation drivers.Rotation
frameRate FrameRate frameRate FrameRate
batchLength int32 batchLength int32
batchData pixel.Image[T] // "image" with (width, height) of (batchLength, 1)
isBGR bool isBGR bool
vSyncLines int16 vSyncLines int16
cmdBuf [1]byte cmdBuf [1]byte
@@ -71,11 +84,17 @@ type Config struct {
// New creates a new ST7789 connection. The SPI wire must already be configured. // 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 { 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}) dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{ return DeviceOf[T]{
bus: bus, bus: bus,
dcPin: dcPin, dcPin: dcPin,
resetPin: resetPin, 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 // Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) { func (d *DeviceOf[T]) Configure(cfg Config) {
if cfg.Width != 0 { if cfg.Width != 0 {
d.width = cfg.Width d.width = cfg.Width
} else { } else {
@@ -137,7 +156,14 @@ func (d *Device) Configure(cfg Config) {
d.sendCommand(SLPOUT, nil) // Exit sleep mode d.sendCommand(SLPOUT, nil) // Exit sleep mode
// Memory initialization // 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) time.Sleep(10 * time.Millisecond)
d.setRotation(d.rotation) // Memory orientation 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 // 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, // pin (it must be low when calling). The DC pin is left high after return,
// meaning that data can be sent right away. // 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.cmdBuf[0] = command
d.dcPin.Low() d.dcPin.Low()
err := d.bus.Tx(d.cmdBuf[:1], nil) 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 // startWrite must be called at the beginning of all exported methods to set the
// chip select pin low. // chip select pin low.
func (d *Device) startWrite() { func (d *DeviceOf[T]) startWrite() {
if d.csPin != machine.NoPin { if d.csPin != machine.NoPin {
d.csPin.Low() 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 // endWrite must be called at the end of all exported methods to set the chip
// select pin high. // select pin high.
func (d *Device) endWrite() { func (d *DeviceOf[T]) endWrite() {
if d.csPin != machine.NoPin { if d.csPin != machine.NoPin {
d.csPin.High() 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 // Sync waits for the display to hit the next VSYNC pause
func (d *Device) Sync() { func (d *DeviceOf[T]) Sync() {
d.SyncToScanLine(0) d.SyncToScanLine(0)
} }
@@ -232,7 +267,7 @@ func (d *Device) Sync() {
// NOTE: Use GetHighestScanLine and GetLowestScanLine to obtain the highest // NOTE: Use GetHighestScanLine and GetLowestScanLine to obtain the highest
// and lowest useful values. Values are affected by front and back porch // and lowest useful values. Values are affected by front and back porch
// vsync settings (derived from VSyncLines configuration option). // vsync settings (derived from VSyncLines configuration option).
func (d *Device) SyncToScanLine(scanline uint16) { func (d *DeviceOf[T]) SyncToScanLine(scanline uint16) {
scan := d.GetScanLine() scan := d.GetScanLine()
// Sometimes GetScanLine returns erroneous 0 on first call after draw, so double check // 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 // GetScanLine reads the current scanline value from the display
func (d *Device) GetScanLine() uint16 { func (d *DeviceOf[T]) GetScanLine() uint16 {
d.startWrite() d.startWrite()
data := []uint8{0x00, 0x00} data := []uint8{0x00, 0x00}
d.dcPin.Low() d.dcPin.Low()
@@ -277,24 +312,24 @@ func (d *Device) GetScanLine() uint16 {
} }
// GetHighestScanLine calculates the last scanline id in the frame before VSYNC pause // 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 // Last scanline id appears to be backporch/2 + 320/2
return uint16(math.Ceil(float64(d.vSyncLines)/2)/2) + 160 return uint16(math.Ceil(float64(d.vSyncLines)/2)/2) + 160
} }
// GetLowestScanLine calculate the first scanline id to appear after VSYNC pause // 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 // First scanline id appears to be backporch/2 + 1
return uint16(math.Ceil(float64(d.vSyncLines)/2)/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 // 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 return nil
} }
// SetPixel sets a pixel in the screen // 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 || if x < 0 || y < 0 ||
(((d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180) && (x >= d.width || y >= d.height)) || (((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))) { ((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 // 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 x += d.columnOffset
y += d.rowOffset y += d.rowOffset
copy(d.buf[:4], []uint8{uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)}) 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 // 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() d.startWrite()
err := d.fillRectangle(x, y, width, height, c) err := d.fillRectangle(x, y, width, height, c)
d.endWrite() d.endWrite()
return err 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() k, i := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 || if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= k || (x+width) > k || y >= i || (y+height) > i { x >= k || (x+width) > k || y >= i || (y+height) > i {
return errors.New("rectangle coordinates outside display area") return errors.New("rectangle coordinates outside display area")
} }
d.setWindow(x, y, width, height) d.setWindow(x, y, width, height)
c565 := RGBATo565(c)
c1 := uint8(c565 >> 8)
c2 := uint8(c565)
data := make([]uint8, d.batchLength*2) image := d.getBuffer()
for i := int32(0); i < d.batchLength; i++ { image.FillSolidColor(pixel.NewColor[T](c.R, c.G, c.B))
data[i*2] = c1 j := int(width) * int(height)
data[i*2+1] = c2
}
j := int32(width) * int32(height)
for j > 0 { for j > 0 {
// The DC pin is already set to data in the setWindow call, so we can // The DC pin is already set to data in the setWindow call, so we can
// just write bytes on the SPI bus. // just write bytes on the SPI bus.
if j >= d.batchLength { if j >= image.Len() {
d.bus.Tx(data, nil) d.bus.Tx(image.RawBuffer(), nil)
} else { } 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 return nil
} }
// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates // 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() k, i := d.Size()
if x < 0 || y < 0 || w <= 0 || h <= 0 || if x < 0 || y < 0 || w <= 0 || h <= 0 ||
x >= k || (x+w) > k || y >= i || (y+h) > i { 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 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. // 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() i, j := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 || if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= i || (x+width) > i || y >= j || (y+height) > j { 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.startWrite()
d.setWindow(x, y, width, height) d.setWindow(x, y, width, height)
k := int32(width) * int32(height) k := int(width) * int(height)
data := make([]uint8, d.batchLength*2) image := d.getBuffer()
offset := int32(0) offset := 0
for k > 0 { for k > 0 {
for i := int32(0); i < d.batchLength; i++ { for i := 0; i < image.Len(); i++ {
if offset+i < int32(len(buffer)) { if offset+i < len(buffer) {
c565 := RGBATo565(buffer[offset+i]) c := buffer[offset+i]
c1 := uint8(c565 >> 8) image.Set(i, 0, pixel.NewColor[T](c.R, c.G, c.B))
c2 := uint8(c565)
data[i*2] = c1
data[i*2+1] = c2
} }
} }
// The DC pin is already set to data in the setWindow call, so we don't // The DC pin is already set to data in the setWindow call, so we don't
// have to set it here. // have to set it here.
if k >= d.batchLength { if k >= image.Len() {
d.bus.Tx(data, nil) d.bus.Tx(image.RawBuffer(), nil)
} else { } else {
d.bus.Tx(data[:k*2], nil) d.bus.Tx(image.Rescale(k, 1).RawBuffer(), nil)
} }
k -= d.batchLength k -= image.Len()
offset += d.batchLength offset += image.Len()
} }
d.endWrite() d.endWrite()
return nil return nil
} }
// DrawFastVLine draws a vertical line faster than using SetPixel // 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 { if y0 > y1 {
y0, y1 = y1, y0 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 // 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 { if x0 > x1 {
x0, x1 = x1, x0 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 // 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.startWrite()
d.fillScreen(c) d.fillScreen(c)
d.endWrite() 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 { if d.rotation == NO_ROTATION || d.rotation == ROTATION_180 {
d.fillRectangle(0, 0, d.width, d.height, c) d.fillRectangle(0, 0, d.width, d.height, c)
} else { } 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 // 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 // like SetPixel, FillRectangle, etc. Instead, you can write color data in the
// specified color format using DrawRGBBitmap8. // specified color format using DrawRGBBitmap8.
func (d *Device) SetColorFormat(format ColorFormat) { func (d *DeviceOf[T]) SetColorFormat(format ColorFormat) {
d.startWrite() d.startWrite()
d.setColorFormat(format) d.setColorFormat(format)
d.endWrite() 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. // Lower 4 bits set the color format used in SPI.
// Upper 4 bits set the color format used in the direct RGB interface. // 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 // 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. // Rotation returns the current rotation of the device.
func (d *Device) Rotation() drivers.Rotation { func (d *DeviceOf[T]) Rotation() drivers.Rotation {
return d.rotation return d.rotation
} }
// SetRotation changes the rotation of the device (clock-wise) // 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.rotation = rotation
d.startWrite() d.startWrite()
err := d.setRotation(rotation) err := d.setRotation(rotation)
@@ -470,24 +505,24 @@ func (d *Device) SetRotation(rotation Rotation) error {
return err return err
} }
func (d *Device) setRotation(rotation Rotation) error { func (d *DeviceOf[T]) setRotation(rotation Rotation) error {
madctl := uint8(0) madctl := uint8(0)
switch rotation % 4 { switch rotation % 4 {
case drivers.Rotation0: 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.rowOffset = 0
d.columnOffset = 0 d.columnOffset = 0
case drivers.Rotation270: case drivers.Rotation90:
madctl = MADCTL_MX | MADCTL_MV madctl = MADCTL_MX | MADCTL_MV
d.rowOffset = 0 d.rowOffset = 0
d.columnOffset = 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 { if d.isBGR {
madctl |= MADCTL_BGR madctl |= MADCTL_BGR
@@ -496,7 +531,7 @@ func (d *Device) setRotation(rotation Rotation) error {
} }
// Size returns the current size of the display. // 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 { if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 {
return d.width, d.height return d.width, d.height
} }
@@ -504,7 +539,7 @@ func (d *Device) Size() (w, h int16) {
} }
// EnableBacklight enables or disables the backlight // EnableBacklight enables or disables the backlight
func (d *Device) EnableBacklight(enable bool) { func (d *DeviceOf[T]) EnableBacklight(enable bool) {
if enable { if enable {
d.blPin.High() d.blPin.High()
} else { } 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 // 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 // less power. The LCD won't display an image anymore, but the memory contents
// will be kept. // will be kept.
func (d *Device) Sleep(sleepEnabled bool) error { func (d *DeviceOf[T]) Sleep(sleepEnabled bool) error {
if sleepEnabled { if sleepEnabled {
d.startWrite() d.startWrite()
d.sendCommand(SLPIN, nil) d.sendCommand(SLPIN, nil)
@@ -537,7 +572,7 @@ func (d *Device) Sleep(sleepEnabled bool) error {
} }
// InvertColors inverts the colors of the screen // InvertColors inverts the colors of the screen
func (d *Device) InvertColors(invert bool) { func (d *DeviceOf[T]) InvertColors(invert bool) {
d.startWrite() d.startWrite()
if invert { if invert {
d.sendCommand(INVON, nil) d.sendCommand(INVON, nil)
@@ -548,15 +583,28 @@ func (d *Device) InvertColors(invert bool) {
} }
// IsBGR changes the color mode (RGB/BGR) // IsBGR changes the color mode (RGB/BGR)
func (d *Device) IsBGR(bgr bool) { func (d *DeviceOf[T]) IsBGR(bgr bool) {
d.isBGR = bgr d.isBGR = bgr
} }
// SetScrollArea sets an area to scroll with fixed top and bottom parts of the display. // 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{ copy(d.buf[:6], []uint8{
uint8(topFixedArea >> 8), uint8(topFixedArea), 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)}) uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)})
d.startWrite() d.startWrite()
d.sendCommand(VSCRDEF, d.buf[:6]) 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. // 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[0] = uint8(line >> 8)
d.buf[1] = uint8(line) d.buf[1] = uint8(line)
d.startWrite() d.startWrite()
@@ -573,16 +626,8 @@ func (d *Device) SetScroll(line int16) {
} }
// StopScroll returns the display to its normal state. // StopScroll returns the display to its normal state.
func (d *Device) StopScroll() { func (d *DeviceOf[T]) StopScroll() {
d.startWrite() d.startWrite()
d.sendCommand(NORON, nil) d.sendCommand(NORON, nil)
d.endWrite() 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
View File
@@ -5,10 +5,10 @@ const MaxRegisters = 255
type I2CDevice interface { type I2CDevice interface {
// ReadRegister implements I2C.ReadRegister. // ReadRegister implements I2C.ReadRegister.
ReadRegister(r uint8, buf []byte) error readRegister(r uint8, buf []byte) error
// WriteRegister implements I2C.WriteRegister. // WriteRegister implements I2C.WriteRegister.
WriteRegister(r uint8, buf []byte) error writeRegister(r uint8, buf []byte) error
// Tx implements I2C.Tx // Tx implements I2C.Tx
Tx(w, r []byte) error Tx(w, r []byte) error
+14 -4
View File
@@ -33,7 +33,7 @@ func (d *I2CDevice16) Addr() uint8 {
} }
// ReadRegister implements I2C.ReadRegister. // 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 { if d.Err != nil {
return d.Err return d.Err
} }
@@ -54,7 +54,7 @@ func (d *I2CDevice16) ReadRegister(r uint8, buf []byte) error {
} }
// WriteRegister implements I2C.WriteRegister. // 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 { if d.Err != nil {
return d.Err return d.Err
} }
@@ -75,6 +75,16 @@ func (d *I2CDevice16) WriteRegister(r uint8, buf []byte) error {
// Tx implements I2C.Tx. // Tx implements I2C.Tx.
func (bus *I2CDevice16) Tx(w, r []byte) error { func (bus *I2CDevice16) Tx(w, r []byte) error {
// TODO: implement this switch len(w) {
return nil 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
View File
@@ -34,17 +34,20 @@ func (d *I2CDevice8) Addr() uint8 {
} }
// ReadRegister implements I2C.ReadRegister. // 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 { if d.Err != nil {
return d.Err return d.Err
} }
if len(buf) == 0 {
d.c.Fatalf("no register buffer to read into")
}
d.assertRegisterRange(r, buf) d.assertRegisterRange(r, buf)
copy(buf, d.Registers[r:]) copy(buf, d.Registers[r:])
return nil return nil
} }
// WriteRegister implements I2C.WriteRegister. // 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 { if d.Err != nil {
return d.Err return d.Err
} }
@@ -55,8 +58,18 @@ func (d *I2CDevice8) WriteRegister(r uint8, buf []byte) error {
// Tx implements I2C.Tx. // Tx implements I2C.Tx.
func (bus *I2CDevice8) Tx(w, r []byte) error { func (bus *I2CDevice8) Tx(w, r []byte) error {
// TODO: implement this switch len(w) {
return nil 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 // assertRegisterRange asserts that reading or writing the given
+2 -2
View File
@@ -50,7 +50,7 @@ func (d *I2CDeviceCmd) Addr() uint8 {
} }
// ReadRegister implements I2C.ReadRegister. // 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 { if d.Err != nil {
return d.Err return d.Err
} }
@@ -59,7 +59,7 @@ func (d *I2CDeviceCmd) ReadRegister(r uint8, buf []byte) error {
} }
// WriteRegister implements I2C.WriteRegister. // 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 { if d.Err != nil {
return d.Err return d.Err
} }
+2 -2
View File
@@ -38,12 +38,12 @@ func (bus *I2CBus) NewDevice(addr uint8) *I2CDevice8 {
// ReadRegister implements I2C.ReadRegister. // ReadRegister implements I2C.ReadRegister.
func (bus *I2CBus) ReadRegister(addr uint8, r uint8, buf []byte) error { 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. // WriteRegister implements I2C.WriteRegister.
func (bus *I2CBus) WriteRegister(addr uint8, r uint8, buf []byte) error { 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. // Tx implements I2C.Tx.
+6 -3
View File
@@ -4,7 +4,10 @@
package tmp102 // import "tinygo.org/x/drivers/tmp102" 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. // Device holds the already configured I2C bus and the address of the sensor.
type Device struct { 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. // Connected checks if the config register can be read and that the configuration is correct.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
configData := make([]byte, 2) 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. // Check the reset configuration values.
if err != nil || configData[0] != 0x60 || configData[1] != 0xA0 { if err != nil || configData[0] != 0x60 || configData[1] != 0xA0 {
return false return false
@@ -50,7 +53,7 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
tmpData := make([]byte, 2) tmpData := make([]byte, 2)
err = d.bus.ReadRegister(d.address, RegTemperature, tmpData) err = legacy.ReadRegister(d.bus, d.address, RegTemperature, tmpData)
if err != nil { if err != nil {
return return
+1 -1
View File
@@ -2,4 +2,4 @@ package drivers
// Version returns a user-readable string showing the version of the drivers package for support purposes. // 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. // Update this value before release of new version of software.
const Version = "0.25.0" const Version = "0.26.0"
+41 -19
View File
@@ -15,8 +15,8 @@ import (
type Config struct { type Config struct {
Width int16 // Width is the display resolution Width int16 // Width is the display resolution
Height int16 Height int16
LogicalWidth int16 // LogicalWidth must be a multiple of 8 and same size or bigger than Width LogicalWidth int16 // LogicalWidth must be a multiple of 8 and same size or bigger than Width
Rotation Rotation // Rotation is clock-wise Rotation drivers.Rotation
} }
type Device struct { type Device struct {
@@ -30,10 +30,11 @@ type Device struct {
height int16 height int16
buffer []uint8 buffer []uint8
bufferLength uint32 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 // Look up table for full updates
var lutFullUpdate = [30]uint8{ var lutFullUpdate = [30]uint8{
@@ -130,6 +131,17 @@ func (d *Device) DeepSleep() {
d.WaitUntilIdle() 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 // SendCommand sends a command to the display
func (d *Device) SendCommand(command uint8) { func (d *Device) SendCommand(command uint8) {
d.sendDataCommand(true, command) d.sendDataCommand(true, command)
@@ -167,18 +179,22 @@ func (d *Device) SetLUT(fullUpdate bool) {
} }
// SetPixel modifies the internal buffer in a single pixel. // SetPixel modifies the internal buffer in a single pixel.
// The display have 2 colors: black and white // The display have 2 colors: black and white. We use a very simple cutoff to
// We use RGBA(0,0,0, 255) as white (transparent) // determine whether a pixel is black or white (darker colors are black, lighter
// Anything else as black // colors are white).
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) { func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
x, y = d.xy(x, y) x, y = d.xy(x, y)
if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height { if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height {
return return
} }
byteIndex := (x + y*d.logicalWidth) / 8 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) d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
} else { // WHITE / EMPTY } else { // dark, convert to black
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8) 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 { if x < 0 || y < 0 || x >= d.logicalWidth || y >= d.height || width < 0 || height < 0 {
return errors.New("wrong rectangle") return errors.New("wrong rectangle")
} }
if d.rotation == ROTATION_90 { if d.rotation == drivers.Rotation90 {
width, height = height, width width, height = height, width
x -= width x -= width
} else if d.rotation == ROTATION_180 { } else if d.rotation == drivers.Rotation180 {
x -= width - 1 x -= width - 1
y -= height - 1 y -= height - 1
} else if d.rotation == ROTATION_270 { } else if d.rotation == drivers.Rotation270 {
width, height = height, width width, height = height, width
y -= height y -= height
} }
@@ -301,27 +317,33 @@ func (d *Device) ClearBuffer() {
// Size returns the current size of the display. // Size returns the current size of the display.
func (d *Device) Size() (w, h int16) { 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.height, d.logicalWidth
} }
return d.logicalWidth, d.height return d.logicalWidth, d.height
} }
// SetRotation changes the rotation (clock-wise) of the device // Rotation returns the current rotation of the device.
func (d *Device) SetRotation(rotation Rotation) { 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 d.rotation = rotation
return nil
} }
// xy chages the coordinates according to the rotation // xy chages the coordinates according to the rotation
func (d *Device) xy(x, y int16) (int16, int16) { func (d *Device) xy(x, y int16) (int16, int16) {
switch d.rotation { switch d.rotation {
case NO_ROTATION: case drivers.Rotation0:
return x, y return x, y
case ROTATION_90: case drivers.Rotation90:
return d.width - y - 1, x return d.width - y - 1, x
case ROTATION_180: case drivers.Rotation180:
return d.width - x - 1, d.height - y - 1 return d.width - x - 1, d.height - y - 1
case ROTATION_270: case drivers.Rotation270:
return y, d.height - x - 1 return y, d.height - x - 1
} }
return x, y return x, y
+6 -4
View File
@@ -1,5 +1,7 @@
package epd2in13 package epd2in13
import "tinygo.org/x/drivers"
// Registers // Registers
const ( const (
DRIVER_OUTPUT_CONTROL = 0x01 DRIVER_OUTPUT_CONTROL = 0x01
@@ -24,8 +26,8 @@ const (
SET_RAM_Y_ADDRESS_COUNTER = 0x4F SET_RAM_Y_ADDRESS_COUNTER = 0x4F
TERMINATE_FRAME_READ_WRITE = 0xFF TERMINATE_FRAME_READ_WRITE = 0xFF
NO_ROTATION Rotation = 0 NO_ROTATION = drivers.Rotation0
ROTATION_90 Rotation = 1 // 90 degrees clock-wise rotation ROTATION_90 = drivers.Rotation90 // 90 degrees clock-wise rotation
ROTATION_180 Rotation = 2 ROTATION_180 = drivers.Rotation180
ROTATION_270 Rotation = 3 ROTATION_270 = drivers.Rotation270
) )

Some files were not shown because too many files have changed in this diff Show More