Compare commits

...

44 Commits

Author SHA1 Message Date
deadprogram e376785596 Prepare for Drivers release 0.14.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-09-17 10:09:25 +02:00
Amblyopius 4c98d0bc34 - Fix for RAMWR bug:
It can not be skipped as you may call setwindow even if device is not ready for data write to buffer.
Data can only be written if last command was Memory Write or Write Memory Continue which was not guaranteed
There's no performance loss as having the exact same window on a consecutive call is extremely unlikely
- Fixed some typos and added a few missing comments to please go-lint
2020-09-11 11:31:07 +02:00
Ayke van Laethem 65f8299153 ws2812: add support for ESP8266
This patch adds support for the ESP8266 chip by adding support for 80MHz
operation. This should also work when the ESP32 is changed to 80MHz, but
I didn't test it (as there is not currently a way to do that).

Verified on a NodeMCU dev board with an ESP8266.
2020-09-09 22:43:35 +02:00
BCG a689aef543 Renamed NewSpi() to NewSPI() in accordance with Go naming conventions 2020-09-07 09:11:25 +02:00
Ayke van Laethem 7cd7df7bb8 ws2812: add support for the Xtensa architecture
This allows WS2812 LEDs to be controlled by an ESP32. Right now it only
supports 160MHz operation, which is the default with current ESP32
support in TinyGo.
2020-09-05 14:01:22 +02:00
deadprogram 705f474897 adt7410: add connection test and for that matter connection method
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-09-01 13:08:28 +02:00
Jason Striegel ce9c93f228 Add support for setting framerate, vsync pause, and reading scanline data.
- FrameRate option sets device framerates from 39-111Hz
- VSyncLines option adjusts device vsync pause using st7789 "porch control" feature
- Added Rx(cmd, bytes[]) to support retrieving scanline timing data over SPI
- Added Sync functions to support syncronization of animation to vertical scanline timing
- Minor adjustments to Configure to clear screen memory before display is visible
2020-09-01 11:26:06 +02:00
deadprogram db02cbb8a4 i2c: switch all i2c drivers definitions to use i2c bus interface type instead of machine package concrete type
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-29 17:19:27 +02:00
deadprogram a07b20f2f9 tester: correct interface definition for I2C Tx function
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-27 12:17:47 +02:00
deadprogram 4ad210060f lis2mdl: better examples showing how to create unit tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-27 12:15:57 +02:00
deadprogram 2ae950e96d tester: improve API surface and implement one more test function in lis2mdl driver
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-27 12:15:57 +02:00
deadprogram dd34b83e9f lis2mdl: starting point for adding unit tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-27 12:15:57 +02:00
deadprogram ce81b66fe2 tester: add very basic mock structs for testing i2c devices, based on work done by @rogpeppe
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-27 12:15:57 +02:00
Tim Boldt b1ae52d1b9 Add Waveshare 4.2in B/W e-paper driver (#183)
* waveshare-epd/epd4in2: Add Waveshare 4.2in e-paper driver
2020-08-25 18:57:13 +02:00
sago35 794a9c202f Fix smoke-test unless avr-gcc installed 2020-08-24 13:49:20 +02:00
deadprogram 38f97a8b45 docs: replace README badge for godocs with pkgdocs
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-21 16:17:38 +02:00
deadprogram 35a146d60e lis2mdl: turn on read mode on every read, to ensure that magnetometer data is updated
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-21 10:12:16 +02:00
deadprogram 4f82c06df9 gps: add speed and heading to fix, as parsed from RMC NMEA sentence
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-21 07:24:41 +02:00
Ron Evans af4efceac1 lis2mdl: add LIS2MDL magnetometer (#187)
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-20 13:21:39 +02:00
Ron Evans 5df96c8138 gps: improvements and bugfixes (#186)
* gps: buxfixes and refactoring of API to separate device from parser

Signed-off-by: deadprogram <ron@hybridgroup.com>

* gps: simplify time parser

Signed-off-by: deadprogram <ron@hybridgroup.com>

* gps: add support for RMC sentences

Signed-off-by: deadprogram <ron@hybridgroup.com>

* gps: small renaming to remove reduntant use of word GPS

Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-20 11:22:07 +02:00
Jason Striegel 88aeec9f69 Fix setWindow bug, add CS pin for Clue compatibility. (#180)
* Fix setWindow bug, add CS pin for Clue compatibility.

- corrected bit shift in setWindow that broke high addresses
- added csPin to constructor (will now work with adafruit clue)
- small adjustments and comments to init routine based on working arduino driver

* Update main.go

Updated test example with CS pin.

* Reverting unnecessary (no-effect) change in setRotation.
2020-08-12 08:31:43 +02:00
deadprogram 06b2023b25 Prepare for release v0.13.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-04 16:44:20 +02:00
ardnew 8163dec7c3 ili9341: cache address window to prevent sending unnecessary commands (#171) 2020-08-03 15:58:16 +02:00
sago35 5a75c9c403 Add ILI9341 TFT driver (SPI) for ATSAMD2x (#174)
* ILI9341: Add ILI9341 SPI support for for ATSAMD2x
2020-08-03 12:42:17 +02:00
deadprogram 75b8a75b4b docs: reorder to correct alpha and adjust count of supported drivers
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-03 12:06:17 +02:00
deadprogram 857e45f18d modules: update go version and dependency
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-08-03 11:59:35 +02:00
Ayke van Laethem 9130e61c55 bmi160: add initial support 2020-07-31 19:31:52 +02:00
Alan Wang 68963a1b42 Add Hd44780i2c driver (#173)
* hd44780i2c: add support for hd44780i2c LCD display.
2020-07-31 19:25:56 +02:00
deadprogram 74c9ff4c76 all: changeover to eliminate all direct use of master/slave terminology
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-07-30 12:03:09 +02:00
Alan Wang de27fae9a2 Update ws2812_avr_16m.go 2020-07-16 15:50:07 +02:00
Alan Wang 5ebc7cb09e bmp180: fix temperature type conversion 2020-07-07 23:47:38 +02:00
Yannis Huber 07ca36ac5a tmp102: add Connected func to check for device 2020-07-01 19:25:15 +02:00
Alan Wang 39f44ef478 lsm303agr (#162)
* lsm303agr: add support for lsm303agr digital compass
2020-07-01 16:59:25 +02:00
Daniel Esteban 61874ea928 added custom import path (#161) 2020-06-25 17:11:49 +02:00
BCG f5e81e6a01 Added smoke tests for UDP functionality 2020-06-21 16:21:06 +02:00
BCG 511a3282b7 Added UDP support 2020-06-21 16:21:06 +02:00
Nerdmeister 1d09194bbc Added support for the Bosch BMP280 temperature and pressure sensor. (#158)
* bmp280: Added support for the Bosch BMP280 temperature and pressure sensor.
2020-06-21 16:16:29 +02:00
sago35 941ea4e28b ILI9341 TFT driver (SPI) (#153)
* ili9341: Add spidriver
2020-06-15 18:46:13 +02:00
Ayke van Laethem 21ba9392e2 ci: support Go modules
Since TinyGo started supporting Go modules, there was an error in CI.
The commit 7967e82fed tries to fix that,
but I think the underlying issue is that we're checking out in GOROOT,
which is definitely not a supported configuration.

I think the best solution is to just switch to using Go modules, by
adding a go.mod file in the root. I've set it to Go version 1.13 as that
is the first Go version that supports number literals, but it could be
set to any supported Go version (1.11-1.14).

Since we use Go modules, the location of the drivers checkout should not
matter so I've removed it. This fixes the error on CircleCI.
2020-06-05 08:18:43 +02:00
deadprogram 7967e82fed build: try vendor in working directory to match expected module path
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-28 19:13:46 +02:00
Ayke van Laethem 91539d9ef8 apa102: avoid creating garbage
Avoid creating unnecessary garbage in the following ways:

  * Use the Transfer method instead of the Tx method for single byte
    transfers.
  * Use a statically allocated buffer for the fixed start-of-frame
    sequence.

Running this for a few minutes did not cause the garbage collector to
run. Previously, it would run every second or so in a
persistence-of-vision application.
2020-05-26 19:50:31 +02:00
Alexander Bloss c3f3af5ffa Fix DrawFastHLine for ST77xx, SSD1331 and SSD1351
DrawFastHLine uses FillRectangle(x,y,width,height,c), so height must be 1 to draw a horizontal line
2020-05-16 18:54:41 +02:00
sago35 41694085a1 Improve performance of ILI9341 on ATSAMD5X 2020-05-06 13:25:43 +02:00
Yannis Huber fd9b1ba89b Add SSD1351 OLED display driver (#146)
* ssd1351: Add SSD1351 driver
2020-04-24 07:30:50 +02:00
99 changed files with 4679 additions and 428 deletions
-1
View File
@@ -6,7 +6,6 @@ jobs:
build:
docker:
- image: tinygo/tinygo-dev
working_directory: /usr/local/go/src/tinygo.org/x/drivers
steps:
- checkout
- run: tinygo version
+63
View File
@@ -1,3 +1,66 @@
0.14.0
---
- **new devices**
- lis2mdl: add LIS2MDL magnetometer (#187)
- waveshare: add Waveshare 4.2in B/W e-paper driver (#183)
- **enhancements**
- adt7410: add connection test and for that matter connection method
- gps
- add speed and heading to fix, as parsed from RMC NMEA sentence
- improvements and bugfixes (#186)
- ili9341
- add support for setting framerate, vsync pause, and reading scanline data.
- renamed NewSpi() to NewSPI() in accordance with Go naming conventions
- ws2812
- add support for ESP8266
- add support for ESP32
- **bugfixes**
- ili9341
- rix setWindow bug, add CS pin for Clue compatibility. (#180)
- bugfix for RAMWR bug
- lis2mdl: turn on read mode on every read, to ensure that magnetometer data is updated
- **core**
- i2c
- switch all i2c drivers definitions to use i2c bus interface type instead of machine package concrete type
- correct interface definition for I2C Tx function
- **testing**
- fix smoke-test unless avr-gcc installed
- add very basic mock structs for testing i2c devices, based on work done by @rogpeppe
- improve API surface and implement one more test function in lis2mdl driver
- **docs**
- replace README badge for godocs with pkgdocs
0.13.0
---
- **new devices**
- bmi160: add initial support
- bmp280: added support for the Bosch BMP280 temperature and pressure sensor. (#158)
- lsm303agr: add lsm303agr (#162)
- ssd1351: add SSD1351 OLED display driver (#146)
- **enhancements**
- hd44780: add Hd44780i2c driver (#173)
- ili9341
- add ILI9341 TFT driver (SPI) for ATSAMD2x (#174)
- cache address window to prevent sending unnecessary commands (#171)
- ILI9341 TFT driver (SPI) (#153)
- improve performance of ILI9341 on ATSAMD5X
- ST77xx: fix DrawFastHLine for ST77xx, SSD1331 and SSD1351 DrawFastHLine uses FillRectangle(x,y,width,height,c), so height must be 1 to draw a horizontal line
- tmp102: add Connected func to check for device
- wifinina: added UDP support
- ws2812: update ws2812_avr_16m.go
- **bugfixes**
- apa102: avoid creating garbage
- bmp180: fix temperature type conversion
- **core**
- all
- added custom import path (#161)
- changeover to eliminate all direct use of master/slave terminology
- build: try vendor in working directory to match expected module path
- ci: support Go modules
- modules: update go version and dependency
- **docs**
- docs: reorder to correct alpha and adjust count of supported drivers
0.12.0
---
- **new devices**
+1 -1
View File
@@ -20,7 +20,7 @@ Please first open a Github issue. We want to help, and also make sure that there
## How to use our Github repository
The `master` branch of this repo will always have the latest released version of the TinyGo drivers. All of the active development work for the next release will take place in the `dev` branch. The TinyGo drivers will use semantic versioning and will create a tag/release for each release.
The `release` branch of this repo will always have the latest released version of the TinyGo drivers. All of the active development work for the next release will take place in the `dev` branch. The TinyGo drivers will use semantic versioning and will create a tag/release for each release.
Here is how to contribute back some code or documentation:
+28 -2
View File
@@ -25,8 +25,12 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
@@ -55,14 +59,24 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic/main.go
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/scroll/main.go
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/basic
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/pyportal_boing
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/scroll
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/scroll
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
@@ -105,14 +119,22 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/ntpclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/udpstation/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/tcpclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
@md5sum ./build/test.hex
ifneq ($(AVR), 0)
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
@md5sum ./build/test.hex
endif
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
@@ -131,5 +153,9 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nucleo-f103rb ./examples/shiftregister/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
@md5sum ./build/test.hex
test: clean fmt-check smoke-test
+8 -3
View File
@@ -1,6 +1,6 @@
# TinyGo Drivers
[![GoDoc](https://godoc.org/tinygo.org/x/drivers?status.svg)](https://godoc.org/tinygo.org/x/drivers) [![CircleCI](https://circleci.com/gh/tinygo-org/drivers/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/drivers/tree/dev)
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![CircleCI](https://circleci.com/gh/tinygo-org/drivers/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/drivers/tree/dev)
This package provides a collection of hardware drivers for devices that can be used together with [TinyGo](https://tinygo.org).
@@ -52,7 +52,7 @@ func main() {
## Currently supported devices
The following 48 devices are supported.
The following 53 devices are supported.
| Device Name | Interface Type |
|----------|-------------|
@@ -65,7 +65,9 @@ The following 48 devices are supported.
| [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C |
| [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C |
| [BME280 humidity/pressure sensor](https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf) | I2C |
| [BMI160 accelerometer/gyroscope](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi160-ds000.pdf) | SPI |
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
| [BMP280 temperature/barometer](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | I2C |
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
@@ -73,11 +75,12 @@ The following 48 devices are supported.
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
| [HC-SR04 Ultrasonic distance sensor](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) | GPIO |
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO |
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
@@ -94,6 +97,7 @@ The following 48 devices are supported.
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
| [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | SPI |
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | SPI |
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
| [Stepper motor "Easystepper" controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
@@ -103,6 +107,7 @@ The following 48 devices are supported.
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
## Contributing
+30 -28
View File
@@ -1,8 +1,13 @@
package adt7410
// Package adt7410 provides a driver for the adt7410 I2C Temperature Sensor.
//
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf
//
package adt7410 // import "tinygo.org/x/drivers/adt7410"
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
type Error uint8
@@ -21,40 +26,37 @@ func (e Error) Error() string {
}
type Device struct {
bus *machine.I2C
buf []byte
addr uint8
bus drivers.I2C
buf []byte
Address uint8
}
// New returns ADT7410 device for the provided I2C bus and address. The ADT7410
// has a default address of 0x48 (1001000). The last 2 bits of the address
// New returns ADT7410 device for the provided I2C bus using default address.
// of 0x48 (1001000). To use multiple ADT7410 devices, the last 2 bits of the address
// can be set using by connecting to the A1 and A0 pins to VDD or GND (for a
// total of up to 4 devices on a I2C bus). Also note that 10k pullups are
// recommended for the SDA and SCL lines.
func New(i2c *machine.I2C, addressBits uint8) *Device {
func New(i2c drivers.I2C) *Device {
return &Device{
bus: i2c,
buf: make([]byte, 2),
addr: Address | (addressBits & 0x3),
bus: i2c,
buf: make([]byte, 2),
Address: Address,
}
}
func (dev *Device) Configure() (err error) {
// verify the chip ID
// TODO: According to datasheet, the check below should work; however
// this does not seem to be working right, but is not exactly
// necessary, so can revisit later to see if there is a bug
//id := dev.ReadByte(RegID) & 0xF8
//if id != 0xC8 {
// err = ErrInvalidID
//}
// Configure the ADT7410 device.
func (d *Device) Configure() (err error) {
// reset the chip
dev.writeByte(RegReset, 0xFF)
d.writeByte(RegReset, 0xFF)
time.Sleep(10 * time.Millisecond)
return
}
// Connected returns whether sensor has been found.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), RegID, data)
return data[0]&0xF8 == 0xC8
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
@@ -62,13 +64,13 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
return (int32(d.readUint16(RegTempValueMSB)) * 1000) / 128, nil
}
// ReadTempC returns the value in the temperature value register, in Celcius
// ReadTempC returns the value in the temperature value register, in Celsius.
func (d *Device) ReadTempC() float32 {
t := d.readUint16(RegTempValueMSB)
return float32(int(t)) / 128.0
}
// ReadTempF returns the value in the temperature value register, in Fahrenheit
// ReadTempF returns the value in the temperature value register, in Fahrenheit.
func (d *Device) ReadTempF() float32 {
return d.ReadTempC()*1.8 + 32.0
}
@@ -76,15 +78,15 @@ func (d *Device) ReadTempF() float32 {
func (d *Device) writeByte(reg uint8, data byte) {
d.buf[0] = reg
d.buf[1] = data
d.bus.Tx(uint16(d.addr), d.buf, nil)
d.bus.Tx(uint16(d.Address), d.buf, nil)
}
func (d *Device) readByte(reg uint8) byte {
d.bus.ReadRegister(d.addr, reg, d.buf)
d.bus.ReadRegister(d.Address, reg, d.buf)
return d.buf[0]
}
func (d *Device) readUint16(reg uint8) uint16 {
d.bus.ReadRegister(d.addr, reg, d.buf)
d.bus.ReadRegister(d.Address, reg, d.buf)
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
}
+49
View File
@@ -0,0 +1,49 @@
package adt7410
import (
"testing"
qt "github.com/frankban/quicktest"
"tinygo.org/x/drivers/tester"
)
func TestDefaultI2CAddress(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
dev := New(bus)
c.Assert(dev.Address, qt.Equals, uint8(Address))
}
func TestWhoAmI(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice(c, Address)
fake.SetupRegisters(defaultRegisters())
bus.AddDevice(fake)
dev := New(bus)
c.Assert(dev.Connected(), qt.Equals, true)
fake.SetupRegister(RegID, 0x99)
c.Assert(dev.Connected(), qt.Equals, false)
}
// defaultRegisters returns the default values for all of the device's registers.
// see table 22 on page 27 of the datasheet.
func defaultRegisters() []uint8 {
return []uint8{
RegTempValueMSB: 0,
RegTempValueLSB: 0,
RegStatus: 0,
RegConfig: 0,
RegTHIGHMsbReg: 0x20,
RegTHIGHLsbReg: 0,
RegTLOWMsbReg: 0x05,
RegTLOWLsbReg: 0,
RegTCRITMsbReg: 0x49,
RegTCRITLsbReg: 0x80,
RegTHYSTReg: 0x05,
RegID: 0xC8,
RegReset: 0,
}
}
+48 -2
View File
@@ -1,8 +1,33 @@
package adt7410
// 0x00 Temperature value most significant byte 0x00
// 0x01 Temperature value least significant byte 0x00
// 0x02 Status 0x00
// 0x03 Configuration 0x00
// 0x04 THIGH setpoint most significant byte 0x20 (64°C)
// 0x05 THIGH setpoint least significant byte 0x00 (64°C)
// 0x06 TLOW setpoint most significant byte 0x05 (10°C)
// 0x07 TLOW setpoint least significant byte 0x00 (10°C)
// 0x08 TCRIT setpoint most significant byte 0x49 (147°C)
// 0x09 TCRIT setpoint least significant byte 0x80 (147°C)
// 0x0A THYST setpoint 0x05 (5°C)
// 0x0B ID 0xCX
// 0x0C Reserved 0xXX
// 0x0D Reserved 0xXX
// 0x2E Reserved 0xXX
// 0x2F Software reset 0xXX
const (
// Default I2C address
// Address is default I2C address.
Address = 0x48
// Address1 is for first device, aka the default.
Address1 = Address
// Address2 is for second device.
Address2 = 0x49
// Address3 is for third device.
Address3 = 0x4A
// Address4 is for fourth device.
Address4 = 0x4B
// Temperature Value MSB Register
RegTempValueMSB = 0x0
@@ -16,7 +41,28 @@ const (
// Config Register
RegConfig = 0x3
// ID Register
// THIGH setpoint most significant byte 0x20 (64°C)
RegTHIGHMsbReg = 0x4
// THIGH setpoint least significant byte 0x00 (64°C)
RegTHIGHLsbReg = 0x5
// TLOW setpoint most significant byte 0x05 (10°C)
RegTLOWMsbReg = 0x6
// TLOW setpoint least significant byte 0x00 (10°C)
RegTLOWLsbReg = 0x7
// TCRIT setpoint most significant byte 0x49 (147°C)
RegTCRITMsbReg = 0x8
// TCRIT setpoint least significant byte 0x80 (147°C)
RegTCRITLsbReg = 0x9
// THYST setpoint 0x05 (5°C)
RegTHYSTReg = 0xA
// ID Register (0xCx)
RegID = 0x0B
// Software Reset Register
+3 -5
View File
@@ -6,9 +6,7 @@
//
package adxl345 // import "tinygo.org/x/drivers/adxl345"
import (
"machine"
)
import "tinygo.org/x/drivers"
type Range uint8
type Rate uint8
@@ -40,7 +38,7 @@ type bwRate struct {
// Device wraps an I2C connection to a ADXL345 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
powerCtl powerCtl
dataFormat dataFormat
@@ -52,7 +50,7 @@ type Device struct {
//
// This function only creates the Device object, it does not init the device.
// To do that you must call the Configure() method on the Device before using it.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
powerCtl: powerCtl{
+4 -3
View File
@@ -5,13 +5,14 @@
package amg88xx // import "tinygo.org/x/drivers/amg88xx"
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a AMG88xx device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
data []uint8
interruptMode InterruptMode
@@ -27,7 +28,7 @@ type Config struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: AddressHigh,
+17 -14
View File
@@ -19,6 +19,8 @@ const (
GRB
)
var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
// Device wraps APA102 SPI LEDs.
type Device struct {
bus SPI
@@ -30,6 +32,7 @@ type Device struct {
// SPI from the TinyGo "machine" package implements this already.
type SPI interface {
Tx(w, r []byte) error
Transfer(b byte) (byte, error)
}
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
@@ -39,8 +42,8 @@ func New(b SPI) Device {
// NewSoftwareSPI returns a new APA102 driver that will use a software based
// implementation of the SPI protocol.
func NewSoftwareSPI(sckPin, mosiPin machine.Pin, delay uint32) Device {
return New(&bbSPI{SCK: sckPin, MOSI: mosiPin, Delay: delay})
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) Device {
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
@@ -51,22 +54,22 @@ func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
// write data
for _, c := range cs {
// brightness is scaled to 5 bit value
d.bus.Tx([]byte{0xe0 | (c.A >> 3)}, nil)
d.bus.Transfer(0xe0 | (c.A >> 3))
// set the colors
switch d.Order {
case BRG:
d.bus.Tx([]byte{c.B}, nil)
d.bus.Tx([]byte{c.R}, nil)
d.bus.Tx([]byte{c.G}, nil)
d.bus.Transfer(c.B)
d.bus.Transfer(c.R)
d.bus.Transfer(c.G)
case GRB:
d.bus.Tx([]byte{c.G}, nil)
d.bus.Tx([]byte{c.R}, nil)
d.bus.Tx([]byte{c.B}, nil)
d.bus.Transfer(c.G)
d.bus.Transfer(c.R)
d.bus.Transfer(c.B)
case BGR:
d.bus.Tx([]byte{c.B}, nil)
d.bus.Tx([]byte{c.G}, nil)
d.bus.Tx([]byte{c.R}, nil)
d.bus.Transfer(c.B)
d.bus.Transfer(c.G)
d.bus.Transfer(c.R)
}
}
@@ -86,7 +89,7 @@ func (d Device) Write(buf []byte) (n int, err error) {
// startFrame sends the start bytes for a strand of LEDs.
func (d Device) startFrame() {
d.bus.Tx([]byte{0x00, 0x00, 0x00, 0x00}, nil)
d.bus.Tx(startFrame, nil)
}
// endFrame sends the end frame marker with one extra bit per LED so
@@ -94,6 +97,6 @@ func (d Device) startFrame() {
// See https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
func (d Device) endFrame(count int) {
for i := 0; i < count/16; i++ {
d.bus.Tx([]byte{0xff}, nil)
d.bus.Transfer(0xff)
}
}
+13 -11
View File
@@ -9,16 +9,16 @@ import "machine"
// this more generic and include it in the TinyGo "machine" package instead.
type bbSPI struct {
SCK machine.Pin
MOSI machine.Pin
SDO machine.Pin
Delay uint32
}
// Configure sets up the SCK and MOSI pins as outputs and sets them low
// Configure sets up the SCK and SDO pins as outputs and sets them low
func (s *bbSPI) Configure() {
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.MOSI.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SCK.Low()
s.MOSI.Low()
s.SDO.Low()
if s.Delay == 0 {
s.Delay = 1
}
@@ -41,19 +41,20 @@ func (s *bbSPI) delay() {
}
}
// Transfer is used to send a single byte.
func (s *bbSPI) Transfer(b byte) {
// Transfer matches signature of machine.SPI.Transfer() and is used to send a
// single byte. The received data is ignored and no error will ever be returned.
func (s *bbSPI) Transfer(b byte) (byte, error) {
for i := uint8(0); i < 8; i++ {
// half clock cycle high to start
s.SCK.High()
s.delay()
// write the value to MOSI (MSB first)
// write the value to SDO (MSB first)
if b&(1<<(7-i)) == 0 {
s.MOSI.Low()
s.SDO.Low()
} else {
s.MOSI.High()
s.SDO.High()
}
s.delay()
@@ -61,8 +62,9 @@ func (s *bbSPI) Transfer(b byte) {
s.SCK.Low()
s.delay()
// for actual SPI would try to read the MISO value here
// for actual SPI would try to read the SDI value here
s.delay()
}
return 0, nil
}
+4 -3
View File
@@ -6,13 +6,14 @@ package at24cx // import "tinygo.org/x/drivers/at24cx"
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a DS3231 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
pageSize uint16
currentRAMAddress uint16
@@ -30,7 +31,7 @@ type Config struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
+3 -3
View File
@@ -8,7 +8,7 @@ package bh1750 // import "tinygo.org/x/drivers/bh1750"
import (
"time"
"machine"
"tinygo.org/x/drivers"
)
// SamplingMode is the sampling's resolution of the measurement
@@ -16,7 +16,7 @@ type SamplingMode byte
// Device wraps an I2C connection to a bh1750 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
mode SamplingMode
}
@@ -25,7 +25,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
+4 -5
View File
@@ -1,15 +1,14 @@
// Package blinkm implements a driver for the BlinkM I2C RGB LED.
//
// Datasheet: http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf
//
package blinkm // import "tinygo.org/x/drivers/blinkm"
import (
"machine"
)
import "tinygo.org/x/drivers"
// Device wraps an I2C connection to a BlinkM device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
}
@@ -17,7 +16,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus, Address}
}
+4 -3
View File
@@ -7,8 +7,9 @@
package bme280
import (
"machine"
"math"
"tinygo.org/x/drivers"
)
// calibrationCoefficients reads at startup and stores the calibration coefficients
@@ -35,7 +36,7 @@ type calibrationCoefficients struct {
// Device wraps an I2C connection to a BME280 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
calibrationCoefficients calibrationCoefficients
}
@@ -44,7 +45,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
+201
View File
@@ -0,0 +1,201 @@
package bmi160
import "machine"
import "time"
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
// also an I2C interface, but it is not yet supported.
type DeviceSPI struct {
// Chip select pin
CSB machine.Pin
// SPI bus (requires chip select to be usable).
Bus machine.SPI
}
// NewSPI returns a new device driver. The pin and SPI interface are not
// touched, provide a fully configured SPI object and call Configure to start
// using this device.
func NewSPI(csb machine.Pin, spi machine.SPI) *DeviceSPI {
return &DeviceSPI{
CSB: csb, // chip select
Bus: spi,
}
}
// Configure configures the BMI160 for use. It configures the CSB pin and
// configures the BMI160, but it does not configure the SPI interface (it is
// assumed to be up and running).
func (d *DeviceSPI) Configure() error {
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.CSB.High()
// The datasheet recommends doing a register read from address 0x7F to get
// SPI communication going:
// > If CSB sees a rising edge after power-up, the BMI160 interface switches
// > to SPI until a reset or the next power-up occurs. Therefore, a CSB
// > rising edge is needed before starting the SPI communication. Hence, it
// > is recommended to perform a SPI single read access to the ADDRESS 0x7F
// > before the actual communication in order to use the SPI interface.
d.readRegister(0x7F)
// Power up the accelerometer. 0b0001_00nn is the command format, with 0b01
// indicating normal mode.
d.runCommand(0b0001_0001)
// Power up the gyroscope. 0b0001_01nn is the command format, with 0b01
// indicating normal mode.
d.runCommand(0b0001_0101)
// Wait until the device is fully initialized. Even after the command has
// finished, the gyroscope may not be fully powered on. Therefore, wait
// until we get an expected value.
// This takes 30ms or so.
for {
// Wait for the acc_pmu_status and gyr_pmu_status to both be 0b01.
if d.readRegister(reg_PMU_STATUS) == 0b0001_0100 {
break
}
}
return nil
}
// Connected check whether the device appears to be properly connected. It reads
// the CHIPID, which must be 0xD1 for the BMI160.
func (d *DeviceSPI) Connected() bool {
return d.readRegister(reg_CHIPID) == 0xD1
}
// Reset restores the device to the state after power up. This can be useful to
// easily disable the accelerometer and gyroscope to reduce current consumption.
func (d *DeviceSPI) Reset() error {
d.runCommand(0xB6) // softreset
return nil
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
data := []byte{0x80 | reg_TEMPERATURE_0, 0, 0}
d.CSB.Low()
err = d.Bus.Tx(data, data)
d.CSB.High()
if err != nil {
return
}
rawTemperature := int16(uint16(data[1]) | uint16(data[2])<<8)
// 0x0000 is 23°C
// 0x7fff is ~87°C
// We use 0x8000 instead of 0x7fff to make the formula easier. The result
// should be near identical and shouldn't affect the result too much (the
// temperature sensor has an offset of around 2°C so isn't very reliable).
// So the formula is as follows:
// 1. Scale from 0x0000..0x8000 to 0..(87-23).
// rawTemperature * (87-23) / 0x8000
// 2. Convert to centidegrees.
// rawTemperature * 1000 * (87-23) / 0x8000
// 3. Add 23°C offset.
// rawTemperature * 1000 * (87-23) / 0x8000 + 23000
// 4. Simplify.
// rawTemperature * 1000 * 64 / 0x8000 + 23000
// rawTemperature * 64000 / 0x8000 + 23000
// rawTemperature * 125 / 64 + 23000
temperature = int32(rawTemperature)*125/64 + 23000
return
}
// 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 *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
data := []byte{0x80 | reg_ACC_XL, 0, 0, 0, 0, 0, 0}
d.CSB.Low()
err = d.Bus.Tx(data, data)
d.CSB.High()
if err != nil {
return
}
// 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[1])|uint16(data[2])<<8)) * 15625 / 256
y = int32(int16(uint16(data[3])|uint16(data[4])<<8)) * 15625 / 256
z = int32(int16(uint16(data[5])|uint16(data[6])<<8)) * 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 *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
data := []byte{0x80 | reg_GYR_XL, 0, 0, 0, 0, 0, 0}
d.CSB.Low()
err = d.Bus.Tx(data, data)
d.CSB.High()
if err != nil {
return
}
// 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).
// The default is 2000°/s full scale range for -32768..32767.
// The formula works as follows (taking X as an example):
// 1. Scale from 32768 to 2000. This means that it is in °/s units.
// rawX * 2000 / 32768
// 2. Scale to µ°/s by multiplying by 1e6.
// rawX * 1e6 * 2000 / 32768
// 3. Simplify.
// rawX * 2e9 / 32768
// rawX * 1953125 / 32
rawX := int32(int16(uint16(data[1]) | uint16(data[2])<<8))
rawY := int32(int16(uint16(data[3]) | uint16(data[4])<<8))
rawZ := int32(int16(uint16(data[5]) | uint16(data[6])<<8))
x = int32(int64(rawX) * 1953125 / 32)
y = int32(int64(rawY) * 1953125 / 32)
z = int32(int64(rawZ) * 1953125 / 32)
return
}
// runCommand runs a BMI160 command through the CMD register. It waits for the
// command to complete before returning.
func (d *DeviceSPI) runCommand(command uint8) {
d.writeRegister(reg_CMD, command)
for {
response := d.readRegister(reg_CMD)
if response == 0 {
return // command was completed
}
}
}
// readRegister reads from a single BMI160 register. It should only be used for
// single register reads, not for reading multiple registers at once.
func (d *DeviceSPI) readRegister(address uint8) uint8 {
// I don't know why but it appears necessary to sleep for a bit here.
time.Sleep(time.Millisecond)
data := []byte{0x80 | address, 0}
d.CSB.Low()
d.Bus.Tx(data, data)
d.CSB.High()
return data[1]
}
// writeRegister writes a single byte BMI160 register. It should only be used
// for writing to a single register.
func (d *DeviceSPI) writeRegister(address, data uint8) {
// I don't know why but it appears necessary to sleep for a bit here.
time.Sleep(time.Millisecond)
d.CSB.Low()
d.Bus.Tx([]byte{address, data}, []byte{0, 0})
d.CSB.High()
}
+44
View File
@@ -0,0 +1,44 @@
package bmi160
const (
reg_CHIPID = 0x00
reg_ERR_REG = 0x02
reg_PMU_STATUS = 0x03
reg_MAG_XL = 0x04
reg_MAG_XH = 0x05
reg_MAG_YL = 0x06
reg_MAG_YH = 0x07
reg_MAG_ZL = 0x08
reg_MAG_ZH = 0x09
reg_RHALL_L = 0x0A
reg_RHALL_H = 0x0B
reg_GYR_XL = 0x0C
reg_GYR_XH = 0x0D
reg_GYR_YL = 0x0E
reg_GYR_YH = 0x0F
reg_GYR_ZL = 0x10
reg_GYR_ZH = 0x11
reg_ACC_XL = 0x12
reg_ACC_XH = 0x13
reg_ACC_YL = 0x14
reg_ACC_YH = 0x15
reg_ACC_ZL = 0x16
reg_ACC_ZH = 0x17
reg_SENSORTIME_0 = 0x18
reg_SENSORTIME_1 = 0x19
reg_SENSORTIME_2 = 0x1A
reg_STATUS = 0x1B
reg_INT_STATUS_0 = 0x1C
reg_INT_STATUS_1 = 0x1D
reg_INT_STATUS_2 = 0x1E
reg_INT_STATUS_3 = 0x1F
reg_TEMPERATURE_0 = 0x20
reg_TEMPERATURE_1 = 0x21
reg_FIFO_LENGTH_0 = 0x22
reg_FIFO_LENGTH_1 = 0x23
reg_FIFO_DATA = 0x24
// ...
reg_CMD = 0x7E
)
+7 -7
View File
@@ -9,7 +9,7 @@ package bmp180 // import "tinygo.org/x/drivers/bmp180"
import (
"time"
"machine"
"tinygo.org/x/drivers"
)
// OversamplingMode is the oversampling ratio of the pressure measurement.
@@ -32,7 +32,7 @@ type calibrationCoefficients struct {
// Device wraps an I2C connection to a BMP180 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
mode OversamplingMode
calibrationCoefficients calibrationCoefficients
@@ -43,7 +43,7 @@ type Device struct {
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
@@ -125,7 +125,7 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
}
// rawTemp returns the sensor's raw values of the temperature
func (d *Device) rawTemp() (int16, error) {
func (d *Device) rawTemp() (int32, error) {
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
time.Sleep(5 * time.Millisecond)
data := make([]byte, 2)
@@ -133,12 +133,12 @@ func (d *Device) rawTemp() (int16, error) {
if err != nil {
return 0, err
}
return readInt(data[0], data[1]), nil
return int32(uint16(data[0])<<8 | uint16(data[1])), nil
}
// calculateB5 calculates intermediate value B5 as per page 15 of datasheet
func (d *Device) calculateB5(rawTemp int16) int32 {
x1 := (int32(rawTemp) - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
func (d *Device) calculateB5(rawTemp int32) int32 {
x1 := (rawTemp - int32(d.calibrationCoefficients.ac6)) * int32(d.calibrationCoefficients.ac5) >> 15
x2 := int32(d.calibrationCoefficients.mc) << 11 / (x1 + int32(d.calibrationCoefficients.md))
return x1 + x2
}
+244
View File
@@ -0,0 +1,244 @@
package bmp280
import (
"time"
"tinygo.org/x/drivers"
)
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
type Oversampling uint
// Mode is the Power Mode.
type Mode uint
// Standby is the inactive period between the reads when the sensor is in normal power mode.
type Standby uint
// Filter unwanted changes in measurement caused by external (environmental) or internal changes (IC).
type Filter uint
// Device wraps an I2C connection to a BMP280 device.
type Device struct {
bus drivers.I2C
Address uint16
cali calibrationCoefficients
Temperature Oversampling
Pressure Oversampling
Mode Mode
Standby Standby
Filter Filter
}
type calibrationCoefficients struct {
// Temperature compensation
t1 uint16
t2 int16
t3 int16
// Pressure compensation
p1 uint16
p2 int16
p3 int16
p4 int16
p5 int16
p6 int16
p7 int16
p8 int16
p9 int16
}
// New creates a new BMP280 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
}
}
// Connected returns whether a BMP280 has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := make([]byte, 1)
d.bus.ReadRegister(uint8(d.Address), REG_ID, data)
return data[0] == CHIP_ID
}
// Reset preforms complete power-on-reset procedure.
// It is required to call Configure afterwards.
func (d *Device) Reset() {
d.bus.WriteRegister(uint8(d.Address), REG_RESET, []byte{CMD_RESET})
}
// Configure sets up the device for communication and
// read the calibration coefficients.
func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pres Oversampling, mode Mode) {
d.Standby = standby
d.Filter = filter
d.Temperature = temp
d.Pressure = pres
d.Mode = mode
// Write the configuration (standby, filter, spi 3 wire)
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
d.bus.WriteRegister(uint8(d.Address), REG_CONFIG, []byte{byte(config)})
// Write the control (temperature oversampling, pressure oversampling,
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
// Read Calibration data
data := make([]byte, 24)
err := d.bus.ReadRegister(uint8(d.Address), REG_CALI, data)
if err != nil {
return
}
// Datasheet: 3.11.2 Trimming parameter readout
d.cali.t1 = readUintLE(data[0], data[1])
d.cali.t2 = readIntLE(data[2], data[3])
d.cali.t3 = readIntLE(data[4], data[5])
d.cali.p1 = readUintLE(data[6], data[7])
d.cali.p2 = readIntLE(data[8], data[9])
d.cali.p3 = readIntLE(data[10], data[11])
d.cali.p4 = readIntLE(data[12], data[13])
d.cali.p5 = readIntLE(data[14], data[15])
d.cali.p6 = readIntLE(data[16], data[17])
d.cali.p7 = readIntLE(data[18], data[19])
d.cali.p8 = readIntLE(data[20], data[21])
d.cali.p9 = readIntLE(data[22], data[23])
}
// PrintCali prints the Calibration information.
func (d *Device) PrintCali() {
println("T1:", d.cali.t1)
println("T2:", d.cali.t2)
println("T3:", d.cali.t3)
println("P1:", d.cali.p1)
println("P2:", d.cali.p2)
println("P3:", d.cali.p3)
println("P4:", d.cali.p4)
println("P5:", d.cali.p5)
println("P6:", d.cali.p6)
println("P7:", d.cali.p7)
println("P8:", d.cali.p8)
println("P9:", d.cali.p9, "\n")
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
data, err := d.readData(REG_TEMP, 3)
if err != nil {
return
}
rawTemp := convert3Bytes(data[0], data[1], data[2])
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
// Temperature compensation
var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
int32(d.cali.t3) >> 14
tFine := var1 + var2
// Convert from degrees to milli degrees by multiplying by 10.
// Will output 30250 milli degrees celsius for 30.25 degrees celsius
temperature = 10 * ((tFine*5 + 128) >> 8)
return
}
// ReadPressure returns the pressure in milli pascals (mPa).
func (d *Device) ReadPressure() (pressure int32, err error) {
// First 3 bytes are Pressure, last 3 bytes are Temperature
data, err := d.readData(REG_PRES, 6)
if err != nil {
return
}
rawTemp := convert3Bytes(data[3], data[4], data[5])
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
// Calculate tFine (temperature), used for the Pressure compensation
var1 := ((rawTemp >> 3) - int32(d.cali.t1<<1)) * int32(d.cali.t2) >> 11
var2 := (((rawTemp >> 4) - int32(d.cali.t1)) * ((rawTemp >> 4) - int32(d.cali.t1)) >> 12) *
int32(d.cali.t3) >> 14
tFine := var1 + var2
rawPres := convert3Bytes(data[0], data[1], data[2])
// Datasheet: 8.2 Compensation formula in 32 bit fixed point
// Pressure compensation
var1 = (tFine >> 1) - 64000
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * int32(d.cali.p6)
var2 = var2 + ((var1 * int32(d.cali.p5)) << 1)
var2 = (var2 >> 2) + (int32(d.cali.p4) << 16)
var1 = (((int32(d.cali.p3) * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) +
((int32(d.cali.p2) * var1) >> 1)) >> 18
var1 = ((32768 + var1) * int32(d.cali.p1)) >> 15
if var1 == 0 {
return 0, nil
}
p := uint32(((1048576 - rawPres) - (var2 >> 12)) * 3125)
if p < 0x80000000 {
p = (p << 1) / uint32(var1)
} else {
p = (p / uint32(var1)) * 2
}
var1 = (int32(d.cali.p9) * int32(((p>>3)*(p>>3))>>13)) >> 12
var2 = (int32(p>>2) * int32(d.cali.p8)) >> 13
return 1000 * (int32(p) + ((var1 + var2 + int32(d.cali.p7)) >> 4)), nil
}
// readData reads n number of bytes of the specified register
func (d *Device) readData(register int, n int) ([]byte, error) {
// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
if d.Mode != MODE_NORMAL {
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
}
// Check STATUS register, wait if data is not available yet
status := make([]byte, 1)
for d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]) {
time.Sleep(time.Millisecond)
}
// Read the requested register
data := make([]byte, n)
err := d.bus.ReadRegister(uint8(d.Address), uint8(register), data[:])
return data, err
}
// convert3Bytes converts three bytes to int32
func convert3Bytes(msb byte, b1 byte, lsb byte) int32 {
return int32(((((uint32(msb) << 8) | uint32(b1)) << 8) | uint32(lsb)) >> 4)
}
// readUint converts two bytes to uint16
func readUint(msb byte, lsb byte) uint16 {
return (uint16(msb) << 8) | uint16(lsb)
}
// readUintLE converts two little endian bytes to uint16
func readUintLE(msb byte, lsb byte) uint16 {
temp := readUint(msb, lsb)
return (temp >> 8) | (temp << 8)
}
// readIntLE converts two little endian bytes to int16
func readIntLE(msb byte, lsb byte) int16 {
return int16(readUintLE(msb, lsb))
}
+56
View File
@@ -0,0 +1,56 @@
// Package bmp280 provides a driver for the BMP280 digital temperature & pressure sensor by Bosch.
//
// Datasheet: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
package bmp280
// The I2C address which this device listens to.
const Address = 0x77
// Registers
const (
REG_ID = 0xD0 // WHO_AM_I
REG_RESET = 0xE0
REG_STATUS = 0xF3
REG_CTRL_MEAS = 0xF4
REG_CONFIG = 0xF5
REG_TEMP = 0xFA
REG_PRES = 0xF7
REG_CALI = 0x88
CHIP_ID = 0x58
CMD_RESET = 0xB6
)
const (
SAMPLING_SKIPPED Oversampling = iota
SAMPLING_1X
SAMPLING_2X
SAMPLING_4X
SAMPLING_8X
SAMPLING_16X
)
const (
MODE_SLEEP Mode = 0x00
MODE_FORCED Mode = 0x01
MODE_NORMAL Mode = 0x03
)
const (
STANDBY_1MS Standby = iota
STANDBY_63MS
STANDBY_125MS
STANDBY_250MS
STANDBY_500MS
STANDBY_1000MS
STANDBY_2000MS
STANDBY_4000MS
)
const (
FILTER_OFF Filter = iota
FILTER_2X
FILTER_4X
FILTER_8X
FILTER_16X
)
+3 -3
View File
@@ -9,18 +9,18 @@ import (
"errors"
"time"
"machine"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a DS1307 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint8
AddressSRAM uint8
}
// New creates a new DS1307 connection. I2C bus must be already configured.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus: bus,
Address: uint8(I2CAddress),
AddressSRAM: SRAMBeginAddres,
+4 -3
View File
@@ -5,15 +5,16 @@
package ds3231 // import "tinygo.org/x/drivers/ds3231"
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
type Mode uint8
// Device wraps an I2C connection to a DS3231 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
}
@@ -21,7 +22,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
+1 -1
View File
@@ -10,7 +10,7 @@ import (
var (
i2c = &machine.I2C0
sensor = adt7410.New(i2c, 0)
sensor = adt7410.New(i2c)
)
func main() {
+2 -2
View File
@@ -13,8 +13,8 @@ func main() {
machine.SPI1.Configure(machine.SPIConfig{
SCK: machine.SPI1_SCK_PIN,
MOSI: machine.SPI1_MOSI_PIN,
MISO: machine.SPI1_MISO_PIN,
SDO: machine.SPI1_SDO_PIN,
SDI: machine.SPI1_SDI_PIN,
Frequency: 8000000,
})
machine.I2C0.Configure(machine.I2CConfig{SCL: machine.SCL_PIN, SDA: machine.SDA_PIN})
+46
View File
@@ -0,0 +1,46 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/bmi160"
)
func main() {
time.Sleep(5 * time.Second)
machine.SPI0.Configure(machine.SPIConfig{})
sensor := bmi160.NewSPI(machine.A5, machine.SPI0)
sensor.Configure()
if !sensor.Connected() {
println("BMI160 not connected")
return
}
for {
time.Sleep(time.Second)
t, err := sensor.ReadTemperature()
if err != nil {
println("Error reading temperature", err)
continue
}
fmt.Printf("Temperature: %.2f °C\n", float32(t)/1000)
accelX, accelY, accelZ, err := sensor.ReadAcceleration()
if err != nil {
println("Error reading acceleration", err)
continue
}
fmt.Printf("Acceleration: %.2fg %.2fg %.2fg\n", float32(accelX)/1e6, float32(accelY)/1e6, float32(accelZ)/1e6)
gyroX, gyroY, gyroZ, err := sensor.ReadRotation()
if err != nil {
println("Error reading rotation", err)
continue
}
fmt.Printf("Rotation: %.2f°/s %.2f°/s %.2f°/s\n", float32(gyroX)/1e6, float32(gyroY)/1e6, float32(gyroZ)/1e6)
}
}
+44
View File
@@ -0,0 +1,44 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/bmp280"
)
func main() {
time.Sleep(5 * time.Second)
machine.I2C0.Configure(machine.I2CConfig{})
sensor := bmp280.New(machine.I2C0)
sensor.Configure(bmp280.STANDBY_125MS, bmp280.FILTER_4X, bmp280.SAMPLING_16X, bmp280.SAMPLING_16X, bmp280.MODE_FORCED)
connected := sensor.Connected()
if !connected {
println("\nBMP280 Sensor not detected\n")
return
}
println("\nBMP280 Sensor detected\n")
println("Calibration:")
sensor.PrintCali()
for {
t, err := sensor.ReadTemperature()
if err != nil {
println("Error reading temperature")
}
// Temperature in degrees Celsius
fmt.Printf("Temperature: %.2f °C\n", float32(t)/1000)
p, err := sensor.ReadPressure()
if err != nil {
println("Error reading pressure")
}
// Pressure in hectoPascal
fmt.Printf("Pressure: %.2f hPa\n", float32(p)/100000)
time.Sleep(5 * time.Second)
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ package main
import (
"machine"
"tinygo.org/x/drivers/examples/flash/console"
console_example "tinygo.org/x/drivers/examples/flash/console"
"tinygo.org/x/drivers/flash"
)
@@ -11,8 +11,8 @@ func main() {
console_example.RunFor(
flash.NewSPI(
&machine.SPI1,
machine.SPI1_MOSI_PIN,
machine.SPI1_MISO_PIN,
machine.SPI1_SDO_PIN,
machine.SPI1_SDI_PIN,
machine.SPI1_SCK_PIN,
machine.SPI1_CS_PIN,
),
+27 -6
View File
@@ -1,8 +1,8 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/gps"
)
@@ -11,19 +11,40 @@ func main() {
println("GPS I2C Example")
machine.I2C0.Configure(machine.I2CConfig{})
ublox := gps.NewI2C(&machine.I2C0)
parser := gps.Parser(ublox)
parser := gps.NewParser()
var fix gps.Fix
for {
fix = parser.NextFix()
s, err := ublox.NextSentence()
if err != nil {
println(err)
continue
}
fix, err = parser.Parse(s)
if err != nil {
println(err)
continue
}
if fix.Valid {
print(fix.Time.Format("15:04:05"))
print(", lat=", fmt.Sprintf("%f", fix.Latitude))
print(", long=", fmt.Sprintf("%f", fix.Longitude))
print(", altitude:=", fix.Altitude)
print(", lat=")
print(fix.Latitude)
print(", long=")
print(fix.Longitude)
print(", altitude=", fix.Altitude)
print(", satellites=", fix.Satellites)
if fix.Speed != 0 {
print(", speed=")
print(fix.Speed)
}
if fix.Heading != 0 {
print(", heading=")
print(fix.Heading)
}
println()
} else {
println("No fix")
}
time.Sleep(200 * time.Millisecond)
}
}
+27 -6
View File
@@ -1,8 +1,8 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/gps"
)
@@ -11,19 +11,40 @@ func main() {
println("GPS UART Example")
machine.UART1.Configure(machine.UARTConfig{BaudRate: 9600})
ublox := gps.NewUART(&machine.UART1)
parser := gps.Parser(ublox)
parser := gps.NewParser()
var fix gps.Fix
for {
fix = parser.NextFix()
s, err := ublox.NextSentence()
if err != nil {
println(err)
continue
}
fix, err = parser.Parse(s)
if err != nil {
println(err)
continue
}
if fix.Valid {
print(fix.Time.Format("15:04:05"))
print(", lat=", fmt.Sprintf("%f", fix.Latitude))
print(", long=", fmt.Sprintf("%f", fix.Longitude))
print(", altitude:=", fix.Altitude)
print(", lat=")
print(fix.Latitude)
print(", long=")
print(fix.Longitude)
print(", altitude=", fix.Altitude)
print(", satellites=", fix.Satellites)
if fix.Speed != 0 {
print(", speed=")
print(fix.Speed)
}
if fix.Heading != 0 {
print(", heading=")
print(fix.Heading)
}
println()
} else {
println("No fix")
}
time.Sleep(200 * time.Millisecond)
}
}
+60
View File
@@ -0,0 +1,60 @@
package main
import (
"machine"
"strconv"
"time"
"tinygo.org/x/drivers/hd44780i2c"
)
func main() {
// Note: most HD44780 LCD modules requires 5V power, however some variations
// use 3.3V (and may be damaged by 5V).
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
})
lcd := hd44780i2c.New(machine.I2C0, 0x27) // some modules have address 0x3F
lcd.Configure(hd44780i2c.Config{
Width: 16, // required
Height: 2, // required
CursorOn: true,
CursorBlink: true,
})
lcd.Print([]byte(" TinyGo\n LCD Test "))
// CGRAM address 0x0-0x7 can be used to store 8 custom characters
lcd.CreateCharacter(0x0, []byte{0x00, 0x11, 0x0E, 0x1F, 0x15, 0x1F, 0x1F, 0x1F})
lcd.Print([]byte{0x0})
// You can use https://maxpromer.github.io/LCD-Character-Creator/
// to crete your own characters.
time.Sleep(time.Millisecond * 7000)
for i := 0; i < 5; i++ {
lcd.BacklightOn(false)
time.Sleep(time.Millisecond * 250)
lcd.BacklightOn(true)
time.Sleep(time.Millisecond * 250)
}
lcd.CursorOn(false)
lcd.CursorBlink(false)
i := 0
for {
lcd.ClearDisplay()
lcd.SetCursor(2, 1)
lcd.Print([]byte(strconv.FormatInt(int64(i), 10)))
i++
time.Sleep(time.Millisecond * 100)
}
}
+29
View File
@@ -0,0 +1,29 @@
// +build atsamd21
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewSPI(
machine.SPI0,
machine.D0,
machine.D1,
machine.D2,
)
backlight = machine.D3
)
func init() {
machine.SPI0.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 24000000,
})
}
+2 -11
View File
@@ -9,15 +9,6 @@ import (
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
black = color.RGBA{0, 0, 0, 255}
white = color.RGBA{255, 255, 255, 255}
red = color.RGBA{255, 0, 0, 255}
@@ -27,13 +18,13 @@ var (
func main() {
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
backlight.Configure(machine.PinConfig{machine.PinOutput})
display.Configure(ili9341.Config{})
width, height := display.Size()
display.FillScreen(black)
machine.TFT_BACKLIGHT.High()
backlight.High()
display.FillRectangle(0, 0, width/2, height/2, white)
display.FillRectangle(width/2, 0, width/2, height/2, red)
+22
View File
@@ -0,0 +1,22 @@
// +build pyportal
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
backlight = machine.TFT_BACKLIGHT
)
+29
View File
@@ -0,0 +1,29 @@
// +build wioterminal
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewSPI(
machine.SPI3,
machine.LCD_DC,
machine.LCD_SS_PIN,
machine.LCD_RESET,
)
backlight = machine.LCD_BACKLIGHT
)
func init() {
machine.SPI3.Configure(machine.SPIConfig{
SCK: machine.LCD_SCK_PIN,
SDO: machine.LCD_SDO_PIN,
SDI: machine.LCD_SDI_PIN,
Frequency: 40000000,
})
}
+2 -11
View File
@@ -25,15 +25,6 @@ const (
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8)]uint16{}
startTime int64
@@ -56,7 +47,7 @@ var (
func main() {
// configure backlight
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
backlight.Configure(machine.PinConfig{machine.PinOutput})
// configure display
display.Configure(ili9341.Config{})
@@ -64,7 +55,7 @@ func main() {
width, height := display.Size()
println(width, height)
machine.TFT_BACKLIGHT.High()
backlight.High()
display.SetRotation(ili9341.Rotation270)
DrawBackground()
@@ -0,0 +1,22 @@
// +build pyportal
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
backlight = machine.TFT_BACKLIGHT
)
@@ -0,0 +1,29 @@
// +build wioterminal
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewSPI(
machine.SPI3,
machine.LCD_DC,
machine.LCD_SS_PIN,
machine.LCD_RESET,
)
backlight = machine.LCD_BACKLIGHT
)
func init() {
machine.SPI3.Configure(machine.SPIConfig{
SCK: machine.LCD_SCK_PIN,
SDO: machine.LCD_SDO_PIN,
SDI: machine.LCD_SDI_PIN,
Frequency: 40000000,
})
}
+29
View File
@@ -0,0 +1,29 @@
// +build atsamd21
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewSPI(
machine.SPI0,
machine.D0,
machine.D1,
machine.D2,
)
backlight = machine.D3
)
func init() {
machine.SPI0.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 24000000,
})
}
+2 -11
View File
@@ -9,15 +9,6 @@ import (
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
red = color.RGBA{255, 0, 0, 255}
blue = color.RGBA{0, 0, 255, 255}
green = color.RGBA{0, 255, 0, 255}
@@ -27,13 +18,13 @@ var (
func main() {
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
backlight.Configure(machine.PinConfig{machine.PinOutput})
display.Configure(ili9341.Config{})
width, height := display.Size()
display.FillScreen(black)
machine.TFT_BACKLIGHT.High()
backlight.High()
display.FillRectangle(0, 0, width/2, height/2, white)
display.FillRectangle(width/2, 0, width/2, height/2, red)
+22
View File
@@ -0,0 +1,22 @@
// +build pyportal
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
backlight = machine.TFT_BACKLIGHT
)
+29
View File
@@ -0,0 +1,29 @@
// +build wioterminal
package main
import (
"machine"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewSPI(
machine.SPI3,
machine.LCD_DC,
machine.LCD_SS_PIN,
machine.LCD_RESET,
)
backlight = machine.LCD_BACKLIGHT
)
func init() {
machine.SPI3.Configure(machine.SPIConfig{
SCK: machine.LCD_SCK_PIN,
SDO: machine.LCD_SDO_PIN,
SDI: machine.LCD_SDI_PIN,
Frequency: 40000000,
})
}
+29
View File
@@ -0,0 +1,29 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lis2mdl"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
compass := lis2mdl.New(machine.I2C0)
if !compass.Connected() {
for {
println("LIS2MDL not connected!")
time.Sleep(1 * time.Second)
}
}
compass.Configure(lis2mdl.Configuration{}) //default settings
for {
heading := compass.ReadCompass()
println("Heading:", heading)
time.Sleep(time.Millisecond * 100)
}
}
+39
View File
@@ -0,0 +1,39 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm303agr"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel_mag := lsm303agr.New(machine.I2C0)
if !accel_mag.Connected() {
println("LSM303AGR/MAG not connected!")
return
}
accel_mag.Configure(lsm303agr.Configuration{}) //default settings
for {
accel_x, accel_y, accel_z := accel_mag.ReadAcceleration()
pitch, roll := accel_mag.ReadPitchRoll()
mag_x, mag_y, mag_z := accel_mag.ReadMagneticField()
heading := accel_mag.ReadCompass()
temp, _ := accel_mag.ReadTemperature()
println("ACCEL_X:", accel_x, " ACCEL_Y:", accel_y, " ACCEL_Z:", accel_z)
println("MAG_X:", mag_x, " MAG_Y:", mag_y, " MAG_Z:", mag_z)
println("Pitch:", pitch, " Roll:", roll)
println("Heading:", heading)
println("Temperature:", temp/1000)
println("\n")
time.Sleep(time.Millisecond * 100)
}
}
+36
View File
@@ -0,0 +1,36 @@
package ssd1351
import (
"machine"
"image/color"
"tinygo.org/x/drivers/ssd1351"
)
func main() {
machine.SPI1.Configure(machine.SPIConfig{
Frequency: 2000000,
})
display := ssd1351.New(machine.SPI1, machine.D18, machine.D17, machine.D16, machine.D4, machine.D19)
display.Configure(ssd1351.Config{
Width: 96,
Height: 96,
ColumnOffset: 16,
})
width, height := display.Size()
white := color.RGBA{255, 255, 255, 255}
red := color.RGBA{255, 0, 0, 255}
blue := color.RGBA{0, 0, 255, 255}
green := color.RGBA{0, 255, 0, 255}
display.FillRectangle(0, 0, width, height/4, white)
display.FillRectangle(0, height/4, width, height/4, red)
display.FillRectangle(0, height/2, width, height/4, green)
display.FillRectangle(0, 3*height/4, width, height/4, blue)
display.Display()
}
+28 -3
View File
@@ -9,12 +9,37 @@ import (
)
func main() {
// Example configuration for Adafruit Clue
// machine.SPI1.Configure(machine.SPIConfig{
// Frequency: 8000000,
// SCK: machine.TFT_SCK,
// SDO: machine.TFT_SDO,
// SDI: machine.TFT_SDO,
// Mode: 0,
// })
// display := st7789.New(machine.SPI1,
// machine.TFT_RESET,
// machine.TFT_DC,
// machine.TFT_CS,
// machine.TFT_LITE)
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
Mode: 3,
Mode: 0,
})
display := st7789.New(machine.SPI0,
machine.P6, // TFT_RESET
machine.P7, // TFT_DC
machine.P8, // TFT_CS
machine.P9) // TFT_LITE
display.Configure(st7789.Config{
Rotation: st7789.NO_ROTATION,
RowOffset: 80,
FrameRate: st7789.FRAMERATE_111,
VSyncLines: st7789.MAX_VSYNC_SCANLINES,
})
display := st7789.New(machine.SPI0, machine.P6, machine.P7, machine.P8)
display.Configure(st7789.Config{Rotation: st7789.NO_ROTATION})
width, height := display.Size()
+56
View File
@@ -0,0 +1,56 @@
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/waveshare-epd/epd4in2"
)
var display epd4in2.Device
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
Mode: 0,
})
display = epd4in2.New(machine.SPI0, machine.P6, machine.P7, machine.P8, machine.P9)
display.Configure(epd4in2.Config{})
black := color.RGBA{1, 1, 1, 255}
display.ClearBuffer()
println("Clear the display")
display.ClearDisplay()
display.WaitUntilIdle()
println("Waiting for 2 seconds")
time.Sleep(2 * time.Second)
// Show a checkered board
for i := int16(0); i < 16; i++ {
for j := int16(0); j < 25; j++ {
if (i+j)%2 == 0 {
showRect(i*8, j*10, 8, 10, black)
}
}
}
println("Show checkered board")
display.Display()
display.WaitUntilIdle()
println("Waiting for 2 seconds")
time.Sleep(2 * time.Second)
println("You could remove power now")
}
func showRect(x int16, y int16, w int16, h int16, c color.RGBA) {
for i := x; i < x+w; i++ {
for j := y; j < y+h; j++ {
display.SetPixel(i, j, c)
}
}
}
+2 -2
View File
@@ -62,8 +62,8 @@ func main() {
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
+2 -2
View File
@@ -69,8 +69,8 @@ func main() {
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
+166
View File
@@ -0,0 +1,166 @@
// This is an example of using the wifinina driver to implement a NTP client.
// It creates a UDP connection to request the current time and parse the
// response from a NTP server.
package main
import (
"errors"
"fmt"
"machine"
"runtime"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const ntpHost = "129.6.15.29"
const NTP_PACKET_SIZE = 48
var (
// this is the ESP chip that has the WIFININA firmware flashed on it
// these are the default pins for the Arduino Nano33 IoT.
adaptor = wifinina.Device{
SPI: machine.NINA_SPI,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
b = make([]byte, NTP_PACKET_SIZE)
console = machine.UART0
)
func main() {
// Init esp32
// Configure SPI for 8Mhz, Mode 0, MSB First
machine.NINA_SPI.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
adaptor.Configure()
// connect to access point
connectToAP()
// now make UDP connection
ip := net.ParseIP(ntpHost)
raddr := &net.UDPAddr{IP: ip, Port: 123}
laddr := &net.UDPAddr{Port: 2390}
conn, err := net.DialUDP("udp", laddr, raddr)
if err != nil {
for {
time.Sleep(time.Second)
println(err)
}
}
for {
// send data
println("Requesting NTP time...")
t, err := getCurrentTime(conn)
if err != nil {
message("Error getting current time: %v", err)
} else {
message("NTP time: %v", t)
}
runtime.AdjustTimeOffset(-1 * int64(time.Since(t)))
for i := 0; i < 10; i++ {
message("Current time: %v", time.Now())
time.Sleep(1 * time.Second)
}
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting UDP...")
conn.Close()
println("Done.")
}
func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) {
if err := sendNTPpacket(conn); err != nil {
return time.Time{}, err
}
clearBuffer()
for now := time.Now(); time.Since(now) < time.Second; {
time.Sleep(5 * time.Millisecond)
if n, err := conn.Read(b); err != nil {
return time.Time{}, fmt.Errorf("error reading UDP packet: %w", err)
} else if n == 0 {
continue // no packet received yet
} else if n != NTP_PACKET_SIZE {
if n != NTP_PACKET_SIZE {
return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n)
}
}
return parseNTPpacket(), nil
}
return time.Time{}, errors.New("no packet received after 1 second")
}
func sendNTPpacket(conn *net.UDPSerialConn) error {
clearBuffer()
b[0] = 0b11100011 // LI, Version, Mode
b[1] = 0 // Stratum, or type of clock
b[2] = 6 // Polling Interval
b[3] = 0xEC // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
b[12] = 49
b[13] = 0x4E
b[14] = 49
b[15] = 52
if _, err := conn.Write(b); err != nil {
return err
}
return nil
}
func parseNTPpacket() time.Time {
// the timestamp starts at byte 40 of the received packet and is four bytes,
// this is NTP time (seconds since Jan 1 1900):
t := uint32(b[40])<<24 | uint32(b[41])<<16 | uint32(b[42])<<8 | uint32(b[43])
const seventyYears = 2208988800
return time.Unix(int64(t-seventyYears), 0)
}
func clearBuffer() {
for i := range b {
b[i] = 0
}
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip.String())
}
func message(format string, args ...interface{}) {
println(fmt.Sprintf(format, args...), "\r")
}
+2 -2
View File
@@ -55,8 +55,8 @@ func main() {
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
+100
View File
@@ -0,0 +1,100 @@
// This is a sensor station that uses a ESP32 running nina-fw over SPI.
// It creates a UDP connection you can use to get info to/from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> SPI <--> ESP32
//
package main
import (
"machine"
"strconv"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/wifinina"
)
// access point info
const ssid = ""
const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const hubIP = ""
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// this is the ESP chip that has the WIFININA firmware flashed on it
// these are the default pins for the Arduino Nano33 IoT.
adaptor = &wifinina.Device{
SPI: machine.NINA_SPI,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
)
func main() {
// Init esp8266/esp32
// Configure SPI for 8Mhz, Mode 0, MSB First
machine.NINA_SPI.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
adaptor.Configure()
// connect to access point
connectToAP()
// now make UDP connection
ip := net.ParseIP(hubIP)
raddr := &net.UDPAddr{IP: ip, Port: 2222}
laddr := &net.UDPAddr{Port: 2222}
println("Dialing UDP connection...")
conn, _ := net.DialUDP("udp", laddr, raddr)
for {
// send data
println("Sending data...")
for i := 0; i < 25; i++ {
conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n"))
}
time.Sleep(1000 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting UDP...")
conn.Close()
println("Done.")
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
time.Sleep(2 * time.Second)
ip, _, _, err := adaptor.GetIP()
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip.String())
}
func message(msg string) {
println(msg, "\r")
}
+2 -2
View File
@@ -56,8 +56,8 @@ func main() {
// Configure SPI for 8Mhz, Mode 0, MSB First
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
MOSI: machine.NINA_MOSI,
MISO: machine.NINA_MISO,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
+13 -13
View File
@@ -16,24 +16,24 @@ type transport interface {
// NewSPI returns a pointer to a flash device that uses a SPI peripheral to
// communicate with a serial memory chip.
func NewSPI(spi *machine.SPI, mosi, miso, sck, cs machine.Pin) *Device {
func NewSPI(spi *machine.SPI, sdo, sdi, sck, cs machine.Pin) *Device {
return &Device{
trans: &spiTransport{
spi: spi,
mosi: mosi,
miso: miso,
sck: sck,
ss: cs,
spi: spi,
sdo: sdo,
sdi: sdi,
sck: sck,
ss: cs,
},
}
}
type spiTransport struct {
spi *machine.SPI
mosi machine.Pin
miso machine.Pin
sck machine.Pin
ss machine.Pin
spi *machine.SPI
sdo machine.Pin
sdi machine.Pin
sck machine.Pin
ss machine.Pin
}
func (tr *spiTransport) configure(config *DeviceConfig) {
@@ -53,8 +53,8 @@ func (tr *spiTransport) setClockSpeed(hz uint32) error {
}
tr.spi.Configure(machine.SPIConfig{
Frequency: hz,
MISO: tr.miso,
MOSI: tr.mosi,
SDI: tr.sdi,
SDO: tr.sdo,
SCK: tr.sck,
LSBFirst: false,
Mode: 0,
+8
View File
@@ -0,0 +1,8 @@
module tinygo.org/x/drivers
go 1.15
require (
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/frankban/quicktest v1.10.2
)
+13
View File
@@ -0,0 +1,13 @@
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk=
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
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/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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=
+33 -21
View File
@@ -3,24 +3,32 @@ package gps // import "tinygo.org/x/drivers/gps"
import (
"encoding/hex"
"errors"
"machine"
"strings"
"time"
"tinygo.org/x/drivers"
)
var (
errInvalidNMEASentenceLength = errors.New("invalid NMEA sentence length")
errInvalidNMEAChecksum = errors.New("invalid NMEA sentence checksum")
)
// Device wraps a connection to a GPS device.
type GPSDevice struct {
type Device struct {
buffer []byte
bufIdx int
sentence strings.Builder
uart *machine.UART
bus *machine.I2C
bus drivers.I2C
address uint16
}
// NewUART creates a new UART GPS connection. The UART must already be configured.
func NewUART(uart *machine.UART) GPSDevice {
return GPSDevice{
func NewUART(uart *machine.UART) Device {
return Device{
uart: uart,
buffer: make([]byte, bufferSize),
bufIdx: bufferSize,
@@ -29,8 +37,8 @@ func NewUART(uart *machine.UART) GPSDevice {
}
// NewI2C creates a new I2C GPS connection.
func NewI2C(bus *machine.I2C) GPSDevice {
return GPSDevice{
func NewI2C(bus drivers.I2C) Device {
return Device{
bus: bus,
address: I2C_ADDRESS,
buffer: make([]byte, bufferSize),
@@ -39,17 +47,17 @@ func NewI2C(bus *machine.I2C) GPSDevice {
}
}
// ReadNextSentence returns the next valid NMEA sentence from the GPS device.
func (gps *GPSDevice) NextSentence() (sentence string) {
// NextSentence returns the next valid NMEA sentence from the GPS device.
func (gps *Device) NextSentence() (sentence string, err error) {
sentence = gps.readNextSentence()
for !validSentence(sentence) {
sentence = gps.readNextSentence()
if err = validSentence(sentence); err != nil {
return "", err
}
return sentence
return sentence, nil
}
// readNextSentence returns the next sentence from the GPS device.
func (gps *GPSDevice) readNextSentence() (sentence string) {
func (gps *Device) readNextSentence() (sentence string) {
gps.sentence.Reset()
var b byte = ' '
@@ -69,7 +77,7 @@ func (gps *GPSDevice) readNextSentence() (sentence string) {
return sentence
}
func (gps *GPSDevice) readNextByte() (b byte) {
func (gps *Device) readNextByte() (b byte) {
gps.bufIdx += 1
if gps.bufIdx >= bufferSize {
gps.fillBuffer()
@@ -77,7 +85,7 @@ func (gps *GPSDevice) readNextByte() (b byte) {
return gps.buffer[gps.bufIdx]
}
func (gps *GPSDevice) fillBuffer() {
func (gps *Device) fillBuffer() {
if gps.uart != nil {
gps.uartFillBuffer()
} else {
@@ -85,7 +93,7 @@ func (gps *GPSDevice) fillBuffer() {
}
}
func (gps *GPSDevice) uartFillBuffer() {
func (gps *Device) uartFillBuffer() {
for gps.uart.Buffered() < bufferSize {
time.Sleep(100 * time.Millisecond)
}
@@ -93,7 +101,7 @@ func (gps *GPSDevice) uartFillBuffer() {
gps.bufIdx = 0
}
func (gps *GPSDevice) i2cFillBuffer() {
func (gps *Device) i2cFillBuffer() {
for gps.available() < bufferSize {
time.Sleep(100 * time.Millisecond)
}
@@ -102,7 +110,7 @@ func (gps *GPSDevice) i2cFillBuffer() {
}
// Available returns how many bytes of GPS data are currently available.
func (gps *GPSDevice) available() (available int) {
func (gps *Device) available() (available int) {
var lengthBytes [2]byte
gps.bus.Tx(gps.address, []byte{BYTES_AVAIL_REG}, lengthBytes[0:2])
available = int(lengthBytes[0])*256 + int(lengthBytes[1])
@@ -110,7 +118,7 @@ func (gps *GPSDevice) available() (available int) {
}
// WriteBytes sends data/commands to the GPS device
func (gps *GPSDevice) WriteBytes(bytes []byte) {
func (gps *Device) WriteBytes(bytes []byte) {
if gps.uart != nil {
gps.uart.Write(bytes)
} else {
@@ -119,14 +127,18 @@ func (gps *GPSDevice) WriteBytes(bytes []byte) {
}
// validSentence checks if a sentence has been received uncorrupted
func validSentence(sentence string) bool {
func validSentence(sentence string) error {
if len(sentence) < 4 || sentence[0] != '$' || sentence[len(sentence)-3] != '*' {
return false
return errInvalidNMEASentenceLength
}
var cs byte = 0
for i := 1; i < len(sentence)-3; i++ {
cs ^= sentence[i]
}
checksum := hex.EncodeToString([]byte{cs})
return (checksum[0] == sentence[len(sentence)-2]) && (checksum[1] == sentence[len(sentence)-1])
if (checksum[0] != sentence[len(sentence)-2]) || (checksum[1] != sentence[len(sentence)-1]) {
return errInvalidNMEAChecksum
}
return nil
}
+120 -63
View File
@@ -1,92 +1,131 @@
package gps
import (
"errors"
"strconv"
"strings"
"time"
)
type GPSParser struct {
gpsDevice GPSDevice
var (
errEmptyNMEASentence = errors.New("cannot parse empty NMEA sentence")
errUnknownNMEASentence = errors.New("unsupported NMEA sentence type")
errInvalidGGASentence = errors.New("invalid GGA NMEA sentence")
errInvalidRMCSentence = errors.New("invalid RMC NMEA sentence")
)
// Parser for GPS NMEA sentences.
type Parser struct {
}
// fix is a GPS location fix
// Fix is a GPS location fix
type Fix struct {
Valid bool
Time time.Time
Latitude float32
Longitude float32
Altitude int32
// Valid if the fix was valid.
Valid bool
// Time that the fix was taken, in UTC time.
Time time.Time
// Latitude is the decimal latitude. Negative numbers indicate S.
Latitude float32
// Longitude is the decimal longitude. Negative numbers indicate E.
Longitude float32
// Altitude is only returned for GGA sentences.
Altitude int32
// Satellites is the number of visible satellites, but is only returned for GGA sentences.
Satellites int16
// Speed based on reported movement. Only returned for RMC sentences.
Speed float32
// Heading based on reported movement. Only returned for RMC sentences.
Heading float32
}
func Parser(gpsDevice GPSDevice) GPSParser {
return GPSParser{
gpsDevice: gpsDevice,
// NewParser returns a GPS NMEA Parser.
func NewParser() Parser {
return Parser{}
}
// Parse parses a NMEA sentence looking for fix info.
func (parser *Parser) Parse(sentence string) (fix Fix, err error) {
if sentence == "" {
err = errEmptyNMEASentence
return
}
}
// NextFix returns the next GPS location Fix from the GPS device
func (parser *GPSParser) NextFix() (fix Fix) {
var ggaSentence = nextGGA(parser.gpsDevice)
var ggaFields = strings.Split(ggaSentence, ",")
fix.Altitude = findAltitude(ggaFields)
fix.Satellites = findSatellites(ggaFields)
fix.Longitude = findLongitude(ggaFields)
fix.Latitude = findLatitude(ggaFields)
fix.Time = findTime(ggaFields)
fix.Valid = (fix.Altitude != -99999) && (fix.Satellites > 0)
return fix
}
// nextGGA returns the next GGA type sentence from the GPS device
// $--GGA,,,,,,,,,,,,,,*hh
func nextGGA(gpsDevice GPSDevice) (sentence string) {
for {
sentence = gpsDevice.NextSentence()
if sentence[3:6] == "GGA" {
return sentence
typ := sentence[3:6]
switch typ {
case "GGA":
fields := strings.Split(sentence, ",")
if len(fields) != 15 {
err = errInvalidGGASentence
return
}
fix.Altitude = findAltitude(fields[9])
fix.Satellites = findSatellites(fields[7])
fix.Longitude = findLongitude(fields[4], fields[5])
fix.Latitude = findLatitude(fields[2], fields[3])
fix.Time = findTime(fields[1])
fix.Valid = (fix.Altitude != -99999) && (fix.Satellites > 0)
case "RMC":
fields := strings.Split(sentence, ",")
if len(fields) != 13 {
err = errInvalidRMCSentence
return
}
fix.Longitude = findLongitude(fields[5], fields[6])
fix.Latitude = findLatitude(fields[3], fields[4])
fix.Time = findTime(fields[1])
fix.Speed = findSpeed(fields[7])
fix.Heading = findHeading(fields[8])
fix.Valid = (len(fields[2]) > 0 && fields[2][0:1] == "A")
default:
err = errUnknownNMEASentence
}
return
}
// findTime returns the time from a GGA sentence:
// findTime returns the time from an NMEA sentence:
// $--GGA,hhmmss.ss,,,,,,,,,,,,,*xx
func findTime(ggaFields []string) time.Time {
if len(ggaFields) < 1 || len(ggaFields[1]) < 6 {
func findTime(val string) time.Time {
if len(val) < 6 {
return time.Time{}
}
ts := strings.Builder{}
ts.WriteString(ggaFields[1][0:2])
ts.WriteString(":")
ts.WriteString(ggaFields[1][2:4])
ts.WriteString(":")
ts.WriteString(ggaFields[1][4:6])
var t, _ = time.Parse("15:04:05", ts.String())
h, _ := strconv.ParseInt(val[0:2], 10, 8)
m, _ := strconv.ParseInt(val[2:4], 10, 8)
s, _ := strconv.ParseInt(val[4:6], 10, 8)
ms, _ := strconv.ParseInt(val[7:10], 10, 16)
t := time.Date(0, 0, 0, int(h), int(m), int(s), int(ms), time.UTC)
return t
}
// findAltitude returns the altitude from a GGA sentence:
// findAltitude returns the altitude from an NMEA sentence:
// $--GGA,,,,,,,,,25.8,,,,,*63
func findAltitude(ggaFields []string) int32 {
if len(ggaFields) > 8 && len(ggaFields[9]) > 0 {
var v, _ = strconv.ParseFloat(ggaFields[9], 32)
func findAltitude(val string) int32 {
if len(val) > 0 {
var v, _ = strconv.ParseFloat(val, 32)
return int32(v)
}
return -99999
}
// findLatitude returns the Latitude from a GGA sentence:
// findLatitude returns the Latitude from an NMEA sentence:
// $--GGA,,ddmm.mmmmm,x,,,,,,,,,,,*hh
func findLatitude(ggaFields []string) float32 {
if len(ggaFields) > 2 && len(ggaFields[2]) > 8 {
var dd = ggaFields[2][0:2]
var mm = ggaFields[2][2:]
func findLatitude(val, hemi string) float32 {
if len(val) > 8 {
var dd = val[0:2]
var mm = val[2:]
var d, _ = strconv.ParseFloat(dd, 32)
var m, _ = strconv.ParseFloat(mm, 32)
var v = float32(d + (m / 60))
if ggaFields[3] == "S" {
if hemi == "S" {
v *= -1
}
return v
@@ -94,16 +133,16 @@ func findLatitude(ggaFields []string) float32 {
return 0.0
}
// findLatitude returns the longitude from a GGA sentence:
// findLatitude returns the longitude from an NMEA sentence:
// $--GGA,,,,dddmm.mmmmm,x,,,,,,,,,*hh
func findLongitude(ggaFields []string) float32 {
if len(ggaFields) > 4 && len(ggaFields[4]) > 8 {
var ddd = ggaFields[4][0:3]
var mm = ggaFields[4][3:]
func findLongitude(val, hemi string) float32 {
if len(val) > 8 {
var ddd = val[0:3]
var mm = val[3:]
var d, _ = strconv.ParseFloat(ddd, 32)
var m, _ = strconv.ParseFloat(mm, 32)
var v = float32(d + (m / 60))
if ggaFields[5] == "W" {
if hemi == "W" {
v *= -1
}
return v
@@ -111,14 +150,32 @@ func findLongitude(ggaFields []string) float32 {
return 0.0
}
// findSatellites returns the satellites from a GGA sentence:
// findSatellites returns the satellites from an NMEA sentence:
// $--GGA,,,,,,,nn,,,,,,,*hh
func findSatellites(ggaFields []string) (n int16) {
if len(ggaFields) > 6 && len(ggaFields[7]) > 0 {
var nn = ggaFields[7]
func findSatellites(val string) (n int16) {
if len(val) > 0 {
var nn = val
var v, _ = strconv.ParseInt(nn, 10, 32)
n = int16(v)
return n
}
return 0
}
// findSpeed returns the speed from an RMC NMEA sentence.
func findSpeed(val string) float32 {
if len(val) > 0 {
var v, _ = strconv.ParseFloat(val, 32)
return float32(v)
}
return 0
}
// findHeading returns the speed from an RMC NMEA sentence.
func findHeading(val string) float32 {
if len(val) > 0 {
var v, _ = strconv.ParseFloat(val, 32)
return float32(v)
}
return 0
}
+1 -1
View File
@@ -13,5 +13,5 @@ const (
)
const (
bufferSize = 32
bufferSize = 100
)
+11 -11
View File
@@ -24,25 +24,25 @@ var cfg_gnss_cmd = [...]byte{
0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00,
0x01, 0x01, 0xFC, 0x11}
func FlightMode(gpsDevice GPSDevice) (err error) {
err = sendCommand(gpsDevice, flight_mode_cmd[:])
func FlightMode(d Device) (err error) {
err = sendCommand(d, flight_mode_cmd[:])
return err
}
func SetCfgGNSS(gpsDevice GPSDevice) (err error) {
err = sendCommand(gpsDevice, cfg_gnss_cmd[:])
func SetCfgGNSS(d Device) (err error) {
err = sendCommand(d, cfg_gnss_cmd[:])
return err
}
func sendCommand(gpsDevice GPSDevice, command []byte) (err error) {
gpsDevice.WriteBytes(command)
func sendCommand(d Device, command []byte) (err error) {
d.WriteBytes(command)
start := time.Now()
for time.Now().Sub(start) < 1000 {
if gpsDevice.readNextByte() == '\n' {
if gpsDevice.readNextByte() == 0xB5 {
gpsDevice.readNextByte()
if gpsDevice.readNextByte() == 0x05 {
if gpsDevice.readNextByte() == 0x01 {
if d.readNextByte() == '\n' {
if d.readNextByte() == 0xB5 {
d.readNextByte()
if d.readNextByte() == 0x05 {
if d.readNextByte() == 0x01 {
return
}
}
+243
View File
@@ -0,0 +1,243 @@
// Package hd44780i2c implements a driver for the Hitachi HD44780 LCD display module
// with an I2C adapter.
//
// Datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
//
package hd44780i2c
import (
"errors"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a HD44780 I2C LCD with related data.
type Device struct {
bus drivers.I2C
addr uint8
width uint8
height uint8
cursor cursor
backlight uint8
displayfunction uint8
displaycontrol uint8
displaymode uint8
}
type cursor struct {
x, y uint8
}
// Config for HD44780 I2C LCD.
type Config struct {
Width uint8
Height uint8
Font uint8
CursorOn bool
CursorBlink bool
}
// New creates a new HD44780 I2C LCD 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, addr uint8) Device {
if addr == 0 {
addr = 0x27
}
return Device{
bus: bus,
addr: addr,
}
}
// Configure sets up the display. Display itself and backlight is default on.
func (d *Device) Configure(cfg Config) error {
if cfg.Width == 0 || cfg.Height == 0 {
return errors.New("width and height must be set")
}
d.width = uint8(cfg.Width)
d.height = uint8(cfg.Height)
delayms(50)
d.backlight = BACKLIGHT_ON
d.expanderWrite(0)
delayms(1000)
d.write4bits(0x03 << 4)
delayus(4500)
d.write4bits(0x03 << 4)
delayus(4500)
d.write4bits(0x03 << 4)
delayus(150)
d.write4bits(0x02 << 4)
d.displayfunction = DATA_LENGTH_4BIT | ONE_LINE | FONT_5X8
if d.height > 1 {
d.displayfunction |= TWO_LINE
}
if cfg.Font != 0 && d.height == 1 {
d.displayfunction |= FONT_5X10
}
d.sendCommand(FUNCTION_MODE | d.displayfunction)
d.displaycontrol = DISPLAY_ON | CURSOR_OFF | CURSOR_BLINK_OFF
if cfg.CursorOn {
d.displaycontrol |= CURSOR_ON
}
if cfg.CursorBlink {
d.displaycontrol |= CURSOR_BLINK_ON
}
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
d.ClearDisplay()
d.displaymode = CURSOR_INCREASE | DISPLAY_NO_SHIFT
d.sendCommand(ENTRY_MODE | d.displaymode)
d.Home()
return nil
}
// ClearDisplay clears all texts on the display.
func (d *Device) ClearDisplay() {
d.sendCommand(DISPLAY_CLEAR)
d.cursor.x = 0
d.cursor.y = 0
delayus(2000)
}
// Home sets the cursor back to position (0, 0).
func (d *Device) Home() {
d.sendCommand(CURSOR_HOME)
d.cursor.x = 0
d.cursor.y = 0
delayus(2000)
}
// SetCursor sets the cursor to a specific position (x, y).
//
// if y (row) is set larger than actual rows, it would be set to 0.
func (d *Device) SetCursor(x, y uint8) {
rowOffset := []uint8{0x0, 0x40, 0x14, 0x54}
if y > (d.height - 1) {
y = 0
}
d.cursor.x = x
d.cursor.y = y
d.sendCommand(DDRAM_SET | (x + (rowOffset[y])))
}
// Print prints text on the display (started from current cursor position).
//
// It would automatically break to new line when the text is too long.
// You can also use \n as line breakers.
func (d *Device) Print(data []byte) {
for _, chr := range data {
if chr == '\n' {
d.newLine()
} else {
d.cursor.x++
if d.cursor.x >= d.width {
d.newLine()
}
d.sendData(uint8(rune(chr)))
}
}
}
// CreateCharacter crates custom characters (using data parameter)
// and stores it under CGRAM address (using cgramAddr, 0x0-0x7).
func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
cgramAddr &= 0x7
d.sendCommand(CGRAM_SET | cgramAddr<<3)
for _, dd := range data {
d.sendData(dd)
}
d.SetCursor(d.cursor.x, d.cursor.y)
}
// DisplayOn turns on/off the display.
func (d *Device) DisplayOn(option bool) {
if option {
d.displaycontrol |= DISPLAY_ON
} else {
d.displaycontrol &= ^uint8(DISPLAY_ON)
}
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
}
// CursorOn display/hides the cursor.
func (d *Device) CursorOn(option bool) {
if option {
d.displaycontrol |= CURSOR_ON
} else {
d.displaycontrol &= ^uint8(CURSOR_ON)
}
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
}
// CursorBlink turns on/off the blinking cursor mode.
func (d *Device) CursorBlink(option bool) {
if option {
d.displaycontrol |= CURSOR_BLINK_ON
} else {
d.displaycontrol &= ^uint8(CURSOR_BLINK_ON)
}
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
}
// BacklightOn turns on/off the display backlight.
func (d *Device) BacklightOn(option bool) {
if option {
d.backlight = BACKLIGHT_ON
} else {
d.backlight = BACKLIGHT_OFF
}
d.expanderWrite(0)
}
func (d *Device) newLine() {
d.cursor.x = 0
d.cursor.y++
d.SetCursor(d.cursor.x, d.cursor.y)
}
func delayms(t uint16) {
time.Sleep(time.Millisecond * time.Duration(t))
}
func delayus(t uint16) {
time.Sleep(time.Microsecond * time.Duration(t))
}
func (d *Device) expanderWrite(value uint8) {
d.bus.Tx(uint16(d.addr), []uint8{value | d.backlight}, nil)
}
func (d *Device) pulseEnable(value uint8) {
d.expanderWrite(value | En)
delayus(1)
d.expanderWrite(value & ^uint8(En))
delayus(50)
}
func (d *Device) write4bits(value uint8) {
d.expanderWrite(value)
d.pulseEnable(value)
}
func (d *Device) write(value uint8, mode uint8) {
d.write4bits(uint8(value&0xf0) | mode)
d.write4bits(uint8((value<<4)&0xf0) | mode)
}
func (d *Device) sendCommand(value uint8) {
d.write(value, 0)
}
func (d *Device) sendData(value uint8) {
d.write(value, Rs)
}
+44
View File
@@ -0,0 +1,44 @@
package hd44780i2c
const (
// commands
DISPLAY_CLEAR = 0x01
CURSOR_HOME = 0x02
ENTRY_MODE = 0x04
DISPLAY_ON_OFF = 0x08
CURSOR_DISPLAY_SHIFT = 0x10
FUNCTION_MODE = 0x20
CGRAM_SET = 0x40
DDRAM_SET = 0x80
// flags for display entry mode
// CURSOR_DECREASE = 0x00
CURSOR_INCREASE = 0x02
// DISPLAY_SHIFT = 0x01
DISPLAY_NO_SHIFT = 0x00
// flags for display on/off control
DISPLAY_ON = 0x04
DISPLAY_OFF = 0x00
CURSOR_ON = 0x02
CURSOR_OFF = 0x00
CURSOR_BLINK_ON = 0x01
CURSOR_BLINK_OFF = 0x00
// flags for function set
// DATA_LENGTH_8BIT = 0x10
DATA_LENGTH_4BIT = 0x00
TWO_LINE = 0x08
ONE_LINE = 0x00
FONT_5X10 = 0x04
FONT_5X8 = 0x00
// flags for backlight control
BACKLIGHT_ON = 0x08
BACKLIGHT_OFF = 0x00
En = 0x04 // Enable bit
// Rw = 0x02 // Read/Write bit
Rs = 0x01 // Register select bit
)
+9
View File
@@ -0,0 +1,9 @@
package drivers
// I2C represents an I2C bus. It is notably implemented by the
// machine.I2C type.
type I2C interface {
ReadRegister(addr uint8, r uint8, buf []byte) error
WriteRegister(addr uint8, r uint8, buf []byte) error
Tx(addr uint16, w, r []byte) error
}
+32 -13
View File
@@ -19,12 +19,16 @@ type Device struct {
rotation Rotation
driver driver
x0, x1 int16 // cached address window; prevents useless/expensive
y0, y1 int16 // syscalls to PASET and CASET
dc machine.Pin
cs machine.Pin
rst machine.Pin
rd machine.Pin
}
// Configure prepares display for use
func (d *Device) Configure(config Config) {
if config.Width == 0 {
@@ -37,6 +41,10 @@ func (d *Device) Configure(config Config) {
d.height = config.Height
d.rotation = config.Rotation
// try to pick an initial cache miss for one of the points
d.x0, d.x1 = -(d.width + 1), d.x0
d.y0, d.y1 = -(d.height + 1), d.y0
output := machine.PinConfig{machine.PinOutput}
// configure chip select if there is one
@@ -138,6 +146,7 @@ func (d *Device) Display() error {
return nil
}
// DrawRGBBitmap copies an RGB bitmap to the internal buffer at given coordinates
func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
k, i := d.Size()
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
@@ -151,7 +160,7 @@ func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
return nil
}
// FillRectangle fills a rectangle at a 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 {
k, i := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
@@ -166,7 +175,7 @@ func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
return nil
}
// DrawRectangle fills a rectangle at a given coordinates with a color
// DrawRectangle draws a rectangle at given coordinates with a color
func (d *Device) DrawRectangle(x, y, w, h int16, c color.RGBA) error {
if err := d.DrawFastHLine(x, x+w-1, y, c); err != nil {
return err
@@ -208,6 +217,7 @@ func (d *Device) FillScreen(c color.RGBA) {
}
}
// GetRotation returns the current rotation of the device
func (d *Device) GetRotation() Rotation {
return d.rotation
}
@@ -229,7 +239,8 @@ func (d *Device) SetRotation(rotation Rotation) {
d.rotation = rotation
}
// SetScrollWindow sets an area to scroll with fixed top and bottom 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
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
d.sendCommand(VSCRDEF, []uint8{
uint8(topFixedArea >> 8), uint8(topFixedArea),
@@ -244,7 +255,7 @@ func (d *Device) SetScroll(line int16) {
d.sendCommand(VSCRSADD, []uint8{uint8(line >> 8), uint8(line)})
}
// SpotScroll returns the display to its normal state
// StopScroll returns the display to its normal state
func (d *Device) StopScroll() {
d.sendCommand(NORON, nil)
}
@@ -253,12 +264,20 @@ func (d *Device) StopScroll() {
func (d *Device) setWindow(x, y, w, h int16) {
//x += d.columnOffset
//y += d.rowOffset
d.sendCommand(CASET, []uint8{
uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1),
})
d.sendCommand(PASET, []uint8{
uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1),
})
x1 := x + w - 1
if x != d.x0 || x1 != d.x1 {
d.sendCommand(CASET, []uint8{
uint8(x >> 8), uint8(x), uint8(x1 >> 8), uint8(x1),
})
d.x0, d.x1 = x, x1
}
y1 := y + h - 1
if y != d.y0 || y1 != d.y1 {
d.sendCommand(PASET, []uint8{
uint8(y >> 8), uint8(y), uint8(y1 >> 8), uint8(y1),
})
d.y0, d.y1 = y, y1
}
d.sendCommand(RAMWR, nil)
}
@@ -281,15 +300,15 @@ func (d *Device) sendCommand(cmd byte, data []byte) {
d.dc.Low()
d.driver.write8(cmd)
d.dc.High()
for _, b := range data {
d.driver.write8(b)
}
d.driver.write8sl(data)
d.endWrite()
}
type driver interface {
configure(config *Config)
write8(b byte)
write8n(b byte, n int)
write8sl(b []byte)
write16(data uint16)
write16n(data uint16, n int)
write16sl(data []uint16)
+28 -6
View File
@@ -5,14 +5,14 @@ package ili9341
import (
"machine"
"runtime/volatile"
"unsafe"
)
type parallelDriver struct {
d0 machine.Pin
wr machine.Pin
setPort *uint32
setMask uint32
setPort *uint8
clrPort *uint32
clrMask uint32
@@ -46,8 +46,12 @@ func (pd *parallelDriver) configure(config *Config) {
pd.wr.Configure(output)
pd.wr.High()
pd.setPort, _ = pd.d0.PortMaskSet()
pd.setMask = uint32(pd.d0) & 0x1f
// Calculates the address of the OUT register from the OUTSET register and obtains an address that allows 8-bit access.
// OUT : offset = 0x10
// OUTSET : offset = 0x18
setPort, _ := pd.d0.PortMaskSet()
setMask := uint32(pd.d0) & 0x1f
pd.setPort = (*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(setPort)) - uintptr(8) + uintptr(setMask/8)))
pd.clrPort, _ = (pd.d0).PortMaskClear()
pd.clrMask = 0xFF << uint32(pd.d0)
@@ -58,12 +62,30 @@ func (pd *parallelDriver) configure(config *Config) {
//go:inline
func (pd *parallelDriver) write8(b byte) {
volatile.StoreUint32(pd.clrPort, pd.clrMask)
volatile.StoreUint32(pd.setPort, uint32(b)<<pd.setMask)
volatile.StoreUint8(pd.setPort, uint8(b))
pd.wrx()
}
//go:inline
func (pd *parallelDriver) wrx() {
volatile.StoreUint32(pd.wrPortClr, pd.wrMaskClr)
volatile.StoreUint32(pd.wrPortSet, pd.wrMaskSet)
}
//go:inline
func (pd *parallelDriver) write8n(b byte, n int) {
for i := 0; i < n; i++ {
pd.write8(b)
}
}
//go:inline
func (pd *parallelDriver) write8sl(b []byte) {
for i := 0; i < len(b); i++ {
pd.write8(b[i])
}
}
//go:inline
func (pd *parallelDriver) write16(data uint16) {
pd.write8(byte(data >> 8))
+116
View File
@@ -0,0 +1,116 @@
// +build atsamd21
package ili9341
import (
"device/sam"
"machine"
)
type spiDriver struct {
bus machine.SPI
}
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
return &Device{
dc: dc,
cs: cs,
rst: rst,
rd: machine.NoPin,
driver: &spiDriver{
bus: bus,
},
}
}
func (pd *spiDriver) configure(config *Config) {
}
func (pd *spiDriver) write8(b byte) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPI_CTRLB_RXEN)
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(b))
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPI_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write8n(b byte, n int) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPI_CTRLB_RXEN)
for i, c := 0, n; i < c; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(b))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPI_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write8sl(b []byte) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPI_CTRLB_RXEN)
for i, c := 0, len(b); i < c; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(b[i]))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPI_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write16(data uint16) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPI_CTRLB_RXEN)
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data >> 8)))
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data)))
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPI_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write16n(data uint16, n int) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPI_CTRLB_RXEN)
for i := 0; i < n; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data >> 8)))
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data)))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPI_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write16sl(data []uint16) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPI_CTRLB_RXEN)
for i, c := 0, len(data); i < c; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data[i] >> 8)))
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data[i])))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPI_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPI_SYNCBUSY_CTRLB) {
}
}
+116
View File
@@ -0,0 +1,116 @@
// +build atsamd51
package ili9341
import (
"device/sam"
"machine"
)
type spiDriver struct {
bus machine.SPI
}
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
return &Device{
dc: dc,
cs: cs,
rst: rst,
rd: machine.NoPin,
driver: &spiDriver{
bus: bus,
},
}
}
func (pd *spiDriver) configure(config *Config) {
}
func (pd *spiDriver) write8(b byte) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(b))
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write8n(b byte, n int) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for i, c := 0, n; i < c; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(b))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write8sl(b []byte) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for i, c := 0, len(b); i < c; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(b[i]))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write16(data uint16) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data >> 8)))
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data)))
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write16n(data uint16, n int) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for i := 0; i < n; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data >> 8)))
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data)))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
}
}
func (pd *spiDriver) write16sl(data []uint16) {
pd.bus.Bus.CTRLB.ClearBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for i, c := 0, len(data); i < c; i++ {
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data[i] >> 8)))
for !pd.bus.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
pd.bus.Bus.DATA.Set(uint32(uint8(data[i])))
}
pd.bus.Bus.CTRLB.SetBits(sam.SERCOM_SPIM_CTRLB_RXEN)
for pd.bus.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_CTRLB) {
}
}
+126
View File
@@ -0,0 +1,126 @@
// Package lis2mdl implements a driver for the LIS2MDL,
// a magnetic sensor which is included on BBC micro:bit v1.5.
//
// Datasheet: https://www.st.com/resource/en/datasheet/lis2mdl.pdf
//
package lis2mdl // import "tinygo.org/x/drivers/lis2mdl"
import (
"math"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a LIS2MDL device.
type Device struct {
bus drivers.I2C
Address uint8
PowerMode uint8
SystemMode uint8
DataRate uint8
}
// Configuration for LIS2MDL device.
type Configuration struct {
PowerMode uint8
SystemMode uint8
DataRate uint8
}
// New creates a new LIS2MDL 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: bus, Address: ADDRESS}
}
// Connected returns whether LIS2MDL sensor has been found.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
return data[0] == 0x40
}
// Configure sets up the LIS2MDL device for communication.
func (d *Device) Configure(cfg Configuration) {
if cfg.PowerMode != 0 {
d.PowerMode = cfg.PowerMode
} else {
d.PowerMode = POWER_NORMAL
}
if cfg.DataRate != 0 {
d.DataRate = cfg.DataRate
} else {
d.DataRate = DATARATE_100HZ
}
if cfg.SystemMode != 0 {
d.SystemMode = cfg.SystemMode
} else {
d.SystemMode = SYSTEM_CONTINUOUS
}
cmd := []byte{0}
// reset
cmd[0] = byte(1 << 5)
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
time.Sleep(100 * time.Millisecond)
// reboot
cmd[0] = byte(1 << 6)
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
time.Sleep(100 * time.Millisecond)
// bdu
cmd[0] = byte(1 << 4)
d.bus.WriteRegister(uint8(d.Address), CFG_REG_C, cmd)
// Temperature compensation is on for magnetic sensor (0x80)
cmd[0] = byte(0x80)
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
// speed
cmd[0] = byte(0x80 | d.DataRate)
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
}
// ReadMagneticField reads the current magnetic field from the device and returns
// it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
func (d *Device) ReadMagneticField() (x int32, y int32, z int32) {
// turn back on read mode, even though it is supposed to be continuous?
cmd := []byte{0}
cmd[0] = byte(0x80 | d.PowerMode<<4 | d.DataRate<<2 | d.SystemMode)
d.bus.WriteRegister(uint8(d.Address), CFG_REG_A, cmd)
time.Sleep(10 * time.Millisecond)
data := make([]byte, 6)
d.bus.ReadRegister(uint8(d.Address), OUTX_L_REG, data)
x = int32(int16((uint16(data[0]) << 8) | uint16(data[1])))
y = int32(int16((uint16(data[2]) << 8) | uint16(data[3])))
z = int32(int16((uint16(data[4]) << 8) | uint16(data[5])))
return
}
// ReadCompass reads the current compass heading from the device and returns
// it in degrees. When the z axis is pointing straight to Earth and
// the y axis is pointing to North, the heading would be zero.
//
// However, the heading may be off due to electronic compasses would be effected
// by strong magnetic fields and require constant calibration.
func (d *Device) ReadCompass() (h int32) {
x, y, _ := d.ReadMagneticField()
xf, yf := float64(x)*0.15, float64(y)*0.15
rh := (math.Atan2(yf, xf) * 180) / math.Pi
if rh < 0 {
rh = 360 + rh
}
return int32(rh)
}
+59
View File
@@ -0,0 +1,59 @@
package lis2mdl
import (
"testing"
qt "github.com/frankban/quicktest"
"tinygo.org/x/drivers/tester"
)
func TestDefaultI2CAddress(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
dev := New(bus)
c.Assert(dev.Address, qt.Equals, uint8(ADDRESS))
}
func TestWhoAmI(t *testing.T) {
c := qt.New(t)
bus := tester.NewI2CBus(c)
fake := tester.NewI2CDevice(c, ADDRESS)
fake.SetupRegisters(defaultRegisters())
bus.AddDevice(fake)
dev := New(bus)
c.Assert(dev.Connected(), qt.Equals, true)
fake.SetupRegister(WHO_AM_I, 0x99)
c.Assert(dev.Connected(), qt.Equals, false)
}
// defaultRegisters returns the default values for all of the device's registers.
// see table 22 on page 27 of the datasheet.
func defaultRegisters() []uint8 {
return []uint8{
OFFSET_X_REG_L: 0,
OFFSET_X_REG_H: 0,
OFFSET_Y_REG_L: 0,
OFFSET_Y_REG_H: 0,
OFFSET_Z_REG_L: 0,
OFFSET_Z_REG_H: 0,
WHO_AM_I: 0x40,
CFG_REG_A: 0x03,
CFG_REG_B: 0,
CFG_REG_C: 0,
INT_CRTL_REG: 0xE0,
INT_SOURCE_REG: 0,
INT_THS_L_REG: 0,
INT_THS_H_REG: 0,
STATUS_REG: 0,
OUTX_L_REG: 0,
OUTX_H_REG: 0,
OUTY_L_REG: 0,
OUTY_H_REG: 0,
OUTZ_L_REG: 0,
OUTZ_H_REG: 0,
TEMP_OUT_L_REG: 0,
TEMP_OUT_H_REG: 0,
}
}
+45
View File
@@ -0,0 +1,45 @@
package lis2mdl
const (
// Constants/addresses used for I2C.
ADDRESS = 0x1E
// magnetic sensor registers.
OFFSET_X_REG_L = 0x45
OFFSET_X_REG_H = 0x46
OFFSET_Y_REG_L = 0x47
OFFSET_Y_REG_H = 0x48
OFFSET_Z_REG_L = 0x49
OFFSET_Z_REG_H = 0x4A
WHO_AM_I = 0x4F
CFG_REG_A = 0x60
CFG_REG_B = 0x61
CFG_REG_C = 0x62
INT_CRTL_REG = 0x63
INT_SOURCE_REG = 0x64
INT_THS_L_REG = 0x65
INT_THS_H_REG = 0x66
STATUS_REG = 0x67
OUTX_L_REG = 0x68
OUTX_H_REG = 0x69
OUTY_L_REG = 0x6A
OUTY_H_REG = 0x6B
OUTZ_L_REG = 0x6C
OUTZ_H_REG = 0x6D
TEMP_OUT_L_REG = 0x6E
TEMP_OUT_H_REG = 0x6F
// magnetic sensor power mode.
POWER_NORMAL = 0x00 // default
POWER_LOW = 0x01
// magnetic sensor operate mode.
SYSTEM_CONTINUOUS = 0x00 // default
SYSTEM_SINGLE = 0x01
// magnetic sensor data rate
DATARATE_10HZ = 0x00 // default
DATARATE_20HZ = 0x01
DATARATE_50HZ = 0x02
DATARATE_100HZ = 0x03
)
+3 -5
View File
@@ -4,13 +4,11 @@
//
package lis3dh // import "tinygo.org/x/drivers/lis3dh"
import (
"machine"
)
import "tinygo.org/x/drivers"
// Device wraps an I2C connection to a LIS3DH device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
r Range
}
@@ -18,7 +16,7 @@ type Device struct {
// New creates a new LIS3DH connection. The I2C bus must already be configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: Address0}
}
+204
View File
@@ -0,0 +1,204 @@
// Package lsm303agr implements a driver for the LSM303AGR,
// a 3 axis accelerometer/magnetic sensor which is included on BBC micro:bits v1.5.
//
// Datasheet: https://www.st.com/resource/en/datasheet/lsm303agr.pdf
//
package lsm303agr // import "tinygo.org/x/drivers/lsm303agr"
import (
"math"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a LSM303AGR device.
type Device struct {
bus drivers.I2C
AccelAddress uint8
MagAddress uint8
AccelPowerMode uint8
AccelRange uint8
AccelDataRate uint8
MagPowerMode uint8
MagSystemMode uint8
MagDataRate uint8
}
// Configuration for LSM303AGR device.
type Configuration struct {
AccelPowerMode uint8
AccelRange uint8
AccelDataRate uint8
MagPowerMode uint8
MagSystemMode uint8
MagDataRate uint8
}
// New creates a new LSM303AGR 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: bus, AccelAddress: ACCEL_ADDRESS, MagAddress: MAG_ADDRESS}
}
// Connected returns whether both sensor on LSM303AGR has been found.
// It does two "who am I" requests and checks the responses.
func (d *Device) Connected() bool {
data1, data2 := []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_WHO_AM_I, data1)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_WHO_AM_I, data2)
return data1[0] == 0x33 && data2[0] == 0x40
}
// Configure sets up the LSM303AGR device for communication.
func (d *Device) Configure(cfg Configuration) {
if cfg.AccelDataRate != 0 {
d.AccelDataRate = cfg.AccelDataRate
} else {
d.AccelDataRate = ACCEL_DATARATE_100HZ
}
if cfg.AccelPowerMode != 0 {
d.AccelPowerMode = cfg.AccelPowerMode
} else {
d.AccelPowerMode = ACCEL_POWER_NORMAL
}
if cfg.AccelRange != 0 {
d.AccelRange = cfg.AccelRange
} else {
d.AccelRange = ACCEL_RANGE_2G
}
if cfg.MagPowerMode != 0 {
d.MagPowerMode = cfg.MagPowerMode
} else {
d.MagPowerMode = MAG_POWER_NORMAL
}
if cfg.MagDataRate != 0 {
d.MagDataRate = cfg.MagDataRate
} else {
d.MagDataRate = MAG_DATARATE_10HZ
}
if cfg.MagSystemMode != 0 {
d.MagSystemMode = cfg.MagSystemMode
} else {
d.MagSystemMode = MAG_SYSTEM_CONTINUOUS
}
cmd := []byte{0}
cmd[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, cmd)
cmd[0] = byte(0x80 | d.AccelRange<<4)
d.bus.WriteRegister(uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, cmd)
cmd[0] = byte(0xC0)
d.bus.WriteRegister(uint8(d.AccelAddress), TEMP_CFG_REG_A, cmd)
// Temperature compensation is on for magnetic sensor
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, cmd)
}
// 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() (x int32, y int32, z int32) {
data1, data2, data3, data4, data5, data6 := []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_X_H_A, data1)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_X_L_A, data2)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Y_H_A, data3)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Y_L_A, data4)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Z_H_A, data5)
d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_Z_L_A, data6)
rangeFactor := int16(0)
switch d.AccelRange {
case ACCEL_RANGE_2G:
rangeFactor = 1
case ACCEL_RANGE_4G:
rangeFactor = 2
case ACCEL_RANGE_8G:
rangeFactor = 4
case ACCEL_RANGE_16G:
rangeFactor = 12 // the readings in 16G are a bit lower
}
x = int32(int32(int16((uint16(data1[0])<<8|uint16(data2[0])))>>4*rangeFactor) * 1000000 / 1024)
y = int32(int32(int16((uint16(data3[0])<<8|uint16(data4[0])))>>4*rangeFactor) * 1000000 / 1024)
z = int32(int32(int16((uint16(data5[0])<<8|uint16(data6[0])))>>4*rangeFactor) * 1000000 / 1024)
return
}
// ReadPitchRoll reads the current pitch and roll angles from the device and
// returns it in micro-degrees. When the z axis is pointing straight to Earth
// the returned values of pitch and roll would be zero.
func (d *Device) ReadPitchRoll() (pitch int32, roll int32) {
x, y, z := d.ReadAcceleration()
xf, yf, zf := float64(x), float64(y), float64(z)
pitch = int32((math.Round(math.Atan2(yf, math.Sqrt(math.Pow(xf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
roll = int32((math.Round(math.Atan2(xf, math.Sqrt(math.Pow(yf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
return
}
// ReadMagneticField reads the current magnetic field from the device and returns
// it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
func (d *Device) ReadMagneticField() (x int32, y int32, z int32) {
if d.MagSystemMode == MAG_SYSTEM_SINGLE {
cmd := []byte{0}
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
d.bus.WriteRegister(uint8(d.MagAddress), MAG_MR_REG_M, cmd)
}
data1, data2, data3, data4, data5, data6 := []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_X_H_M, data1)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_X_L_M, data2)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Y_H_M, data3)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Y_L_M, data4)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Z_H_M, data5)
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_Z_L_M, data6)
x = int32(int16((uint16(data1[0])<<8 | uint16(data2[0]))))
y = int32(int16((uint16(data3[0])<<8 | uint16(data4[0]))))
z = int32(int16((uint16(data5[0])<<8 | uint16(data6[0]))))
return
}
// ReadCompass reads the current compass heading from the device and returns
// it in micro-degrees. When the z axis is pointing straight to Earth and
// the y axis is pointing to North, the heading would be zero.
//
// However, the heading may be off due to electronic compasses would be effected
// by strong magnetic fields and require constant calibration.
func (d *Device) ReadCompass() (h int32) {
x, y, _ := d.ReadMagneticField()
xf, yf := float64(x), float64(y)
h = int32(float32((180/math.Pi)*math.Atan2(yf, xf)) * 1000000)
return
}
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (c int32, e error) {
data1, data2 := []byte{0}, []byte{0}
d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_H_A, data1)
d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L_A, data2)
t := int16((uint16(data1[0])<<8 | uint16(data2[0]))) >> 4 // temperature offsef from 25 °C
c = int32((float32(25) + float32(t)/8) * 1000)
e = nil
return
}
+68
View File
@@ -0,0 +1,68 @@
package lsm303agr
const (
// Constants/addresses used for I2C.
ACCEL_ADDRESS = 0x19
MAG_ADDRESS = 0x1E
// accelerometer registers.
ACCEL_WHO_AM_I = 0x0F
ACCEL_CTRL_REG1_A = 0x20
ACCEL_CTRL_REG4_A = 0x23
ACCEL_OUT_X_L_A = 0x28
ACCEL_OUT_X_H_A = 0x29
ACCEL_OUT_Y_L_A = 0x2A
ACCEL_OUT_Y_H_A = 0x2B
ACCEL_OUT_Z_L_A = 0x2C
ACCEL_OUT_Z_H_A = 0x2D
// magnetic sensor registers.
MAG_WHO_AM_I = 0x4F
MAG_MR_REG_M = 0x60
MAG_OUT_X_L_M = 0x68
MAG_OUT_X_H_M = 0x69
MAG_OUT_Y_L_M = 0x6A
MAG_OUT_Y_H_M = 0x6B
MAG_OUT_Z_L_M = 0x6C
MAG_OUT_Z_H_M = 0x6D
// temperature sensor registers.
TEMP_CFG_REG_A = 0x1F
OUT_TEMP_L_A = 0x0C
OUT_TEMP_H_A = 0x0D
// accelerometer power mode.
ACCEL_POWER_NORMAL = 0x00 // default
ACCEL_POWER_LOW = 0x08
// accelerometer range.
ACCEL_RANGE_2G = 0x00 // default
ACCEL_RANGE_4G = 0x01
ACCEL_RANGE_8G = 0x02
ACCEL_RANGE_16G = 0x03
// accelerometer data rate.
ACCEL_DATARATE_1HZ = 0x01
ACCEL_DATARATE_10HZ = 0x02
ACCEL_DATARATE_25HZ = 0x03
ACCEL_DATARATE_50HZ = 0x04
ACCEL_DATARATE_100HZ = 0x05 // default
ACCEL_DATARATE_200HZ = 0x06
ACCEL_DATARATE_400HZ = 0x07
ACCEL_DATARATE_1344HZ = 0x09 // 5376Hz in low-power mode
// magnetic sensor power mode.
MAG_POWER_NORMAL = 0x00 // default
MAG_POWER_LOW = 0x01
// magnetic sensor operate mode.
MAG_SYSTEM_CONTINUOUS = 0x00 // default
MAG_SYSTEM_SINGLE = 0x01
// magnetic sensor data rate
MAG_DATARATE_10HZ = 0x00 // default
MAG_DATARATE_20HZ = 0x01
MAG_DATARATE_50HZ = 0x02
MAG_DATARATE_100HZ = 0x03
)
+3 -5
View File
@@ -5,9 +5,7 @@
//
package lsm6ds3 // import "tinygo.org/x/drivers/lsm6ds3"
import (
"machine"
)
import "tinygo.org/x/drivers"
type AccelRange uint8
type AccelSampleRate uint8
@@ -18,7 +16,7 @@ type GyroSampleRate uint8
// Device wraps an I2C connection to a LSM6DS3 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
accelRange AccelRange
accelSampleRate AccelSampleRate
@@ -44,7 +42,7 @@ type Configuration struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus: bus, Address: Address}
}
+3 -5
View File
@@ -5,13 +5,11 @@
//
package mag3110 // import "tinygo.org/x/drivers/mag3110"
import (
"machine"
)
import "tinygo.org/x/drivers"
// Device wraps an I2C connection to a MAG3110 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
}
@@ -19,7 +17,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus, Address}
}
+3 -5
View File
@@ -6,13 +6,11 @@
//
package mma8653 // import "tinygo.org/x/drivers/mma8653"
import (
"machine"
)
import "tinygo.org/x/drivers"
// Device wraps an I2C connection to a MMA8653 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
sensitivity Sensitivity
}
@@ -21,7 +19,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus, Address, Sensitivity2G}
}
+3 -5
View File
@@ -7,13 +7,11 @@
//
package mpu6050 // import "tinygo.org/x/drivers/mpu6050"
import (
"machine"
)
import "tinygo.org/x/drivers"
// Device wraps an I2C connection to a MPU6050 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
}
@@ -21,7 +19,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{bus, Address}
}
+5 -5
View File
@@ -91,11 +91,11 @@ const (
EXT_SENS_DATA_22 = 0x5F
EXT_SENS_DATA_23 = 0x60
// I2C slave data out
I2C_SLV0_DO = 0x63
I2C_SLV1_DO = 0x64
I2C_SLV2_DO = 0x65
I2C_SLV3_DO = 0x66
// I2C peripheral data out
I2C_PER0_DO = 0x63
I2C_PER1_DO = 0x64
I2C_PER2_DO = 0x65
I2C_PER3_DO = 0x66
I2C_MST_DELAY_CT = 0x67
SIGNAL_PATH_RES = 0x68 // Signal path reset
+4 -3
View File
@@ -7,13 +7,14 @@
package sht3x // import "tinygo.org/x/drivers/sht3x"
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a SHT31 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
}
@@ -22,7 +23,7 @@ type Device struct {
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: AddressA,
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an SPI connection.
@@ -30,7 +32,7 @@ type Config struct {
}
type I2CBus struct {
wire machine.I2C
wire drivers.I2C
Address uint16
}
@@ -50,7 +52,7 @@ type Buser interface {
type VccMode uint8
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
func NewI2C(bus machine.I2C) Device {
func NewI2C(bus drivers.I2C) Device {
return Device{
bus: &I2CBus{
wire: bus,
+1 -1
View File
@@ -220,7 +220,7 @@ func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
if x0 > x1 {
x0, x1 = x1, x0
}
d.FillRectangle(x0, y, x1-x0+1, y, c)
d.FillRectangle(x0, y, x1-x0+1, 1, c)
}
// FillScreen fills the screen with a given color
+38
View File
@@ -0,0 +1,38 @@
package ssd1351
// Commands
const (
SET_COLUMN_ADDRESS = 0x15
SET_ROW_ADDRESS = 0x75
WRITE_RAM = 0x5C
READ_RAM = 0x5D
SET_REMAP_COLORDEPTH = 0xA0
SET_DISPLAY_START_LINE = 0xA1
SET_DISPLAY_OFFSET = 0xA2
SET_DISPLAY_MODE_ALLOFF = 0xA4
SET_DISPLAY_MODE_ALLON = 0xA5
SET_DISPLAY_MODE_RESET = 0xA6
SET_DISPLAY_MODE_INVERT = 0xA7
FUNCTION_SELECTION = 0xAB
SLEEP_MODE_DISPLAY_OFF = 0xAE
SLEEP_MODE_DISPLAY_ON = 0xAF
SET_PHASE_PERIOD = 0xB1
ENHANCED_DRIVING_SCHEME = 0xB2
SET_FRONT_CLOCK_DIV = 0xB3
SET_SEGMENT_LOW_VOLTAGE = 0xB4
SET_GPIO = 0xB5
SET_SECOND_PRECHARGE_PERIOD = 0xB6
GRAY_SCALE_LOOKUP = 0xB8
LINEAR_LUT = 0xB9
SET_PRECHARGE_VOLTAGE = 0xBB
SET_VCOMH_VOLTAGE = 0xBE
SET_CONTRAST = 0xC1
MASTER_CONTRAST = 0xC7
SET_MUX_RATIO = 0xCA
NOP0 = 0xD1
NOP1 = 0xE3
SET_COMMAND_LOCK = 0xFD
HORIZONTAL_SCROLL = 0x96
STOP_MOVING = 0x9E
START_MOVING = 0x9F
)
+297
View File
@@ -0,0 +1,297 @@
// Package ssd1351 implements a driver for the SSD1351 OLED color displays.
//
// Datasheet: https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf
//
package ssd1351 // import "tinygo.org/x/drivers/ssd1351"
import (
"errors"
"image/color"
"machine"
"time"
)
var (
errDrawingOutOfBounds = errors.New("rectangle coordinates outside display area")
errBufferSizeMismatch = errors.New("buffer length does not match with rectangle size")
)
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
enPin machine.Pin
rwPin machine.Pin
width int16
height int16
rowOffset int16
columnOffset int16
bufferLength int16
}
// Config is the configuration for the display
type Config struct {
Width int16
Height int16
RowOffset int16
ColumnOffset int16
}
// New creates a new SSD1351 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device {
return Device{
bus: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
enPin: enPin,
rwPin: rwPin,
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
if cfg.Width == 0 {
cfg.Width = 128
}
if cfg.Height == 0 {
cfg.Height = 128
}
d.width = cfg.Width
d.height = cfg.Height
d.rowOffset = cfg.RowOffset
d.columnOffset = cfg.ColumnOffset
d.bufferLength = d.width
if d.height > d.width {
d.bufferLength = d.height
}
// configure GPIO pins
d.dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.enPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.rwPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
// reset the device
d.resetPin.High()
time.Sleep(100 * time.Millisecond)
d.resetPin.Low()
time.Sleep(100 * time.Millisecond)
d.resetPin.High()
time.Sleep(200 * time.Millisecond)
d.rwPin.Low()
d.dcPin.Low()
d.enPin.High()
// Initialization
d.Command(SET_COMMAND_LOCK)
d.Data(0x12)
d.Command(SET_COMMAND_LOCK)
d.Data(0xB1)
d.Command(SLEEP_MODE_DISPLAY_OFF)
d.Command(SET_FRONT_CLOCK_DIV)
d.Data(0xF1)
d.Command(SET_MUX_RATIO)
d.Data(0x7F)
d.Command(SET_REMAP_COLORDEPTH)
d.Data(0x72)
d.Command(SET_COLUMN_ADDRESS)
d.Data(0x00)
d.Data(0x7F)
d.Command(SET_ROW_ADDRESS)
d.Data(0x00)
d.Data(0x7F)
d.Command(SET_DISPLAY_START_LINE)
d.Data(0x00)
d.Command(SET_DISPLAY_OFFSET)
d.Data(0x00)
d.Command(SET_GPIO)
d.Data(0x00)
d.Command(FUNCTION_SELECTION)
d.Data(0x01)
d.Command(SET_PHASE_PERIOD)
d.Data(0x32)
d.Command(SET_SEGMENT_LOW_VOLTAGE)
d.Data(0xA0)
d.Data(0xB5)
d.Data(0x55)
d.Command(SET_PRECHARGE_VOLTAGE)
d.Data(0x17)
d.Command(SET_VCOMH_VOLTAGE)
d.Data(0x05)
d.Command(SET_CONTRAST)
d.Data(0xC8)
d.Data(0x80)
d.Data(0xC8)
d.Command(MASTER_CONTRAST)
d.Data(0x0F)
d.Command(SET_SECOND_PRECHARGE_PERIOD)
d.Data(0x01)
d.Command(SET_DISPLAY_MODE_RESET)
d.Command(SLEEP_MODE_DISPLAY_ON)
}
// Display does nothing, there's no buffer as it might be too big for some boards
func (d *Device) Display() error {
return nil
}
// SetPixel sets a pixel in the buffer
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
if x < 0 || y < 0 || x >= d.width || y >= d.height {
return
}
d.FillRectangle(x, y, 1, 1, c)
}
// setWindow prepares the screen memory to be modified at given coordinates
func (d *Device) setWindow(x, y, w, h int16) {
x += d.columnOffset
y += d.rowOffset
d.Command(SET_COLUMN_ADDRESS)
d.Tx([]byte{uint8(x), uint8(x + w - 1)}, false)
d.Command(SET_ROW_ADDRESS)
d.Tx([]byte{uint8(y), uint8(y + h - 1)}, false)
d.Command(WRITE_RAM)
}
// FillRectangle fills a rectangle at given coordinates with a color
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= d.width || (x+width) > d.width || y >= d.height || (y+height) > d.height {
return errDrawingOutOfBounds
}
d.setWindow(x, y, width, height)
c565 := RGBATo565(c)
c1 := uint8(c565 >> 8)
c2 := uint8(c565)
dim := int16(width * height)
if d.bufferLength < dim {
dim = d.bufferLength
}
data := make([]uint8, dim*2)
for i := int16(0); i < dim; i++ {
data[i*2] = c1
data[i*2+1] = c2
}
dim = int16(width * height)
for dim > 0 {
if dim >= d.bufferLength {
d.Tx(data, false)
} else {
d.Tx(data[:dim*2], false)
}
dim -= d.bufferLength
}
return nil
}
// FillRectangleWithBuffer fills a rectangle at given coordinates with a buffer
func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= d.width || (x+width) > d.width || y >= d.height || (y+height) > d.height {
return errDrawingOutOfBounds
}
dim := int16(width * height)
l := int16(len(buffer))
if dim != l {
return errBufferSizeMismatch
}
d.setWindow(x, y, width, height)
bl := dim
if d.bufferLength < dim {
bl = d.bufferLength
}
data := make([]uint8, bl*2)
offset := int16(0)
for dim > 0 {
for i := int16(0); i < bl; i++ {
if offset+i < l {
c565 := RGBATo565(buffer[offset+i])
c1 := uint8(c565 >> 8)
c2 := uint8(c565)
data[i*2] = c1
data[i*2+1] = c2
}
}
if dim >= d.bufferLength {
d.Tx(data, false)
} else {
d.Tx(data[:dim*2], false)
}
dim -= d.bufferLength
offset += d.bufferLength
}
return nil
}
// DrawFastVLine draws a vertical line faster than using SetPixel
func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) {
if y0 > y1 {
y0, y1 = y1, y0
}
d.FillRectangle(x, y0, 1, y1-y0+1, c)
}
// DrawFastHLine draws a horizontal line faster than using SetPixel
func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
if x0 > x1 {
x0, x1 = x1, x0
}
d.FillRectangle(x0, y, x1-x0+1, 1, c)
}
// FillScreen fills the screen with a given color
func (d *Device) FillScreen(c color.RGBA) {
d.FillRectangle(0, 0, d.width, d.height, c)
}
// SetContrast sets the three contrast values (A, B & C)
func (d *Device) SetContrast(contrastA, contrastB, contrastC uint8) {
d.Command(SET_CONTRAST)
d.Tx([]byte{contrastA, contrastB, contrastC}, false)
}
// Command sends a command byte to the display
func (d *Device) Command(command uint8) {
d.Tx([]byte{command}, true)
}
// Data sends a data byte to the display
func (d *Device) Data(data uint8) {
d.Tx([]byte{data}, false)
}
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.dcPin.Set(!isCommand)
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
}
// Size returns the current size of the display
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
// 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))
}
+1 -1
View File
@@ -323,7 +323,7 @@ func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
if x0 > x1 {
x0, x1 = x1, x0
}
d.FillRectangle(x0, y, x1-x0+1, y, c)
d.FillRectangle(x0, y, x1-x0+1, 1, c)
}
// FillScreen fills the screen with a given color
+39
View File
@@ -33,7 +33,9 @@ const (
RDID3 = 0xDC
RDID4 = 0xDD
FRMCTR1 = 0xB1
RGBCTRL = 0xB1
FRMCTR2 = 0xB2
PORCTRL = 0xB2
FRMCTR3 = 0xB3
INVCTR = 0xB4
DISSET5 = 0xB6
@@ -43,12 +45,49 @@ const (
PWCTR4 = 0xC3
PWCTR5 = 0xC4
VMCTR1 = 0xC5
FRCTRL2 = 0xC6
PWCTR6 = 0xFC
GMCTRP1 = 0xE0
GMCTRN1 = 0xE1
GSCAN = 0x45
NO_ROTATION Rotation = 0
ROTATION_90 Rotation = 1 // 90 degrees clock-wise rotation
ROTATION_180 Rotation = 2
ROTATION_270 Rotation = 3
// Allowable frame rate codes for FRCTRL2 (Identifier is in Hz)
FRAMERATE_111 FrameRate = 0x01
FRAMERATE_105 FrameRate = 0x02
FRAMERATE_99 FrameRate = 0x03
FRAMERATE_94 FrameRate = 0x04
FRAMERATE_90 FrameRate = 0x05
FRAMERATE_86 FrameRate = 0x06
FRAMERATE_82 FrameRate = 0x07
FRAMERATE_78 FrameRate = 0x08
FRAMERATE_75 FrameRate = 0x09
FRAMERATE_72 FrameRate = 0x0A
FRAMERATE_69 FrameRate = 0x0B
FRAMERATE_67 FrameRate = 0x0C
FRAMERATE_64 FrameRate = 0x0D
FRAMERATE_62 FrameRate = 0x0E
FRAMERATE_60 FrameRate = 0x0F // 60 is default
FRAMERATE_58 FrameRate = 0x10
FRAMERATE_57 FrameRate = 0x11
FRAMERATE_55 FrameRate = 0x12
FRAMERATE_53 FrameRate = 0x13
FRAMERATE_52 FrameRate = 0x14
FRAMERATE_50 FrameRate = 0x15
FRAMERATE_49 FrameRate = 0x16
FRAMERATE_48 FrameRate = 0x17
FRAMERATE_46 FrameRate = 0x18
FRAMERATE_45 FrameRate = 0x19
FRAMERATE_44 FrameRate = 0x1A
FRAMERATE_43 FrameRate = 0x1B
FRAMERATE_42 FrameRate = 0x1C
FRAMERATE_41 FrameRate = 0x1D
FRAMERATE_40 FrameRate = 0x1E
FRAMERATE_39 FrameRate = 0x1F
MAX_VSYNC_SCANLINES = 254
)
+156 -25
View File
@@ -1,12 +1,14 @@
// Package st7789 implements a driver for the ST7789 TFT displays, it comes in various screen sizes.
//
// Datasheet: https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf
// Datasheets: https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf
// http://www.newhavendisplay.com/appnotes/datasheets/LCDs/ST7789V.pdf
//
package st7789 // import "tinygo.org/x/drivers/st7789"
import (
"image/color"
"machine"
"math"
"time"
"errors"
@@ -14,11 +16,14 @@ import (
type Rotation uint8
type FrameRate uint8
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
width int16
height int16
@@ -27,8 +32,10 @@ type Device struct {
columnOffset int16
rowOffset int16
rotation Rotation
frameRate FrameRate
batchLength int32
isBGR bool
vSyncLines int16
}
// Config is the configuration for the display
@@ -38,17 +45,21 @@ type Config struct {
Rotation Rotation
RowOffset int16
ColumnOffset int16
FrameRate FrameRate
VSyncLines int16
}
// New creates a new ST7789 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, blPin machine.Pin) Device {
func New(bus machine.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
blPin: blPin,
}
}
@@ -65,42 +76,149 @@ func (d *Device) Configure(cfg Config) {
} else {
d.height = 240
}
d.rotation = cfg.Rotation
d.rowOffsetCfg = cfg.RowOffset
d.columnOffsetCfg = cfg.ColumnOffset
if cfg.FrameRate != 0 {
d.frameRate = cfg.FrameRate
} else {
d.frameRate = FRAMERATE_60
}
if cfg.VSyncLines >= 2 && cfg.VSyncLines <= 254 {
d.vSyncLines = cfg.VSyncLines
} else {
d.vSyncLines = 16
}
d.batchLength = int32(d.width)
if d.height > d.width {
d.batchLength = int32(d.height)
}
d.batchLength += d.batchLength & 1
// reset the device
// Reset the device
d.resetPin.High()
time.Sleep(5 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
d.resetPin.Low()
time.Sleep(20 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
d.resetPin.High()
time.Sleep(150 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
// Common initialization
d.Command(SWRESET)
time.Sleep(150 * time.Millisecond)
d.Command(SLPOUT)
time.Sleep(500 * time.Millisecond)
d.Command(COLMOD)
d.Data(0x55)
time.Sleep(10 * time.Millisecond)
d.Command(SWRESET) // Soft reset
time.Sleep(150 * time.Millisecond) //
d.SetRotation(d.rotation)
d.InvertColors(true)
d.Command(SLPOUT) // Exit sleep mode
time.Sleep(500 * time.Millisecond) //
d.Command(NORON)
time.Sleep(10 * time.Millisecond)
d.Command(DISPON)
time.Sleep(500 * time.Millisecond)
// Memory initialization
d.Command(COLMOD) // Set color mode
d.Data(0x55) // 16-bit color
time.Sleep(10 * time.Millisecond) //
d.blPin.High()
d.SetRotation(d.rotation) // Memory orientation
d.setWindow(0, 0, d.width, d.height) // Full draw window
d.FillScreen(color.RGBA{0, 0, 0, 255}) // Clear screen
// Framerate
d.Command(FRCTRL2) // Frame rate for normal mode
d.Data(uint8(d.frameRate)) // Default is 60Hz
// Frame vertical sync and "porch"
//
// Front and back porch controls vertical scanline sync time before and after
// a frame, where memory can be safely written without tearing.
//
fp := uint8(d.vSyncLines / 2) // Split the desired pause half and half
bp := uint8(d.vSyncLines - int16(fp)) // between front and back porch.
d.Command(PORCTRL)
d.Data(bp) // Back porch 5bit (0x7F max 0x08 default)
d.Data(fp) // Front porch 5bit (0x7F max 0x08 default)
d.Data(0x00) // Seprarate porch (TODO: what is this?)
d.Data(0x22) // Idle mode porch (4bit-back 4bit-front 0x22 default)
d.Data(0x22) // Partial mode porch (4bit-back 4bit-front 0x22 default)
// Ready to display
d.Command(INVON) // Inversion ON
time.Sleep(10 * time.Millisecond) //
d.Command(NORON) // Normal mode ON
time.Sleep(10 * time.Millisecond) //
d.Command(DISPON) // Screen ON
time.Sleep(10 * time.Millisecond) //
d.blPin.High() // Backlight ON
}
// Sync waits for the display to hit the next VSYNC pause
func (d *Device) Sync() {
d.SyncToScanLine(0)
}
// SyncToScanLine waits for the display to hit a specific scanline
//
// A scanline value of 0 will forward to the beginning of the next VSYNC,
// even if the display is currently in a VSYNC pause.
//
// Syncline values appear to increment once for every two vertical
// lines on the display.
//
// NOTE: Use GetHighestScanLine and GetLowestScanLine to obtain the highest
// and lowest useful values. Values are affected by front and back porch
// vsync settings (derived from VSyncLines configuration option).
//
func (d *Device) SyncToScanLine(scanline uint16) {
scan := d.GetScanLine()
// Sometimes GetScanLine returns erroneous 0 on first call after draw, so double check
if scan == 0 {
scan = d.GetScanLine()
}
if scanline == 0 {
// we dont know where we are in an ongoing vsync so go around
for scan < 1 {
time.Sleep(1 * time.Millisecond)
scan = d.GetScanLine()
}
for scan > 0 {
scan = d.GetScanLine()
}
} else {
// go around unless we're very close to the target
for scan > scanline+4 {
time.Sleep(1 * time.Millisecond)
scan = d.GetScanLine()
}
for scan < scanline {
scan = d.GetScanLine()
}
}
}
// GetScanLine reads the current scanline value from the display
func (d *Device) GetScanLine() uint16 {
data := []uint8{0x00, 0x00}
d.Rx(GSCAN, data)
return uint16(data[0])<<8 + uint16(data[1])
}
// GetHighestScanLine calculates the last scanline id in the frame before VSYNC pause
func (d *Device) GetHighestScanLine() uint16 {
// Last scanline id appears to be backporch/2 + 320/2
return uint16(math.Ceil(float64(d.vSyncLines)/2)/2) + 160
}
// GetLowestScanLine calculate the first scanline id to appear after VSYNC pause
func (d *Device) GetLowestScanLine() uint16 {
// First scanline id appears to be backporch/2 + 1
return uint16(math.Ceil(float64(d.vSyncLines)/2)/2) + 1
}
// Display does nothing, there's no buffer as it might be too big for some boards
@@ -123,7 +241,7 @@ func (d *Device) setWindow(x, y, w, h int16) {
x += d.columnOffset
y += d.rowOffset
d.Tx([]uint8{CASET}, true)
d.Tx([]uint8{uint8(x << 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)}, false)
d.Tx([]uint8{uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)}, false)
d.Tx([]uint8{RASET}, true)
d.Tx([]uint8{uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1)}, false)
d.Command(RAMWR)
@@ -158,6 +276,7 @@ func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
return nil
}
// FillRectangle fills a rectangle at a given coordinates with a buffer
func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
i, j := d.Size()
@@ -207,7 +326,7 @@ func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) {
if x0 > x1 {
x0, x1 = x1, x0
}
d.FillRectangle(x0, y, x1-x0+1, y, c)
d.FillRectangle(x0, y, x1-x0+1, 1, c)
}
// FillScreen fills the screen with a given color
@@ -248,7 +367,6 @@ func (d *Device) SetRotation(rotation Rotation) {
}
d.Command(MADCTL)
d.Data(madctl)
}
// Command sends a command to the display
@@ -265,11 +383,24 @@ func (d *Device) Data(data uint8) {
func (d *Device) Tx(data []byte, isCommand bool) {
if isCommand {
d.dcPin.Low()
d.bus.Tx(data, nil)
} else {
d.dcPin.High()
d.bus.Tx(data, nil)
}
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
}
// Rx reads data from the display
func (d *Device) Rx(command uint8, read_bytes []byte) {
d.dcPin.Low()
d.csPin.Low()
d.bus.Transfer(command)
d.dcPin.High()
for i := range read_bytes {
read_bytes[i], _ = d.bus.Transfer(0xFF)
}
d.csPin.High()
}
// Size returns the current size of the display.
+83
View File
@@ -0,0 +1,83 @@
package tester
// MaxRegisters is the maximum number of registers supported for a Device.
const MaxRegisters = 200
// I2CDevice represents a mock I2C device on a mock I2C bus.
type I2CDevice struct {
c Failer
// addr is the i2c device address.
addr uint8
// Registers holds the device registers. It can be inspected
// or changed as desired for testing.
registers [MaxRegisters]uint8
// If Err is non-nil, it will be returned as the error from the
// I2C methods.
Err error
}
// NewI2CDevice returns a new mock I2C device.
func NewI2CDevice(c Failer, addr uint8) *I2CDevice {
return &I2CDevice{
c: c,
addr: addr,
}
}
// Addr returns the Device address.
func (d *I2CDevice) Addr() uint8 {
return d.addr
}
// SetupRegisters sets all of the Device registers.
// It is intended to be used when setting up a fake device
// for testing expected vs. actual values.
func (d *I2CDevice) SetupRegisters(regs []uint8) {
if len(regs) > MaxRegisters {
panic("exceeded maximum number of registers for fake device")
}
for k, v := range regs {
d.registers[k] = v
}
}
// SetupRegister sets one of the Device registers.
// It is intended to be used when setting up a fake device
// for testing expected vs. actual values.
func (d *I2CDevice) SetupRegister(r, v uint8) {
if r > MaxRegisters {
panic("exceeded maximum number of registers for fake device")
}
d.registers[r] = v
}
// ReadRegister implements I2C.ReadRegister.
func (d *I2CDevice) ReadRegister(r uint8, buf []byte) error {
if d.Err != nil {
return d.Err
}
d.AssertRegisterRange(r, buf)
copy(buf, d.registers[r:])
return nil
}
// WriteRegister implements I2C.WriteRegister.
func (d *I2CDevice) WriteRegister(r uint8, buf []byte) error {
if d.Err != nil {
return d.Err
}
d.AssertRegisterRange(r, buf)
copy(d.registers[r:], buf)
return nil
}
// AssertRegisterRange asserts that reading or writing the given
// register and subsequent registers is in range of the available registers.
func (d *I2CDevice) AssertRegisterRange(r uint8, buf []byte) {
if int(r) >= len(d.registers) {
d.c.Fatalf("register read/write [%#x, %#x] start out of range", r, int(r)+len(buf))
}
if int(r)+len(buf) > len(d.registers) {
d.c.Fatalf("register read/write [%#x, %#x] end out of range", r, int(r)+len(buf))
}
}
+48
View File
@@ -0,0 +1,48 @@
package tester
// I2CBus implements the I2C interface in memory for testing.
type I2CBus struct {
c Failer
devices []*I2CDevice
}
// NewI2CBus returns an I2CBus mock I2C instance that uses c to flag errors
// if they happen. After creating a I2C instance, add devices
// to it with addDevice before using NewI2CBus interface.
func NewI2CBus(c Failer) *I2CBus {
return &I2CBus{
c: c,
}
}
// AddDevice adds a new mock device to the mock I2C bus.
func (bus *I2CBus) AddDevice(d *I2CDevice) {
bus.devices = append(bus.devices, d)
}
// ReadRegister implements I2C.ReadRegister.
func (bus *I2CBus) ReadRegister(addr uint8, r uint8, buf []byte) error {
return bus.FindDevice(addr).ReadRegister(r, buf)
}
// WriteRegister implements I2C.WriteRegister.
func (bus *I2CBus) WriteRegister(addr uint8, r uint8, buf []byte) error {
return bus.FindDevice(addr).WriteRegister(r, buf)
}
// Tx implements I2C.Tx.
func (bus *I2CBus) Tx(addr uint16, w, r []byte) error {
// TODO: implement this
return nil
}
// FindDevice returns the device with the given address.
func (bus *I2CBus) FindDevice(addr uint8) *I2CDevice {
for _, dev := range bus.devices {
if dev.Addr() == addr {
return dev
}
}
bus.c.Fatalf("invalid device addr %#x passed to i2c bus", addr)
panic("unreachable")
}
+13
View File
@@ -0,0 +1,13 @@
// Package tester contains mock structs to make it easier to test I2C devices.
//
// TODO: info on how to use this.
//
package tester // import "tinygo.org/x/drivers/tester"
// Failer is used by the I2CDevice type to abort when it's used in
// unexpected ways, such as reading an out-of-range register.
type Failer interface {
// Fatalf prints the Printf-formatted message and exits the current
// goroutine.
Fatalf(f string, a ...interface{})
}
+15 -5
View File
@@ -4,13 +4,11 @@
package tmp102 // import "tinygo.org/x/drivers/tmp102"
import (
"machine"
)
import "tinygo.org/x/drivers"
// Device holds the already configured I2C bus and the address of the sensor.
type Device struct {
bus machine.I2C
bus drivers.I2C
address uint8
}
@@ -20,7 +18,7 @@ type Config struct {
}
// New creates a new TMP102 connection. The I2C bus must already be configured.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
}
@@ -35,6 +33,18 @@ func (d *Device) Configure(cfg Config) {
d.address = cfg.Address
}
// Connected checks if the config register can be read and that the configuration is correct.
func (d *Device) Connected() bool {
configData := make([]byte, 2)
err := d.bus.ReadRegister(d.address, RegConfiguration, configData)
// Check the reset configuration values.
if err != nil || configData[0] != 0x60 || configData[1] != 0xA0 {
return false
}
return true
}
// Reads the temperature from the sensor and returns it in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
+4 -4
View File
@@ -11,12 +11,12 @@ package veml6070 // import "tinygo.org/x/drivers/veml6070"
import (
"time"
"machine"
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a VEML6070 device.
type Device struct {
bus machine.I2C
bus drivers.I2C
AddressLow uint16
AddressHigh uint16
RSET uint32
@@ -28,7 +28,7 @@ type Device struct {
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
AddressLow: ADDR_L,
@@ -119,7 +119,7 @@ func (d *Device) enable() error {
func (d *Device) readData(address uint16) (byte, error) {
data := []byte{0}
err := machine.I2C0.Tx(address, []byte{}, data)
err := d.bus.Tx(address, []byte{}, data)
return data[0], err
}
+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.
// Update this value before release of new version of software.
const Version = "0.12.0"
const Version = "0.14.0"
+4 -3
View File
@@ -10,8 +10,9 @@
package vl53l1x // import "tinygo.org/x/drivers/vl53l1x"
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
type DistanceMode uint8
@@ -35,7 +36,7 @@ type resultBuffer struct {
// Device wraps an I2C connection to a VL53L1X device.
type Device struct {
bus machine.I2C
bus drivers.I2C
Address uint16
mode DistanceMode
timeout uint32
@@ -52,7 +53,7 @@ type Device struct {
// configured.
//
// This function only creates the Device object, it does not touch the device.
func New(bus machine.I2C) Device {
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: Address,
+355
View File
@@ -0,0 +1,355 @@
// Package epd4in2 implements a driver for Waveshare 4.2in black and white e-paper device.
//
// Derived from:
// https://github.com/tinygo-org/drivers/tree/master/waveshare-epd
// https://github.com/waveshare/e-Paper/blob/master/Arduino/epd4in2/epd4in2.cpp
//
// Datasheet: https://www.waveshare.com/wiki/4.2inch_e-Paper_Module
//
package epd4in2
import (
"image/color"
"machine"
"time"
)
type Config struct {
Width int16 // Width is the display resolution
Height int16
LogicalWidth int16 // LogicalWidth must be a multiple of 8 and same size or bigger than Width
Rotation Rotation // Rotation is clock-wise
}
type Device struct {
bus machine.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
logicalWidth int16
width int16
height int16
buffer []uint8
bufferLength uint32
rotation Rotation
}
type Rotation uint8
// New returns a new epd4in2 driver. Pass in a fully configured SPI bus.
func New(bus machine.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
}
}
// Configure sets up the device.
func (d *Device) Configure(cfg Config) {
if cfg.LogicalWidth != 0 {
d.logicalWidth = cfg.LogicalWidth
} else {
d.logicalWidth = EPD_WIDTH
}
if cfg.Width != 0 {
d.width = cfg.Width
} else {
d.width = EPD_WIDTH
}
if cfg.Height != 0 {
d.height = cfg.Height
} else {
d.height = EPD_HEIGHT
}
d.rotation = cfg.Rotation
d.bufferLength = (uint32(d.logicalWidth) * uint32(d.height)) / 8
d.buffer = make([]uint8, d.bufferLength)
for i := uint32(0); i < d.bufferLength; i++ {
d.buffer[i] = 0xFF
}
d.cs.Low()
d.dc.Low()
d.rst.Low()
d.Reset()
d.SendCommand(POWER_SETTING)
d.SendData(0x03) // VDS_EN, VDG_EN
d.SendData(0x00) // VCOM_HV, VGHL_LV[1], VGHL_LV[0]
d.SendData(0x2b) // VDH
d.SendData(0x2b) // VDL
d.SendData(0xff) // VDHR
d.SendCommand(BOOSTER_SOFT_START)
d.SendData(0x17)
d.SendData(0x17)
d.SendData(0x17) //07 0f 17 1f 27 2F 37 2f
d.SendCommand(POWER_ON)
d.WaitUntilIdle()
d.SendCommand(PANEL_SETTING)
d.SendData(0xbf) // KW-BF KWR-AF BWROTP 0f
d.SendData(0x0b)
d.SendCommand(PLL_CONTROL)
d.SendData(0x3c) // 3A 100HZ 29 150Hz 39 200HZ 31 171HZ
}
// Reset resets the device
func (d *Device) Reset() {
d.rst.Low()
time.Sleep(200 * time.Millisecond)
d.rst.High()
time.Sleep(200 * time.Millisecond)
}
// DeepSleep puts the display into deepsleep
func (d *Device) DeepSleep() {
d.SendCommand(VCOM_AND_DATA_INTERVAL_SETTING)
d.SendData(0x17) //border floating
d.SendCommand(VCM_DC_SETTING) //VCOM to 0V
d.SendCommand(PANEL_SETTING)
time.Sleep(100 * time.Millisecond)
d.SendCommand(POWER_SETTING) //VG&VS to 0V fast
d.SendData(0x00)
d.SendData(0x00)
d.SendData(0x00)
d.SendData(0x00)
d.SendData(0x00)
time.Sleep(100 * time.Millisecond)
d.SendCommand(POWER_OFF) //power off
d.WaitUntilIdle()
d.SendCommand(DEEP_SLEEP) //deep sleep
d.SendData(0xA5)
}
// SendCommand sends a command to the display
func (d *Device) SendCommand(command uint8) {
d.sendDataCommand(true, command)
}
// SendData sends a data byte to the display
func (d *Device) SendData(data uint8) {
d.sendDataCommand(false, data)
}
// sendDataCommand sends image data or a command to the screen
func (d *Device) sendDataCommand(isCommand bool, data uint8) {
if isCommand {
d.dc.Low()
} else {
d.dc.High()
}
d.cs.Low()
d.bus.Transfer(data)
d.cs.High()
}
// SetLUT sets the look up tables for full or partial updates
func (d *Device) SetLUT() {
lut_vcom0 := []uint8{
0x00, 0x17, 0x00, 0x00, 0x00, 0x02,
0x00, 0x17, 0x17, 0x00, 0x00, 0x02,
0x00, 0x0A, 0x01, 0x00, 0x00, 0x01,
0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // 44 bytes, unlike the others
}
lut_ww := []uint8{
0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
lut_bw := []uint8{
0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
lut_bb := []uint8{
0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
lut_wb := []uint8{
0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
d.SendCommand(LUT_FOR_VCOM) //vcom
for count := 0; count < 44; count++ {
d.SendData(lut_vcom0[count])
}
d.SendCommand(LUT_WHITE_TO_WHITE) //ww --
for count := 0; count < 42; count++ {
d.SendData(lut_ww[count])
}
d.SendCommand(LUT_BLACK_TO_WHITE) //bw r
for count := 0; count < 42; count++ {
d.SendData(lut_bw[count])
}
d.SendCommand(LUT_WHITE_TO_BLACK) //wb w
for count := 0; count < 42; count++ {
d.SendData(lut_bb[count])
}
d.SendCommand(LUT_BLACK_TO_BLACK) //bb b
for count := 0; count < 42; count++ {
d.SendData(lut_wb[count])
}
}
// SetPixel modifies the internal buffer in a single pixel.
// The display have 2 colors: black and white
// We use RGBA(0,0,0, 255) as white (transparent)
// Anything else as black
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
x, y = d.xy(x, y)
if x < 0 || x >= d.logicalWidth || y < 0 || y >= d.height {
return
}
byteIndex := (uint32(x) + uint32(y)*uint32(d.logicalWidth)) / 8
if c.R == 0 && c.G == 0 && c.B == 0 { // TRANSPARENT / WHITE
d.buffer[byteIndex] |= 0x80 >> uint8(x%8)
} else { // WHITE / EMPTY
d.buffer[byteIndex] &^= 0x80 >> uint8(x%8)
}
}
// Display sends the buffer to the screen.
func (d *Device) Display() error {
d.SendCommand(RESOLUTION_SETTING)
d.SendData(uint8(d.height >> 8))
d.SendData(uint8(d.logicalWidth & 0xff))
d.SendData(uint8(d.height >> 8))
d.SendData(uint8(d.height & 0xff))
d.SendCommand(VCM_DC_SETTING)
d.SendData(0x12)
d.SendCommand(VCOM_AND_DATA_INTERVAL_SETTING)
d.SendCommand(0x97) //VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
d.SendCommand(DATA_START_TRANSMISSION_1)
var i int16
for i = 0; i < d.logicalWidth/8*d.height; i++ {
d.SendData(0xFF) // bit set: white, bit reset: black
}
time.Sleep(2 * time.Millisecond)
d.SendCommand(DATA_START_TRANSMISSION_2)
for i = 0; i < d.logicalWidth/8*d.height; i++ {
d.SendData(d.buffer[i])
}
time.Sleep(2 * time.Millisecond)
d.SetLUT()
d.SendCommand(DISPLAY_REFRESH)
time.Sleep(100 * time.Millisecond)
d.WaitUntilIdle()
return nil
}
// ClearDisplay erases the device SRAM
func (d *Device) ClearDisplay() {
d.SendCommand(RESOLUTION_SETTING)
d.SendData(uint8(d.height >> 8))
d.SendData(uint8(d.logicalWidth & 0xff))
d.SendData(uint8(d.height >> 8))
d.SendData(uint8(d.height & 0xff))
d.SendCommand(DATA_START_TRANSMISSION_1)
time.Sleep(2 * time.Millisecond)
var i int16
for i = 0; i < d.logicalWidth/8*d.height; i++ {
d.SendData(0xFF)
}
time.Sleep(2 * time.Millisecond)
d.SendCommand(DATA_START_TRANSMISSION_2)
time.Sleep(2 * time.Millisecond)
for i = 0; i < d.logicalWidth/8*d.height; i++ {
d.SendData(0xFF)
}
time.Sleep(2 * time.Millisecond)
d.SetLUT()
d.SendCommand(DISPLAY_REFRESH)
time.Sleep(100 * time.Millisecond)
d.WaitUntilIdle()
}
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for d.busy.Get() {
time.Sleep(100 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
}
// ClearBuffer sets the buffer to 0xFF (white)
func (d *Device) ClearBuffer() {
for i := uint32(0); i < d.bufferLength; i++ {
d.buffer[i] = 0xFF
}
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
if d.rotation == ROTATION_90 || d.rotation == ROTATION_270 {
return d.height, d.logicalWidth
}
return d.logicalWidth, d.height
}
// SetRotation changes the rotation (clock-wise) of the device
func (d *Device) SetRotation(rotation Rotation) {
d.rotation = rotation
}
// xy chages the coordinates according to the rotation
func (d *Device) xy(x, y int16) (int16, int16) {
switch d.rotation {
case NO_ROTATION:
return x, y
case ROTATION_90:
return d.width - y - 1, x
case ROTATION_180:
return d.width - x - 1, d.height - y - 1
case ROTATION_270:
return y, d.height - x - 1
}
return x, y
}
+55
View File
@@ -0,0 +1,55 @@
package epd4in2
// Derived from https://github.com/waveshare/e-Paper/blob/master/Arduino/epd4in2/epd4in2.h
// Registers
const (
// Display resolution
EPD_WIDTH = 400
EPD_HEIGHT = 300
// EPD4IN2 commands
PANEL_SETTING = 0x00
POWER_SETTING = 0x01
POWER_OFF = 0x02
POWER_OFF_SEQUENCE_SETTING = 0x03
POWER_ON = 0x04
POWER_ON_MEASURE = 0x05
BOOSTER_SOFT_START = 0x06
DEEP_SLEEP = 0x07
DATA_START_TRANSMISSION_1 = 0x10
DATA_STOP = 0x11
DISPLAY_REFRESH = 0x12
DATA_START_TRANSMISSION_2 = 0x13
LUT_FOR_VCOM = 0x20
LUT_WHITE_TO_WHITE = 0x21
LUT_BLACK_TO_WHITE = 0x22
LUT_WHITE_TO_BLACK = 0x23
LUT_BLACK_TO_BLACK = 0x24
PLL_CONTROL = 0x30
TEMPERATURE_SENSOR_COMMAND = 0x40
TEMPERATURE_SENSOR_SELECTION = 0x41
TEMPERATURE_SENSOR_WRITE = 0x42
TEMPERATURE_SENSOR_READ = 0x43
VCOM_AND_DATA_INTERVAL_SETTING = 0x50
LOW_POWER_DETECTION = 0x51
TCON_SETTING = 0x60
RESOLUTION_SETTING = 0x61
GSST_SETTING = 0x65
GET_STATUS = 0x71
AUTO_MEASUREMENT_VCOM = 0x80
READ_VCOM_VALUE = 0x81
VCM_DC_SETTING = 0x82
PARTIAL_WINDOW = 0x90
PARTIAL_IN = 0x91
PARTIAL_OUT = 0x92
PROGRAM_MODE = 0xA0
ACTIVE_PROGRAMMING = 0xA1
READ_OTP = 0xA2
POWER_SAVING = 0xE3
NO_ROTATION Rotation = 0
ROTATION_90 Rotation = 1 // 90 degrees clock-wise rotation
ROTATION_180 Rotation = 2
ROTATION_270 Rotation = 3
)
+80 -11
View File
@@ -20,6 +20,10 @@ type Driver struct {
dev *Device
sock uint8
readBuf readBuffer
proto uint8
ip uint32
port uint16
}
type readBuffer struct {
@@ -43,6 +47,8 @@ func (drv *Driver) ConnectSSLSocket(addr, portStr string) error {
func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
drv.proto, drv.ip, drv.port = mode, 0, 0
// convert port to uint16
p64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
@@ -90,8 +96,56 @@ func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
return ErrConnectionTimeout
}
func (drv *Driver) ConnectUDPSocket(addr, sport, lport string) error {
return ErrNotImplemented
func convertPort(portStr string) (uint16, error) {
p64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return 0, fmt.Errorf("could not convert port to uint16: %w", err)
}
return uint16(p64), nil
}
func (drv *Driver) ConnectUDPSocket(addr, portStr, lportStr string) (err error) {
drv.proto, drv.ip, drv.port = ProtoModeUDP, 0, 0
// convert remote port to uint16
if drv.port, err = convertPort(portStr); err != nil {
return err
}
// convert local port to uint16
var lport uint16
if lport, err = convertPort(lportStr); err != nil {
return err
}
// look up the hostname if necessary; if an IP address was specified, the
// same will be returned. Otherwise, an IPv4 for the hostname is returned.
ipAddr, err := drv.dev.GetHostByName(addr)
if err != nil {
return err
}
drv.ip = ipAddr.AsUint32()
// check to see if socket is already set; if so, stop it
// TODO: we can probably have more than one socket at once right?
if drv.sock != NoSocketAvail {
if err := drv.stop(); err != nil {
return err
}
}
// get a socket from the device
if drv.sock, err = drv.dev.GetSocket(); err != nil {
return err
}
// start listening for UDP packets on the local port
if err := drv.dev.StartServer(lport, drv.sock, drv.proto); err != nil {
return err
}
return nil
}
func (drv *Driver) DisconnectSocket() error {
@@ -114,16 +168,31 @@ func (drv *Driver) Write(b []byte) (n int, err error) {
if len(b) == 0 {
return 0, ErrNoData
}
written, err := drv.dev.SendData(b, drv.sock)
if err != nil {
return 0, err
}
if written == 0 {
return 0, ErrDataNotWritten
}
if sent, _ := drv.dev.CheckDataSent(drv.sock); !sent {
return 0, ErrCheckDataError
if drv.proto == ProtoModeUDP {
if err := drv.dev.StartClient(drv.ip, drv.port, drv.sock, drv.proto); err != nil {
return 0, fmt.Errorf("error in startClient: %w", err)
}
if _, err := drv.dev.InsertDataBuf(b, drv.sock); err != nil {
return 0, fmt.Errorf("error in insertDataBuf: %w", err)
}
if _, err := drv.dev.SendUDPData(drv.sock); err != nil {
return 0, fmt.Errorf("error in sendUDPData: %w", err)
}
return len(b), nil
} else {
written, err := drv.dev.SendData(b, drv.sock)
if err != nil {
return 0, err
}
if written == 0 {
return 0, ErrDataNotWritten
}
if sent, _ := drv.dev.CheckDataSent(drv.sock); !sent {
return 0, ErrCheckDataError
}
return len(b), nil
}
return len(b), nil
}
+98 -54
View File
@@ -134,23 +134,23 @@ const (
CmdSetDigitalWrite = 0x51
CmdSetAnalogWrite = 0x52
ErrTimeoutSlaveReady Error = 0x01
ErrTimeoutSlaveSelect Error = 0x02
ErrCheckStartCmd Error = 0x03
ErrWaitRsp Error = 0x04
ErrUnexpectedLength Error = 0xE0
ErrNoParamsReturned Error = 0xE1
ErrIncorrectSentinel Error = 0xE2
ErrCmdErrorReceived Error = 0xEF
ErrNotImplemented Error = 0xF0
ErrUnknownHost Error = 0xF1
ErrSocketAlreadySet Error = 0xF2
ErrConnectionTimeout Error = 0xF3
ErrNoData Error = 0xF4
ErrDataNotWritten Error = 0xF5
ErrCheckDataError Error = 0xF6
ErrBufferTooSmall Error = 0xF7
ErrNoSocketAvail Error = 0xFF
ErrTimeoutChipReady Error = 0x01
ErrTimeoutChipSelect Error = 0x02
ErrCheckStartCmd Error = 0x03
ErrWaitRsp Error = 0x04
ErrUnexpectedLength Error = 0xE0
ErrNoParamsReturned Error = 0xE1
ErrIncorrectSentinel Error = 0xE2
ErrCmdErrorReceived Error = 0xEF
ErrNotImplemented Error = 0xF0
ErrUnknownHost Error = 0xF1
ErrSocketAlreadySet Error = 0xF2
ErrConnectionTimeout Error = 0xF3
ErrNoData Error = 0xF4
ErrDataNotWritten Error = 0xF5
ErrCheckDataError Error = 0xF6
ErrBufferTooSmall Error = 0xF7
ErrNoSocketAvail Error = 0xFF
NoSocketAvail uint8 = 0xFF
)
@@ -296,8 +296,8 @@ func (d *Device) StartClient(addr uint32, port uint16, sock uint8, mode uint8) e
println("[StartClient] called StartClient()\r")
fmt.Printf("[StartClient] addr: % 02X, port: %d, sock: %d\r\n", addr, port, sock)
}
if err := d.waitForSlaveSelect(); err != nil {
d.spiSlaveDeselect()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return err
}
l := d.sendCmd(CmdStartClientTCP, 4)
@@ -306,7 +306,7 @@ func (d *Device) StartClient(addr uint32, port uint16, sock uint8, mode uint8) e
l += d.sendParam8(sock, false)
l += d.sendParam8(mode, true)
d.addPadding(l)
d.spiSlaveDeselect()
d.spiChipDeselect()
_, err := d.waitRspCmd1(CmdStartClientTCP)
return err
}
@@ -320,15 +320,15 @@ func (d *Device) GetClientState(sock uint8) (uint8, error) {
}
func (d *Device) SendData(buf []byte, sock uint8) (uint16, error) {
if err := d.waitForSlaveSelect(); err != nil {
d.spiSlaveDeselect()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0, err
}
l := d.sendCmd(CmdSendDataTCP, 2)
l += d.sendParamBuf([]byte{sock}, false)
l += d.sendParamBuf(buf, true)
d.addPadding(l)
d.spiSlaveDeselect()
d.spiChipDeselect()
return d.getUint16(d.waitRspCmd1(CmdSendDataTCP))
}
@@ -348,8 +348,8 @@ func (d *Device) CheckDataSent(sock uint8) (bool, error) {
}
func (d *Device) GetDataBuf(sock uint8, buf []byte) (int, error) {
if err := d.waitForSlaveSelect(); err != nil {
d.spiSlaveDeselect()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0, err
}
p := uint16(len(buf))
@@ -357,13 +357,13 @@ func (d *Device) GetDataBuf(sock uint8, buf []byte) (int, error) {
l += d.sendParamBuf([]byte{sock}, false)
l += d.sendParamBuf([]byte{uint8(p & 0x00FF), uint8((p) >> 8)}, true)
d.addPadding(l)
d.spiSlaveDeselect()
if err := d.waitForSlaveSelect(); err != nil {
d.spiSlaveDeselect()
d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return 0, err
}
n, err := d.waitRspBuf16(CmdGetDatabufTCP, buf)
d.spiSlaveDeselect()
d.spiChipDeselect()
return int(n), err
}
@@ -375,6 +375,50 @@ func (d *Device) StopClient(sock uint8) error {
return err
}
func (d *Device) StartServer(port uint16, sock uint8, mode uint8) error {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return err
}
l := d.sendCmd(CmdStartServerTCP, 3)
l += d.sendParam16(port, false)
l += d.sendParam8(sock, false)
l += d.sendParam8(mode, true)
d.addPadding(l)
d.spiChipDeselect()
_, err := d.waitRspCmd1(CmdStartClientTCP)
return err
}
// InsertDataBuf adds data to the buffer used for sending UDP data
func (d *Device) InsertDataBuf(buf []byte, sock uint8) (bool, error) {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return false, err
}
l := d.sendCmd(CmdInsertDataBuf, 2)
l += d.sendParamBuf([]byte{sock}, false)
l += d.sendParamBuf(buf, true)
d.addPadding(l)
d.spiChipDeselect()
n, err := d.getUint8(d.waitRspCmd1(CmdInsertDataBuf))
return n == 1, err
}
// SendUDPData sends the data previously added to the UDP buffer
func (d *Device) SendUDPData(sock uint8) (bool, error) {
if err := d.waitForChipSelect(); err != nil {
d.spiChipDeselect()
return false, err
}
l := d.sendCmd(CmdSendDataUDP, 1)
l += d.sendParam8(sock, true)
d.addPadding(l)
d.spiChipDeselect()
n, err := d.getUint8(d.waitRspCmd1(CmdSendDataUDP))
return n == 1, err
}
// ---------- /client methods (should this be a separate struct?) ------------
/*
@@ -666,8 +710,8 @@ func (d *Device) reqRspStr0(cmd uint8, sl []string) (l uint8, err error) {
if err := d.sendCmd0(cmd); err != nil {
return 0, err
}
defer d.spiSlaveDeselect()
if err = d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err = d.waitForChipSelect(); err != nil {
return
}
return d.waitRspStr(cmd, sl)
@@ -678,16 +722,16 @@ func (d *Device) reqRspStr1(cmd uint8, data uint8, sl []string) (uint8, error) {
if err := d.sendCmdPadded1(cmd, data); err != nil {
return 0, err
}
defer d.spiSlaveDeselect()
if err := d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return 0, err
}
return d.waitRspStr(cmd, sl)
}
func (d *Device) sendCmd0(cmd uint8) error {
defer d.spiSlaveDeselect()
if err := d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
d.sendCmd(cmd, 0)
@@ -695,8 +739,8 @@ func (d *Device) sendCmd0(cmd uint8) error {
}
func (d *Device) sendCmdPadded1(cmd uint8, data uint8) error {
defer d.spiSlaveDeselect()
if err := d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
d.sendCmd(cmd, 1)
@@ -707,8 +751,8 @@ func (d *Device) sendCmdPadded1(cmd uint8, data uint8) error {
}
func (d *Device) sendCmdStr(cmd uint8, p1 string) (err error) {
defer d.spiSlaveDeselect()
if err := d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
l := d.sendCmd(cmd, 1)
@@ -718,8 +762,8 @@ func (d *Device) sendCmdStr(cmd uint8, p1 string) (err error) {
}
func (d *Device) sendCmdStr2(cmd uint8, p1 string, p2 string) (err error) {
defer d.spiSlaveDeselect()
if err := d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err := d.waitForChipSelect(); err != nil {
return err
}
l := d.sendCmd(cmd, 2)
@@ -730,8 +774,8 @@ func (d *Device) sendCmdStr2(cmd uint8, p1 string, p2 string) (err error) {
}
func (d *Device) waitRspCmd1(cmd uint8) (l uint8, err error) {
defer d.spiSlaveDeselect()
if err = d.waitForSlaveSelect(); err != nil {
defer d.spiChipDeselect()
if err = d.waitForChipSelect(); err != nil {
return
}
return d.waitRspCmd(cmd, 1)
@@ -856,29 +900,29 @@ func (d *Device) checkStartCmd() (bool, error) {
return true, nil
}
func (d *Device) waitForSlaveSelect() (err error) {
err = d.waitForSlaveReady()
func (d *Device) waitForChipSelect() (err error) {
err = d.waitForChipReady()
if err == nil {
err = d.spiSlaveSelect()
err = d.spiChipSelect()
}
return
}
func (d *Device) waitForSlaveReady() error {
func (d *Device) waitForChipReady() error {
if _debug {
println("waitForSlaveReady()\r")
println("waitForChipReady()\r")
}
for t := newTimer(10 * time.Second); !(d.ACK.Get() == false); {
if t.Expired() {
return ErrTimeoutSlaveReady
return ErrTimeoutChipReady
}
}
return nil
}
func (d *Device) spiSlaveSelect() error {
func (d *Device) spiChipSelect() error {
if _debug {
println("spiSlaveSelect()\r")
println("spiChipSelect()\r")
}
d.CS.Low()
for t := newTimer(5 * time.Millisecond); !t.Expired(); {
@@ -886,12 +930,12 @@ func (d *Device) spiSlaveSelect() error {
return nil
}
}
return ErrTimeoutSlaveSelect
return ErrTimeoutChipSelect
}
func (d *Device) spiSlaveDeselect() {
func (d *Device) spiChipDeselect() {
if _debug {
println("spiSlaveDeselect\r")
println("spiChipDeselect\r")
}
d.CS.High()
}
+1 -1
View File
@@ -1,4 +1,4 @@
// +build arduino
// +build atmega328p
package ws2812
+346
View File
@@ -0,0 +1,346 @@
// +build xtensa
package ws2812
import (
"device"
"errors"
"machine"
"unsafe"
)
var errUnknownClockSpeed = errors.New("ws2812: unknown CPU clock speed")
func (d Device) WriteByte(c byte) error {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
switch machine.CPUFrequency() {
case 160e6: // 160MHz
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// Because I do not know the exact instruction timings, I'm going to
// assume that every instruction executes in one cycle. Branches and
// load/stores will probably be slower than that, but as long as all
// timings are only increased a little bit this should not be a problem
// (see above post).
// T0H: 40 cycles or 333.3ns
// T0L: 131 cycles or 1091.7ns
// +: 171 cycles or 1425.0ns
// T1H: 95 cycles or 791.7ns
// T1L: 75 cycles or 625.0ns
// +: 170 cycles or 1416.7ns
// Some documentation:
// http://cholla.mmto.org/esp8266/xtensa.html
// https://0x04.net/~mwk/doc/xtensa.pdf
device.AsmFull(`
1: // send_bit
s32i {maskSet}, {portSet}, 0 // [1] T0H and T1H start here
nop // [37]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
slli {value}, {value}, 1 // [1] shift {value} to the left by 1
bbsi {value}, 8, 2f // [1] branch to skip_store if bit 8 is set
s32i {maskClear}, {portClear}, 0 // [1] T0H -> T0L transition
2: // skip_store
nop // [55]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
s32i {maskClear}, {portClear}, 0 // [1] T1H -> T1L transition
nop // [72]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
addi {i}, {i}, -1 // [1]
bnez {i}, 1b // [1] send_bit, T1H and T1L end here
// Restore original values after modifying them in the inline
// assembly. Not doing that would result in undefined behavior as
// the compiler doesn't know we're modifying these values.
movi.n {i}, 8
slli {value}, {value}, 8
`, map[string]interface{}{
// Note: casting pointers to uintptr here because of what might be
// an Xtensa backend bug with inline assembly.
"value": uint32(c),
"i": 8,
"maskSet": maskSet,
"portSet": uintptr(unsafe.Pointer(portSet)),
"maskClear": maskClear,
"portClear": uintptr(unsafe.Pointer(portClear)),
})
return nil
case 80e6: // 80MHz
// See docs for 160MHz.
// T0H: 21 cycles or 262.5ns
// T0L: 67 cycles or 837.5ns
// +: 88 cycles or 1100.0ns
// T1H: 47 cycles or 587.5ns
// T1L: 39 cycles or 487.5ns
// +: 86 cycles or 1075.0ns
device.AsmFull(`
1: // send_bit
s32i {maskSet}, {portSet}, 0 // [1] T0H and T1H start here
nop // [18]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
slli {value}, {value}, 1 // [1] shift {value} to the left by 1
bbsi {value}, 8, 2f // [1] branch to skip_store if bit 8 is set
s32i {maskClear}, {portClear}, 0 // [1] T0H -> T0L transition
2: // skip_store
nop // [27]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
s32i {maskClear}, {portClear}, 0 // [1] T1H -> T1L transition
nop // [36]
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
addi {i}, {i}, -1 // [1]
bnez {i}, 1b // [1] send_bit, T1H and T1L end here
// Restore original values after modifying them in the inline
// assembly. Not doing that would result in undefined behavior as
// the compiler doesn't know we're modifying these values.
movi.n {i}, 8
slli {value}, {value}, 8
`, map[string]interface{}{
// Note: casting pointers to uintptr here because of what might be
// an Xtensa backend bug with inline assembly.
"value": uint32(c),
"i": 8,
"maskSet": maskSet,
"portSet": uintptr(unsafe.Pointer(portSet)),
"maskClear": maskClear,
"portClear": uintptr(unsafe.Pointer(portClear)),
})
return nil
default:
return errUnknownClockSpeed
}
}