mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 18:48:41 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38c606d813 | |||
| d02d21ecea | |||
| 805e2a02f8 | |||
| c4ff8242a7 | |||
| 82c41dbf14 | |||
| c4168864fd | |||
| dbc9022f6a | |||
| d41bc0b85f | |||
| e7f90166ad | |||
| 156d6e7c9c | |||
| 6e04decf19 | |||
| afb1ea39c2 | |||
| 382e79a748 | |||
| 06dd60fba2 | |||
| 27a7d5031e | |||
| 6281f36662 | |||
| ccfcfa837e | |||
| 87c205fd65 | |||
| f57b5ecee9 | |||
| 486949686c | |||
| 00578a3a81 | |||
| 17f273243d | |||
| 0623bb425c | |||
| 019bbbe5fb | |||
| d04e68bef8 | |||
| 76a4276b5d | |||
| 7d7efe25e7 | |||
| 0186d0905d | |||
| 2f3b5ca59a | |||
| ecae5e28ad | |||
| bcf3b84654 | |||
| 4fb5d7a0e5 | |||
| f12454d4f7 | |||
| 829ae09651 | |||
| 30f540c29f | |||
| 6d431e0726 | |||
| f308f8fce0 | |||
| 1c2b802f47 | |||
| ce80e7582f | |||
| 109cab4f3b | |||
| 07216d3051 | |||
| d688fa3f3f | |||
| dd44f9220b | |||
| ee3842f639 |
@@ -0,0 +1,2 @@
|
||||
# These are supported funding model platforms
|
||||
open_collective: tinygo
|
||||
@@ -1,3 +1,96 @@
|
||||
0.32.0
|
||||
---
|
||||
- **enhancements**
|
||||
- **bmp280**
|
||||
- remove alloc on read sensor data
|
||||
- **ws2812**
|
||||
- add 200MHz support for the Cortex-M0/rp2040
|
||||
|
||||
- **bugfixes**
|
||||
- **ssd1306**
|
||||
- remove time.Sleep from SSD1306 SPI transfer code
|
||||
- **tmc2209**
|
||||
- tmc2209 bug fixes (#755)
|
||||
|
||||
- **docs**
|
||||
- **contributing**
|
||||
- add driver design pointer to CONTRIBUTING.md
|
||||
|
||||
|
||||
0.31.0
|
||||
---
|
||||
---
|
||||
- **enhancements**
|
||||
- **spi**
|
||||
- update all SPI usage to use either *machine.SPI or drivers.SPI
|
||||
|
||||
|
||||
0.30.0
|
||||
---
|
||||
- **new devices**
|
||||
- **comboat**
|
||||
- Add wifi driver comboat for Elecrow W5 rp2040 and rp2350 devices (#741)
|
||||
- **max6675**
|
||||
- Add MAX6675 device
|
||||
- **TMC2209**
|
||||
- Added TMC2209 support (#727)
|
||||
- **TMC5160**
|
||||
- Added TMC5160 support (#725)
|
||||
- **sharpmem**
|
||||
- Add sharpmem (#724)
|
||||
|
||||
- **enhancements**
|
||||
- **net**
|
||||
- move to latest golang.org/x/net v0.33.0 (#732)
|
||||
- **microphone**
|
||||
- update microphone driver to use latest i2s interface
|
||||
|
||||
- **bugfixes**
|
||||
- **net**
|
||||
- fix typo in DHCP error message
|
||||
- **aht20**
|
||||
- Fixed bug in aht20 driver
|
||||
- **hub75**
|
||||
- fix data buffering
|
||||
|
||||
|
||||
0.29.0
|
||||
---
|
||||
- **new devices**
|
||||
- **epd1in54**
|
||||
- Waveshare 1.54inch B/W e-Paper display (#704)
|
||||
- **touch**
|
||||
- add capacitive touch sensing on normal GPIO pins
|
||||
- **INA219**
|
||||
- I2C INA219 driver (#705)
|
||||
- **pcf8591**
|
||||
- add ADC only implementation for I2C ADC/DAC (#690)
|
||||
|
||||
- **enhancements**
|
||||
- **pixel**
|
||||
- add NewImageFromBytes() function to allow creating image from existing slice
|
||||
- **servo**
|
||||
- Add function `SetAngleWithMicroseconds` (#695)
|
||||
- **onewire**
|
||||
- onewire improvements
|
||||
- **ssd1306**
|
||||
- Add function `SetFlip` and `GetFlip` (#702)
|
||||
- **uc8151**
|
||||
- add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
|
||||
- **ssd1306**
|
||||
- add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
|
||||
|
||||
- **bugfixes**
|
||||
- **pixel**
|
||||
- fix Monochrome setPixel
|
||||
|
||||
- **docs**
|
||||
- **readme**
|
||||
- discuss need to change variables in examples
|
||||
- **sponsor**
|
||||
- Add sponsor button to key repositories
|
||||
|
||||
|
||||
0.28.0
|
||||
---
|
||||
- **new devices**
|
||||
|
||||
@@ -8,6 +8,9 @@ We would like your help to make this project better, so we appreciate any contri
|
||||
|
||||
We'd love to get your feedback on getting started with TinyGo. Run into any difficulty, confusion, or anything else? You are not alone. We want to know about your experience, so we can help the next people. Please open a Github issue with your questions, or you can also get in touch directly with us on our Slack channel at [https://gophers.slack.com/messages/CDJD3SUP6](https://gophers.slack.com/messages/CDJD3SUP6).
|
||||
|
||||
### Driver design
|
||||
Before porting or writing a driver from scratch please read **[Driver Design for TinyGo](https://tinygo.org/docs/guides/driver-design)**.
|
||||
|
||||
### One of the TinyGo drivers is not working as you expect
|
||||
|
||||
Please open a Github issue with your problem, and we will be happy to assist.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2024 The TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2025 The TinyGo Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
||||
@@ -16,7 +16,7 @@ go get tinygo.org/x/drivers
|
||||
|
||||
## How to use
|
||||
|
||||
Here is an example in TinyGo that uses the BMP180 digital barometer:
|
||||
Here is an example in TinyGo that uses the BMP180 digital barometer. This example should work on any board that supports I2C:
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -53,6 +53,28 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Examples Using GPIO or SPI
|
||||
|
||||
If compiling these examples directly you are likely to need to make minor changes to the defined variables to map the pins for the board you are using. For example, this block in main.go:
|
||||
|
||||
```golang
|
||||
var (
|
||||
spi = machine.SPI0
|
||||
csPin = machine.D5
|
||||
)
|
||||
```
|
||||
|
||||
It might not be obvious, but you need to change these to match how you wired your specific board. Constants are [defined for each supported microcontroller](https://tinygo.org/docs/reference/microcontrollers/).
|
||||
|
||||
For example, to change the definitions for use on a Raspberry Pi Pico using typical wiring, you might need to do this:
|
||||
|
||||
```golang
|
||||
var (
|
||||
spi = machine.SPI0
|
||||
csPin = machine.GP17
|
||||
)
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Your contributions are welcome!
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ func New(bus drivers.I2C) Device {
|
||||
func (d *Device) Configure() {
|
||||
// Check initialization state
|
||||
status := d.Status()
|
||||
if status&0x08 == 1 {
|
||||
if status&STATUS_CALIBRATED == 1 {
|
||||
// Device is initialized
|
||||
return
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func (d *Device) Read() error {
|
||||
}
|
||||
|
||||
// If measurement complete, store values
|
||||
if data[0]&0x04 != 0 && data[0]&0x80 == 0 {
|
||||
if data[0]&STATUS_CALIBRATED != 0 && data[0]&STATUS_BUSY == 0 {
|
||||
d.humidity = uint32(data[1])<<12 | uint32(data[2])<<4 | uint32(data[3])>>4
|
||||
d.temp = (uint32(data[3])&0xF)<<16 | uint32(data[4])<<8 | uint32(data[5])
|
||||
return nil
|
||||
|
||||
+1
-2
@@ -5,7 +5,6 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
@@ -37,7 +36,7 @@ func New(b drivers.SPI) *Device {
|
||||
|
||||
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
||||
// implementation of the SPI protocol.
|
||||
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
|
||||
func NewSoftwareSPI(sckPin, sdoPin drivers.Pin, delay uint32) *Device {
|
||||
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -1,6 +1,8 @@
|
||||
package apa102
|
||||
|
||||
import "machine"
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
|
||||
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
|
||||
@@ -8,15 +10,14 @@ import "machine"
|
||||
// most purposes other than the APA102 package. It might be desirable to make
|
||||
// this more generic and include it in the TinyGo "machine" package instead.
|
||||
type bbSPI struct {
|
||||
SCK machine.Pin
|
||||
SDO machine.Pin
|
||||
SCK drivers.Pin
|
||||
SDO drivers.Pin
|
||||
Delay uint32
|
||||
}
|
||||
|
||||
// Configure sets up the SCK and SDO pins as outputs and sets them low
|
||||
// Configure sets the SCK and SDO pins to low.
|
||||
// Note that the SCK and SDO pins must already be configured as outputs.
|
||||
func (s *bbSPI) Configure() {
|
||||
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
s.SCK.Low()
|
||||
s.SDO.Low()
|
||||
if s.Delay == 0 {
|
||||
|
||||
+4
-7
@@ -1,7 +1,6 @@
|
||||
package bmi160
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -11,7 +10,7 @@ import (
|
||||
// also an I2C interface, but it is not yet supported.
|
||||
type DeviceSPI struct {
|
||||
// Chip select pin
|
||||
CSB machine.Pin
|
||||
CSB drivers.Pin
|
||||
|
||||
buf [7]byte
|
||||
|
||||
@@ -22,18 +21,16 @@ type DeviceSPI struct {
|
||||
// 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 drivers.SPI) *DeviceSPI {
|
||||
func NewSPI(csb drivers.Pin, spi drivers.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).
|
||||
// Configure configures the BMI160 for use. The CSB pin anf the SPI interface
|
||||
// should be configured already. This function configures the BMI160 only.
|
||||
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
|
||||
|
||||
+7
-8
@@ -23,6 +23,7 @@ type Filter uint
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
buf [6]byte
|
||||
cali calibrationCoefficients
|
||||
Temperature Oversampling
|
||||
Pressure Oversampling
|
||||
@@ -134,8 +135,8 @@ func (d *Device) PrintCali() {
|
||||
|
||||
// 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 {
|
||||
data := d.buf[:3]
|
||||
if err = d.readData(REG_TEMP, data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -158,8 +159,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
// 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 {
|
||||
data := d.buf[:6]
|
||||
if err = d.readData(REG_PRES, data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -203,7 +204,7 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
|
||||
}
|
||||
|
||||
// readData reads n number of bytes of the specified register
|
||||
func (d *Device) readData(register int, n int) ([]byte, error) {
|
||||
func (d *Device) readData(register int, data []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 {
|
||||
@@ -218,9 +219,7 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
|
||||
}
|
||||
|
||||
// Read the requested register
|
||||
data := make([]byte, n)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
||||
return data, err
|
||||
return legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
||||
}
|
||||
|
||||
// convert3Bytes converts three bytes to int32
|
||||
|
||||
+4
-4
@@ -2,20 +2,20 @@
|
||||
package buzzer // import "tinygo.org/x/drivers/buzzer"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps a GPIO connection to a buzzer.
|
||||
type Device struct {
|
||||
pin machine.Pin
|
||||
pin drivers.Pin
|
||||
High bool
|
||||
BPM float64
|
||||
}
|
||||
|
||||
// New returns a new buzzer driver given which pin to use
|
||||
func New(pin machine.Pin) Device {
|
||||
func New(pin drivers.Pin) Device {
|
||||
return Device{
|
||||
pin: pin,
|
||||
High: false,
|
||||
|
||||
@@ -0,0 +1,711 @@
|
||||
// Package comboat implements WiFi driver for the Aithinker-Combo-AT WiFi
|
||||
// device found on the Elecrow W5 rp2040 and rp2350 devices. Ths WiFi device
|
||||
// is a RTL8720d variant. The driver interface is via AT command set over UART
|
||||
// (see reference docs below).
|
||||
//
|
||||
// NOTE: the driver doesn't support UDP/TCP server connections in STA mode,
|
||||
// currently. UDP/TCP/TLS client connections are supported in STA mode.
|
||||
//
|
||||
// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/instruction/index.html
|
||||
// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-set/index.html
|
||||
// https://aithinker-combo-guide.readthedocs.io/en/latest/docs/command-examples/index.html
|
||||
|
||||
package comboat // import "tinygo.org/x/drivers/comboat"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"machine"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/netdev"
|
||||
"tinygo.org/x/drivers/netlink"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
BaudRate uint32
|
||||
Uart *machine.UART
|
||||
Tx machine.Pin
|
||||
Rx machine.Pin
|
||||
}
|
||||
|
||||
type socket struct {
|
||||
protocol int
|
||||
id string
|
||||
rx chan []byte
|
||||
remainder []byte
|
||||
laddr netip.AddrPort // Set in Bind()
|
||||
}
|
||||
|
||||
type device struct {
|
||||
cfg *Config
|
||||
uart *machine.UART
|
||||
uartMu sync.Mutex
|
||||
mac net.HardwareAddr
|
||||
ip netip.Addr
|
||||
gateway netip.Addr
|
||||
buf [1500]byte
|
||||
pos int
|
||||
last []byte
|
||||
ok chan bool
|
||||
txReady chan bool
|
||||
accept chan string
|
||||
err chan error
|
||||
sockets [8]*socket
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewDevice(cfg *Config) *device {
|
||||
return &device{
|
||||
cfg: cfg,
|
||||
ok: make(chan bool),
|
||||
txReady: make(chan bool),
|
||||
accept: make(chan string),
|
||||
err: make(chan error),
|
||||
}
|
||||
}
|
||||
|
||||
func logDebug(msg string) {
|
||||
//println("[DEBUG] " + msg)
|
||||
}
|
||||
|
||||
func logError(msg string) {
|
||||
println("[ERROR] " + msg)
|
||||
}
|
||||
|
||||
func split(resp []byte, part int, del, on string) string {
|
||||
parts := bytes.Split(resp, []byte(del))
|
||||
if part >= len(parts) {
|
||||
return "Split parts error getting " + on
|
||||
}
|
||||
return string(parts[part])
|
||||
}
|
||||
|
||||
func (d *device) getFWVersion() string {
|
||||
return split(d.last, 1, ":", "FW version")
|
||||
}
|
||||
|
||||
func (d *device) saveMAC() {
|
||||
raw := split(d.last, 1, ":", "MAC")
|
||||
if len(raw) > 11 {
|
||||
macStr := fmt.Sprintf("%s:%s:%s:%s:%s:%s",
|
||||
raw[0:2], raw[2:4], raw[4:6],
|
||||
raw[6:8], raw[8:10], raw[10:12])
|
||||
d.mac, _ = net.ParseMAC(macStr)
|
||||
}
|
||||
}
|
||||
|
||||
var countryCodes = map[int]string{
|
||||
1: "JP Japan",
|
||||
2: "American Samoa",
|
||||
3: "CA Canada",
|
||||
4: "US",
|
||||
5: "CN China",
|
||||
6: "Hong Kong, China",
|
||||
7: "Taiwan, China",
|
||||
8: "MO Macau, China",
|
||||
9: "IL Israel",
|
||||
10: "Singapore",
|
||||
11: "KR South Korea",
|
||||
12: "TR Türkiye",
|
||||
13: "AU Australia",
|
||||
14: "ZA South Africa",
|
||||
15: "BR Brazil",
|
||||
}
|
||||
|
||||
func (d *device) getCountry() (code string) {
|
||||
code = split(d.last, 1, ":", "county code")
|
||||
codeNum, err := strconv.Atoi(code)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if val, ok := countryCodes[codeNum]; ok {
|
||||
code = val
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *device) saveIP() {
|
||||
ipStr := split(d.last, 7, ",", "IP address")
|
||||
gwStr := split(d.last, 8, ",", "gateway address")
|
||||
d.ip, _ = netip.ParseAddr(ipStr)
|
||||
d.gateway, _ = netip.ParseAddr(gwStr)
|
||||
}
|
||||
|
||||
func (d *device) execute(cmd string, timeout int) (err error) {
|
||||
logDebug("EXECUTE " + cmd)
|
||||
|
||||
d.uartMu.Lock()
|
||||
_, err = d.uart.Write([]byte(cmd + "\r\n"))
|
||||
d.uartMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t := time.NewTicker(time.Duration(timeout) * time.Millisecond)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
return errors.New("Timed out")
|
||||
case <-d.ok:
|
||||
return
|
||||
case err = <-d.err:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) send(cmd string, timeout int) (err error) {
|
||||
logDebug("EXECUTE " + cmd)
|
||||
|
||||
d.uartMu.Lock()
|
||||
_, err = d.uart.Write([]byte(cmd + "\r\n"))
|
||||
d.uartMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t := time.NewTicker(time.Duration(timeout) * time.Millisecond)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
return errors.New("Timed out")
|
||||
case <-d.txReady:
|
||||
return
|
||||
case err = <-d.err:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) findSocket(id string) (*socket, error) {
|
||||
for _, s := range d.sockets {
|
||||
if s.id == id {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Socket not found with id: " + id)
|
||||
}
|
||||
|
||||
func (d *device) getSocket(sockfd int) (*socket, error) {
|
||||
if sockfd < 0 || sockfd+1 > len(d.sockets) {
|
||||
return nil, netdev.ErrInvalidSocketFd
|
||||
}
|
||||
if d.sockets[sockfd] == nil {
|
||||
return nil, netdev.ErrInvalidSocketFd
|
||||
}
|
||||
return d.sockets[sockfd], nil
|
||||
}
|
||||
|
||||
func (d *device) handle(event []byte) {
|
||||
logDebug("GOT EVENT " + string(event))
|
||||
switch {
|
||||
|
||||
// SocketDisconnect,<id>
|
||||
case bytes.HasPrefix(event, []byte("SocketDisconnect")):
|
||||
id := split(event, 1, ",", "SocketDisconnect")
|
||||
s, err := d.findSocket(id)
|
||||
if err == nil {
|
||||
close(s.rx) // Sends io.EOF
|
||||
}
|
||||
|
||||
// SocketSeed,<id>,<server id>
|
||||
case bytes.HasPrefix(event, []byte("SocketSeed,2,1")):
|
||||
//d.uart.Write([]byte("AT+SOCKET?" + "\r\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) processUART() {
|
||||
|
||||
if d.pos == 1 && d.buf[0] == '>' {
|
||||
d.pos = 0
|
||||
logDebug("GOT >")
|
||||
d.txReady <- true
|
||||
}
|
||||
|
||||
sofar := d.buf[:d.pos]
|
||||
|
||||
if !bytes.HasSuffix(sofar, []byte("\r\n")) {
|
||||
return
|
||||
}
|
||||
|
||||
// Strip CR/LF off end
|
||||
sofar = sofar[:len(sofar)-2]
|
||||
|
||||
switch {
|
||||
|
||||
case bytes.HasPrefix(sofar, []byte("+EVENT:SocketDown")):
|
||||
// +EVENT:SocketDown,<id>,<length>,<data>
|
||||
parts := bytes.SplitN(sofar, []byte(","), 4)
|
||||
if len(parts) != 4 {
|
||||
logError("Error parsing +EVENT:SocketDown: " + string(sofar))
|
||||
d.pos = 0
|
||||
return
|
||||
}
|
||||
id := string(parts[1])
|
||||
length, err := strconv.Atoi(string(parts[2]))
|
||||
if err != nil {
|
||||
logError("Error parsing length from: " + string(parts[2]))
|
||||
d.pos = 0
|
||||
return
|
||||
}
|
||||
if length != len(parts[3]) {
|
||||
// This can happen if <data> actually contains a CR/LF.
|
||||
// Return without resetting d.pos to continue reading
|
||||
// in the full <data>.
|
||||
return
|
||||
}
|
||||
s, err := d.findSocket(id)
|
||||
if err != nil {
|
||||
logError(err.Error())
|
||||
d.pos = 0
|
||||
return
|
||||
}
|
||||
logDebug("GOT +EVENT:SocketDown," + id + "," + string(parts[2]))
|
||||
d.pos = 0
|
||||
data := make([]byte, len(parts[3]))
|
||||
copy(data, parts[3])
|
||||
s.rx <- data
|
||||
|
||||
case bytes.HasPrefix(sofar, []byte("OK")):
|
||||
d.pos = 0
|
||||
logDebug("GOT OK")
|
||||
d.ok <- true
|
||||
|
||||
case bytes.HasPrefix(sofar, []byte("ERROR")):
|
||||
d.pos = 0
|
||||
logDebug("GOT ERROR")
|
||||
errStr := getErrStr(d.last)
|
||||
d.err <- errors.New(errStr)
|
||||
|
||||
case bytes.HasPrefix(sofar, []byte("+EVENT:")):
|
||||
d.pos = 0
|
||||
event := sofar[len("+EVENT:"):]
|
||||
d.handle(event)
|
||||
|
||||
default:
|
||||
// Catch everything else and store in d.last
|
||||
d.pos = 0
|
||||
size := len(sofar)
|
||||
if size > 0 {
|
||||
d.last = make([]byte, size)
|
||||
copy(d.last, sofar[:size])
|
||||
logDebug("GOT LINE " + string(d.last))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) serviceUART() {
|
||||
for {
|
||||
d.uartMu.Lock()
|
||||
for d.uart.Buffered() > 0 {
|
||||
if d.pos >= len(d.buf) {
|
||||
println("Trying to write past buffer")
|
||||
d.pos = 0
|
||||
break
|
||||
}
|
||||
var err error
|
||||
d.buf[d.pos], err = d.uart.ReadByte()
|
||||
if err == nil {
|
||||
d.pos++
|
||||
d.processUART()
|
||||
}
|
||||
}
|
||||
d.uartMu.Unlock()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) NetConnect(params *netlink.ConnectParams) error {
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
d.uart = d.cfg.Uart
|
||||
d.uart.Configure(machine.UARTConfig{
|
||||
BaudRate: d.cfg.BaudRate,
|
||||
TX: d.cfg.Tx,
|
||||
RX: d.cfg.Rx,
|
||||
})
|
||||
|
||||
go d.serviceUART()
|
||||
|
||||
fmt.Printf("\r\n")
|
||||
fmt.Printf("TinyGo Combo-AT WiFi network device driver\r\n")
|
||||
|
||||
fmt.Printf("\r\n")
|
||||
fmt.Printf("Driver version : %s\r\n", drivers.Version)
|
||||
|
||||
if len(params.Ssid) == 0 {
|
||||
return netlink.ErrMissingSSID
|
||||
}
|
||||
|
||||
// AT Test to see if device is alive
|
||||
if err := d.execute("AT", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Disable echo
|
||||
if err := d.execute("ATE0", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get FW version
|
||||
if err := d.execute("AT+GMR", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Combo-AT firmware version : %s\r\n", d.getFWVersion())
|
||||
|
||||
// Get/save MAC addresses
|
||||
if err := d.execute("AT+CIPSTAMAC_DEF?", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
d.saveMAC()
|
||||
fmt.Printf("MAC address : %s\r\n", d.mac.String())
|
||||
|
||||
// Set country code US
|
||||
if err := d.execute("AT+WCOUNTRY=4", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get country code
|
||||
if err := d.execute("AT+WCOUNTRY?", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("WiFi country code : %s\r\n", d.getCountry())
|
||||
|
||||
// Set Wi-Fi working mode to STA and save to flash
|
||||
if err := d.execute("AT+WMODE=1,1", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Connect to Wifi AP (keep trying until connected)
|
||||
fmt.Printf("\r\n")
|
||||
cmd := "AT+WJAP=" + params.Ssid + "," + params.Passphrase
|
||||
|
||||
for {
|
||||
fmt.Printf("Connecting to WiFi SSID '%s'...", params.Ssid)
|
||||
if err := d.execute(cmd, 20000); err != nil {
|
||||
fmt.Printf("FAILED (%s)\r\n", err.Error())
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Printf("CONNECTED\r\n")
|
||||
|
||||
// Automatically reconnect to Wi-Fi after power on
|
||||
if err := d.execute("AT+WAUTOCONN=1", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get/save IP/gateway addresses
|
||||
if err := d.execute("AT+WJAP?", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
d.saveIP()
|
||||
|
||||
fmt.Printf("\r\n")
|
||||
fmt.Printf("DHCP-assigned IP : %s\r\n", d.ip)
|
||||
fmt.Printf("DHCP-assigned gateway : %s\r\n", d.gateway)
|
||||
fmt.Printf("\r\n")
|
||||
|
||||
// Set socket receiving mode to active
|
||||
if err := d.execute("AT+SOCKETRECVCFG=1", 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *device) NetDisconnect() {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
// Disconnect from WiFi AP
|
||||
d.execute("AT+WDISCONNECT", 1000)
|
||||
}
|
||||
|
||||
func (d *device) NetNotify(cb func(netlink.Event)) {
|
||||
fmt.Printf("\r\n%s\r\n", netlink.ErrNotSupported)
|
||||
}
|
||||
|
||||
func (d *device) GetHardwareAddr() (net.HardwareAddr, error) {
|
||||
return d.mac, nil
|
||||
}
|
||||
|
||||
func (d *device) _getHostByName(name string) (ip netip.Addr, err error) {
|
||||
if err = d.execute("AT+WDOMAIN="+name, 1000); err != nil {
|
||||
return
|
||||
}
|
||||
ipStr := split(d.last, 1, ":", "host by name")
|
||||
return netip.ParseAddr(ipStr)
|
||||
}
|
||||
|
||||
func (d *device) GetHostByName(name string) (ip netip.Addr, err error) {
|
||||
|
||||
// If it's already a dotted-network address, and not a host name,
|
||||
// return it
|
||||
ip, err = netip.ParseAddr(name)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
return d._getHostByName(name)
|
||||
}
|
||||
|
||||
func (d *device) Addr() (netip.Addr, error) {
|
||||
return d.ip, nil
|
||||
}
|
||||
|
||||
func (d *device) Socket(domain, stype, protocol int) (int, error) {
|
||||
|
||||
switch domain {
|
||||
case netdev.AF_INET:
|
||||
default:
|
||||
return -1, netdev.ErrFamilyNotSupported
|
||||
}
|
||||
|
||||
switch {
|
||||
case protocol == netdev.IPPROTO_TCP && stype == netdev.SOCK_STREAM:
|
||||
case protocol == netdev.IPPROTO_TLS && stype == netdev.SOCK_STREAM:
|
||||
case protocol == netdev.IPPROTO_UDP && stype == netdev.SOCK_DGRAM:
|
||||
default:
|
||||
return -1, netdev.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
// Search for empty slot in sockets array
|
||||
for fd, s := range d.sockets {
|
||||
if s == nil {
|
||||
// Found one
|
||||
d.sockets[fd] = &socket{
|
||||
protocol: protocol,
|
||||
rx: make(chan []byte, 10),
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
}
|
||||
|
||||
return -1, netdev.ErrNoMoreSockets
|
||||
}
|
||||
|
||||
func (d *device) Bind(sockfd int, ip netip.AddrPort) error {
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
s, err := d.getSocket(sockfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.laddr = ip
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *device) Connect(sockfd int, host string, ip netip.AddrPort) error {
|
||||
|
||||
var addr string
|
||||
var cmd string
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
s, err := d.getSocket(sockfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
addr = ip.Addr().String()
|
||||
} else {
|
||||
ip, err := d._getHostByName(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addr = ip.String()
|
||||
}
|
||||
port := strconv.Itoa(int(ip.Port()))
|
||||
|
||||
switch s.protocol {
|
||||
case netdev.IPPROTO_UDP:
|
||||
cmd = "AT+SOCKET=2," + addr + "," + port
|
||||
case netdev.IPPROTO_TCP:
|
||||
cmd = "AT+SOCKET=4," + addr + "," + port
|
||||
case netdev.IPPROTO_TLS:
|
||||
cmd = "AT+SOCKET=7," + addr + "," + port
|
||||
}
|
||||
|
||||
if cmd == "" {
|
||||
return netdev.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
if err := d.execute(cmd, 20000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.id = split(d.last, 1, "=", "connection ID")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *device) Listen(sockfd, backlog int) error {
|
||||
|
||||
// TODO Creating a TCP server socket isn't working when in STA mode,
|
||||
// TODO returning error "Socket bind error".
|
||||
// TODO The reference example shows a TCP server example in AP mode.
|
||||
|
||||
/*
|
||||
var cmd string
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
s, err := d.getSocket(sockfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port := strconv.Itoa(int(s.laddr.Port()))
|
||||
|
||||
switch s.protocol {
|
||||
case netdev.IPPROTO_UDP:
|
||||
cmd = "AT+SOCKET=1," + port
|
||||
case netdev.IPPROTO_TCP:
|
||||
cmd = "AT+SOCKET=3," + port
|
||||
}
|
||||
|
||||
if cmd == "" {
|
||||
return netdev.ErrProtocolNotSupported
|
||||
}
|
||||
|
||||
if err := d.execute(cmd, 20000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.id = split(d.last, 1, "=", "connection ID")
|
||||
*/
|
||||
|
||||
return netdev.ErrNotSupported
|
||||
}
|
||||
|
||||
func (d *device) Accept(sockfd int) (int, netip.AddrPort, error) {
|
||||
return 0, netip.AddrPort{}, netdev.ErrNotSupported
|
||||
}
|
||||
|
||||
func (d *device) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
s, err := d.getSocket(sockfd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("AT+SOCKETSEND=%s,%d", s.id, len(buf))
|
||||
|
||||
if err := d.send(cmd, 1000); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// AT+SOCKETSEND will sub-packet send data into 1024-byte chunks,
|
||||
// automatically, so send the full buffer in one shot, even if it's
|
||||
// bigger than 1024 bytes.
|
||||
|
||||
d.uartMu.Lock()
|
||||
n, err := d.uart.Write(buf)
|
||||
d.uartMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Expecting "OK" after good send, or "ERROR"
|
||||
|
||||
t := time.NewTicker(time.Duration(1000) * time.Millisecond)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
return 0, errors.New("Timed out")
|
||||
case <-d.ok:
|
||||
return n, nil
|
||||
case err = <-d.err:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
func (d *device) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
s, err := d.getSocket(sockfd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 1. Use leftover data first
|
||||
if len(s.remainder) > 0 {
|
||||
n := copy(buf, s.remainder)
|
||||
s.remainder = s.remainder[n:]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// 2. Get new data from the channel
|
||||
data, ok := <-s.rx
|
||||
if !ok {
|
||||
// Socket closed, return EOF
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// 3. Copy data, handle leftovers
|
||||
n := copy(buf, data)
|
||||
if n < len(data) {
|
||||
s.remainder = data[n:]
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *device) Close(sockfd int) error {
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
s, err := d.getSocket(sockfd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete socket only if connection was successful (s.id is set)
|
||||
if s.id != "" {
|
||||
cmd := fmt.Sprintf("AT+SOCKETDEL=%s", s.id)
|
||||
if err = d.execute(cmd, 1000); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d.sockets[sockfd] = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *device) SetSockOpt(sockfd, level, opt int, value interface{}) error {
|
||||
return netdev.ErrNotSupported
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package comboat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var errStrings = map[int]string{
|
||||
|
||||
// System framework related error codes
|
||||
|
||||
0: "success",
|
||||
1: "The command is not supported (the combo framework contains the command but the current platform has not transplanted or adapted to support it)",
|
||||
2: "The command parameters contain unsupported operations (the current platform only supports some operations for this command)",
|
||||
3: "The instruction format is incorrect (this refers to the wrong number of parameters, for example, two parameters are required, but only one parameter is entered)",
|
||||
4: "Parameter error (the content of the parameter is wrong, for example, a number between 0 and 9 is required, but 10 or xyz is passed in, which is a parameter error)",
|
||||
5: "Parameter length error (command length exceeds the maximum supported length)",
|
||||
31: "The current command has not ended and needs to report the status asynchronously. This value is used by the state machine to determine the use of the command and no message is returned.",
|
||||
32: "Unknown error (or unhandled error type)",
|
||||
|
||||
// Common error codes
|
||||
|
||||
33: "malloc error",
|
||||
34: "Failed to read buf",
|
||||
35: "Failed to write buf",
|
||||
36: "Configuration error (configuration error loaded from memory, for example, we set port -1 for OTA upgrade, and check port error when executing AT+OTA, then configuration error will be reported)",
|
||||
37: "Failed to create task",
|
||||
38: "Flash read and write failure",
|
||||
39: "Serial port configuration error, unsupported baud rate",
|
||||
40: "Serial port configuration error, unsupported data bits",
|
||||
41: "Serial port configuration error, unsupported stop bit",
|
||||
42: "Serial port configuration error, unsupported parity bit",
|
||||
43: "Serial port configuration error, unsupported flow control",
|
||||
44: "Serial port configuration failed",
|
||||
45: "Wrong username/password",
|
||||
46: "Low power mode error or unsupported low power mode",
|
||||
47: "Uninitialized configuration data error (including io mapping data)",
|
||||
63: "General error code (without other information)",
|
||||
|
||||
// Wi-Fi related error codes
|
||||
|
||||
64: "Wi-Fi not initialized or initialization failed",
|
||||
65: "Wi-Fi mode error (unable to connect to Wi-Fi in single AP mode)",
|
||||
66: "Wi-Fi connection failed",
|
||||
67: "Wi-Fi connection successful, error in obtaining IP (DHCP)",
|
||||
68: "Failed to obtain encryption method",
|
||||
69: "The specified AP was not found.",
|
||||
70: "Wi-Fi scan start failed",
|
||||
71: "Wi-Fi scan timeout",
|
||||
72: "Failed to enable AP hotspot",
|
||||
73: "Failed to obtain the Wi-Fi information of the router or the AP information that you enabled yourself",
|
||||
74: "The network card (STA/AP) is not running",
|
||||
75: "Wi-Fi country code error (unsupported Wi-Fi country code)",
|
||||
76: "The current network configuration mode is wrong.",
|
||||
95: "Wi-Fi connection unknown error",
|
||||
|
||||
// Socket related error codes
|
||||
|
||||
96: "Failed to create socket",
|
||||
97: "Socket connection failed",
|
||||
98: "DNS Failure",
|
||||
99: "The socket status is wrong (for example, TCP is not connected yet)",
|
||||
100: "Socket type error",
|
||||
101: "Socket send failed",
|
||||
102: "Socket receive failed",
|
||||
103: "Socket monitoring thread creation failed",
|
||||
104: "Socket bind error",
|
||||
105: "The current connection cannot be transparently linked (wrong socket type or number)",
|
||||
106: "PING test failed (all packets lost)",
|
||||
107: "Wi-Fi country code error (unsupported Wi-Fi country code)",
|
||||
108: "SSL Config Error",
|
||||
109: "SSL verification error (usually caused by unsupported SSL encryption type or certificate error)",
|
||||
127: "Unknown socket error",
|
||||
}
|
||||
|
||||
func getErrStr(errLine []byte) (errStr string) {
|
||||
errStr = "Can't parse ERROR response"
|
||||
tokens := bytes.Split(errLine, []byte(":"))
|
||||
if len(tokens) > 1 {
|
||||
errCode, err := strconv.Atoi(string(tokens[1]))
|
||||
if err == nil {
|
||||
errStr = errStrings[errCode]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
+2
-2
@@ -19,7 +19,7 @@ type OneWireDevice interface {
|
||||
Write(uint8)
|
||||
Read() uint8
|
||||
Select([]uint8) error
|
||||
Сrc8([]uint8, int) uint8
|
||||
Сrc8([]uint8) uint8
|
||||
}
|
||||
|
||||
// Device wraps a connection to an 1-Wire devices.
|
||||
@@ -69,7 +69,7 @@ func (d Device) ReadTemperatureRaw(romid []uint8) ([]uint8, error) {
|
||||
for i := 0; i < 9; i++ {
|
||||
spb[i] = d.owd.Read()
|
||||
}
|
||||
if d.owd.Сrc8(spb, 8) != spb[8] {
|
||||
if d.owd.Сrc8(spb) != 0 {
|
||||
return nil, errReadTemperature
|
||||
}
|
||||
return spb[:2:2], nil
|
||||
|
||||
@@ -3,8 +3,9 @@ package easystepper // import "tinygo.org/x/drivers/easystepper"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// StepMode determines the coil sequence used to perform a single step
|
||||
@@ -33,7 +34,7 @@ func (sm StepMode) stepCount() uint {
|
||||
// DeviceConfig contains the configuration data for a single easystepper driver
|
||||
type DeviceConfig struct {
|
||||
// Pin1 ... Pin4 determines the pins to configure and use for the device
|
||||
Pin1, Pin2, Pin3, Pin4 machine.Pin
|
||||
Pin1, Pin2, Pin3, Pin4 drivers.Pin
|
||||
// StepCount is the number of steps required to perform a full revolution of the stepper motor
|
||||
StepCount uint
|
||||
// RPM determines the speed of the stepper motor in 'Revolutions per Minute'
|
||||
@@ -46,12 +47,12 @@ type DeviceConfig struct {
|
||||
type DualDeviceConfig struct {
|
||||
DeviceConfig
|
||||
// Pin5 ... Pin8 determines the pins to configure and use for the second device
|
||||
Pin5, Pin6, Pin7, Pin8 machine.Pin
|
||||
Pin5, Pin6, Pin7, Pin8 drivers.Pin
|
||||
}
|
||||
|
||||
// Device holds the pins and the delay between steps
|
||||
type Device struct {
|
||||
pins [4]machine.Pin
|
||||
pins [4]drivers.Pin
|
||||
stepDelay time.Duration
|
||||
stepNumber uint8
|
||||
stepMode StepMode
|
||||
@@ -68,17 +69,15 @@ func New(config DeviceConfig) (*Device, error) {
|
||||
return nil, errors.New("config.StepCount and config.RPM must be > 0")
|
||||
}
|
||||
return &Device{
|
||||
pins: [4]machine.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4},
|
||||
pins: [4]drivers.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4},
|
||||
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
|
||||
stepMode: config.Mode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
// Configure does nothing, as it assumes that the pins of the Device have already
|
||||
// been configured by the user as outputs.
|
||||
func (d *Device) Configure() {
|
||||
for _, pin := range d.pins {
|
||||
pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
}
|
||||
|
||||
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
console_example.RunFor(
|
||||
flash.NewSPI(
|
||||
&machine.SPI1,
|
||||
machine.SPI1,
|
||||
machine.SPI1_SDO_PIN,
|
||||
machine.SPI1_SDI_PIN,
|
||||
machine.SPI1_SCK_PIN,
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
println("GPS I2C Example")
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
ublox := gps.NewI2C(machine.I2C0)
|
||||
ublox := gps.NewI2CWithAddress(machine.I2C0, gps.UBLOX_I2C_ADDRESS)
|
||||
parser := gps.NewParser()
|
||||
var fix gps.Fix
|
||||
for {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ina219"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
dev := ina219.New(machine.I2C0)
|
||||
dev.Configure()
|
||||
|
||||
for {
|
||||
busVoltage, shuntVoltage, current, power, err := dev.Measurements()
|
||||
if err != nil {
|
||||
println("Error reading measurements", err)
|
||||
}
|
||||
|
||||
println("Bus Voltage:", busVoltage, "V")
|
||||
println("Shunt Voltage:", shuntVoltage/100, "mV")
|
||||
println("Current:", current, "mA")
|
||||
println("Power:", power, "mW")
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/max6675"
|
||||
)
|
||||
|
||||
// example for reading temperature from a thermocouple
|
||||
func main() {
|
||||
// Pins are for an Adafruit Feather nRF52840 Express
|
||||
machine.D5.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.D5.High()
|
||||
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
Frequency: 1_000_000,
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
})
|
||||
|
||||
thermocouple := max6675.NewDevice(machine.SPI0, machine.D5)
|
||||
|
||||
for {
|
||||
temp, err := thermocouple.Read()
|
||||
if err != nil {
|
||||
println(err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%0.02f C : %0.02f F\n", temp, (temp*9/5)+32)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Note: It may be necessary to increase the stack size when using
|
||||
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Note: It may be necessary to increase the stack size when using
|
||||
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// It creates a UDP connection to request the current time and parse the
|
||||
// response from a NTP server. The system time is set to NTP time.
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
// func. This forces segments to connect and run concurrently, which is a good
|
||||
// test of the underlying driver's ability to handle concurrent connections.
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// nc -lk 8080
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// nc -lk 8080
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || pico
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// }
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Note: It may be necessary to increase the stack size when using
|
||||
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
|
||||
|
||||
//go:build ninafw || wioterminal
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Connects to a pcf8591 ADC via I2C.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/pcf8591"
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = machine.I2C0
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c.Configure(machine.I2CConfig{})
|
||||
adc := pcf8591.New(i2c)
|
||||
adc.Configure()
|
||||
|
||||
// get "CH0" aka "machine.ADC" interface to channel 0 from ADC.
|
||||
p := adc.CH0
|
||||
|
||||
for {
|
||||
val := p.Get()
|
||||
println(val)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI0
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI1
|
||||
spi = machine.SPI1
|
||||
sckPin = machine.SDCARD_SCK_PIN
|
||||
sdoPin = machine.SDCARD_SDO_PIN
|
||||
sdiPin = machine.SDCARD_SDI_PIN
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI0
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SDCARD_SPI
|
||||
spi = machine.SDCARD_SPI
|
||||
sckPin = machine.SDCARD_SCK_PIN
|
||||
sdoPin = machine.SDCARD_SDO_PIN
|
||||
sdiPin = machine.SDCARD_SDI_PIN
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI0
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI0
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = &machine.SPI2
|
||||
spi = machine.SPI2
|
||||
sckPin = machine.SCK2
|
||||
sdoPin = machine.SDO2
|
||||
sdiPin = machine.SDI2
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"math/rand/v2"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sharpmem"
|
||||
)
|
||||
|
||||
var (
|
||||
// example wiring using a nice!view and nice!nano:
|
||||
// (view) (nano)
|
||||
// MOSI --> P0.24
|
||||
// SCK ---> P0.22
|
||||
// GND ---> GND
|
||||
// VCC ---> 3.3V
|
||||
// CS ----> P0.06
|
||||
|
||||
spi = machine.SPI0
|
||||
|
||||
sckPin = machine.SPI0_SCK_PIN // SCK
|
||||
sdoPin = machine.SPI0_SDO_PIN // MOSI
|
||||
sdiPin = machine.SPI0_SDI_PIN // (any pin)
|
||||
|
||||
csPin = machine.P0_06 // CS
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
err := spi.Configure(machine.SPIConfig{
|
||||
Frequency: 2000000,
|
||||
SCK: sckPin,
|
||||
SDO: sdoPin,
|
||||
SDI: sdiPin,
|
||||
Mode: 0,
|
||||
LSBFirst: true,
|
||||
})
|
||||
if err != nil {
|
||||
println("spi.Configure() failed, error:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
display := sharpmem.New(spi, csPin)
|
||||
|
||||
cfg := sharpmem.ConfigLS011B7DH03
|
||||
display.Configure(cfg)
|
||||
|
||||
// clear the display before first use
|
||||
err = display.Clear()
|
||||
if err != nil {
|
||||
println("display.Clear() failed, error:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// random boxes pop into and out of existence
|
||||
for {
|
||||
x0 := int16(rand.IntN(int(cfg.Width - 7)))
|
||||
y0 := int16(rand.IntN(int(cfg.Height - 7)))
|
||||
|
||||
for x2 := int16(0); x2 < 16; x2++ {
|
||||
x2 := x2
|
||||
c := color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||||
|
||||
if x2 >= 8 {
|
||||
// effectively erases the box after it showed up
|
||||
x2 = x2 - 8
|
||||
c = color.RGBA{R: 0, G: 0, B: 0, A: 255}
|
||||
}
|
||||
|
||||
for x := int16(0); x < x2; x++ {
|
||||
for y := int16(0); y < 8; y++ {
|
||||
display.SetPixel(x0+x, y0+y, c)
|
||||
}
|
||||
}
|
||||
|
||||
err = display.Display()
|
||||
if err != nil {
|
||||
println("display.Display() failed, error:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
time.Sleep(33 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/tmc2209"
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart := machine.UART0
|
||||
comm := tmc2209.NewUARTComm(*uart, 0)
|
||||
// Create an instance of the TMC2209 with UART communication
|
||||
tmc := tmc2209.NewTMC2209(comm, 0x00) // Replace 0x00 with the appropriate address
|
||||
|
||||
// Set up the TMC2209 driver
|
||||
err := tmc.Setup()
|
||||
if err != nil {
|
||||
println("Failed to set up TMC2209: ", err)
|
||||
}
|
||||
|
||||
// Write to a register (example: setting a register value)
|
||||
err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value
|
||||
if err != nil {
|
||||
println("Failed to write register:", err)
|
||||
}
|
||||
|
||||
// Read from a register (example: reading a register value)
|
||||
value, err := tmc.ReadRegister(0x10)
|
||||
if err != nil {
|
||||
println("Failed to read register: ", err)
|
||||
}
|
||||
|
||||
// Output the read value
|
||||
println("Register value: ", value)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Connects to SPI1 on a RP2040 (Pico)
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/tmc5160"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Step 1. Setup your protocol. SPI setup shown below
|
||||
spi := machine.SPI1
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues
|
||||
Mode: 3,
|
||||
LSBFirst: false,
|
||||
})
|
||||
// Step 2. Set up all associated Pins
|
||||
csPin0 := machine.GPIO13
|
||||
csPin0.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
enn0 := machine.GPIO18
|
||||
enn0.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
// csPins is a map of all chip select pins in a multi driver setup.
|
||||
//Only one pin csPin0 mapped to "0"is shown in this example, but add more mappings as required
|
||||
csPins := map[uint8]machine.Pin{0: csPin0}
|
||||
//bind csPin to driverAdddress
|
||||
driverAddress := uint8(0) // Let's assume we are working with driver at address 0x01
|
||||
// Step 3. Bind the communication interface to the protocol
|
||||
comm := tmc5160.NewSPIComm(spi, csPins)
|
||||
// Step 4. Define your stepper like this below
|
||||
//stepper := tmc5160.NewStepper(angle , gearRatio vSupply rCoil , lCoil , iPeak , rSense , mSteps, fclk )
|
||||
stepper := tmc5160.NewDefaultStepper() // Default Stepper should be used only for testing.
|
||||
// Step 5. Instantiate your driver
|
||||
driver := tmc5160.NewDriver(
|
||||
comm,
|
||||
driverAddress,
|
||||
enn0,
|
||||
stepper)
|
||||
|
||||
// Setting and getting mode
|
||||
rampMode := tmc5160.NewRAMPMODE(comm, driverAddress)
|
||||
err := rampMode.SetMode(tmc5160.PositioningMode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mode, err := rampMode.GetMode()
|
||||
if err != nil {
|
||||
println("Error getting mode:", err)
|
||||
} else {
|
||||
println("Current Mode:", mode)
|
||||
}
|
||||
|
||||
// Read GCONF register
|
||||
GCONF := tmc5160.NewGCONF()
|
||||
gconfVal, err := driver.ReadRegister(tmc5160.GCONF)
|
||||
// Uppack the register to get all the bits and bytes of the register
|
||||
GCONF.Unpack(gconfVal)
|
||||
//E.g. MultiStepFlit is retrieved from the GCONF register
|
||||
println("GCONF:MultiStepFlit:", GCONF.MultistepFilt)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Capacitive touch sensing example.
|
||||
//
|
||||
// This capacitive touch sensor works by charging a normal GPIO pin, then slowly
|
||||
// discharging it through a 1MΩ resistor and seeing how long it takes to go from
|
||||
// high to low.
|
||||
//
|
||||
// Use as follows:
|
||||
// - Change touchPin below as needed.
|
||||
// - Connect this pin to some metal surface, like a piece of aluminimum foil.
|
||||
// Make sure this surface is covered (using paper, Scotch tape, etc).
|
||||
// - Also connect this same pin to ground through a 1MΩ resistor.
|
||||
//
|
||||
// This sensor is very sensitive to noise on the power source, so you should
|
||||
// probably try to limit it by running from a battery for example. Especially
|
||||
// phone chargers can produce a lot of noise.
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/touch/capacitive"
|
||||
)
|
||||
|
||||
const touchPin = machine.GP16 // Raspberry Pi Pico
|
||||
|
||||
func main() {
|
||||
time.Sleep(time.Second * 2)
|
||||
println("start")
|
||||
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
led.Low()
|
||||
|
||||
// Configure the array of GPIO pins used for capacitive touch sensing.
|
||||
// We're using only one pin.
|
||||
array := capacitive.NewArray([]machine.Pin{touchPin})
|
||||
|
||||
// Use a dynamic threshold, meaning the GPIO pin is automatically calibrated
|
||||
// and re-calibrated to adjust for varying environments (e.g. changing
|
||||
// humidity).
|
||||
array.SetDynamicThreshold(100)
|
||||
|
||||
wasTouching := false
|
||||
for i := uint32(0); ; i++ {
|
||||
// Update the GPIO pin. This must be called very often.
|
||||
array.Update()
|
||||
touching := array.Touching(0)
|
||||
|
||||
// Indicate whether the pin is touched via the LED.
|
||||
led.Set(touching)
|
||||
|
||||
// Print something when the touch state changed.
|
||||
if wasTouching != touching {
|
||||
wasTouching = touching
|
||||
if touching {
|
||||
println(" touch!")
|
||||
} else {
|
||||
println(" release!")
|
||||
}
|
||||
}
|
||||
|
||||
// Print the current value, as a debugging aid. It's not really meant to
|
||||
// be used directly.
|
||||
if i%128 == 32 {
|
||||
println("touch value:", array.RawValue(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/waveshare-epd/epd1in54"
|
||||
"tinygo.org/x/tinyfont"
|
||||
"tinygo.org/x/tinyfont/gophers"
|
||||
)
|
||||
|
||||
var (
|
||||
spi0 = machine.SPI0
|
||||
cs = machine.D10
|
||||
dc = machine.D9
|
||||
rst = machine.D6
|
||||
busy = machine.D5
|
||||
|
||||
black = color.RGBA{R: 1, G: 1, B: 1, A: 255}
|
||||
)
|
||||
|
||||
func main() {
|
||||
display := epd1in54.New(spi0, cs, dc, rst, busy)
|
||||
|
||||
display.LDirInit(epd1in54.Config{})
|
||||
display.Clear()
|
||||
display.ClearBuffer()
|
||||
|
||||
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 150, 0, "A B C", black, tinyfont.ROTATION_90)
|
||||
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 100, 0, "D E F", black, tinyfont.ROTATION_90)
|
||||
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 50, 0, "G H I", black, tinyfont.ROTATION_90)
|
||||
tinyfont.WriteLineRotated(&display, &gophers.Regular58pt, 0, 0, "J K L", black, tinyfont.ROTATION_90)
|
||||
|
||||
display.Display()
|
||||
display.Sleep()
|
||||
}
|
||||
+4
-6
@@ -5,8 +5,6 @@
|
||||
package ft6336
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/touch"
|
||||
@@ -17,11 +15,11 @@ type Device struct {
|
||||
bus drivers.I2C
|
||||
buf []byte
|
||||
Address uint8
|
||||
intPin machine.Pin
|
||||
intPin drivers.Pin
|
||||
}
|
||||
|
||||
// New returns FT6336 device for the provided I2C bus using default address.
|
||||
func New(i2c drivers.I2C, intPin machine.Pin) *Device {
|
||||
func New(i2c drivers.I2C, intPin drivers.Pin) *Device {
|
||||
return &Device{
|
||||
bus: i2c,
|
||||
buf: make([]byte, 11),
|
||||
@@ -34,10 +32,10 @@ func New(i2c drivers.I2C, intPin machine.Pin) *Device {
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// Configure the FT6336 device.
|
||||
// Configure the FT6336 device. Note that the interrupt pin must be configured
|
||||
// separately as an input.
|
||||
func (d *Device) Configure(config Config) error {
|
||||
d.write1Byte(0xA4, 0x00)
|
||||
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+7
-11
@@ -5,7 +5,6 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01"
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
@@ -22,10 +21,10 @@ type FrameRate uint8
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
blPin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
resetPin drivers.Pin
|
||||
csPin drivers.Pin
|
||||
blPin drivers.Pin
|
||||
width int16
|
||||
height int16
|
||||
columnOffsetCfg int16
|
||||
@@ -51,12 +50,9 @@ type Config struct {
|
||||
Height int16
|
||||
}
|
||||
|
||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
// New creates a new ST7789 connection. The SPI wire must already be configured, along with the
|
||||
// data/command and reset pins as outputs.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
resetPin: resetPin,
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
module tinygo.org/x/drivers
|
||||
|
||||
go 1.18
|
||||
|
||||
replace tinygo.org/x/drivers/mcp9808 => /home/kasterby/Documents/drivers/mcp9808
|
||||
go 1.22.1
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/frankban/quicktest v1.10.2
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/orsinium-labs/tinymath v1.1.0
|
||||
github.com/soypat/natiu-mqtt v0.5.1
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||
golang.org/x/net v0.33.0
|
||||
tinygo.org/x/tinyfont v0.3.0
|
||||
tinygo.org/x/tinyterm v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.2 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.1.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
)
|
||||
|
||||
@@ -3,8 +3,9 @@ github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t
|
||||
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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
@@ -12,12 +13,15 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTeV1vS5UA=
|
||||
github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
|
||||
github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc=
|
||||
github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4=
|
||||
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
|
||||
+7
-1
@@ -69,10 +69,16 @@ func NewUART(uart drivers.UART) Device {
|
||||
}
|
||||
|
||||
// NewI2C creates a new I2C GPS connection.
|
||||
// Uses the default i2c address (0x42) for backward compatibility reasons.
|
||||
func NewI2C(bus drivers.I2C) Device {
|
||||
return NewI2CWithAddress(bus, I2C_ADDRESS)
|
||||
}
|
||||
|
||||
// NewI2CWithAddress creates a new I2C GPS connection on the provided address
|
||||
func NewI2CWithAddress(bus drivers.I2C, i2cAddress uint16) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
address: I2C_ADDRESS,
|
||||
address: i2cAddress,
|
||||
buffer: make([]byte, bufferSize),
|
||||
bufIdx: bufferSize,
|
||||
sentence: strings.Builder{},
|
||||
|
||||
+5
-1
@@ -4,7 +4,11 @@ package gps
|
||||
|
||||
// The I2C address which this device listens to.
|
||||
const (
|
||||
I2C_ADDRESS = 0x42
|
||||
// To ensure backward compatibility
|
||||
I2C_ADDRESS = UBLOX_I2C_ADDRESS
|
||||
|
||||
UBLOX_I2C_ADDRESS = 0x42
|
||||
PA1010D_I2C_ADDRESS = 0x10
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+8
-7
@@ -5,30 +5,31 @@
|
||||
package hcsr04
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
const TIMEOUT = 23324 // max sensing distance (4m)
|
||||
|
||||
// Device holds the pins
|
||||
type Device struct {
|
||||
trigger machine.Pin
|
||||
echo machine.Pin
|
||||
trigger drivers.Pin
|
||||
echo drivers.Pin
|
||||
}
|
||||
|
||||
// New returns a new ultrasonic driver given 2 pins
|
||||
func New(trigger, echo machine.Pin) Device {
|
||||
func New(trigger, echo drivers.Pin) Device {
|
||||
return Device{
|
||||
trigger: trigger,
|
||||
echo: echo,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
// Configure expects that the pins of the Device have already
|
||||
// been configured by the user. Trigger pin should be an output
|
||||
// and echo pin should be an input.
|
||||
func (d *Device) Configure() {
|
||||
d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.echo.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
}
|
||||
|
||||
// ReadDistance returns the distance of the object in mm
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ func (d *Device) fillMatrixBuffer(x int16, y int16, r uint8, g uint8, b uint8) {
|
||||
if r > colorTresh {
|
||||
d.buffer[c][offsetR] |= 1 << bitSelect
|
||||
} else {
|
||||
d.buffer[c][offsetR] = d.buffer[c][offsetR] &^ 1 << bitSelect
|
||||
d.buffer[c][offsetR] &^= 1 << bitSelect
|
||||
}
|
||||
if g > colorTresh {
|
||||
d.buffer[(c+d.colorThirdStep)%d.colorDepth][offsetG] |= 1 << bitSelect
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
)
|
||||
|
||||
type spiDriver struct {
|
||||
bus machine.SPI
|
||||
bus *machine.SPI
|
||||
}
|
||||
|
||||
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
func NewSPI(bus *machine.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
return &Device{
|
||||
dc: dc,
|
||||
cs: cs,
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
)
|
||||
|
||||
type spiDriver struct {
|
||||
bus machine.SPI
|
||||
bus *machine.SPI
|
||||
}
|
||||
|
||||
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
func NewSPI(bus *machine.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
return &Device{
|
||||
dc: dc,
|
||||
cs: cs,
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package ina219
|
||||
|
||||
type Config struct {
|
||||
// BusVoltageRange sets the bus voltage range.
|
||||
BusVoltageRange BusVoltageRange
|
||||
|
||||
// PGA sets the programmable gain amplifier.
|
||||
PGA PGA
|
||||
|
||||
// BusADC sets the bus ADC resolution.
|
||||
BusADC BusADC
|
||||
|
||||
// ShuntADC sets the shunt ADC resolution.
|
||||
ShuntADC ShuntADC
|
||||
|
||||
// Mode sets the operating mode.
|
||||
Mode Mode
|
||||
|
||||
// Calibration sets the calibration value for the expected
|
||||
// voltage and current values.
|
||||
Calibration Calibration
|
||||
|
||||
// 1000 / uA per bit
|
||||
CurrentDivider float32
|
||||
|
||||
// 1mW per bit
|
||||
PowerMultiplier float32
|
||||
}
|
||||
|
||||
// RegisterValue returns the register value of the configuration.
|
||||
func (c *Config) RegisterValue() uint16 {
|
||||
return c.BusVoltageRange.RegisterValue() |
|
||||
c.PGA.RegisterValue() |
|
||||
c.BusADC.RegisterValue() |
|
||||
c.ShuntADC.RegisterValue() |
|
||||
c.Mode.RegisterValue()
|
||||
}
|
||||
|
||||
// Generate a new configuration from a register value.
|
||||
func NewConfig(config int16, calibration int16) Config {
|
||||
return Config{
|
||||
BusVoltageRange: BusVoltageRange(config >> 13 & 0x1),
|
||||
PGA: PGA(config >> 11 & 0x3),
|
||||
BusADC: BusADC(config >> 7 & 0xF),
|
||||
ShuntADC: ShuntADC(config >> 3 & 0xF),
|
||||
Mode: Mode(config & 0x7),
|
||||
Calibration: Calibration(calibration),
|
||||
}
|
||||
}
|
||||
|
||||
// Configurations from
|
||||
// https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.cpp
|
||||
var (
|
||||
// Config32V2A is a configuration for a 32V 2A range.
|
||||
Config32V2A = Config{
|
||||
BusVoltageRange: Range32V,
|
||||
PGA: PGA8,
|
||||
BusADC: ADC12,
|
||||
ShuntADC: SADC12,
|
||||
Mode: ModeContShuntBus,
|
||||
Calibration: Calibration16V400mA,
|
||||
CurrentDivider: 10.0,
|
||||
PowerMultiplier: 2.0,
|
||||
}
|
||||
|
||||
// Config32V1A is a configuration for a 32V 1A range.
|
||||
Config32V1A = Config{
|
||||
BusVoltageRange: Range32V,
|
||||
PGA: PGA8,
|
||||
BusADC: ADC12,
|
||||
ShuntADC: SADC12,
|
||||
Mode: ModeContShuntBus,
|
||||
Calibration: Calibration32V1A,
|
||||
CurrentDivider: 25.0,
|
||||
PowerMultiplier: 0.8,
|
||||
}
|
||||
|
||||
// Config16V400mA is a configuration for a 16V 400mA range.
|
||||
Config16V400mA = Config{
|
||||
BusVoltageRange: Range16V,
|
||||
PGA: PGA1,
|
||||
BusADC: ADC12,
|
||||
ShuntADC: SADC12,
|
||||
Mode: ModeContShuntBus,
|
||||
Calibration: Calibration16V400mA,
|
||||
CurrentDivider: 20.0,
|
||||
PowerMultiplier: 1.0,
|
||||
}
|
||||
)
|
||||
|
||||
// BusVoltageRange is the bus voltage range.
|
||||
type BusVoltageRange int8
|
||||
|
||||
const (
|
||||
Range16V BusVoltageRange = 0 // 0-16V
|
||||
Range32V BusVoltageRange = 1 // 0-32V
|
||||
)
|
||||
|
||||
func (r BusVoltageRange) RegisterValue() uint16 {
|
||||
return uint16(r) << 13
|
||||
}
|
||||
|
||||
// PGA is the programmable gain amplifier.
|
||||
type PGA int8
|
||||
|
||||
const (
|
||||
PGA1 PGA = 0 // 40mV
|
||||
PGA2 PGA = 1 // 80mV
|
||||
PGA4 PGA = 2 // 160mV
|
||||
PGA8 PGA = 3 // 320mV
|
||||
)
|
||||
|
||||
func (p PGA) RegisterValue() uint16 {
|
||||
return uint16(p) << 11
|
||||
}
|
||||
|
||||
// BusADC is the bus ADC resolution.
|
||||
type BusADC int8
|
||||
|
||||
const (
|
||||
ADC9 BusADC = 0 // 9-bit
|
||||
ADC10 BusADC = 1 // 10-bit
|
||||
ADC11 BusADC = 2 // 11-bit
|
||||
ADC12 BusADC = 3 // 12-bit
|
||||
)
|
||||
|
||||
func (b BusADC) RegisterValue() uint16 {
|
||||
return uint16(b) << 7
|
||||
}
|
||||
|
||||
// ShuntADC is the shunt ADC resolution.
|
||||
type ShuntADC int8
|
||||
|
||||
const (
|
||||
SADC9 ShuntADC = 0 // 9-bit
|
||||
SADC10 ShuntADC = 1 // 10-bit
|
||||
SADC11 ShuntADC = 2 // 11-bit
|
||||
SADC12 ShuntADC = 3 // 12-bit
|
||||
)
|
||||
|
||||
func (s ShuntADC) RegisterValue() uint16 {
|
||||
return uint16(s) << 3
|
||||
}
|
||||
|
||||
// Mode is the operating mode.
|
||||
type Mode int8
|
||||
|
||||
const (
|
||||
ModePowerDown Mode = 0 // power-down
|
||||
ModeTrigShunt Mode = 1 // triggered shunt voltage
|
||||
ModeTrigBus Mode = 2 // triggered bus voltage
|
||||
ModeTrigShuntBus Mode = 3 // triggered shunt and bus voltage
|
||||
ModeADCOff Mode = 4 // ADC off
|
||||
ModeContShunt Mode = 5 // continuous shunt voltage
|
||||
ModeContBus Mode = 6 // continuous bus voltage
|
||||
ModeContShuntBus Mode = 7 // continuous shunt and bus voltage
|
||||
)
|
||||
|
||||
// ModeTriggered is a mask for triggered modes.
|
||||
const ModeTriggeredMask Mode = 0x4
|
||||
|
||||
// ModeTriggered returns true if the mode is a triggered mode.
|
||||
func ModeTriggered(m Mode) bool {
|
||||
return m != ModePowerDown && m&ModeTriggeredMask == 0
|
||||
}
|
||||
|
||||
func (m Mode) RegisterValue() uint16 {
|
||||
return uint16(m)
|
||||
}
|
||||
|
||||
// Calibration is the calibration register for the INA219. Values from:
|
||||
// https://github.com/adafruit/Adafruit_INA219/blob/master/Adafruit_INA219.cpp
|
||||
type Calibration uint16
|
||||
|
||||
const (
|
||||
Calibration32V2A Calibration = 4096
|
||||
Calibration32V1A Calibration = 10240
|
||||
Calibration16V400mA Calibration = 8192
|
||||
)
|
||||
|
||||
func (c Calibration) RegisterValue() uint16 {
|
||||
return uint16(c)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ina219
|
||||
|
||||
type ErrOverflow struct{}
|
||||
|
||||
func (e ErrOverflow) Error() string { return "overflow" }
|
||||
|
||||
type ErrNotReady struct{}
|
||||
|
||||
func (e ErrNotReady) Error() string { return "not ready" }
|
||||
|
||||
type ErrConfigMismatch struct{}
|
||||
|
||||
func (e ErrConfigMismatch) Error() string { return "config mismatch" }
|
||||
@@ -0,0 +1,202 @@
|
||||
package ina219
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// An INA219 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
config Config
|
||||
}
|
||||
|
||||
// Create a new INA219 device with the default configuration
|
||||
// and the given I2C bus at the default address.
|
||||
//
|
||||
// Set Address after New to change the address.
|
||||
//
|
||||
// Call Configure after New to write the configuration to the
|
||||
// device. If you don't call Configure, the device may have a
|
||||
// different configuration and the power divider and current
|
||||
// multiplier are probably wrong.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: Address,
|
||||
config: Config32V2A,
|
||||
}
|
||||
}
|
||||
|
||||
// Set the configuration for the device. This only changes the
|
||||
// configuration in memory, not on the device. Call Configure
|
||||
// to write the configuration to the device.
|
||||
func (d *Device) SetConfig(config Config) {
|
||||
d.config = config
|
||||
}
|
||||
|
||||
// Write the current configuration to the device.
|
||||
func (d *Device) Configure() (err error) {
|
||||
if err = d.WriteRegister(
|
||||
RegConfig,
|
||||
d.config.RegisterValue(),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = d.WriteRegister(
|
||||
RegCalibration,
|
||||
d.config.Calibration.RegisterValue(),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var readConfig Config
|
||||
// make sure the configuration is read back correctly
|
||||
if readConfig, err = d.ReadConfig(); err != nil {
|
||||
return
|
||||
} else if readConfig.RegisterValue() != d.config.RegisterValue() {
|
||||
err = ErrConfigMismatch{}
|
||||
} else if readConfig.Calibration.RegisterValue() != d.config.Calibration.RegisterValue() {
|
||||
err = ErrConfigMismatch{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Trigger a conversion. This is only necessary if the device is in
|
||||
// trigger mode. In continuous mode (the default), the device will
|
||||
// automatically trigger conversions and this has no effect. See
|
||||
// config.go.
|
||||
//
|
||||
// Triggering a conversion or reading the "power" register resets
|
||||
// the conversion ready bit.
|
||||
func (d *Device) Trigger() (err error) {
|
||||
// Only trigger if the mode is one of the triggered modes.
|
||||
if ModeTriggered(d.config.Mode) {
|
||||
err = d.WriteRegister(RegConfig, d.config.RegisterValue())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Measurements reads the bus voltage, shunt voltage, current, and power
|
||||
// from the device.
|
||||
func (d *Device) Measurements() (
|
||||
busVoltage int16,
|
||||
shuntVoltage int16,
|
||||
current float32,
|
||||
power float32,
|
||||
err error,
|
||||
) {
|
||||
// Attempt to read bus voltage first, so we can check for overflow
|
||||
// or conversion not ready.
|
||||
if busVoltage, err = d.BusVoltage(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Read the rest of the values, reading Power last, which resets
|
||||
// the conversion ready bit (relevant for triggered modes).
|
||||
if shuntVoltage, err = d.ShuntVoltage(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if current, err = d.Current(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if power, err = d.Power(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BusVoltage reads the "bus" voltage in millivolts.
|
||||
//
|
||||
// It returns an error if the value is invalid due to overflow
|
||||
// or if the conversion is not ready yet. In a continuous mode
|
||||
// there should always be a measurement available after the
|
||||
// device is ready. See above notes on Trigger.
|
||||
func (d *Device) BusVoltage() (voltage int16, err error) {
|
||||
val, err := d.ReadRegister(RegBusVoltage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// The overflow bit is set, so the values are invalid.
|
||||
if val&(1<<0) != 0 {
|
||||
err = ErrOverflow{}
|
||||
return
|
||||
}
|
||||
|
||||
// The conversion is not ready yet.
|
||||
if ModeTriggered(d.config.Mode) && val&(1<<1) != 0 {
|
||||
err = ErrNotReady{}
|
||||
return
|
||||
}
|
||||
|
||||
voltage = (int16(val) >> 3) * 4
|
||||
return
|
||||
}
|
||||
|
||||
// ShuntVoltage reads the "shunt" voltage in 100ths of a millivolt.
|
||||
func (d *Device) ShuntVoltage() (voltage int16, err error) {
|
||||
return d.ReadRegister(RegShuntVoltage)
|
||||
}
|
||||
|
||||
// Current reads the current in milliamps.
|
||||
func (d *Device) Current() (current float32, err error) {
|
||||
val, err := d.ReadRegister(RegCurrent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
current = float32(val) / d.config.CurrentDivider
|
||||
return
|
||||
}
|
||||
|
||||
// Power reads the power in milliwatts.
|
||||
func (d *Device) Power() (power float32, err error) {
|
||||
val, err := d.ReadRegister(RegPower)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
power = float32(val) * d.config.PowerMultiplier
|
||||
return
|
||||
}
|
||||
|
||||
// Read the configuration from the device.
|
||||
func (d *Device) ReadConfig() (config Config, err error) {
|
||||
var cfg, cal int16
|
||||
if cfg, err = d.ReadRegister(RegConfig); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cal, err = d.ReadRegister(RegCalibration); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
config = NewConfig(cfg, cal)
|
||||
return
|
||||
}
|
||||
|
||||
// Read a register from the device.
|
||||
func (d *Device) ReadRegister(reg uint8) (val int16, err error) {
|
||||
buf := make([]byte, 2)
|
||||
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), reg, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
val = int16(buf[0])<<8 | int16(buf[1]&0xff)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to a register on the device.
|
||||
func (d *Device) WriteRegister(reg uint8, val uint16) error {
|
||||
buf := []byte{byte(val >> 8), byte(val & 0xff)}
|
||||
return legacy.WriteRegister(d.bus, uint8(d.Address), reg, buf)
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package ina219
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"tinygo.org/x/drivers/tester"
|
||||
)
|
||||
|
||||
func TestDefaultAddress(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
dev := New(bus)
|
||||
c.Assert(dev.Address, qt.Equals, uint16(Address))
|
||||
}
|
||||
|
||||
func TestBusVoltage(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegBusVoltage: (4200 << 3) / 4, // 4.2V
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
voltage, err := dev.BusVoltage()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(voltage, qt.Equals, int16(4200))
|
||||
})
|
||||
|
||||
t.Run("overflow", func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegBusVoltage: (1 >> 0), // overflow
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
_, err := dev.BusVoltage()
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
c.Assert(err, qt.ErrorMatches, ErrOverflow{}.Error())
|
||||
})
|
||||
|
||||
t.Run("not ready", func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegBusVoltage: ((4200 << 3) / 4) | (1 << 1), // not ready
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
dev.config.Mode = ModeTrigBus
|
||||
_, err := dev.BusVoltage()
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
c.Assert(err, qt.ErrorMatches, ErrNotReady{}.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestShuntVoltage(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegShuntVoltage: 0x1234,
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
voltage, err := dev.ShuntVoltage()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(voltage, qt.Equals, int16(0x1234))
|
||||
}
|
||||
|
||||
func TestCurrent(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegCurrent: 420 * 6.9, // 420mA
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
dev.config.CurrentDivider = 6.9
|
||||
current, err := dev.Current()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(current, qt.Equals, float32(420))
|
||||
}
|
||||
|
||||
func TestPower(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegPower: 420 / 0.8, // 420mW
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
dev.config.PowerMultiplier = 0.8
|
||||
power, err := dev.Power()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(power, qt.Equals, float32(420))
|
||||
}
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
// use the default configurations
|
||||
for _, tc := range []Config{
|
||||
Config16V400mA,
|
||||
Config32V2A,
|
||||
Config32V1A,
|
||||
} {
|
||||
n := fmt.Sprintf("%x/%x", tc.RegisterValue(), tc.Calibration.RegisterValue())
|
||||
t.Run(n, func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegConfig: tc.RegisterValue(),
|
||||
RegCalibration: tc.Calibration.RegisterValue(),
|
||||
}
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
config, err := dev.ReadConfig()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(config.BusADC, qt.Equals, tc.BusADC)
|
||||
c.Assert(config.BusVoltageRange, qt.Equals, tc.BusVoltageRange)
|
||||
c.Assert(config.Calibration, qt.Equals, tc.Calibration)
|
||||
c.Assert(config.Mode, qt.Equals, tc.Mode)
|
||||
c.Assert(config.PGA, qt.Equals, tc.PGA)
|
||||
c.Assert(config.ShuntADC, qt.Equals, tc.ShuntADC)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteConfig(t *testing.T) {
|
||||
// use the default configurations
|
||||
for _, tc := range []Config{
|
||||
Config16V400mA,
|
||||
Config32V2A,
|
||||
Config32V1A,
|
||||
} {
|
||||
n := fmt.Sprintf("%x/%x", tc.RegisterValue(), tc.Calibration.RegisterValue())
|
||||
t.Run(n, func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
bus.AddDevice(fake)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegConfig: 0,
|
||||
RegCalibration: 0,
|
||||
}
|
||||
|
||||
dev := New(bus)
|
||||
dev.config = tc
|
||||
err := dev.Configure()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(fake.Registers[RegConfig], qt.Equals, tc.RegisterValue())
|
||||
c.Assert(fake.Registers[RegCalibration], qt.Equals, tc.Calibration.RegisterValue())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetConfig(t *testing.T) {
|
||||
for _, tc := range []Config{
|
||||
Config16V400mA,
|
||||
Config32V2A,
|
||||
Config32V1A,
|
||||
} {
|
||||
n := fmt.Sprintf("%x/%x", tc.RegisterValue(), tc.Calibration.RegisterValue())
|
||||
t.Run(n, func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
dev := New(bus)
|
||||
dev.SetConfig(tc)
|
||||
c.Assert(dev.config, qt.Equals, tc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrigger(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
bus.AddDevice(fake)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegConfig: Config32V2A.RegisterValue(),
|
||||
}
|
||||
|
||||
dev := New(bus)
|
||||
dev.config = Config32V2A
|
||||
dev.config.Mode = ModeTrigBus
|
||||
err := dev.Trigger()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(fake.Registers[RegConfig], qt.Equals, dev.config.RegisterValue())
|
||||
}
|
||||
|
||||
func TestMeasurements(t *testing.T) {
|
||||
bvVal := int16(4200)
|
||||
svVal := int16(1234)
|
||||
iVal := float32(420)
|
||||
pVal := float32(420)
|
||||
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
bus.AddDevice(fake)
|
||||
fake.Registers = map[uint8]uint16{
|
||||
RegBusVoltage: uint16(((4200 << 3) / 4) | (1 << 1)),
|
||||
RegShuntVoltage: uint16(svVal),
|
||||
RegCurrent: uint16(iVal * Config16V400mA.CurrentDivider),
|
||||
RegPower: uint16(pVal / Config16V400mA.PowerMultiplier),
|
||||
}
|
||||
|
||||
dev := New(bus)
|
||||
dev.config = Config16V400mA
|
||||
bv, sv, i, p, err := dev.Measurements()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(bv, qt.Equals, bvVal)
|
||||
c.Assert(sv, qt.Equals, svVal)
|
||||
c.Assert(i, qt.Equals, iVal)
|
||||
c.Assert(p, qt.Equals, pVal)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ina219
|
||||
|
||||
// The default I2C address for this device.
|
||||
const Address = 0x40
|
||||
|
||||
const (
|
||||
RegConfig uint8 = 0x0
|
||||
RegShuntVoltage uint8 = 0x1
|
||||
RegBusVoltage uint8 = 0x2
|
||||
RegPower uint8 = 0x3
|
||||
RegCurrent uint8 = 0x4
|
||||
RegCalibration uint8 = 0x5
|
||||
)
|
||||
+35
-24
@@ -8,7 +8,6 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -26,7 +25,7 @@ type Device struct {
|
||||
accelSampleRate AccelSampleRate
|
||||
gyroRange GyroRange
|
||||
gyroSampleRate GyroSampleRate
|
||||
buf [6]uint8
|
||||
buf [7]uint8 // up to 6 bytes for read + 1 byte for the register address
|
||||
}
|
||||
|
||||
// Configuration for LSM6DS3TR device.
|
||||
@@ -84,30 +83,20 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
d.gyroSampleRate = GYRO_SR_104
|
||||
}
|
||||
|
||||
data := d.buf[:1]
|
||||
|
||||
// Configure accelerometer
|
||||
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
err = d.writeByte(CTRL1_XL, uint8(d.accelRange)|uint8(d.accelSampleRate))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set ODR bit
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
|
||||
data[0] |= BW_SCAL_ODR_ENABLED
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL4_C, data)
|
||||
// Enable ODR scaling
|
||||
err = d.setBits(CTRL4_C, BW_SCAL_ODR_ENABLED)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure gyroscope
|
||||
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
|
||||
err = d.writeByte(CTRL2_G, uint8(d.gyroRange)|uint8(d.gyroSampleRate))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -118,8 +107,10 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// Connected returns whether a LSM6DS3TR has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := d.buf[:1]
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
data, err := d.readBytes(WHO_AM_I, 1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return data[0] == 0x6A
|
||||
}
|
||||
|
||||
@@ -128,8 +119,7 @@ func (d *Device) Connected() bool {
|
||||
// and the sensor is not moving the returned value will be around 1000000 or
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_XL, data)
|
||||
data, err := d.readBytes(OUTX_L_XL, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -153,8 +143,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
// rotation along one axis and while doing so integrate all values over time,
|
||||
// you would get a value close to 360000000.
|
||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
|
||||
data, err := d.readBytes(OUTX_L_G, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -177,8 +166,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data := d.buf[:2]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
|
||||
data, err := d.readBytes(OUT_TEMP_L, 2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -187,3 +175,26 @@ func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/32
|
||||
return
|
||||
}
|
||||
|
||||
func (d *Device) readBytes(reg, size uint8) ([]byte, error) {
|
||||
d.buf[0] = reg
|
||||
err := d.bus.Tx(d.Address, d.buf[0:1], d.buf[1:size+1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.buf[1 : size+1], nil
|
||||
}
|
||||
|
||||
func (d *Device) writeByte(reg, value uint8) error {
|
||||
d.buf[0] = reg
|
||||
d.buf[1] = value
|
||||
return d.bus.Tx(d.Address, d.buf[0:2], nil)
|
||||
}
|
||||
|
||||
func (d *Device) setBits(reg, bits uint8) error {
|
||||
data, err := d.readBytes(reg, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.writeByte(reg, (data[0]&^bits)|bits)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6675.pdf
|
||||
package max6675
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// ErrThermocoupleOpen is returned when the thermocouple input is open.
|
||||
// i.e. not attached or faulty
|
||||
var ErrThermocoupleOpen = errors.New("thermocouple input open")
|
||||
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
cs drivers.Pin
|
||||
}
|
||||
|
||||
// Create a new Device to read from a MAX6675 thermocouple.
|
||||
// Pins must be configured before use. Frequency for SPI
|
||||
// should be 4.3MHz maximum.
|
||||
func NewDevice(bus drivers.SPI, cs drivers.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
// Read and return the temperature in celsius
|
||||
func (d *Device) Read() (float32, error) {
|
||||
var (
|
||||
read []byte = []byte{0, 0}
|
||||
value uint16
|
||||
)
|
||||
|
||||
d.cs.Low()
|
||||
if err := d.bus.Tx([]byte{0, 0}, read); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.cs.High()
|
||||
|
||||
// datasheet: Bit D2 is normally low and goes high if the thermocouple input is open.
|
||||
if read[1]&0x04 == 0x04 {
|
||||
return 0, ErrThermocoupleOpen
|
||||
}
|
||||
|
||||
// data is 12 bits, split across the two bytes
|
||||
// -XXXXXXX XXXXX---
|
||||
value = (uint16(read[0]) << 5) | (uint16(read[1]) >> 3)
|
||||
|
||||
return float32(value) * 0.25, nil
|
||||
}
|
||||
+5
-8
@@ -3,29 +3,26 @@
|
||||
package max72xx
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
cs machine.Pin
|
||||
bus drivers.SPI
|
||||
cs drivers.Pin
|
||||
}
|
||||
|
||||
// NewDriver creates a new max7219 connection. The SPI wire must already be configured
|
||||
// The SPI frequency must not be higher than 10MHz.
|
||||
// parameter cs: the datasheet also refers to this pin as "load" pin.
|
||||
func NewDevice(bus machine.SPI, cs machine.Pin) *Device {
|
||||
func NewDevice(bus drivers.SPI, cs drivers.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure setups the pins.
|
||||
// Configure expects that the pin of the Device has already been configured by the user as output.
|
||||
func (driver *Device) Configure() {
|
||||
outPutConfig := machine.PinConfig{Mode: machine.PinOutput}
|
||||
|
||||
driver.cs.Configure(outPutConfig)
|
||||
}
|
||||
|
||||
// SetScanLimit sets the scan limit. Maximum is 8.
|
||||
|
||||
+4
-5
@@ -8,7 +8,6 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
// Device wraps MCP2515 SPI CAN Module.
|
||||
type Device struct {
|
||||
spi SPI
|
||||
cs machine.Pin
|
||||
cs drivers.Pin
|
||||
msg *CANMsg
|
||||
mcpMode byte
|
||||
}
|
||||
@@ -36,7 +35,7 @@ const (
|
||||
)
|
||||
|
||||
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
|
||||
func New(b drivers.SPI, csPin machine.Pin) *Device {
|
||||
func New(b drivers.SPI, csPin drivers.Pin) *Device {
|
||||
d := &Device{
|
||||
spi: SPI{
|
||||
bus: b,
|
||||
@@ -50,9 +49,9 @@ func New(b drivers.SPI, csPin machine.Pin) *Device {
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
// Configure sets up the device for communication. It expects the SPI interface to be already
|
||||
// configured, and the CS configured as output.
|
||||
func (d *Device) Configure() {
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
const beginTimeoutValue int = 10
|
||||
|
||||
@@ -64,7 +64,7 @@ func (d *Device) Read(r []int32) (int, error) {
|
||||
count := len(r)
|
||||
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(d.buf)
|
||||
machine.I2S0.ReadStereo(d.buf)
|
||||
|
||||
if len(r) > len(d.buf) {
|
||||
count = len(d.buf)
|
||||
@@ -83,7 +83,7 @@ func (d *Device) ReadWithFilter(r []int32) (int, error) {
|
||||
for i := 0; i < len(r); i++ {
|
||||
|
||||
// get the next group of samples
|
||||
machine.I2S0.Read(d.buf)
|
||||
machine.I2S0.ReadStereo(d.buf)
|
||||
|
||||
// filter
|
||||
sum = applySincFilter(d.buf)
|
||||
|
||||
+2
-2
@@ -35,8 +35,8 @@ var (
|
||||
var (
|
||||
ErrFamilyNotSupported = errors.New("Address family not supported")
|
||||
ErrProtocolNotSupported = errors.New("Socket protocol/type not supported")
|
||||
ErrStartingDHCPClient = errors.New("Error starting DHPC client")
|
||||
ErrStartingDHCPServer = errors.New("Error starting DHPC server")
|
||||
ErrStartingDHCPClient = errors.New("Error starting DHCP client")
|
||||
ErrStartingDHCPServer = errors.New("Error starting DHCP server")
|
||||
ErrNoMoreSockets = errors.New("No more sockets")
|
||||
ErrClosingSocket = errors.New("Error closing socket")
|
||||
ErrNotSupported = errors.New("Not supported")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build comboat_fw
|
||||
|
||||
package probe
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/comboat"
|
||||
"tinygo.org/x/drivers/netdev"
|
||||
"tinygo.org/x/drivers/netlink"
|
||||
)
|
||||
|
||||
func Probe() (netlink.Netlinker, netdev.Netdever) {
|
||||
|
||||
cfg := comboat.Config{
|
||||
BaudRate: 115200,
|
||||
Uart: machine.UART1,
|
||||
Tx: machine.UART1_TX_PIN,
|
||||
Rx: machine.UART1_RX_PIN,
|
||||
}
|
||||
|
||||
combo := comboat.NewDevice(&cfg)
|
||||
netdev.UseNetdev(combo)
|
||||
|
||||
return combo, combo
|
||||
}
|
||||
+17
-9
@@ -27,8 +27,9 @@ type Config struct{}
|
||||
|
||||
// Errors list
|
||||
var (
|
||||
errNoPresence = errors.New("Error: OneWire. No devices on the bus.")
|
||||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||||
errNoPresence = errors.New("Error: OneWire. No devices on the bus.")
|
||||
errTooManyDevices = errors.New("Error: OneWire. Too many devices on the bus.")
|
||||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||||
)
|
||||
|
||||
// New creates a new GPIO 1-Wire connection.
|
||||
@@ -46,7 +47,7 @@ func (d *Device) Configure(config Config) {}
|
||||
func (d Device) Reset() error {
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
time.Sleep(480 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(70 * time.Microsecond)
|
||||
precence := d.p.Get()
|
||||
time.Sleep(410 * time.Microsecond)
|
||||
@@ -61,11 +62,11 @@ func (d Device) WriteBit(data uint8) {
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
if data&1 == 1 { // Send '1'
|
||||
time.Sleep(5 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
} else { // Send '0'
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(5 * time.Microsecond)
|
||||
}
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func (d Device) Write(data uint8) {
|
||||
func (d Device) ReadBit() (data uint8) {
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
time.Sleep(3 * time.Microsecond)
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(8 * time.Microsecond)
|
||||
if d.p.Get() {
|
||||
data = 1
|
||||
@@ -111,7 +112,7 @@ func (d Device) ReadAddress() ([]uint8, error) {
|
||||
for i := 0; i < 8; i++ {
|
||||
romid[i] = d.Read()
|
||||
}
|
||||
if d.Сrc8(romid, 7) != romid[7] {
|
||||
if d.Сrc8(romid) != 0 {
|
||||
return nil, errReadAddress
|
||||
}
|
||||
return romid, nil
|
||||
@@ -187,15 +188,22 @@ func (d Device) Search(cmd uint8) ([][]uint8, error) {
|
||||
}
|
||||
d.WriteBit(bit)
|
||||
}
|
||||
if d.Сrc8(lastAddress) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
lastFork = lastZero
|
||||
copy(romIDs[romIndex], lastAddress)
|
||||
romIndex++
|
||||
if romIndex >= 32 {
|
||||
return romIDs, errTooManyDevices
|
||||
}
|
||||
}
|
||||
return romIDs[:romIndex:romIndex], nil
|
||||
}
|
||||
|
||||
// Crc8 compute a Dallas Semiconductor 8 bit CRC.
|
||||
func (d Device) Сrc8(buffer []uint8, size int) (crc uint8) {
|
||||
func (_ Device) Сrc8(buffer []uint8) (crc uint8) {
|
||||
// Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0
|
||||
// Tiny 2x16 entry CRC table created by Arjen Lentz
|
||||
// See http://lentz.com.au/blog/calculating-crc-with-a-tiny-32-entry-lookup-table
|
||||
@@ -205,7 +213,7 @@ func (d Device) Сrc8(buffer []uint8, size int) (crc uint8) {
|
||||
0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,
|
||||
0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74,
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
for i := 0; i < len(buffer); i++ {
|
||||
crc = buffer[i] ^ crc // just re-using crc as intermediate
|
||||
crc = crc8_table[crc&0x0f] ^ crc8_table[16+((crc>>4)&0x0f)]
|
||||
}
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@ import (
|
||||
)
|
||||
|
||||
type P1AM struct {
|
||||
bus machine.SPI
|
||||
bus *machine.SPI
|
||||
|
||||
slaveSelectPin, slaveAckPin, baseEnablePin machine.Pin
|
||||
|
||||
// SkipAutoConfig will skip loading a default configuration into each module.
|
||||
|
||||
+5
-6
@@ -6,7 +6,6 @@ package pcd8544 // import "tinygo.org/x/drivers/pcd8544"
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -15,9 +14,9 @@ import (
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
rstPin machine.Pin
|
||||
scePin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
rstPin drivers.Pin
|
||||
scePin drivers.Pin
|
||||
buffer []byte
|
||||
width int16
|
||||
height int16
|
||||
@@ -29,8 +28,8 @@ type Config struct {
|
||||
Height int16
|
||||
}
|
||||
|
||||
// New creates a new PCD8544 connection. The SPI bus must already be configured.
|
||||
func New(bus drivers.SPI, dcPin, rstPin, scePin machine.Pin) *Device {
|
||||
// New creates a new PCD8544 connection. The SPI bus and pins must already be configured.
|
||||
func New(bus drivers.SPI, dcPin, rstPin, scePin drivers.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Package pcf8591 implements a driver for the PCF8591 Analog to Digital/Digital to Analog Converter.
|
||||
//
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8591.pdf
|
||||
package pcf8591 // import "tinygo.org/x/drivers/pcf8591"
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps PCF8591 ADC functions.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
CH0 ADCPin
|
||||
CH1 ADCPin
|
||||
CH2 ADCPin
|
||||
CH3 ADCPin
|
||||
}
|
||||
|
||||
// ADCPin is the implementation of the ADConverter interface.
|
||||
type ADCPin struct {
|
||||
machine.Pin
|
||||
d *Device
|
||||
}
|
||||
|
||||
// New returns a new PCF8591 driver. Pass in a fully configured I2C bus.
|
||||
func New(b drivers.I2C) *Device {
|
||||
d := &Device{
|
||||
bus: b,
|
||||
Address: defaultAddress,
|
||||
}
|
||||
|
||||
// setup all channels
|
||||
d.CH0 = d.GetADC(0)
|
||||
d.CH1 = d.GetADC(1)
|
||||
d.CH2 = d.GetADC(2)
|
||||
d.CH3 = d.GetADC(3)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
func (d *Device) Configure() {
|
||||
}
|
||||
|
||||
// Read analog data from channel
|
||||
func (d *Device) Read(ch int) (uint16, error) {
|
||||
if ch < 0 || ch > 3 {
|
||||
return 0, errors.New("invalid channel for pcf8591 Read")
|
||||
}
|
||||
|
||||
return d.GetADC(ch).Get(), nil
|
||||
}
|
||||
|
||||
// GetADC returns an ADC for a specific channel.
|
||||
func (d *Device) GetADC(ch int) ADCPin {
|
||||
return ADCPin{machine.Pin(ch), d}
|
||||
}
|
||||
|
||||
// Get the current reading for a specific ADCPin.
|
||||
func (p ADCPin) Get() uint16 {
|
||||
// TODO: also implement DAC
|
||||
tx := make([]byte, 2)
|
||||
tx[0] = byte(p.Pin)
|
||||
|
||||
rx := make([]byte, 2)
|
||||
|
||||
// The result from the measurement triggered by the first write,
|
||||
// however, the second write is required to get the result.
|
||||
// See section 8.4 "A/D Conversion" in the datasheet for more info
|
||||
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||
|
||||
// scale result to 16bit value like other ADCs
|
||||
return uint16(rx[1]) << 8
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
func (p ADCPin) Configure() {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package pcf8591
|
||||
|
||||
// PCF8591 Default Address
|
||||
const defaultAddress = 0x48
|
||||
|
||||
// control bit for DAC
|
||||
const PCF8591_ENABLE_DAC = 0x40
|
||||
@@ -0,0 +1,10 @@
|
||||
package drivers
|
||||
|
||||
// Pin is a digital pin interface. It is notably implemented by the
|
||||
// machine.Pin type.
|
||||
type Pin interface {
|
||||
Get() bool
|
||||
High()
|
||||
Low()
|
||||
Set(high bool)
|
||||
}
|
||||
+43
-7
@@ -43,6 +43,40 @@ func NewImage[T Color](width, height int) Image[T] {
|
||||
}
|
||||
}
|
||||
|
||||
// NewImageFromBytes creates a new image of the given size using an existing data slice of bytes.
|
||||
func NewImageFromBytes[T Color](width, height int, buf []byte) Image[T] {
|
||||
if width < 0 || height < 0 || int(int16(width)) != width || int(int16(height)) != height {
|
||||
// The width/height are stored as 16-bit integers and should never be
|
||||
// negative.
|
||||
panic("NewImageFromBytes: width/height out of bounds")
|
||||
}
|
||||
var zeroColor T
|
||||
var data unsafe.Pointer
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Typical formats like RGB888 and RGB565.
|
||||
// Each color starts at a whole byte offset from the start.
|
||||
if len(buf) != width*height*int(unsafe.Sizeof(zeroColor)) {
|
||||
panic("NewImageFromBytes: data slice size mismatch")
|
||||
}
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
default:
|
||||
// Formats like RGB444 that have 12 bits per pixel.
|
||||
// We access these as bytes, so allocate the buffer as a byte slice.
|
||||
bufBits := width * height * zeroColor.BitsPerPixel()
|
||||
bufBytes := (bufBits + 7) / 8
|
||||
if len(buf) != bufBytes {
|
||||
panic("NewImageFromBytes: data slice size mismatch")
|
||||
}
|
||||
data = unsafe.Pointer(&buf[0])
|
||||
}
|
||||
return Image[T]{
|
||||
width: int16(width),
|
||||
height: int16(height),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Rescale returns a new Image buffer based on the img buffer.
|
||||
// The contents is undefined after the Rescale operation, and any modification
|
||||
// to the returned image will overwrite the underlying image buffer in undefined
|
||||
@@ -104,15 +138,16 @@ func (img Image[T]) setPixel(index int, c T) {
|
||||
switch {
|
||||
case zeroColor.BitsPerPixel() == 1:
|
||||
// Monochrome.
|
||||
x := index % int(img.width)
|
||||
y := index / int(img.width)
|
||||
offset := x + (y/8)*int(img.width)
|
||||
offset := index / 8
|
||||
bits := index % 8
|
||||
|
||||
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||
if c != zeroColor {
|
||||
*((*byte)(ptr)) |= 1 << uint8(y%8)
|
||||
*((*byte)(ptr)) |= (1 << (7 - uint8(bits)))
|
||||
} else {
|
||||
*((*byte)(ptr)) &^= 1 << uint8(y%8)
|
||||
*((*byte)(ptr)) &^= (1 << (7 - uint8(bits)))
|
||||
}
|
||||
|
||||
return
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Each color starts at a whole byte offset.
|
||||
@@ -166,9 +201,10 @@ func (img Image[T]) Get(x, y int) T {
|
||||
case zeroColor.BitsPerPixel() == 1:
|
||||
// Monochrome.
|
||||
var c Monochrome
|
||||
offset := x + (y/8)*int(img.width)
|
||||
offset := index / 8
|
||||
bits := index % 8
|
||||
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||
c = (*ptr >> uint8(y%8) & 0x1) == 1
|
||||
c = ((*ptr >> (7 - uint8(bits))) & 0x1) > 0
|
||||
return any(c).(T)
|
||||
case zeroColor.BitsPerPixel()%8 == 0:
|
||||
// Colors like RGB565, RGB888, etc.
|
||||
|
||||
+105
-15
@@ -66,9 +66,9 @@ func TestImageRGB444BE(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImageMonochrome(t *testing.T) {
|
||||
image := pixel.NewImage[pixel.Monochrome](5, 3)
|
||||
if width, height := image.Size(); width != 5 && height != 3 {
|
||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
||||
image := pixel.NewImage[pixel.Monochrome](128, 64)
|
||||
if width, height := image.Size(); width != 128 && height != 64 {
|
||||
t.Errorf("image.Size(): expected 128, 64 but got %d, %d", width, height)
|
||||
}
|
||||
for _, expected := range []color.RGBA{
|
||||
{R: 0xff, G: 0xff, B: 0xff},
|
||||
@@ -80,19 +80,101 @@ func TestImageMonochrome(t *testing.T) {
|
||||
{B: 0x00, A: 0xff},
|
||||
} {
|
||||
encoded := pixel.NewColor[pixel.Monochrome](expected.R, expected.G, expected.B)
|
||||
image.Set(4, 2, encoded)
|
||||
actual := image.Get(4, 2).RGBA()
|
||||
image.Set(5, 3, encoded)
|
||||
actual := image.Get(5, 3).RGBA()
|
||||
switch {
|
||||
case expected.R == 0 && expected.G == 0 && expected.B == 0:
|
||||
// should be false eg black
|
||||
if actual.R != 0 || actual.G != 0 || actual.B != 0 {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||
}
|
||||
case int(expected.R)+int(expected.G)+int(expected.B) > 128*3:
|
||||
// should be true eg white
|
||||
if actual.R == 0 || actual.G == 0 || actual.B == 0 {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||
}
|
||||
default:
|
||||
// should be false eg black
|
||||
if actual.R != 0 || actual.G != 0 || actual.B != 0 {
|
||||
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 128x128
|
||||
var rprofile = []byte{
|
||||
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00,
|
||||
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE8, 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xBF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x01, 0xF8, 0x00, 0x5F, 0xFF, 0xFF, 0xFC, 0x00, 0x02, 0x80, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x03, 0xFE, 0x03, 0xFF, 0xFD, 0xBF, 0xFF, 0x80, 0x1F, 0xE0, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x03, 0xFE, 0x01, 0xFF, 0xF7, 0x6B, 0xFF, 0x80, 0x1F, 0xC0, 0x1F, 0xFF, 0xFE, 0x02, 0xFF, 0xFC, 0x03, 0xDF, 0x17, 0xFA, 0x00, 0x00, 0x37, 0xF0, 0x3F, 0xE0, 0x1F, 0xFF, 0xD0,
|
||||
0x00, 0x07, 0xFC, 0x07, 0x07, 0xBF, 0x00, 0x00, 0x00, 0x01, 0xFE, 0xF8, 0x78, 0x3F, 0xF8, 0x00, 0x00, 0x01, 0xFC, 0x06, 0x1B, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xEA, 0x78, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x03, 0x1B, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE2, 0x68, 0x1F, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x07, 0x5B, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC4, 0x38, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xF8, 0x03, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x78, 0x3F, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x68, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x07, 0x9F, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x70, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x03, 0x9E, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3E, 0xE8, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE0, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x01, 0xFF, 0x55, 0xF8, 0x00, 0x00, 0x03, 0xEA, 0xFF, 0xC0, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x01, 0xFE, 0xAF, 0xF0, 0x00, 0x00, 0x03, 0xFD, 0xBF, 0xE0, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x01, 0xF8, 0x00, 0x7C, 0x00, 0x00, 0x07, 0x80, 0x03, 0xE0, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x03, 0xC1, 0xE0, 0x3E, 0x00, 0x00, 0x1F, 0x03, 0xC0, 0xF0, 0x1F, 0x80, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x82, 0xF8, 0x0F, 0x00, 0x00, 0x3E, 0x05, 0xE0, 0x7C, 0x1F, 0x80, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x07, 0x01, 0xF8, 0x17, 0x00, 0x00, 0x3C, 0x09, 0xE0, 0x78, 0x1F, 0xC0, 0x00, 0x00, 0x03, 0xFC, 0x0F, 0x06, 0xFC, 0x07, 0x80, 0x00, 0x7C, 0x1D, 0xE0, 0x3C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0xFF, 0xFC, 0x1E, 0x03, 0xFC, 0x03, 0x80, 0x00, 0x78, 0x1F, 0xE0, 0x1E, 0x1F, 0xFE, 0x80, 0x2F, 0xFF, 0xFC, 0x1C, 0x07, 0xF8, 0x01, 0x80, 0x00, 0x60, 0x1F, 0xE0, 0x16, 0x1F, 0xFF, 0xF8,
|
||||
0x1F, 0xFF, 0xF8, 0x3E, 0x07, 0xFC, 0x01, 0xC0, 0x00, 0xE8, 0x1F, 0xE0, 0x1E, 0x1F, 0xFF, 0xEC, 0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0xF8, 0x01, 0xC0, 0x00, 0xE0, 0x17, 0xE0, 0x07, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x38, 0x03, 0xE8, 0x00, 0xC0, 0x00, 0xC0, 0x07, 0xC0, 0x03, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x38, 0x00, 0xC0, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x78, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x38, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xF8, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x68, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x01, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x68, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0x00, 0x00, 0x01, 0x9F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x68, 0x00, 0x00, 0x00, 0xE0, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x38, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0x03, 0x9F, 0xFF, 0xFE,
|
||||
0x07, 0xFF, 0xFC, 0x78, 0x00, 0x00, 0x00, 0xC0, 0x80, 0xC0, 0x00, 0x00, 0x03, 0x1F, 0xFF, 0xF8, 0x00, 0x37, 0xF8, 0x38, 0x00, 0x00, 0x01, 0xC7, 0xF0, 0xE0, 0x00, 0x00, 0x03, 0x9F, 0xFE, 0x00,
|
||||
0x00, 0x5F, 0xFC, 0x38, 0x00, 0x00, 0x01, 0xC3, 0xE8, 0xE0, 0x00, 0x00, 0x03, 0x1F, 0xFB, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x01, 0xC7, 0xF8, 0xE0, 0x00, 0x00, 0x07, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x03, 0x9F, 0xFC, 0x70, 0x00, 0x00, 0x07, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x1E, 0x00, 0x00, 0x03, 0xBF, 0xFE, 0x78, 0x00, 0x00, 0x1E, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1E, 0x00, 0x00, 0x07, 0x9F, 0xFF, 0x78, 0x00, 0x00, 0x16, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x1E, 0x00, 0x00, 0x07, 0x73, 0xC3, 0x3C, 0x00, 0x00, 0x1E, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x60, 0x01, 0x9E, 0x00, 0x00, 0x3C, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x1F, 0x80, 0x00, 0x7E, 0x40, 0x01, 0x17, 0x80, 0x00, 0x7E, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1F, 0x80, 0x00, 0x3C, 0x40, 0x01, 0x9F, 0x00, 0x00, 0x7C, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x1F, 0xC0, 0x00, 0xFC, 0x40, 0x01, 0x07, 0xC0, 0x00, 0xFC, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x1F, 0xF8, 0x0B, 0xE8, 0x7B, 0xFF, 0x81, 0xF8, 0x03, 0xFC, 0x1F, 0x80, 0x00, 0x00, 0x03, 0xF8, 0x1C, 0xFF, 0xFF, 0xC0, 0x3F, 0xFE, 0x00, 0xFF, 0xFF, 0xDE, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x05, 0xFC, 0x1C, 0xFF, 0xFF, 0x80, 0x7F, 0xDE, 0x00, 0xFF, 0x7F, 0xDC, 0x1F, 0x80, 0x00, 0x00, 0xFF, 0xFC, 0x1C, 0x3F, 0xFE, 0x00, 0x0E, 0xB8, 0x00, 0x3F, 0xFF, 0x1C, 0x1F, 0xFC, 0x00,
|
||||
0x2F, 0xFF, 0xF8, 0x1C, 0x00, 0x00, 0x00, 0x04, 0x98, 0x00, 0x01, 0x00, 0x1E, 0x1F, 0xFF, 0xF4, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x06, 0x98, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x98, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x02, 0xB8, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x1C, 0x00, 0x00, 0x00, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x7F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFE,
|
||||
0x7F, 0xFF, 0xFC, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x1F, 0xFF, 0xFE, 0x0B, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xF8,
|
||||
0x00, 0x7F, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFE, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x03, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x38, 0x15, 0xB7, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xF8, 0x3C, 0x16, 0xAB, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x16, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x3F, 0xFB, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x1F, 0xE9, 0x00, 0x00, 0x01, 0xF7, 0x80, 0x00, 0x1E, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xF8, 0x3C, 0x03, 0x82, 0x1D, 0xC6, 0x39, 0xC0, 0x07, 0x00, 0x14, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0x01, 0xFC, 0x3C, 0x03, 0x87, 0x14, 0x85, 0x15, 0xC1, 0x03, 0x80, 0x1E, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x3C, 0x03, 0x87, 0x9F, 0xE6, 0x3D, 0xC0, 0x1F, 0xC0, 0x1C, 0x1F, 0xC0, 0x00,
|
||||
0x00, 0xBF, 0xF8, 0x3C, 0x03, 0x83, 0x9F, 0xE7, 0x3F, 0x8D, 0x1E, 0xC0, 0x16, 0x1F, 0xD4, 0x00, 0x17, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9E, 0xE7, 0x79, 0xD7, 0xBC, 0xE0, 0x1C, 0x1F, 0xFF, 0xE8,
|
||||
0x1F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9C, 0xE3, 0x3F, 0x9F, 0xBC, 0xE0, 0x1E, 0x1F, 0xFF, 0xD0, 0x3F, 0xFF, 0xF8, 0x3C, 0x03, 0x83, 0x9E, 0xE7, 0x79, 0xC7, 0xBC, 0xE0, 0x16, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0xBC, 0xE3, 0xF9, 0xC3, 0xBC, 0xE0, 0x1C, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9E, 0xEB, 0xE9, 0xE3, 0xBD, 0xE0, 0x1E, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xF8, 0x3C, 0x03, 0x83, 0x9C, 0xE3, 0xF1, 0xC3, 0x9C, 0xC0, 0x1C, 0x1F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0x9E, 0xE9, 0xE0, 0xFF, 0x9F, 0xC0, 0x16, 0x1F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x03, 0x83, 0xBC, 0x61, 0xE0, 0xFF, 0x07, 0xC0, 0x1E, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x1C, 0x3F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xF8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x1C, 0x1F, 0xFF, 0xFC,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x17, 0xC0, 0x00, 0x00, 0x00, 0x16, 0x3F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0xFF, 0xFE,
|
||||
0x3F, 0xFF, 0xFC, 0x3C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFE, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x7F, 0xFF, 0xFE,
|
||||
0x2F, 0xFF, 0xFF, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x5F, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x01, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
|
||||
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func TestImageFromBytesMonochrome(t *testing.T) {
|
||||
image := pixel.NewImageFromBytes[pixel.Monochrome](128, 128, rprofile)
|
||||
if width, height := image.Size(); width != 128 && height != 128 {
|
||||
t.Errorf("image.Size(): expected 128, 128 but got %d, %d", width, height)
|
||||
}
|
||||
|
||||
raw := image.RawBuffer()
|
||||
for i, b := range raw {
|
||||
if b != rprofile[i] {
|
||||
t.Fatalf("failed to roundtrip image. expected %v but got %v", rprofile[i], b)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,22 +183,30 @@ func TestImageMonochrome(t *testing.T) {
|
||||
// contain the same data afterwards.
|
||||
func TestImageNoise(t *testing.T) {
|
||||
t.Run("RGB888", func(t *testing.T) {
|
||||
testImageNoise[pixel.RGB888](t)
|
||||
testImageNoiseN[pixel.RGB888](t)
|
||||
})
|
||||
t.Run("RGB565BE", func(t *testing.T) {
|
||||
testImageNoise[pixel.RGB565BE](t)
|
||||
testImageNoiseN[pixel.RGB565BE](t)
|
||||
})
|
||||
t.Run("RGB555", func(t *testing.T) {
|
||||
testImageNoise[pixel.RGB555](t)
|
||||
testImageNoiseN[pixel.RGB555](t)
|
||||
})
|
||||
t.Run("RGB444BE", func(t *testing.T) {
|
||||
testImageNoise[pixel.RGB444BE](t)
|
||||
testImageNoiseN[pixel.RGB444BE](t)
|
||||
})
|
||||
t.Run("Monochrome", func(t *testing.T) {
|
||||
testImageNoise[pixel.Monochrome](t)
|
||||
testImageNoiseN[pixel.Monochrome](t)
|
||||
})
|
||||
}
|
||||
|
||||
// Run the testImageNoise multiple times, because a single test might not catch
|
||||
// all bugs (since the test uses random data).
|
||||
func testImageNoiseN[T pixel.Color](t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
testImageNoise[T](t)
|
||||
}
|
||||
}
|
||||
|
||||
func testImageNoise[T pixel.Color](t *testing.T) {
|
||||
// Create an image of a random width/height for extra testing.
|
||||
width := rand.Int()%500 + 10
|
||||
|
||||
@@ -102,3 +102,16 @@ func (s Servo) SetAngle(angle int) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAngleWithMicroseconds sets the angle of the servo in degrees. The angle should be between
|
||||
// 0 and 180, where 0 is the minimum angle and 180 is the maximum angle.
|
||||
// The high duration can be customized
|
||||
// 0° is lowMicroseconds(us), 180° is highMicroseconds(us)
|
||||
func (s Servo) SetAngleWithMicroseconds(angle int, lowMicroseconds, highMicroseconds int) error {
|
||||
if angle < 0 || angle > 180 {
|
||||
return ErrInvalidAngle
|
||||
}
|
||||
microseconds := lowMicroseconds + (highMicroseconds-lowMicroseconds)*angle/180
|
||||
s.SetMicroseconds(int16(microseconds))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
package sharpmem
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
const (
|
||||
bitWriteCmd uint8 = 0b00000001
|
||||
bitVcom uint8 = 0b00000010
|
||||
bitClear uint8 = 0b00000100
|
||||
)
|
||||
|
||||
var (
|
||||
ConfigLS010B7DH04 = Config{Width: 128, Height: 128}
|
||||
ConfigLS011B7DH03 = Config{Width: 160, Height: 68}
|
||||
ConfigLS012B7DD01 = Config{Width: 184, Height: 38}
|
||||
ConfigLS013B7DH03 = ConfigLS010B7DH04
|
||||
ConfigLS013B7DH05 = Config{Width: 144, Height: 168}
|
||||
ConfigLS018B7DH02 = Config{Width: 230, Height: 303}
|
||||
ConfigLS027B7DH01 = Config{Width: 400, Height: 240}
|
||||
ConfigLS027B7DH01A = ConfigLS027B7DH01
|
||||
ConfigLS032B7DD02 = Config{Width: 336, Height: 536}
|
||||
ConfigLS044Q7DH01 = Config{Width: 320, Height: 240}
|
||||
)
|
||||
|
||||
type Pin interface {
|
||||
High()
|
||||
Low()
|
||||
}
|
||||
|
||||
// Device represents a Sharp Memory Display device. This driver implementation
|
||||
// concerns the 1-bit color versions only (black and white memory displays).
|
||||
//
|
||||
// Supported SKUs include:
|
||||
// LS010B7DH04, LS011B7DH03, LS012B7DD01, LS013B7DH03, LS013B7DH05,
|
||||
// LS018B7DH02, LS027B7DH01, LS027B7DH01A, LS032B7DD02, LS044Q7DH01
|
||||
//
|
||||
// Note: Only SKU LS011B7DH03 (160x68) has been tested as of writing.
|
||||
//
|
||||
// The driver includes optimizations (frame and per-line invalidation) that
|
||||
// only transmit the changed lines to the display. These optimizations are on
|
||||
// by default, and they can be disabled with the respective config option.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
csPin Pin
|
||||
buffer []byte
|
||||
txBuf []byte
|
||||
lineDiff []byte
|
||||
width int16
|
||||
height int16
|
||||
bufferSize int16
|
||||
bytesPerLine int16
|
||||
vcom uint8
|
||||
diffing bool
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Width int16
|
||||
Height int16
|
||||
|
||||
// DisableOptimizations disables frame and line invalidation optimizations.
|
||||
// Useful if constant frame times are desired.
|
||||
DisableOptimizations bool
|
||||
}
|
||||
|
||||
// New creates a new device connection.
|
||||
// The SPI bus must have already been configured.
|
||||
func New(bus drivers.SPI, csPin Pin) Device {
|
||||
d := Device{
|
||||
bus: bus,
|
||||
csPin: csPin,
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure initializes the display with specified configuration. It can be
|
||||
// called multiple times on the same display, resetting its internal state.
|
||||
func (d *Device) Configure(cfg Config) {
|
||||
if cfg.Width == 0 {
|
||||
cfg.Width = 160
|
||||
}
|
||||
if cfg.Height == 0 {
|
||||
cfg.Height = 68
|
||||
}
|
||||
|
||||
d.width = cfg.Width
|
||||
d.height = cfg.Height
|
||||
d.diffing = !cfg.DisableOptimizations
|
||||
|
||||
d.initialize()
|
||||
}
|
||||
|
||||
// initialize properly initializes the display and the in-memory image buffers.
|
||||
func (d *Device) initialize() {
|
||||
d.csPin.Low()
|
||||
|
||||
// initialize VCOM as high
|
||||
d.vcom = bitVcom
|
||||
|
||||
// bytesPerLine has to be 16-bit aligned, as some resolutions require
|
||||
// padding to the nearest 2nd byte.
|
||||
d.bytesPerLine = ceilDiv(d.width, 16) * 2
|
||||
|
||||
// preallocate a contiguous byte buffer for all lines, including
|
||||
// protocol-required padding for each line apriori (easier to transfer).
|
||||
d.bufferSize = d.bytesPerLine * d.height
|
||||
d.buffer = make([]byte, d.bufferSize)
|
||||
// A bit being 1 is white (reflective), 0 is black (less reflective).
|
||||
for i := range d.buffer {
|
||||
d.buffer[i] = 0xff
|
||||
}
|
||||
|
||||
// auxiliary buffer for SPI transfers to avoid dynamic allocations
|
||||
d.txBuf = make([]byte, 2)
|
||||
|
||||
if d.diffing {
|
||||
// buffer to store the changed lines. First bit is whether any line has
|
||||
// changed at all (i.e. the frame is invalid), followed by N bits,
|
||||
// one for each line.
|
||||
d.lineDiff = make([]byte, bitfieldBufLen(1+d.height))
|
||||
}
|
||||
}
|
||||
|
||||
// SetPixel enables or disables a pixel in the buffer.
|
||||
// color.RGBA{0, 0, 0, 255} is considered transparent (reflective, white),
|
||||
// anything else will enable a pixel on the screen (make it appear less
|
||||
// reflective, black).
|
||||
func (d *Device) SetPixel(x, y int16, c color.RGBA) {
|
||||
if d.width == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// bounds check
|
||||
if x < 0 || x >= d.width || y < 0 || y >= d.height {
|
||||
return
|
||||
}
|
||||
|
||||
offset := y * d.bytesPerLine
|
||||
|
||||
div := offset + x/8
|
||||
mod := uint8(x % 8)
|
||||
|
||||
prev := hasBit(d.buffer[div], mod)
|
||||
curr := c.R == 0 && c.G == 0 && c.B == 0 && c.A == 255
|
||||
|
||||
if prev == curr {
|
||||
return
|
||||
}
|
||||
|
||||
if curr {
|
||||
d.buffer[div] = setBit(d.buffer[div], mod)
|
||||
} else {
|
||||
d.buffer[div] = unsetBit(d.buffer[div], mod)
|
||||
}
|
||||
|
||||
if d.diffing {
|
||||
d.invalidateLine(y)
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
func (d *Device) Size() (x, y int16) {
|
||||
return d.width, d.height
|
||||
}
|
||||
|
||||
// Display renders the buffer to the screen. It only transmits changed lines if
|
||||
// optimizations are enabled. It should be called at >=1hz, even if the
|
||||
// buffer hasn't been modified.
|
||||
func (d *Device) Display() error {
|
||||
if d.width == 0 {
|
||||
return errors.New("display not configured")
|
||||
}
|
||||
|
||||
if d.diffing {
|
||||
if !hasBit(d.lineDiff[0], 0) {
|
||||
// no pixels have been modified, simply toggle VCOM
|
||||
return d.holdDisplay()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for i := 0; i < len(d.lineDiff); i++ {
|
||||
d.lineDiff[i] = 0x00
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
cmd := bitWriteCmd | d.vcom
|
||||
|
||||
d.toggleVcom()
|
||||
|
||||
// Padding to use for high bits of line numbers that overflow 8 bits.
|
||||
var hiPad = uint8(0)
|
||||
if d.height >= 512 {
|
||||
hiPad = 3 + 3 // 3 mode bits + 3 low bits
|
||||
} else if d.height >= 256 {
|
||||
hiPad = 3 + 4 // 3 mode bits + 4 low bits
|
||||
}
|
||||
|
||||
// start transfer
|
||||
d.csPin.High()
|
||||
|
||||
for i := int16(0); i < d.height; i++ {
|
||||
if d.diffing {
|
||||
// Skip rendering lines that haven't changed.
|
||||
linediv := (i + 1) / 8
|
||||
linemod := uint8((i + 1) % 8)
|
||||
if !hasBit(d.lineDiff[linediv], linemod) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// The first 5 bits are either dummy or part of the current line
|
||||
// (1-indexed) if it overflows 8-bits.
|
||||
// The last 3 bits are the command for the first line and dummy bits
|
||||
// for subsequent lines (set as command for simplicity)
|
||||
hi := uint8((i + 1) >> 8)
|
||||
hi = hi << hiPad
|
||||
d.txBuf[0] = cmd | hi
|
||||
|
||||
// The second byte is the low bits of the current line (1-indexed).
|
||||
// for <8 bits cases, the high bits are dummy, so we leave them as 0.
|
||||
d.txBuf[1] = uint8(i + 1)
|
||||
|
||||
// send the first two bytes
|
||||
err := d.bus.Tx(d.txBuf, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send the line data
|
||||
err = d.bus.Tx(d.buffer[i*d.bytesPerLine:(i+1)*d.bytesPerLine], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Trailer 16 bits (low)
|
||||
d.txBuf[0] = 0x00
|
||||
d.txBuf[1] = 0x00
|
||||
err := d.bus.Tx(d.txBuf, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// end transfer
|
||||
d.csPin.Low()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// holdDisplay simply toggles VCOM without updating any lines.
|
||||
func (d *Device) holdDisplay() error {
|
||||
d.txBuf[0] = d.vcom
|
||||
d.txBuf[1] = 0x00
|
||||
|
||||
d.toggleVcom()
|
||||
|
||||
// begin transaction
|
||||
d.csPin.High()
|
||||
|
||||
err := d.bus.Tx(d.txBuf, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// end transaction
|
||||
d.csPin.Low()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear clears both the in-memory buffer and the display.
|
||||
func (d *Device) Clear() error {
|
||||
if d.width == 0 {
|
||||
return errors.New("display not configured")
|
||||
}
|
||||
|
||||
d.ClearBuffer()
|
||||
return d.ClearDisplay()
|
||||
}
|
||||
|
||||
// ClearBuffer clears the in-memory buffer. The display is not updated.
|
||||
func (d *Device) ClearBuffer() {
|
||||
if d.width == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if d.diffing {
|
||||
// detect what rows need to be reset on the next render
|
||||
d.invalidateModifiedLines()
|
||||
}
|
||||
|
||||
// reset the in-memory buffer
|
||||
for i := 0; i < len(d.buffer); i++ {
|
||||
d.buffer[i] = 0xff
|
||||
}
|
||||
}
|
||||
|
||||
// invalidateModifiedLines marks any line that has at least a single black pixel
|
||||
// as invalidated. Padding bits, if any, are always 1.
|
||||
func (d *Device) invalidateModifiedLines() {
|
||||
for y := int16(0); y < d.height; y++ {
|
||||
offset := y * d.bytesPerLine
|
||||
|
||||
updateLine := false
|
||||
for x := int16(0); x < d.width; x++ {
|
||||
div := offset + x/8
|
||||
mod := uint8(x % 8)
|
||||
|
||||
if !hasBit(d.buffer[div], mod) {
|
||||
updateLine = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if updateLine {
|
||||
d.invalidateLine(y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearDisplay clears the display. The in-memory buffer is not updated. A
|
||||
// subsequent call to Display() will re-render the content as it was before
|
||||
// clearing.
|
||||
func (d *Device) ClearDisplay() error {
|
||||
if d.width == 0 {
|
||||
return errors.New("display not configured")
|
||||
}
|
||||
|
||||
d.txBuf[0] = d.vcom | bitClear
|
||||
d.txBuf[1] = 0x00
|
||||
|
||||
d.toggleVcom()
|
||||
|
||||
// begin transaction
|
||||
d.csPin.High()
|
||||
|
||||
err := d.bus.Tx(d.txBuf, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// end transaction
|
||||
d.csPin.Low()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// invalidateLine marks a line and the frame itself as invalidated.
|
||||
func (d *Device) invalidateLine(line int16) {
|
||||
// mark the frame as invalidated
|
||||
d.lineDiff[0] = setBit(d.lineDiff[0], 0)
|
||||
|
||||
// mark the line as invalidated
|
||||
linediv := (line + 1) / 8
|
||||
linemod := uint8((line + 1) % 8)
|
||||
d.lineDiff[linediv] = setBit(d.lineDiff[linediv], linemod)
|
||||
}
|
||||
|
||||
// toggleVcom toggles the VCOM, as is instructed by the datasheet.
|
||||
// Toggling VCOM can help maintain the display's longevity. It should ideally
|
||||
// be called at least once per second, preferably at 4-100 Hz.
|
||||
// Toggling VCOM causes a tiny bit of flicker, but without it the pixels can
|
||||
// be permanently damaged by the DC bias accumulating over time.
|
||||
func (d *Device) toggleVcom() {
|
||||
if d.vcom != 0 {
|
||||
d.vcom = 0x00
|
||||
} else {
|
||||
d.vcom = bitVcom
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package sharpmem
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math/rand/v2"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func Test_setBit(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
v := uint8(1) << i
|
||||
|
||||
c.Assert(setBit(0x00, i), qt.Equals, v)
|
||||
c.Assert(setBit(0x00, (i+1)%8), qt.Not(qt.Equals), v)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_unsetBit(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
v := uint8(1) << i
|
||||
|
||||
c.Assert(unsetBit(v, i), qt.Equals, uint8(0x00))
|
||||
c.Assert(unsetBit(v, (i+1)%8), qt.Not(qt.Equals), uint8(0x00))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_hasBit(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
v := uint8(1) << i
|
||||
|
||||
c.Assert(hasBit(v, i), qt.Equals, true)
|
||||
c.Assert(hasBit(v, (i+1)%8), qt.Equals, false)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_bitfieldBufLen(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for i := int16(1); i < 536; i++ {
|
||||
requiredBufferSize := i / 8
|
||||
wouldOverflow := i % 8
|
||||
|
||||
if wouldOverflow > 0 {
|
||||
requiredBufferSize += 1
|
||||
}
|
||||
|
||||
c.Assert(bitfieldBufLen(i), qt.Equals, requiredBufferSize)
|
||||
}
|
||||
}
|
||||
|
||||
type mockBus struct {
|
||||
b []byte
|
||||
}
|
||||
|
||||
func (m *mockBus) Tx(w, _ []byte) error {
|
||||
m.b = append(m.b, w...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockBus) Transfer(b byte) (byte, error) {
|
||||
m.b = append(m.b, b)
|
||||
return 0x00, nil
|
||||
}
|
||||
|
||||
type mockPin struct{}
|
||||
|
||||
func (m mockPin) High() {
|
||||
}
|
||||
|
||||
func (m mockPin) Low() {
|
||||
}
|
||||
|
||||
func Test_Device(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
cfgs := []Config{
|
||||
ConfigLS010B7DH04,
|
||||
ConfigLS011B7DH03,
|
||||
ConfigLS012B7DD01,
|
||||
ConfigLS013B7DH03,
|
||||
ConfigLS013B7DH05,
|
||||
ConfigLS018B7DH02,
|
||||
ConfigLS027B7DH01,
|
||||
ConfigLS027B7DH01A,
|
||||
ConfigLS032B7DD02,
|
||||
ConfigLS044Q7DH01,
|
||||
}
|
||||
|
||||
cfgLen := len(cfgs)
|
||||
for i := 0; i < cfgLen; i++ {
|
||||
cfgs = append(cfgs, Config{
|
||||
Width: cfgs[i].Width,
|
||||
Height: cfgs[i].Height,
|
||||
DisableOptimizations: true,
|
||||
})
|
||||
}
|
||||
|
||||
spi := &mockBus{}
|
||||
pin := mockPin{}
|
||||
display := New(spi, pin)
|
||||
|
||||
for _, cfg := range cfgs {
|
||||
display.Configure(cfg)
|
||||
|
||||
x, y := display.Size()
|
||||
c.Assert(x, qt.Equals, cfg.Width)
|
||||
c.Assert(y, qt.Equals, cfg.Height)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
x := int16(rand.IntN(int(cfg.Width)))
|
||||
y := int16(rand.IntN(int(cfg.Height)))
|
||||
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
x := int16(rand.IntN(int(cfg.Width)))
|
||||
y := int16(rand.IntN(int(cfg.Height)))
|
||||
display.SetPixel(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
|
||||
}
|
||||
|
||||
err := display.Display()
|
||||
c.Assert(err, qt.Equals, nil)
|
||||
|
||||
err = display.ClearDisplay()
|
||||
c.Assert(err, qt.Equals, nil)
|
||||
|
||||
display.ClearBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
func Test_HiPad(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
spi := &mockBus{}
|
||||
pin := mockPin{}
|
||||
display := New(spi, pin)
|
||||
|
||||
t.Run("LS011B7DH03, 8-bit address", func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
spi.b = nil
|
||||
})
|
||||
|
||||
display.Configure(ConfigLS011B7DH03)
|
||||
|
||||
display.SetPixel(0, display.height-1, color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||
err := display.Display()
|
||||
c.Assert(err, qt.Equals, nil)
|
||||
|
||||
// 160 perfectly divisible by 16, so 20 bytes of pixel data
|
||||
c.Assert(spi.b, qt.HasLen, 2+20+2)
|
||||
|
||||
// line is 1-indexed on the wire (67+1)
|
||||
// 68 in binary
|
||||
// 0b01000100
|
||||
|
||||
// DDDDDMMM
|
||||
c.Assert(spi.b[0], qt.Equals, uint8(0b00000011)) // mode 1, vcom is high on first run
|
||||
|
||||
c.Assert(spi.b[1], qt.Equals, uint8(0b01000100)) // the actual address
|
||||
})
|
||||
|
||||
t.Run("LS018B7DH02, 9-bit address", func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
spi.b = nil
|
||||
})
|
||||
|
||||
display.Configure(ConfigLS018B7DH02)
|
||||
|
||||
display.SetPixel(0, display.height-1, color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||
err := display.Display()
|
||||
c.Assert(err, qt.Equals, nil)
|
||||
|
||||
// 2 first bytes command+address
|
||||
// 230 bits are not divisible by 16, 240 is (15*16), so 30 bytes for line data
|
||||
// 2 trailing bytes
|
||||
c.Assert(spi.b, qt.HasLen, 2+30+2)
|
||||
|
||||
// line is 1-indexed on the wire (302+1)
|
||||
// 303 in binary (split in 2 bytes)
|
||||
// R
|
||||
// 0b00000001 0b00101111
|
||||
// ^
|
||||
|
||||
// RDDDDMMM
|
||||
c.Assert(spi.b[0], qt.Equals, uint8(0b10000011)) // mode 1, vcom is high on first run
|
||||
// ^
|
||||
|
||||
c.Assert(spi.b[1], qt.Equals, uint8(0b00101111)) // rest of the address (low 8 bits)
|
||||
})
|
||||
|
||||
t.Run("LS032B7DD02, 10-bit address", func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
spi.b = nil
|
||||
})
|
||||
|
||||
display.Configure(ConfigLS032B7DD02)
|
||||
|
||||
display.SetPixel(0, display.height-1, color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||
err := display.Display()
|
||||
c.Assert(err, qt.Equals, nil)
|
||||
|
||||
c.Assert(spi.b, qt.HasLen, 2+336/8+2) // 2 command+address, width / 2, 2 trailing bytes
|
||||
|
||||
// line is 1-indexed on the wire (535+1)
|
||||
// 536 in binary (split in 2 bytes)
|
||||
// RR
|
||||
// 0b00000010 0b00011000
|
||||
// ^^
|
||||
|
||||
// RRDDDMMM
|
||||
c.Assert(spi.b[0], qt.Equals, uint8(0b10000011)) // mode 1, vcom is high on first run
|
||||
// ^^
|
||||
|
||||
c.Assert(spi.b[1], qt.Equals, uint8(0b00011000)) // rest of the address (low 8 bits)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package sharpmem
|
||||
|
||||
// setBit sets the bit at pos in n to 1 and returns the updated number.
|
||||
func setBit(n uint8, pos uint8) uint8 {
|
||||
n |= 1 << pos
|
||||
return n
|
||||
}
|
||||
|
||||
// unsetBit sets the bit at pos in n to 0 and returns the updated number.
|
||||
func unsetBit(n uint8, pos uint8) uint8 {
|
||||
n &^= 1 << pos
|
||||
return n
|
||||
}
|
||||
|
||||
// hasBit returns whether the bit at pos in n is 1.
|
||||
func hasBit(n uint8, pos uint8) bool {
|
||||
n = n & (1 << pos)
|
||||
return n > 0
|
||||
}
|
||||
|
||||
// bitfieldBufLen returns the required buffer size for keeping track of
|
||||
// changed lines.
|
||||
func bitfieldBufLen(bits int16) int16 {
|
||||
return 1 + (bits-1)/8
|
||||
}
|
||||
|
||||
// ceilDiv divides a with b, but it uses the ceiling if modulo is not 0.
|
||||
func ceilDiv(a, b int16) int16 {
|
||||
return 1 + (a-1)/b
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
package shiftregister
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type NumberBit int8
|
||||
@@ -16,20 +16,20 @@ const (
|
||||
|
||||
// Device holds pin number
|
||||
type Device struct {
|
||||
latch, clock, out machine.Pin // IC wiring
|
||||
latch, clock, out drivers.Pin // IC wiring
|
||||
bits NumberBit // Pin number
|
||||
mask uint32 // keep all pins state
|
||||
}
|
||||
|
||||
// ShiftPin is the implementation of the ShiftPin interface.
|
||||
// ShiftPin provide an interface like regular machine.Pin
|
||||
// ShiftPin provide an interface like regular drivers.Pin
|
||||
type ShiftPin struct {
|
||||
mask uint32 // Bit representing the pin
|
||||
d *Device // Reference to the register
|
||||
}
|
||||
|
||||
// New returns a new shift output register device
|
||||
func New(Bits NumberBit, Latch, Clock, Out machine.Pin) *Device {
|
||||
func New(Bits NumberBit, Latch, Clock, Out drivers.Pin) *Device {
|
||||
return &Device{
|
||||
latch: Latch,
|
||||
clock: Clock,
|
||||
@@ -38,11 +38,9 @@ func New(Bits NumberBit, Latch, Clock, Out machine.Pin) *Device {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure set hardware configuration
|
||||
// Configure set hardware configuration. Make sure that the pins are already configured
|
||||
// as output.
|
||||
func (d *Device) Configure() {
|
||||
d.latch.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.clock.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.out.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.latch.High()
|
||||
}
|
||||
|
||||
|
||||
@@ -73,10 +73,12 @@ tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/
|
||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-bluefruit ./examples/tone
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/tm1637/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/touch/capacitive
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl6180x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840-sense ./examples/waveshare-epd/epd1in54/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
|
||||
@@ -106,7 +108,9 @@ tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clk
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/pcf8591/
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina219/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/
|
||||
@@ -132,6 +136,10 @@ tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/mai
|
||||
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
|
||||
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt
|
||||
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mcp9808/main.go
|
||||
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go
|
||||
tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/max6675/main.go
|
||||
# network examples (espat)
|
||||
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
|
||||
# network examples (wifinina)
|
||||
@@ -146,3 +154,6 @@ tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb
|
||||
tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webclient/
|
||||
tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webserver/
|
||||
tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/mqttclient/paho/
|
||||
# network examples (comboat)
|
||||
tinygo build -size short -o ./build/test.hex -target=elecrow-rp2040 -stack-size 8kb ./examples/net/tlsclient/
|
||||
tinygo build -size short -o ./build/test.hex -target=elecrow-rp2350 -stack-size 8kb ./examples/net/ntpclient/
|
||||
|
||||
+7
-11
@@ -5,8 +5,9 @@ package ssd1289
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
type Bus interface {
|
||||
@@ -14,17 +15,17 @@ type Bus interface {
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
rs machine.Pin
|
||||
wr machine.Pin
|
||||
cs machine.Pin
|
||||
rst machine.Pin
|
||||
rs drivers.Pin
|
||||
wr drivers.Pin
|
||||
cs drivers.Pin
|
||||
rst drivers.Pin
|
||||
bus Bus
|
||||
}
|
||||
|
||||
const width = int16(240)
|
||||
const height = int16(320)
|
||||
|
||||
func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bus) Device {
|
||||
func New(rs drivers.Pin, wr drivers.Pin, cs drivers.Pin, rst drivers.Pin, bus Bus) Device {
|
||||
d := Device{
|
||||
rs: rs,
|
||||
wr: wr,
|
||||
@@ -33,11 +34,6 @@ func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bu
|
||||
bus: bus,
|
||||
}
|
||||
|
||||
rs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
wr.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
cs.High()
|
||||
rst.High()
|
||||
wr.High()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ssd1306
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
// Registers
|
||||
const (
|
||||
Address = 0x3D
|
||||
@@ -38,4 +40,7 @@ const (
|
||||
|
||||
EXTERNALVCC VccMode = 0x1
|
||||
SWITCHCAPVCC VccMode = 0x2
|
||||
|
||||
NO_ROTATION = drivers.Rotation0
|
||||
ROTATION_180 = drivers.Rotation180
|
||||
)
|
||||
|
||||
+54
-19
@@ -6,7 +6,6 @@ package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -15,9 +14,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errBufferSize = errors.New("invalid size buffer")
|
||||
errOutOfRange = errors.New("out of screen range")
|
||||
errNotImplemented = errors.New("not implemented")
|
||||
errBufferSize = errors.New("invalid size buffer")
|
||||
errOutOfRange = errors.New("out of screen range")
|
||||
)
|
||||
|
||||
type ResetValue [2]byte
|
||||
@@ -33,6 +31,7 @@ type Device struct {
|
||||
canReset bool
|
||||
resetCol ResetValue
|
||||
resetPage ResetValue
|
||||
rotation drivers.Rotation
|
||||
}
|
||||
|
||||
// Config is the configuration for the display
|
||||
@@ -48,6 +47,7 @@ type Config struct {
|
||||
// If you're using a different size, you might need to set these values manually.
|
||||
ResetCol ResetValue
|
||||
ResetPage ResetValue
|
||||
Rotation drivers.Rotation
|
||||
}
|
||||
|
||||
type I2CBus struct {
|
||||
@@ -57,9 +57,9 @@ type I2CBus struct {
|
||||
|
||||
type SPIBus struct {
|
||||
wire drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
resetPin drivers.Pin
|
||||
csPin drivers.Pin
|
||||
}
|
||||
|
||||
type Buser interface {
|
||||
@@ -80,11 +80,8 @@ func NewI2C(bus drivers.I2C) Device {
|
||||
}
|
||||
}
|
||||
|
||||
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
|
||||
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
// NewSPI creates a new SSD1306 connection. The SPI wire and pins must already be configured.
|
||||
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin drivers.Pin) Device {
|
||||
return Device{
|
||||
bus: &SPIBus{
|
||||
wire: bus,
|
||||
@@ -149,8 +146,8 @@ func (d *Device) Configure(cfg Config) {
|
||||
}
|
||||
d.Command(MEMORYMODE)
|
||||
d.Command(0x00)
|
||||
d.Command(SEGREMAP | 0x1)
|
||||
d.Command(COMSCANDEC)
|
||||
|
||||
d.SetRotation(cfg.Rotation)
|
||||
|
||||
if (d.width == 128 && d.height == 64) || (d.width == 64 && d.height == 48) { // 128x64 or 64x48
|
||||
d.Command(SETCOMPINS)
|
||||
@@ -321,7 +318,6 @@ func (b *SPIBus) tx(data []byte, isCommand bool) error {
|
||||
|
||||
if isCommand {
|
||||
b.csPin.High()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
b.dcPin.Low()
|
||||
b.csPin.Low()
|
||||
|
||||
@@ -329,7 +325,6 @@ func (b *SPIBus) tx(data []byte, isCommand bool) error {
|
||||
b.csPin.High()
|
||||
} else {
|
||||
b.csPin.High()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
b.dcPin.High()
|
||||
b.csPin.Low()
|
||||
|
||||
@@ -363,13 +358,25 @@ func (d *Device) DrawBitmap(x, y int16, bitmap pixel.Image[pixel.Monochrome]) er
|
||||
|
||||
// Rotation returns the currently configured rotation.
|
||||
func (d *Device) Rotation() drivers.Rotation {
|
||||
return drivers.Rotation0
|
||||
return d.rotation
|
||||
}
|
||||
|
||||
// SetRotation changes the rotation of the device (clock-wise).
|
||||
// Would have to be implemented in software for this device.
|
||||
func (d *Device) SetRotation(rotation drivers.Rotation) error {
|
||||
return errNotImplemented
|
||||
d.rotation = rotation
|
||||
switch d.rotation {
|
||||
case drivers.Rotation0:
|
||||
d.Command(SEGREMAP | 0x1) // Reverse horizontal mapping
|
||||
d.Command(COMSCANDEC) // Reverse vertical mapping
|
||||
case drivers.Rotation180:
|
||||
d.Command(SEGREMAP) // Normal horizontal mapping
|
||||
d.Command(COMSCANINC) // Normal vertical mapping
|
||||
// nothing to do
|
||||
default:
|
||||
d.Command(SEGREMAP | 0x1) // Reverse horizontal mapping
|
||||
d.Command(COMSCANDEC) // Reverse vertical mapping
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the sleep mode for this display. When sleeping, the panel uses a lot
|
||||
@@ -383,3 +390,31 @@ func (d *Device) Sleep(sleepEnabled bool) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FillRectangle fills a rectangle at a given coordinates with a color
|
||||
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
|
||||
dw, dh := d.Size()
|
||||
|
||||
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
|
||||
x >= d.width || (x+width) > dw || y >= dh || (y+height) > dh {
|
||||
return errOutOfRange
|
||||
}
|
||||
|
||||
if x+width == dw && y+height == dh && c.R == 0 && c.G == 0 && c.B == 0 {
|
||||
d.ClearDisplay()
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := x; i < x+width; i++ {
|
||||
for j := y; j < y+height; j++ {
|
||||
d.SetPixel(i, j, c)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetScroll sets the vertical scrolling for the display, which is a NOP for this display.
|
||||
func (d *Device) SetScroll(line int16) {
|
||||
return
|
||||
}
|
||||
|
||||
+5
-9
@@ -5,7 +5,6 @@ package ssd1331 // import "tinygo.org/x/drivers/ssd1331"
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
"errors"
|
||||
"time"
|
||||
@@ -19,9 +18,9 @@ type Rotation uint8
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
resetPin drivers.Pin
|
||||
csPin drivers.Pin
|
||||
width int16
|
||||
height int16
|
||||
batchLength int16
|
||||
@@ -35,11 +34,8 @@ type Config struct {
|
||||
Height int16
|
||||
}
|
||||
|
||||
// New creates a new SSD1331 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin machine.Pin) Device {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
// New creates a new SSD1331 connection. The SPI wire and pins must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin drivers.Pin) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
|
||||
+6
-14
@@ -6,7 +6,6 @@ package ssd1351 // import "tinygo.org/x/drivers/ssd1351"
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -20,11 +19,11 @@ var (
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
enPin machine.Pin
|
||||
rwPin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
resetPin drivers.Pin
|
||||
csPin drivers.Pin
|
||||
enPin drivers.Pin
|
||||
rwPin drivers.Pin
|
||||
width int16
|
||||
height int16
|
||||
rowOffset int16
|
||||
@@ -41,7 +40,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
// New creates a new SSD1351 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device {
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin drivers.Pin) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
@@ -72,13 +71,6 @@ func (d *Device) Configure(cfg Config) {
|
||||
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)
|
||||
|
||||
+7
-12
@@ -5,7 +5,6 @@ package st7735 // import "tinygo.org/x/drivers/st7735"
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
@@ -39,10 +38,10 @@ type Device = DeviceOf[pixel.RGB565BE]
|
||||
// formats.
|
||||
type DeviceOf[T Color] struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
blPin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
resetPin drivers.Pin
|
||||
csPin drivers.Pin
|
||||
blPin drivers.Pin
|
||||
width int16
|
||||
height int16
|
||||
columnOffset int16
|
||||
@@ -65,17 +64,13 @@ type Config struct {
|
||||
}
|
||||
|
||||
// New creates a new ST7735 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) Device {
|
||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||
}
|
||||
|
||||
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI
|
||||
// wire must already be configured.
|
||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
// wire and pins must already be configured.
|
||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) DeviceOf[T] {
|
||||
return DeviceOf[T]{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
|
||||
+7
-11
@@ -46,10 +46,10 @@ type Device = DeviceOf[pixel.RGB565BE]
|
||||
// formats.
|
||||
type DeviceOf[T Color] struct {
|
||||
bus drivers.SPI
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
blPin machine.Pin
|
||||
dcPin drivers.Pin
|
||||
resetPin drivers.Pin
|
||||
csPin drivers.Pin
|
||||
blPin drivers.Pin
|
||||
width int16
|
||||
height int16
|
||||
columnOffsetCfg int16
|
||||
@@ -83,17 +83,13 @@ type Config struct {
|
||||
}
|
||||
|
||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) Device {
|
||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||
}
|
||||
|
||||
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
|
||||
// wire must already be configured.
|
||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
|
||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
// wire and pins must already be configured.
|
||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) DeviceOf[T] {
|
||||
return DeviceOf[T]{
|
||||
bus: bus,
|
||||
dcPin: dcPin,
|
||||
|
||||
+2
-3
@@ -6,7 +6,6 @@ package sx127x
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -22,7 +21,7 @@ const (
|
||||
// Device wraps an SPI connection to a SX127x device.
|
||||
type Device struct {
|
||||
spi drivers.SPI // SPI bus for module communication
|
||||
rstPin machine.Pin // GPIO for reset
|
||||
rstPin drivers.Pin // GPIO for reset
|
||||
radioEventChan chan lora.RadioEvent // Channel for Receiving events
|
||||
loraConf lora.Config // Current Lora configuration
|
||||
controller RadioController // to manage interactions with the radio
|
||||
@@ -43,7 +42,7 @@ func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
|
||||
}
|
||||
|
||||
// New creates a new SX127x connection. The SPI bus must already be configured.
|
||||
func New(spi machine.SPI, rstPin machine.Pin) *Device {
|
||||
func New(spi drivers.SPI, rstPin drivers.Pin) *Device {
|
||||
k := Device{
|
||||
spi: spi,
|
||||
rstPin: rstPin,
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
# TMC2209 Go Package
|
||||
|
||||
This package provides a lightweight interface to communicate with the TMC2209 stepper motor driver via UART. It is designed to be used with microcontrollers and supports both default UART communication and custom interface implementations for flexibility.
|
||||
Currently this package only implements the communications with TMC2209. Functions to handle specific operations such as EnableStealthChop() etc. will need to be implemented by the user.
|
||||
## Features
|
||||
- **UART Communication:** Standard communication through UART for controlling the TMC2209 driver.
|
||||
- **Custom Communication Interfaces:** Allows custom implementations of communication interfaces (e.g., USB, SPI, etc.) via the `RegisterComm` interface. The usecase for this is if you have host system that can "talk" to the microcontroller connected to the TMC2209
|
||||
- **Error Handling:** Lightweight error handling for TinyGo compatibility.
|
||||
- **Register Access:** Provides methods to read and write registers on the TMC2209.
|
||||
|
||||
## Setup
|
||||
|
||||
### Prerequisites
|
||||
- **TinyGo**: This package is optimized for TinyGo, which is suitable for running Go on microcontrollers and embedded systems.
|
||||
- **UART Communication**: For microcontrollers with UART support, such as ESP32, STM32, and Raspberry Pi Pico.
|
||||
|
||||
|
||||
## Usage
|
||||
### Using with Microcontrollers (Default UART)
|
||||
|
||||
To use the package with a microcontroller, you need to initialize the UART communication and configure the TMC2209 driver.
|
||||
Example:
|
||||
|
||||
```aiignore
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"github.com/yourusername/tmc2209"
|
||||
"machine"
|
||||
)
|
||||
|
||||
func main() {
|
||||
uart := machine.UART0
|
||||
|
||||
// Create an instance of the TMC2209 with UART communication
|
||||
tmc := tmc2209.NewTMC2209(uart, 0x00) // Replace 0x00 with the appropriate address
|
||||
|
||||
// Set up the TMC2209 driver
|
||||
err := tmc.Setup()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to set up TMC2209: %v", err)
|
||||
}
|
||||
|
||||
// Write to a register (example: setting a register value)
|
||||
err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write register: %v", err)
|
||||
}
|
||||
|
||||
// Read from a register (example: reading a register value)
|
||||
value, err := tmc.ReadRegister(0x10)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read register: %v", err)
|
||||
}
|
||||
|
||||
// Output the read value
|
||||
fmt.Printf("Register value: 0x%X\n", value)
|
||||
}
|
||||
|
||||
```
|
||||
## Microcontroller Notes
|
||||
|
||||
- **TinyGo Support:** This code is optimized for use with TinyGo on supported microcontrollers.
|
||||
- **UART Configuration:** Ensure the UART instance is configured correctly for your microcontroller.
|
||||
- The machine.UART0 in the example is for the default UART on TinyGo-compatible devices like the Raspberry Pi Pico. Check your microcontroller's documentation for the correct UART instance and pin configuration.
|
||||
|
||||
## 2. Custom Interface Implementation
|
||||
|
||||
If you want to use a custom communication interface (e.g., USB, SPI, etc.), you can implement the RegisterComm interface.
|
||||
Custom Interface Example (e.g., USB Communication):
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"github.com/yourusername/tmc2209"
|
||||
"yourcustompackage" // Custom package for communication
|
||||
)
|
||||
|
||||
type CustomComm struct {
|
||||
// Implement your custom communication method here
|
||||
}
|
||||
|
||||
func (c *CustomComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) {
|
||||
// Implement the register read logic using your custom interface (USB, SPI, etc.)
|
||||
// For example, send the register read command over USB and read the response
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *CustomComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error {
|
||||
// Implement the register write logic using your custom interface (USB, SPI, etc.)
|
||||
// For example, send the register write command over USB
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create an instance of the TMC2209 with your custom communication interface
|
||||
customComm := &CustomComm{}
|
||||
tmc := tmc2209.NewTMC2209(customComm, 0x00) // Replace 0x00 with the appropriate address
|
||||
|
||||
// Set up the TMC2209 driver
|
||||
err := tmc.Setup()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to set up TMC2209: %v", err)
|
||||
}
|
||||
|
||||
// Write to a register (example: setting a register value)
|
||||
err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write register: %v", err)
|
||||
}
|
||||
|
||||
// Read from a register (example: reading a register value)
|
||||
value, err := tmc.ReadRegister(0x10)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read register: %v", err)
|
||||
}
|
||||
|
||||
// Output the read value
|
||||
fmt.Printf("Register value: 0x%X\n", value)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Custom Interface Notes:
|
||||
|
||||
- RegisterComm Interface: Your custom communication interface (e.g., USB, SPI) should implement the RegisterComm interface. This ensures that the TMC2209 driver can interact with your custom interface for reading and writing registers.
|
||||
- Error Handling: Implement proper error handling in your custom interface methods (ReadRegister and WriteRegister).
|
||||
|
||||
#### Functions
|
||||
```
|
||||
NewTMC2209(comm RegisterComm, address uint8) *TMC2209
|
||||
```
|
||||
Creates a new TMC2209 instance with the provided communication interface (comm) and driver address (address).
|
||||
|
||||
```Setup() error```
|
||||
|
||||
Initializes the communication interface. This is required before interacting with the TMC2209 driver. The method checks if the communication interface is UART and sets it up accordingly.
|
||||
|
||||
```WriteRegister(register uint8, value uint32) error```
|
||||
|
||||
Writes a value to a specific register on the TMC2209 driver.
|
||||
|
||||
```ReadRegister(register uint8) (uint32, error)```
|
||||
|
||||
Reads a value from a specific register on the TMC2209 driver.
|
||||
|
||||
### Error Handling
|
||||
|
||||
This package uses a lightweight custom error type (CustomError) to ensure compatibility with TinyGo and reduce external dependencies.
|
||||
Example Error:
|
||||
|
||||
```CustomError("communication interface not set")```
|
||||
|
||||
|
||||
Created by Amken3d
|
||||
info@amken3d.us
|
||||
|
||||
|
||||
+1409
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
package tmc2209
|
||||
|
||||
func Constrain(value, low, high uint32) uint32 {
|
||||
if value < low {
|
||||
return low
|
||||
}
|
||||
if value > high {
|
||||
return high
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func SetRunCurrent(percent uint8) {
|
||||
_ = PercentToCurrentSetting(percent)
|
||||
|
||||
// Set the run current register to runCurrent value
|
||||
}
|
||||
|
||||
func SetHoldCurrent(percent uint8) {
|
||||
_ = PercentToCurrentSetting(percent)
|
||||
// Set the hold current register to holdCurrent value
|
||||
}
|
||||
func PercentToCurrentSetting(percent uint8) uint8 {
|
||||
constrainedPercent := Constrain(uint32(percent), 0, 100)
|
||||
return uint8(Map(constrainedPercent, 0, 100, 0, 255))
|
||||
}
|
||||
|
||||
func CurrentSettingToPercent(currentSetting uint8) uint8 {
|
||||
return uint8(Map(uint32(currentSetting), 0, 255, 0, 100))
|
||||
}
|
||||
|
||||
func PercentToHoldDelaySetting(percent uint8) uint8 {
|
||||
constrainedPercent := Constrain(uint32(percent), 0, 100)
|
||||
return uint8(Map(constrainedPercent, 0, 100, 0, 255))
|
||||
}
|
||||
|
||||
func HoldDelaySettingToPercent(holdDelaySetting uint8) uint8 {
|
||||
return uint8(Map(uint32(holdDelaySetting), 0, 255, 0, 100))
|
||||
}
|
||||
func Map(value, fromLow, fromHigh, toLow, toHigh uint32) uint32 {
|
||||
return (value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow) + toLow
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package tmc2209
|
||||
|
||||
func SetMicrostepsPerStep(microsteps uint16) uint8 {
|
||||
exponent := uint8(0)
|
||||
microstepsShifted := microsteps >> 1
|
||||
|
||||
for microstepsShifted > 0 {
|
||||
microstepsShifted = microstepsShifted >> 1
|
||||
exponent++
|
||||
}
|
||||
|
||||
SetMicrostepsPerStepPowerOfTwo(exponent)
|
||||
return exponent
|
||||
}
|
||||
|
||||
func SetMicrostepsPerStepPowerOfTwo(exponent uint8) {
|
||||
switch exponent {
|
||||
case 0:
|
||||
// Set MRES_001
|
||||
case 1:
|
||||
// Set MRES_002
|
||||
case 2:
|
||||
// Set MRES_004
|
||||
case 3:
|
||||
// Set MRES_008
|
||||
case 4:
|
||||
// Set MRES_016
|
||||
case 5:
|
||||
// Set MRES_032
|
||||
case 6:
|
||||
// Set MRES_064
|
||||
case 7:
|
||||
// Set MRES_128
|
||||
default:
|
||||
// Set MRES_256
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package tmc2209
|
||||
|
||||
func EnableStealthChop() {
|
||||
// Set StealthChop enabled in the global config register
|
||||
}
|
||||
|
||||
func DisableStealthChop() {
|
||||
// Set StealthChop disabled in the global config register
|
||||
}
|
||||
|
||||
func EnableCoolStep(lowerThreshold, upperThreshold uint8) {
|
||||
// Enable CoolStep with specified thresholds
|
||||
}
|
||||
|
||||
func DisableCoolStep() {
|
||||
// Disable CoolStep feature
|
||||
}
|
||||
|
||||
func EnableAutomaticCurrentScaling() {
|
||||
// Enable Automatic Current Scaling
|
||||
}
|
||||
|
||||
func DisableAutomaticCurrentScaling() {
|
||||
// Disable Automatic Current Scaling
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//go:build tinygo
|
||||
|
||||
package tmc2209
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// TMC2209 represents a single TMC2209 stepper motor driver on the communication line.
|
||||
type TMC2209 struct {
|
||||
comm RegisterComm
|
||||
address uint8
|
||||
}
|
||||
|
||||
// NewTMC2209 creates a new instance of the TMC2209 driver for a specific address.
|
||||
func NewTMC2209(comm RegisterComm, address uint8) *TMC2209 {
|
||||
return &TMC2209{
|
||||
comm: comm,
|
||||
address: address,
|
||||
}
|
||||
}
|
||||
|
||||
// Setup initializes the communication interface with the TMC2209.
|
||||
func (driver *TMC2209) Setup() error {
|
||||
// Check if comm is of type *UARTComm
|
||||
if uartComm, ok := driver.comm.(*UARTComm); ok {
|
||||
// Call Setup only if comm is a *UARTComm
|
||||
err := uartComm.Setup()
|
||||
if err != nil {
|
||||
return CustomError("Failed to setup UART communication: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
// If it's not a UARTComm, log that it's using a different communication method
|
||||
log.Println("Using a non-UART communication method")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRegister sends a register write command to the TMC2209.
|
||||
func (driver *TMC2209) WriteRegister(reg uint8, value uint32) error {
|
||||
if driver.comm == nil {
|
||||
return CustomError("communication interface not set")
|
||||
}
|
||||
// Use the communication interface (RegisterComm) to write the register
|
||||
return driver.comm.WriteRegister(reg, value, driver.address)
|
||||
}
|
||||
|
||||
// ReadRegister sends a register read command to the TMC2209 and returns the read value.
|
||||
func (driver *TMC2209) ReadRegister(reg uint8) (uint32, error) {
|
||||
if driver.comm == nil {
|
||||
return 0, CustomError("communication interface not set")
|
||||
}
|
||||
// Use the communication interface (RegisterComm) to read the register
|
||||
return driver.comm.ReadRegister(reg, driver.address)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//go:build tinygo
|
||||
|
||||
package tmc2209
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CustomError is a lightweight error type used for TinyGo compatibility.
|
||||
type CustomError string
|
||||
|
||||
func (e CustomError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// UARTComm implements RegisterComm for UART-based communication
|
||||
type UARTComm struct {
|
||||
uart machine.UART
|
||||
address uint8
|
||||
}
|
||||
|
||||
// NewUARTComm creates a new UARTComm instance.
|
||||
func NewUARTComm(uart machine.UART, address uint8) *UARTComm {
|
||||
return &UARTComm{
|
||||
uart: uart,
|
||||
address: address,
|
||||
}
|
||||
}
|
||||
|
||||
// Setup initializes the UART communication with the TMC2209.
|
||||
func (comm *UARTComm) Setup() error {
|
||||
// Check if UART is initialized
|
||||
if comm.uart == (machine.UART{}) {
|
||||
return CustomError("UART not initialized")
|
||||
}
|
||||
|
||||
// Configure the UART interface with the desired baud rate and settings
|
||||
err := comm.uart.Configure(machine.UARTConfig{
|
||||
BaudRate: 115200,
|
||||
})
|
||||
if err != nil {
|
||||
return CustomError("Failed to configure UART")
|
||||
}
|
||||
|
||||
// No built-in timeout in TinyGo, so timeout will be handled in the read/write methods
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRegister sends a register write command to the TMC2209 with a timeout.
|
||||
func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error {
|
||||
buffer := []byte{
|
||||
0x05, // Sync byte
|
||||
comm.address, // Slave address
|
||||
register | 0x80, // Write command (set MSB to 1 for write)
|
||||
byte((value >> 24) & 0xFF), // MSB of value
|
||||
byte((value >> 16) & 0xFF), // Middle byte
|
||||
byte((value >> 8) & 0xFF), // Next byte
|
||||
byte(value & 0xFF), // LSB of value
|
||||
0, // CRC
|
||||
}
|
||||
|
||||
buffer[7] = CalculateCRC(buffer[:7])
|
||||
|
||||
// Write the data to the TMC2209
|
||||
done := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
comm.uart.Write(buffer)
|
||||
done <- nil
|
||||
}()
|
||||
|
||||
// Implementing timeout using a 100ms timer
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
case <-time.After(100 * time.Millisecond): // Timeout after 100ms
|
||||
return CustomError("write timeout")
|
||||
}
|
||||
}
|
||||
|
||||
// ReadRegister sends a register read command to the TMC2209 with a timeout.
|
||||
func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) {
|
||||
var writeBuffer [4]byte
|
||||
writeBuffer[0] = 0x05 // Sync byte
|
||||
writeBuffer[1] = 0x00 // Slave address
|
||||
writeBuffer[2] = register & 0x7F // Read command (MSB clear for read)
|
||||
writeBuffer[3] = CalculateCRC(writeBuffer[:3])
|
||||
|
||||
// Send the read command
|
||||
done := make(chan []byte, 1)
|
||||
go func() {
|
||||
comm.uart.Write(writeBuffer[:])
|
||||
readBuffer := make([]byte, 8)
|
||||
comm.uart.Read(readBuffer)
|
||||
done <- readBuffer
|
||||
}()
|
||||
|
||||
// Implementing timeout using a 100ms timer
|
||||
select {
|
||||
case readBuffer := <-done:
|
||||
checksum := CalculateCRC(readBuffer[:7])
|
||||
if checksum != readBuffer[7] {
|
||||
return 0, CustomError("checksum error")
|
||||
}
|
||||
|
||||
// Return the value from the register
|
||||
return uint32(readBuffer[3])<<24 | uint32(readBuffer[4])<<16 | uint32(readBuffer[5])<<8 | uint32(readBuffer[6]), nil
|
||||
case <-time.After(100 * time.Millisecond): // Timeout after 100ms
|
||||
return 0, CustomError("read timeout")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package tmc2209
|
||||
|
||||
import "log"
|
||||
|
||||
func CalculateCRC(data []byte) uint8 {
|
||||
crc := uint8(0)
|
||||
for _, byte := range data {
|
||||
for i := 0; i < 8; i++ {
|
||||
if (crc>>7)^(byte&0x01) == 1 {
|
||||
crc = (crc << 1) ^ 0x07
|
||||
} else {
|
||||
crc = crc << 1
|
||||
}
|
||||
byte >>= 1
|
||||
}
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
// VerifyCommunication checks the communication with the TMC2209 by reading the version register (IOIN).
|
||||
// It returns true if the communication is successful (i.e., the version matches the expected version).
|
||||
// VerifyCommunication verifies the communication with the TMC2209 by reading the version register (IOIN).
|
||||
// It explicitly resets the struct and de-references it after the check to ensure memory is managed manually.
|
||||
func VerifyCommunication(comm RegisterComm, driverIndex uint8) bool {
|
||||
var io *Ioin
|
||||
if io == nil {
|
||||
io = NewIoin() // Initialize the struct if not already initialized
|
||||
} else {
|
||||
*io = Ioin{}
|
||||
}
|
||||
_, err := ReadRegister(comm, driverIndex, io.GetAddress())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if io.Version == expectedVersion {
|
||||
io = nil
|
||||
return true
|
||||
}
|
||||
io = nil
|
||||
return false
|
||||
}
|
||||
|
||||
// CheckErrorStatus verifies the communication and checks for error flags in the TMC2209 driver status.
|
||||
// It explicitly resets the struct and de-references it when done to ensure memory is managed manually.
|
||||
func CheckErrorStatus(comm RegisterComm, driverIndex uint8) bool {
|
||||
var d *DrvStatus
|
||||
if d == nil {
|
||||
d = NewDrvStatus()
|
||||
} else {
|
||||
*d = DrvStatus{}
|
||||
}
|
||||
_, err := d.Read(comm, driverIndex)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
errorFlags := d.Ola | d.S2vsa | d.S2vsb | d.Ot | d.S2ga | d.S2gb | d.Olb
|
||||
if errorFlags != 0 {
|
||||
log.Printf("TMC2209 Error Detected: %X", errorFlags)
|
||||
return false
|
||||
}
|
||||
d = nil
|
||||
return true
|
||||
}
|
||||
|
||||
// GetInterfaceTransmissionCount reads the IFCNT register to check for UART transmission status
|
||||
func GetInterfaceTransmissionCount(comm RegisterComm, driverIndex uint8) (uint32, error) {
|
||||
ifcnt := NewIfcnt()
|
||||
_, err := ReadRegister(comm, driverIndex, ifcnt.GetAddress())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ifcnt.Bytes, nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package tmc2209
|
||||
|
||||
func MoveAtVelocity(microstepsPerPeriod int32) {
|
||||
// Write velocity to the relevant register
|
||||
}
|
||||
func MoveUsingStepDirInterface() {
|
||||
// Set the interface to use step/direction interface
|
||||
}
|
||||
func GetVelocity() uint32 {
|
||||
// Read the actual velocity register
|
||||
return 0 // Placeholder return value
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
# TMC5160 Driver for Go (TinyGo)
|
||||
|
||||
This repository provides a Go-based driver for the **TMC5160** stepper motor driver, implemented for both **SPI** and **UART** communication modes. The driver allows you to easily interface with the TMC5160 to configure and control stepper motors.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Communication Modes](#communication-modes)
|
||||
- [SPI Mode](#spi-mode)
|
||||
- [UART Mode](#uart-mode)
|
||||
- [Usage Example](#usage-example)
|
||||
- [Setting and Getting Modes](#setting-and-getting-modes)
|
||||
- [Reading and Writing Registers](#reading-and-writing-registers)
|
||||
- [API Reference](#api-reference)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
To use the TMC5160 driver, you'll need to have **TinyGo** installed. You can install TinyGo by following the [official installation guide](https://tinygo.org/getting-started/).
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **machine**: To interface with hardware on platforms like Raspberry Pi, STM32, etc.
|
||||
- **TinyGo**: A Go compiler for embedded systems.
|
||||
|
||||
Add the module
|
||||
|
||||
```bash
|
||||
import "tinygo.org/x/drivers/tmc5160"
|
||||
```
|
||||
|
||||
### Communication Modes
|
||||
|
||||
The TMC5160 supports two communication modes for controlling the motor:
|
||||
|
||||
**SPI Mode**
|
||||
|
||||
To communicate with the TMC5160 in SPI mode, you'll need to configure the SPI bus and the chip-select (CS) pin. This allows full-speed communication between your microcontroller and the TMC5160.
|
||||
SPI Setup
|
||||
|
||||
In SPI mode, you must configure the SPI interface on your microcontroller. Here's how to set up SPI communication for the TMC5160.
|
||||
|
||||
```go
|
||||
spi := machine.SPI1
|
||||
csPin := machine.GPIO13
|
||||
spi.Configure(machine.SPIConfig{
|
||||
SCK: machine.GPIO10,
|
||||
SDI: machine.GPIO11,
|
||||
SDO: machine.GPIO12,
|
||||
Frequency: 5000000,
|
||||
Mode: 3,
|
||||
LSBFirst: false,
|
||||
})
|
||||
|
||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
```
|
||||
**Sending Commands via SPI**
|
||||
|
||||
The driver supports reading and writing registers using the SPIComm interface, which is initialized with the configured SPI bus and CS pins
|
||||
|
||||
```go
|
||||
comm := tmc5160.NewSPIComm(*spi, csPins)
|
||||
driver := tmc5160.NewTMC5160(comm, driverIndex)
|
||||
driver.WriteRegister(tmc5160.GCONF, value)
|
||||
|
||||
```
|
||||
|
||||
**UART Mode**
|
||||
|
||||
Alternatively, you can use UART mode to communicate with the TMC5160. UART mode is useful for cases where SPI is not available or when the TMC5160 is used in multi-driver configurations with limited SPI pins.
|
||||
UART Setup
|
||||
|
||||
In UART mode, you will need to configure the UART interface with the appropriate baud rate and settings:
|
||||
|
||||
```go
|
||||
uart := machine.UART0
|
||||
uart.Configure(machine.UARTConfig{
|
||||
BaudRate: 115200,
|
||||
})
|
||||
```
|
||||
#### Sending Commands via UART
|
||||
|
||||
The UART communication is handled through the UARTComm struct, which wraps the UART interface.
|
||||
|
||||
```go
|
||||
comm := tmc5160.NewUARTComm(uart, 0x01)
|
||||
driver := tmc5160.NewTMC5160(comm, 0)
|
||||
driver.WriteRegister(tmc5160.GCONF, 0x01)
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here’s a simple example of how to use the TMC5160 driver with SPI and UART modes:
|
||||
|
||||
```aiignore
|
||||
// Connects to SPI1 on a RP2040 (Pico)
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/tmc5160"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Step 1. Setup your protocol. SPI setup shown below
|
||||
spi := machine.SPI1
|
||||
spi.Configure(machine.SPIConfig{
|
||||
SCK: machine.GPIO10,
|
||||
SDI: machine.GPIO11,
|
||||
SDO: machine.GPIO12,
|
||||
Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues
|
||||
Mode: 3,
|
||||
LSBFirst: false,
|
||||
})
|
||||
// Step 2. Set up all associated Pins
|
||||
csPin0 := machine.GPIO13
|
||||
csPin0.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
enn0 := machine.GPIO18
|
||||
enn0.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
// csPins is a map of all chip select pins in a multi driver setup.
|
||||
//Only one pin csPin0 mapped to "0"is shown in this example, but add more mappings as required
|
||||
csPins := map[uint8]machine.Pin{0: csPin0}
|
||||
//bind csPin to driverAdddress
|
||||
driverAddress := uint8(0) // Let's assume we are working with driver at address 0x01
|
||||
// Step 3. Bind the communication interface to the protocol
|
||||
comm := tmc5160.NewSPIComm(*spi, csPins)
|
||||
// Step 4. Define your stepper like this below
|
||||
//stepper := tmc5160.NewStepper(angle , gearRatio vSupply rCoil , lCoil , iPeak , rSense , mSteps, fclk )
|
||||
stepper := tmc5160.NewDefaultStepper() // Default Stepper should be used only for testing.
|
||||
// Step 5. Instantiate your driver
|
||||
driver := tmc5160.NewDriver(
|
||||
comm,
|
||||
driverAddress,
|
||||
enn0,
|
||||
stepper)
|
||||
|
||||
// Setting and getting mode
|
||||
rampMode := tmc5160.NewRAMPMODE(comm, driverAddress)
|
||||
err := rampMode.SetMode(tmc5160.PositioningMode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mode, err := rampMode.GetMode()
|
||||
if err != nil {
|
||||
println("Error getting mode:", err)
|
||||
} else {
|
||||
println("Current Mode:", mode)
|
||||
}
|
||||
|
||||
// Read GCONF register
|
||||
GCONF := tmc5160.NewGCONF()
|
||||
gconfVal, err := driver.ReadRegister(tmc5160.GCONF)
|
||||
// Uppack the register to get all the bits and bytes of the register
|
||||
GCONF.Unpack(gconfVal)
|
||||
//E.g. MultiStepFlit is retrieved from the GCONF register
|
||||
println("GCONF:MultiStepFlit:", GCONF.MultistepFilt)
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
## Reading and Writing Registers
|
||||
|
||||
You can easily read and write registers using the WriteRegister and ReadRegister methods:
|
||||
|
||||
```aiignore
|
||||
// Write a value to a register
|
||||
err := driver.WriteRegister(tmc5160.GCONF, 0x01)
|
||||
if err != nil {
|
||||
fmt.Println("Error writing register:", err)
|
||||
}
|
||||
|
||||
// Read a register
|
||||
value, err := driver.ReadRegister(tmc5160.GCONF)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading register:", err)
|
||||
} else {
|
||||
fmt.Println("Read value from GCONF:", value)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
NewSPIComm(spi *machine.SPI, csPins map[uint8]machine.Pin) *SPIComm
|
||||
|
||||
Creates a new SPI communication interface for the TMC5160.
|
||||
|
||||
NewUARTComm(uart machine.UART, address uint8) *UARTComm
|
||||
|
||||
Creates a new UART communication interface for the TMC5160.
|
||||
|
||||
NewTMC5160(comm RegisterComm, address uint8) *TMC5160
|
||||
|
||||
Creates a new instance of the TMC5160 driver.
|
||||
|
||||
WriteRegister(register uint8, value uint32) error
|
||||
|
||||
Writes a value to the specified register.
|
||||
|
||||
ReadRegister(register uint8) (uint32, error)
|
||||
|
||||
Reads a value from the specified register.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user