mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 18:48:41 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71eef4196f | |||
| 6301627338 | |||
| b7bbecc456 |
-118
@@ -1,121 +1,3 @@
|
||||
0.33.0
|
||||
---
|
||||
- **new devices**
|
||||
- **ens160**
|
||||
- Add ens160 i2c driver
|
||||
- **lsm303dlhc**
|
||||
- added support for LSM303DLHC e-Compass; (#783)
|
||||
- **seesaw**
|
||||
- add support for Adafruit Seesaw encoders
|
||||
|
||||
- **enhancements**
|
||||
- **ws2812**
|
||||
- add RP2350 support
|
||||
- **ssd1306**
|
||||
- avoid unnecessary heap allocations (#767)
|
||||
- **gps**
|
||||
- allow gps init with address
|
||||
- **lsm6ds3tr**
|
||||
- avoid unnecessary heap allocations (#766)
|
||||
|
||||
- **bugfixes**
|
||||
- **gps**
|
||||
- Fix gps time calculation (#785)
|
||||
|
||||
|
||||
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,9 +8,6 @@ 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-2025 The TinyGo Authors. All rights reserved.
|
||||
Copyright (c) 2018-2024 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
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ func New(bus drivers.I2C) Device {
|
||||
func (d *Device) Configure() {
|
||||
// Check initialization state
|
||||
status := d.Status()
|
||||
if status&STATUS_CALIBRATED == 1 {
|
||||
if status&0x08 == 1 {
|
||||
// Device is initialized
|
||||
return
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func (d *Device) Read() error {
|
||||
}
|
||||
|
||||
// If measurement complete, store values
|
||||
if data[0]&STATUS_CALIBRATED != 0 && data[0]&STATUS_BUSY == 0 {
|
||||
if data[0]&0x04 != 0 && data[0]&0x80 == 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
|
||||
|
||||
+3
-7
@@ -5,10 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,11 +37,8 @@ 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 pin.Output, delay uint32) *Device {
|
||||
return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() {
|
||||
legacy.ConfigurePinOut(sckPin)
|
||||
legacy.ConfigurePinOut(sdoPin)
|
||||
}})
|
||||
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
|
||||
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
|
||||
}
|
||||
|
||||
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
||||
|
||||
+6
-12
@@ -1,9 +1,6 @@
|
||||
package apa102
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
import "machine"
|
||||
|
||||
// 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.
|
||||
@@ -11,18 +8,15 @@ import (
|
||||
// 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 pin.OutputFunc
|
||||
SDO pin.OutputFunc
|
||||
Delay uint32
|
||||
configurePins func()
|
||||
SCK machine.Pin
|
||||
SDO machine.Pin
|
||||
Delay uint32
|
||||
}
|
||||
|
||||
// Configure sets up the SCK and SDO pins as outputs and sets them low
|
||||
func (s *bbSPI) Configure() {
|
||||
if s.configurePins == nil {
|
||||
panic(legacy.ErrConfigBeforeInstantiated)
|
||||
}
|
||||
s.configurePins()
|
||||
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 {
|
||||
|
||||
+24
-31
@@ -1,36 +1,31 @@
|
||||
package bmi160
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
|
||||
// also an I2C interface, but it is not yet supported.
|
||||
type DeviceSPI struct {
|
||||
// Chip select pin
|
||||
csb pin.OutputFunc
|
||||
CSB machine.Pin
|
||||
|
||||
buf [7]byte
|
||||
|
||||
// SPI bus (requires chip select to be usable).
|
||||
bus drivers.SPI
|
||||
configurePins func()
|
||||
Bus drivers.SPI
|
||||
}
|
||||
|
||||
// NewSPI returns a new device driver. The pin and SPI interface are not
|
||||
// touched, provide a fully configured SPI object and call Configure to start
|
||||
// using this device.
|
||||
func NewSPI(csb pin.Output, spi drivers.SPI) *DeviceSPI {
|
||||
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
|
||||
return &DeviceSPI{
|
||||
csb: csb.Set, // chip select
|
||||
bus: spi,
|
||||
configurePins: func() {
|
||||
legacy.ConfigurePinOut(csb)
|
||||
},
|
||||
CSB: csb, // chip select
|
||||
Bus: spi,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +33,9 @@ func NewSPI(csb pin.Output, spi drivers.SPI) *DeviceSPI {
|
||||
// configures the BMI160, but it does not configure the SPI interface (it is
|
||||
// assumed to be up and running).
|
||||
func (d *DeviceSPI) Configure() error {
|
||||
if d.configurePins == nil {
|
||||
return legacy.ErrConfigBeforeInstantiated
|
||||
}
|
||||
d.configurePins()
|
||||
d.csb.High()
|
||||
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.CSB.High()
|
||||
|
||||
// The datasheet recommends doing a register read from address 0x7F to get
|
||||
// SPI communication going:
|
||||
// > If CSB sees a rising edge after power-up, the BMI160 interface switches
|
||||
@@ -93,9 +86,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
|
||||
data[0] = 0x80 | reg_TEMPERATURE_0
|
||||
data[1] = 0
|
||||
data[2] = 0
|
||||
d.csb.Low()
|
||||
err = d.bus.Tx(data, data)
|
||||
d.csb.High()
|
||||
d.CSB.Low()
|
||||
err = d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -130,9 +123,9 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
for i := 1; i < len(data); i++ {
|
||||
data[i] = 0
|
||||
}
|
||||
d.csb.Low()
|
||||
err = d.bus.Tx(data, data)
|
||||
d.csb.High()
|
||||
d.CSB.Low()
|
||||
err = d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -160,9 +153,9 @@ func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
|
||||
for i := 1; i < len(data); i++ {
|
||||
data[i] = 0
|
||||
}
|
||||
d.csb.Low()
|
||||
err = d.bus.Tx(data, data)
|
||||
d.csb.High()
|
||||
d.CSB.Low()
|
||||
err = d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -208,9 +201,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 {
|
||||
data := d.buf[:2]
|
||||
data[0] = 0x80 | address
|
||||
data[1] = 0
|
||||
d.csb.Low()
|
||||
d.bus.Tx(data, data)
|
||||
d.csb.High()
|
||||
d.CSB.Low()
|
||||
d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
return data[1]
|
||||
}
|
||||
|
||||
@@ -224,7 +217,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) {
|
||||
buf[0] = address
|
||||
buf[1] = data
|
||||
|
||||
d.csb.Low()
|
||||
d.bus.Tx(buf, buf)
|
||||
d.csb.High()
|
||||
d.CSB.Low()
|
||||
d.Bus.Tx(buf, buf)
|
||||
d.CSB.High()
|
||||
}
|
||||
|
||||
+8
-7
@@ -23,7 +23,6 @@ type Filter uint
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
buf [6]byte
|
||||
cali calibrationCoefficients
|
||||
Temperature Oversampling
|
||||
Pressure Oversampling
|
||||
@@ -135,8 +134,8 @@ func (d *Device) PrintCali() {
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
|
||||
func (d *Device) ReadTemperature() (temperature int32, err error) {
|
||||
data := d.buf[:3]
|
||||
if err = d.readData(REG_TEMP, data); err != nil {
|
||||
data, err := d.readData(REG_TEMP, 3)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,8 +158,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 := d.buf[:6]
|
||||
if err = d.readData(REG_PRES, data); err != nil {
|
||||
data, err := d.readData(REG_PRES, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -204,7 +203,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, data []byte) error {
|
||||
func (d *Device) readData(register int, n int) ([]byte, error) {
|
||||
// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
|
||||
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
|
||||
if d.Mode != MODE_NORMAL {
|
||||
@@ -219,7 +218,9 @@ func (d *Device) readData(register int, data []byte) error {
|
||||
}
|
||||
|
||||
// Read the requested register
|
||||
return legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
||||
data := make([]byte, n)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
|
||||
return data, err
|
||||
}
|
||||
|
||||
// convert3Bytes converts three bytes to int32
|
||||
|
||||
+7
-7
@@ -2,22 +2,22 @@
|
||||
package buzzer // import "tinygo.org/x/drivers/buzzer"
|
||||
|
||||
import (
|
||||
"time"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Device wraps a GPIO connection to a buzzer.
|
||||
type Device struct {
|
||||
pin pin.OutputFunc
|
||||
pin machine.Pin
|
||||
High bool
|
||||
BPM float64
|
||||
}
|
||||
|
||||
// New returns a new buzzer driver given which pin to use
|
||||
func New(pin pin.Output) Device {
|
||||
func New(pin machine.Pin) Device {
|
||||
return Device{
|
||||
pin: pin.Set,
|
||||
pin: pin,
|
||||
High: false,
|
||||
BPM: 96.0,
|
||||
}
|
||||
@@ -25,14 +25,14 @@ func New(pin pin.Output) Device {
|
||||
|
||||
// On sets the buzzer to a high state.
|
||||
func (l *Device) On() (err error) {
|
||||
l.pin.High()
|
||||
l.pin.Set(true)
|
||||
l.High = true
|
||||
return
|
||||
}
|
||||
|
||||
// Off sets the buzzer to a low state.
|
||||
func (l *Device) Off() (err error) {
|
||||
l.pin.Low()
|
||||
l.pin.Set(false)
|
||||
l.High = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,711 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
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,9 +2,9 @@
|
||||
package easystepper // import "tinygo.org/x/drivers/easystepper"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// StepMode determines the coil sequence used to perform a single step
|
||||
@@ -30,10 +30,28 @@ 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
|
||||
// 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'
|
||||
RPM uint
|
||||
// Mode determines the coil sequence used to perform a single step
|
||||
Mode StepMode
|
||||
}
|
||||
|
||||
// DualDeviceConfig contains the configuration data for a dual easystepper driver
|
||||
type DualDeviceConfig struct {
|
||||
DeviceConfig
|
||||
// Pin5 ... Pin8 determines the pins to configure and use for the second device
|
||||
Pin5, Pin6, Pin7, Pin8 machine.Pin
|
||||
}
|
||||
|
||||
// Device holds the pins and the delay between steps
|
||||
type Device struct {
|
||||
pins [4]pin.OutputFunc
|
||||
config func()
|
||||
pins [4]machine.Pin
|
||||
stepDelay time.Duration
|
||||
stepNumber uint8
|
||||
stepMode StepMode
|
||||
@@ -44,6 +62,51 @@ type DualDevice struct {
|
||||
devices [2]*Device
|
||||
}
|
||||
|
||||
// New returns a new single easystepper driver given a DeviceConfig
|
||||
func New(config DeviceConfig) (*Device, error) {
|
||||
if config.StepCount == 0 || config.RPM == 0 {
|
||||
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},
|
||||
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
|
||||
stepMode: config.Mode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
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
|
||||
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
|
||||
// Create the first device
|
||||
dev1, err := New(config.DeviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create the second device
|
||||
config.DeviceConfig.Pin1 = config.Pin5
|
||||
config.DeviceConfig.Pin2 = config.Pin6
|
||||
config.DeviceConfig.Pin3 = config.Pin7
|
||||
config.DeviceConfig.Pin4 = config.Pin8
|
||||
dev2, err := New(config.DeviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return composite dual device
|
||||
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
|
||||
}
|
||||
|
||||
// Configure configures the pins of the DualDevice
|
||||
func (d *DualDevice) Configure() {
|
||||
d.devices[0].Configure()
|
||||
d.devices[1].Configure()
|
||||
}
|
||||
|
||||
// Move rotates the motor the number of given steps
|
||||
// (negative steps will rotate it the opposite direction)
|
||||
func (d *Device) Move(steps int32) {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package easystepper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]pin.OutputFunc) (*Device, error) {
|
||||
if stepcount == 0 || rpm == 0 {
|
||||
return nil, errors.New("zero rpm and/or stepcount")
|
||||
}
|
||||
for i := range pins {
|
||||
if pins[i] == nil {
|
||||
return nil, errors.New("nil pin")
|
||||
}
|
||||
}
|
||||
d := &Device{
|
||||
pins: pins,
|
||||
stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)),
|
||||
stepMode: mode,
|
||||
config: func() {},
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
//go:build baremetal
|
||||
|
||||
package easystepper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// New returns a new single easystepper driver given a DeviceConfig
|
||||
func New(config DeviceConfig) (*Device, error) {
|
||||
if config.StepCount == 0 || config.RPM == 0 {
|
||||
return nil, errors.New("config.StepCount and config.RPM must be > 0")
|
||||
}
|
||||
return &Device{
|
||||
pins: [4]pin.OutputFunc{config.Pin1.Set, config.Pin2.Set, config.Pin3.Set, config.Pin4.Set},
|
||||
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
|
||||
stepMode: config.Mode,
|
||||
config: func() {
|
||||
legacy.ConfigurePinOut(config.Pin1)
|
||||
legacy.ConfigurePinOut(config.Pin2)
|
||||
legacy.ConfigurePinOut(config.Pin3)
|
||||
legacy.ConfigurePinOut(config.Pin4)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
func (d *Device) Configure() {
|
||||
if d.config == nil {
|
||||
panic(legacy.ErrConfigBeforeInstantiated)
|
||||
}
|
||||
d.config()
|
||||
}
|
||||
|
||||
// Configure configures the pins of the DualDevice
|
||||
func (d *DualDevice) Configure() {
|
||||
d.devices[0].Configure()
|
||||
d.devices[1].Configure()
|
||||
}
|
||||
|
||||
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
|
||||
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
|
||||
// Create the first device
|
||||
dev1, err := New(config.DeviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create the second device
|
||||
config.DeviceConfig.Pin1 = config.Pin5
|
||||
config.DeviceConfig.Pin2 = config.Pin6
|
||||
config.DeviceConfig.Pin3 = config.Pin7
|
||||
config.DeviceConfig.Pin4 = config.Pin8
|
||||
dev2, err := New(config.DeviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return composite dual device
|
||||
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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'
|
||||
RPM uint
|
||||
// Mode determines the coil sequence used to perform a single step
|
||||
Mode StepMode
|
||||
}
|
||||
|
||||
// DualDeviceConfig contains the configuration data for a dual easystepper driver
|
||||
type DualDeviceConfig struct {
|
||||
DeviceConfig
|
||||
// Pin5 ... Pin8 determines the pins to configure and use for the second device
|
||||
Pin5, Pin6, Pin7, Pin8 machine.Pin
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build tinygo && (rp2040 || rp2350 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
||||
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
||||
|
||||
// Implementation based on:
|
||||
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
// Package ens160 provides a driver for the ScioSense ENS160 digital gas sensor.
|
||||
//
|
||||
// Datasheet: https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
|
||||
package ens160
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeout = 30 * time.Millisecond
|
||||
shortTimeout = 1 * time.Millisecond
|
||||
)
|
||||
|
||||
// Conversion constants for environment data compensation.
|
||||
const (
|
||||
kelvinOffsetMilli = 273150 // 273.15 K in milli-units
|
||||
tempRawFactor = 64 // As per datasheet for TEMP_IN
|
||||
humRawFactor = 512 // As per datasheet for RH_IN
|
||||
milliFactor = 1000 // For converting from milli-units
|
||||
roundingTerm = milliFactor / 2 // For rounding before integer division
|
||||
)
|
||||
|
||||
// validityStrings provides human-readable descriptions for validity flags.
|
||||
var validityStrings = [...]string{
|
||||
ValidityNormalOperation: "normal operation",
|
||||
ValidityWarmUpPhase: "warm-up phase, wait ~3 minutes for valid data",
|
||||
ValidityInitialStartUpPhase: "initial start-up phase, wait ~1 hour for valid data",
|
||||
ValidityInvalidOutput: "invalid output",
|
||||
}
|
||||
|
||||
// Device wraps an I2C connection to an ENS160 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C // I²C implementation
|
||||
addr uint16 // 7‑bit bus address, promoted to uint16 per drivers.I2C
|
||||
|
||||
// shadow registers / last measurements
|
||||
lastTvocPPB uint16
|
||||
lastEco2PPM uint16
|
||||
lastAqiUBA uint8
|
||||
lastValidity uint8 // Store the latest validity status
|
||||
|
||||
// pre‑allocated buffers
|
||||
wbuf [5]byte // longest write: reg + 4 bytes (TEMP+RH)
|
||||
rbuf [5]byte // longest read: DATA burst (5 bytes)
|
||||
}
|
||||
|
||||
// New returns a new ENS160 driver.
|
||||
func New(bus drivers.I2C, addr uint16) *Device {
|
||||
if addr == 0 {
|
||||
addr = DefaultAddress
|
||||
}
|
||||
return &Device{
|
||||
bus: bus,
|
||||
addr: addr,
|
||||
lastValidity: ValidityInvalidOutput,
|
||||
}
|
||||
}
|
||||
|
||||
// Connected returns whether a ENS160 has been found.
|
||||
func (d *Device) Connected() bool {
|
||||
d.wbuf[0] = regPartID
|
||||
err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:2])
|
||||
return err == nil && d.rbuf[0] == LowPartID && d.rbuf[1] == HighPartID
|
||||
}
|
||||
|
||||
// Configure sets up the device for reading.
|
||||
func (d *Device) Configure() error {
|
||||
// 1. Soft-reset. The device will automatically enter IDLE mode.
|
||||
if err := d.write1(regOpMode, ModeReset); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(defaultTimeout)
|
||||
|
||||
// 2. Clear GPR registers, then go to STANDARD mode.
|
||||
if err := d.write1(regCommand, cmdClrGPR); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(defaultTimeout)
|
||||
|
||||
if err := d.write1(regOpMode, ModeStandard); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(defaultTimeout)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateTempRaw converts temperature from milli-degrees Celsius to the sensor's raw format.
|
||||
func calculateTempRaw(tempMilliC int32) uint16 {
|
||||
// Clip temperature
|
||||
const (
|
||||
minC = -40 * 1000
|
||||
maxC = 85 * 1000
|
||||
)
|
||||
if tempMilliC < minC {
|
||||
tempMilliC = minC
|
||||
} else if tempMilliC > maxC {
|
||||
tempMilliC = maxC
|
||||
}
|
||||
|
||||
// Integer fixed-point conversion to format required by the sensor.
|
||||
// Formula from datasheet: T_IN = (T_ambient_C + 273.15) * 64
|
||||
return uint16((((tempMilliC + kelvinOffsetMilli) * tempRawFactor) + roundingTerm) / milliFactor)
|
||||
}
|
||||
|
||||
// calculateHumRaw converts relative humidity from milli-percent to the sensor's raw format.
|
||||
func calculateHumRaw(rhMilliPct int32) uint16 {
|
||||
// Clip humidity
|
||||
if rhMilliPct < 0 {
|
||||
rhMilliPct = 0
|
||||
} else if rhMilliPct > 100*1000 {
|
||||
rhMilliPct = 100 * 1000
|
||||
}
|
||||
|
||||
// Integer fixed-point conversion to format required by the sensor.
|
||||
// Formula from datasheet: RH_IN = (RH_ambient_% * 512)
|
||||
return uint16(((rhMilliPct * humRawFactor) + roundingTerm) / milliFactor)
|
||||
}
|
||||
|
||||
// SetEnvDataMilli sets the ambient temperature and humidity for compensation.
|
||||
//
|
||||
// tempMilliC is the temperature in milli-degrees Celsius.
|
||||
// rhMilliPct is the relative humidity in milli-percent.
|
||||
func (d *Device) SetEnvDataMilli(tempMilliC, rhMilliPct int32) error {
|
||||
tempRaw := calculateTempRaw(tempMilliC)
|
||||
humRaw := calculateHumRaw(rhMilliPct)
|
||||
|
||||
d.wbuf[0] = regTempIn // start address (auto‑increment)
|
||||
binary.LittleEndian.PutUint16(d.wbuf[1:3], tempRaw)
|
||||
binary.LittleEndian.PutUint16(d.wbuf[3:5], humRaw)
|
||||
|
||||
return d.bus.Tx(d.addr, d.wbuf[:5], nil)
|
||||
}
|
||||
|
||||
// Update refreshes the concentration measurements.
|
||||
func (d *Device) Update(which drivers.Measurement) error {
|
||||
if which&drivers.Concentration == 0 {
|
||||
return nil // nothing requested
|
||||
}
|
||||
|
||||
const maxTries = 1000
|
||||
var (
|
||||
status uint8
|
||||
validity uint8
|
||||
)
|
||||
var gotData bool
|
||||
|
||||
// Poll DEVICE_STATUS until NEWDAT or timeout
|
||||
for range maxTries {
|
||||
var err error
|
||||
status, err = d.read1(regStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status&statusSTATER != 0 {
|
||||
return errors.New("ENS160: error (STATER set)")
|
||||
}
|
||||
validity = (status & statusValidityMask) >> statusValidityShift
|
||||
|
||||
if status&statusNEWDAT != 0 {
|
||||
gotData = true
|
||||
break // Always break when data available
|
||||
}
|
||||
time.Sleep(shortTimeout)
|
||||
}
|
||||
if !gotData {
|
||||
return errors.New("ENS160: timeout waiting for NEWDAT")
|
||||
}
|
||||
|
||||
// Burst-read data regardless of validity state
|
||||
d.wbuf[0] = regAQI
|
||||
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:5]); err != nil {
|
||||
return errors.New("ENS160: burst read failed")
|
||||
}
|
||||
|
||||
d.lastAqiUBA = d.rbuf[0]
|
||||
d.lastTvocPPB = binary.LittleEndian.Uint16(d.rbuf[1:3])
|
||||
d.lastEco2PPM = binary.LittleEndian.Uint16(d.rbuf[3:5])
|
||||
d.lastValidity = validity // Store the validity status
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TVOC returns the last total‑VOC concentration in parts‑per‑billion.
|
||||
func (d *Device) TVOC() uint16 { return d.lastTvocPPB }
|
||||
|
||||
// ECO2 returns the last equivalent CO₂ concentration in parts‑per‑million.
|
||||
func (d *Device) ECO2() uint16 { return d.lastEco2PPM }
|
||||
|
||||
// AQI returns the last Air‑Quality Index according to UBA (1–5).
|
||||
func (d *Device) AQI() uint8 { return d.lastAqiUBA }
|
||||
|
||||
// Validity returns the current operating state of the sensor.
|
||||
func (d *Device) Validity() uint8 {
|
||||
return d.lastValidity
|
||||
}
|
||||
|
||||
// ValidityString returns a human-readable string describing the current validity status.
|
||||
func (d *Device) ValidityString() string {
|
||||
if int(d.lastValidity) < len(validityStrings) {
|
||||
return validityStrings[d.lastValidity]
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// write1 writes a single byte to a register.
|
||||
func (d *Device) write1(reg, val uint8) error {
|
||||
d.wbuf[0] = reg
|
||||
d.wbuf[1] = val
|
||||
return d.bus.Tx(d.addr, d.wbuf[:2], nil)
|
||||
}
|
||||
|
||||
// read1 reads a single byte from a register.
|
||||
func (d *Device) read1(reg uint8) (uint8, error) {
|
||||
d.wbuf[0] = reg
|
||||
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:1]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return d.rbuf[0], nil
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package ens160
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCalculateTempRaw(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
tempMilliC int32
|
||||
expectedRaw uint16
|
||||
}{
|
||||
{"25°C", 25000, 19082},
|
||||
{"-10.5°C", -10500, 16810},
|
||||
{"Min temp", -40000, 14922},
|
||||
{"Below min", -50000, 14922},
|
||||
{"Max temp", 85000, 22922},
|
||||
{"Above max", 90000, 22922},
|
||||
{"Zero", 0, 17482},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
raw := calculateTempRaw(tc.tempMilliC)
|
||||
if raw != tc.expectedRaw {
|
||||
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateHumRaw(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
rhMilliPct int32
|
||||
expectedRaw uint16
|
||||
}{
|
||||
{"50%", 50000, 25600},
|
||||
{"0%", 0, 0},
|
||||
{"100%", 100000, 51200},
|
||||
{"Below 0%", -10000, 0},
|
||||
{"Above 100%", 110000, 51200},
|
||||
{"33.3%", 33300, 17050},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
raw := calculateHumRaw(tc.rhMilliPct)
|
||||
if raw != tc.expectedRaw {
|
||||
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package ens160
|
||||
|
||||
// DefaultAddress is the default I2C address for the ENS160 when the ADDR pin is
|
||||
// connected to high (3.3V). When connected to low (GND), the address is 0x52.
|
||||
const DefaultAddress = 0x53
|
||||
|
||||
// Registers
|
||||
const (
|
||||
regPartID = 0x00
|
||||
regOpMode = 0x10
|
||||
regConfig = 0x11
|
||||
regCommand = 0x12
|
||||
regTempIn = 0x13
|
||||
regRhIn = 0x15
|
||||
regStatus = 0x20
|
||||
regAQI = 0x21
|
||||
regTVOC = 0x22
|
||||
regECO2 = 0x24
|
||||
regDataT = 0x30
|
||||
regDataRH = 0x32
|
||||
regMISR = 0x38
|
||||
regGPRWrite = 0x40
|
||||
regGPRRead = 0x48
|
||||
)
|
||||
|
||||
// Operating modes
|
||||
const (
|
||||
ModeDeepSleep = 0x00
|
||||
ModeIdle = 0x01
|
||||
ModeStandard = 0x02
|
||||
ModeReset = 0xF0
|
||||
)
|
||||
|
||||
// Status register bits
|
||||
const (
|
||||
statusSTATAS = 1 << 7
|
||||
statusSTATER = 1 << 6
|
||||
|
||||
statusValidityMask = 0x0C
|
||||
statusValidityShift = 2
|
||||
|
||||
statusNEWDAT = 1 << 1
|
||||
statusNEWGPR = 1 << 0
|
||||
)
|
||||
|
||||
// Validity flags
|
||||
const (
|
||||
ValidityNormalOperation = 0x00
|
||||
ValidityWarmUpPhase = 0x01 // need ~3 minutes until valid data
|
||||
ValidityInitialStartUpPhase = 0x02 // need ~1 hour until valid data
|
||||
ValidityInvalidOutput = 0x03
|
||||
)
|
||||
|
||||
// Commands
|
||||
const (
|
||||
cmdNOP = 0x00
|
||||
cmdGetAppVer = 0x0E
|
||||
cmdClrGPR = 0xCC
|
||||
)
|
||||
|
||||
// Part IDs
|
||||
const (
|
||||
LowPartID = 0x60
|
||||
HighPartID = 0x01
|
||||
)
|
||||
@@ -1,56 +0,0 @@
|
||||
// This example demonstrates ENS160 usage.
|
||||
//
|
||||
// Wiring:
|
||||
// - VCC to 3.3V, GND to ground
|
||||
// - SDA to board SDA, SCL to board SCL
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/ens160"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: 400 * machine.KHz,
|
||||
})
|
||||
if err != nil {
|
||||
println("Failed to configure I2C:", err)
|
||||
}
|
||||
|
||||
dev := ens160.New(machine.I2C0, ens160.DefaultAddress)
|
||||
|
||||
connected := dev.Connected()
|
||||
if !connected {
|
||||
println("ENS160 not detected")
|
||||
return
|
||||
}
|
||||
println("ENS160 detected")
|
||||
|
||||
if err := dev.Configure(); err != nil {
|
||||
println("Failed to configure ENS160:", err)
|
||||
}
|
||||
|
||||
for {
|
||||
err := dev.Update(drivers.Concentration)
|
||||
if err != nil {
|
||||
println("Error reading ENS160: %v\n", err)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
println(
|
||||
"AQI:", dev.AQI(),
|
||||
"TVOC:", dev.TVOC(),
|
||||
"eCO2:", dev.ECO2(),
|
||||
"Validity:", dev.ValidityString(),
|
||||
)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -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.NewI2CWithAddress(machine.I2C0, gps.UBLOX_I2C_ADDRESS)
|
||||
ublox := gps.NewI2C(machine.I2C0)
|
||||
parser := gps.NewParser()
|
||||
var fix gps.Fix
|
||||
for {
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/honeyhsc"
|
||||
)
|
||||
|
||||
// Data taken from https://github.com/rodan/honeywell_hsc_ssc_i2c/blob/master/hsc_ssc_i2c.cpp
|
||||
// these defaults are valid for the HSCMRNN030PA2A3 chip
|
||||
const (
|
||||
i2cAddress = 0x28
|
||||
// 10%
|
||||
outputMinimum = 0x666
|
||||
// 90% of 2^14 - 1
|
||||
outputMax = 0x399A
|
||||
// min is 0 for sensors that give absolute values
|
||||
pressureMin = 0
|
||||
// 30psi (and we want results in millipascals)
|
||||
// pressureMax = 206842.7
|
||||
pressureMax = 206843 * 1000
|
||||
)
|
||||
|
||||
func main() {
|
||||
bus := machine.I2C0
|
||||
err := bus.Configure(machine.I2CConfig{
|
||||
Frequency: 400_000, // 100kHz minimum and 400kHz I2C maximum clock. 50 to 800 for SPI.
|
||||
SDA: machine.I2C0_SDA_PIN,
|
||||
SCL: machine.I2C0_SCL_PIN,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
sensor := honeyhsc.NewDevI2C(bus, i2cAddress, outputMinimum, outputMax, pressureMin, pressureMax)
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
const measuremask = drivers.Pressure | drivers.Temperature
|
||||
err := sensor.Update(measuremask)
|
||||
if err != nil {
|
||||
println("error updating measurements:", err.Error())
|
||||
continue
|
||||
}
|
||||
P := sensor.Pressure()
|
||||
T := sensor.Temperature()
|
||||
println("pressure:", P, "temperature:", T)
|
||||
}
|
||||
}
|
||||
+3
-12
@@ -14,18 +14,9 @@ func main() {
|
||||
i2c.Configure(machine.I2CConfig{SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN})
|
||||
|
||||
accel := lis3dh.New(i2c)
|
||||
err := accel.Configure(lis3dh.Config{
|
||||
Address: lis3dh.Address1, // address on the Circuit Playground Express
|
||||
})
|
||||
for err != nil {
|
||||
println("could not configure LIS3DH:", err)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
err = accel.SetRange(lis3dh.RANGE_2_G)
|
||||
for err != nil {
|
||||
println("could not set acceleration range:", err)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
|
||||
accel.Configure()
|
||||
accel.SetRange(lis3dh.RANGE_2_G)
|
||||
|
||||
println(accel.Connected())
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/lsm303dlhc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// LSM303DLHC is connected to the I2C0 bus on Adafruit Feather M4 via pins: 20(SDA) and 21(SCL).
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
sensor := lsm303dlhc.New(machine.I2C0)
|
||||
//default settings
|
||||
err := sensor.Configure(lsm303dlhc.Configuration{
|
||||
AccelPowerMode: lsm303dlhc.ACCEL_POWER_NORMAL,
|
||||
AccelRange: lsm303dlhc.ACCEL_RANGE_2G,
|
||||
AccelDataRate: lsm303dlhc.ACCEL_DATARATE_100HZ,
|
||||
MagPowerMode: lsm303dlhc.MAG_POWER_NORMAL,
|
||||
MagSystemMode: lsm303dlhc.MAG_SYSTEM_CONTINUOUS,
|
||||
MagDataRate: lsm303dlhc.MAG_DATARATE_10HZ,
|
||||
})
|
||||
if err != nil {
|
||||
for {
|
||||
println("Failed to configure", err.Error())
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
accel_x, accel_y, accel_z, err := sensor.ReadAcceleration()
|
||||
if err != nil {
|
||||
println("Failed to read accel", err.Error())
|
||||
}
|
||||
println("ACCEL_X:", accel_x, " ACCEL_Y:", accel_y, " ACCEL_Z:", accel_z)
|
||||
|
||||
mag_x, mag_y, mag_z, err := sensor.ReadMagneticField()
|
||||
if err != nil {
|
||||
println("Failed to read mag", err.Error())
|
||||
}
|
||||
println("MAG_X:", mag_x, " MAG_Y:", mag_y, " MAG_Z:", mag_z)
|
||||
|
||||
pitch, roll, _ := sensor.ReadPitchRoll()
|
||||
println("Pitch:", float32(pitch), " Roll:", float32(roll))
|
||||
|
||||
heading, _ := sensor.ReadCompass()
|
||||
println("Heading:", float32(heading), "degrees")
|
||||
|
||||
temp, _ := sensor.ReadTemperature()
|
||||
println("Temperature:", float32(temp)/1000, "*C")
|
||||
|
||||
println("\n")
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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 || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// examples/net/webclient (for HTTP)
|
||||
// examples/net/tlsclient (for HTTPS)
|
||||
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
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 || comboat_fw
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
|
||||
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 || comboat_fw
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
|
||||
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 || comboat_fw
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
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 || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// nc -lk 8080
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
//go:build ninafw || wioterminal || challenger_rp2040
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// nc -lk 8080
|
||||
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
|
||||
//go:build ninafw || wioterminal || challenger_rp2040 || pico
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// }
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
//go:build ninafw || wioterminal || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
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 || comboat_fw
|
||||
//go:build ninafw || wioterminal
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/seesaw"
|
||||
)
|
||||
|
||||
// example reading the position of a rotary encoder (4991) powered by a seesaw
|
||||
// https://learn.adafruit.com/adafruit-i2c-qt-rotary-encoder/arduino
|
||||
func main() {
|
||||
// This assumes you are using an Adafruit QT Py RP2040 for its Stemma QT connector
|
||||
// https://www.adafruit.com/product/4900
|
||||
i2c := machine.I2C1
|
||||
i2c.Configure(machine.I2CConfig{
|
||||
SCL: machine.I2C1_QT_SCL_PIN,
|
||||
SDA: machine.I2C1_QT_SDA_PIN,
|
||||
})
|
||||
|
||||
dev := seesaw.New(i2c)
|
||||
dev.Address = 0x36
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
pos, err := dev.GetEncoderPosition(0, false)
|
||||
if err != nil {
|
||||
println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
println(pos)
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/si5351"
|
||||
)
|
||||
|
||||
// Simple demo of the SI5351 clock generator.
|
||||
// This is like the Arduino library example:
|
||||
// https://github.com/adafruit/Adafruit_Si5351_Library/blob/master/examples/si5351/si5351.ino
|
||||
// Which will configure the chip with:
|
||||
// - PLL A at 900mhz
|
||||
// - PLL B at 616.66667mhz
|
||||
// - Clock 0 at 112.5mhz, using PLL A as a source divided by 8
|
||||
// - Clock 1 at 13.5531mhz, using PLL B as a source divided by 45.5
|
||||
// - Clock 2 at 10.76khz, using PLL B as a source divided by 900 and further divided with an R divider of 64.
|
||||
|
||||
func main() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
println("Si5351 Clockgen Test")
|
||||
println()
|
||||
|
||||
// Configure I2C bus
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
// Create driver instance
|
||||
clockgen := si5351.New(machine.I2C0)
|
||||
|
||||
// Verify device wired properly
|
||||
connected, err := clockgen.Connected()
|
||||
if err != nil {
|
||||
println("Unable to read device status")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
if !connected {
|
||||
for {
|
||||
println("Unable to detect si5351 device")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise device
|
||||
clockgen.Configure()
|
||||
|
||||
// Now configue the PLLs and clock outputs.
|
||||
// The PLLs can be configured with a multiplier and division of the on-board
|
||||
// 25mhz reference crystal. For example configure PLL A to 900mhz by multiplying
|
||||
// by 36. This uses an integer multiplier which is more accurate over time
|
||||
// but allows less of a range of frequencies compared to a fractional
|
||||
// multiplier shown next.
|
||||
clockgen.ConfigurePLL(si5351.PLL_A, 36, 0, 1) // Multiply 25mhz by 36
|
||||
println("PLL A frequency: 900mhz")
|
||||
|
||||
// And next configure PLL B to 616.6667mhz by multiplying 25mhz by 24.667 using
|
||||
// the fractional multiplier configuration. Notice you specify the integer
|
||||
// multiplier and then a numerator and denominator as separate values, i.e.
|
||||
// numerator 2 and denominator 3 means 2/3 or 0.667. This fractional
|
||||
// configuration is susceptible to some jitter over time but can set a larger
|
||||
// range of frequencies.
|
||||
clockgen.ConfigurePLL(si5351.PLL_B, 24, 2, 3) // Multiply 25mhz by 24.667 (24 2/3)
|
||||
println("PLL B frequency: 616.6667mhz")
|
||||
|
||||
// Now configure the clock outputs. Each is driven by a PLL frequency as input
|
||||
// and then further divides that down to a specific frequency.
|
||||
// Configure clock 0 output to be driven by PLL A divided by 8, so an output
|
||||
// of 112.5mhz (900mhz / 8). Again this uses the most precise integer division
|
||||
// but can't set as wide a range of values.
|
||||
clockgen.ConfigureMultisynth(0, si5351.PLL_A, 8, 0, 1) // Divide by 8 (8 0/1)
|
||||
println("Clock 0: 112.5mhz")
|
||||
|
||||
// Next configure clock 1 to be driven by PLL B divided by 45.5 to get
|
||||
// 13.5531mhz (616.6667mhz / 45.5). This uses fractional division and again
|
||||
// notice the numerator and denominator are explicitly specified. This is less
|
||||
// precise but allows a large range of frequencies.
|
||||
clockgen.ConfigureMultisynth(1, si5351.PLL_B, 45, 1, 2) // Divide by 45.5 (45 1/2)
|
||||
println("Clock 1: 13.5531mhz")
|
||||
|
||||
// Finally configure clock 2 to be driven by PLL B divided once by 900 to get
|
||||
// down to 685.15 khz and then further divided by a special R divider that
|
||||
// divides 685.15 khz by 64 to get a final output of 10.706khz.
|
||||
clockgen.ConfigureMultisynth(2, si5351.PLL_B, 900, 0, 1) // Divide by 900 (900 0/1)
|
||||
// Set the R divider, this can be a value of:
|
||||
// - R_DIV_1: divider of 1
|
||||
// - R_DIV_2: divider of 2
|
||||
// - R_DIV_4: divider of 4
|
||||
// - R_DIV_8: divider of 8
|
||||
// - R_DIV_16: divider of 16
|
||||
// - R_DIV_32: divider of 32
|
||||
// - R_DIV_64: divider of 64
|
||||
// - R_DIV_128: divider of 128
|
||||
clockgen.ConfigureRdiv(2, si5351.R_DIV_64)
|
||||
println("Clock 2: 10.706khz")
|
||||
|
||||
// After configuring PLLs and clocks, enable the outputs.
|
||||
clockgen.EnableOutputs()
|
||||
|
||||
for {
|
||||
time.Sleep(5 * time.Second)
|
||||
println()
|
||||
println("Clock 0: 112.5mhz")
|
||||
println("Clock 1: 13.5531mhz")
|
||||
println("Clock 2: 10.706khz")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"image/color"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
display := ssd1306.NewI2C(machine.I2C0)
|
||||
display.Configure(ssd1306.Config{
|
||||
Address: ssd1306.Address_128_32,
|
||||
Width: 128,
|
||||
Height: 32,
|
||||
})
|
||||
|
||||
display.ClearDisplay()
|
||||
|
||||
x := int16(0)
|
||||
y := int16(0)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
for {
|
||||
pixel := display.GetPixel(x, y)
|
||||
c := color.RGBA{255, 255, 255, 255}
|
||||
if pixel {
|
||||
c = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
display.SetPixel(x, y, c)
|
||||
display.Display()
|
||||
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == 127 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == 31 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// This example shows how to use 128x64 display over I2C
|
||||
// Tested on Seeeduino XIAO Expansion Board https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
|
||||
//
|
||||
// According to manual, I2C address of the display is 0x78, but that's 8-bit address.
|
||||
// TinyGo operates on 7-bit addresses and respective 7-bit address would be 0x3C, which we use below.
|
||||
//
|
||||
// To learn more about different types of I2C addresses, please see following page
|
||||
// https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"image/color"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
display := ssd1306.NewI2C(machine.I2C0)
|
||||
display.Configure(ssd1306.Config{
|
||||
Address: 0x3C,
|
||||
Width: 128,
|
||||
Height: 64,
|
||||
})
|
||||
|
||||
display.ClearDisplay()
|
||||
|
||||
x := int16(0)
|
||||
y := int16(0)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
for {
|
||||
pixel := display.GetPixel(x, y)
|
||||
c := color.RGBA{255, 255, 255, 255}
|
||||
if pixel {
|
||||
c = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
display.SetPixel(x, y, c)
|
||||
display.Display()
|
||||
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == 127 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == 63 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package main
|
||||
|
||||
// This example shows how to use SSD1306 OLED display driver over I2C and SPI.
|
||||
//
|
||||
// Check the `newSSD1306Display()` functions for I2C and SPI initializations.
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"image/color"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
display := newSSD1306Display()
|
||||
display.ClearDisplay()
|
||||
|
||||
w, h := display.Size()
|
||||
x := int16(0)
|
||||
y := int16(0)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
|
||||
traceTime := time.Now().UnixMilli() + 1000
|
||||
frames := 0
|
||||
ms := runtime.MemStats{}
|
||||
|
||||
for {
|
||||
pixel := display.GetPixel(x, y)
|
||||
c := color.RGBA{255, 255, 255, 255}
|
||||
if pixel {
|
||||
c = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
display.SetPixel(x, y, c)
|
||||
display.Display()
|
||||
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == w-1 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == h-1 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
|
||||
frames++
|
||||
now := time.Now().UnixMilli()
|
||||
if now >= traceTime {
|
||||
runtime.ReadMemStats(&ms)
|
||||
println("TS", now, "| FPS", frames, "| HeapInuse", ms.HeapInuse)
|
||||
traceTime = now + 1000
|
||||
frames = 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
//go:build xiao_ble
|
||||
|
||||
// This initializes SSD1306 OLED display driver over I2C.
|
||||
//
|
||||
// Seeed XIAO BLE board + SSD1306 128x32 I2C OLED display.
|
||||
//
|
||||
// Wiring:
|
||||
// - XIAO GND -> OLED GND
|
||||
// - XIAO 3v3 -> OLED VCC
|
||||
// - XIAO D4 (SDA) -> OLED SDA
|
||||
// - XIAO D5 (SCL) -> OLED SCK
|
||||
//
|
||||
// For your case:
|
||||
// - Connect the display to I2C pins on your board.
|
||||
// - Adjust I2C address and display size as needed.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func newSSD1306Display() *ssd1306.Device {
|
||||
machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: 400 * machine.KHz,
|
||||
SDA: machine.SDA0_PIN,
|
||||
SCL: machine.SCL0_PIN,
|
||||
})
|
||||
display := ssd1306.NewI2C(machine.I2C0)
|
||||
display.Configure(ssd1306.Config{
|
||||
Address: ssd1306.Address_128_32, // or ssd1306.Address
|
||||
Width: 128,
|
||||
Height: 32, // or 64
|
||||
})
|
||||
return display
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
//go:build thumby
|
||||
|
||||
// This initializes SSD1306 OLED display driver over SPI.
|
||||
//
|
||||
// Thumby board has a tiny built-in 72x40 display.
|
||||
//
|
||||
// As the display is built-in, no wiring is needed.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func newSSD1306Display() *ssd1306.Device {
|
||||
machine.SPI0.Configure(machine.SPIConfig{})
|
||||
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
||||
display.Configure(ssd1306.Config{
|
||||
Width: 72,
|
||||
Height: 40,
|
||||
ResetCol: ssd1306.ResetValue{28, 99},
|
||||
ResetPage: ssd1306.ResetValue{0, 5},
|
||||
})
|
||||
return display
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//go:build xiao_rp2040
|
||||
|
||||
// This initializes SSD1306 OLED display driver over SPI.
|
||||
//
|
||||
// Seeed XIAO RP2040 board + SSD1306 128x64 SPI OLED display.
|
||||
//
|
||||
// Wiring:
|
||||
// - XIAO GND -> OLED GND
|
||||
// - XIAO 3v3 -> OLED VCC
|
||||
// - XIAO D8 (SCK) -> OLED D0
|
||||
// - XIAO D10 (SDO) -> OLED D1
|
||||
// - XIAO D4 -> OLED RES
|
||||
// - XIAO D5 -> OLED DC
|
||||
// - XIAO D6 -> OLED CS
|
||||
//
|
||||
// For your case:
|
||||
// - Connect the display to SPI pins on your board.
|
||||
// - Adjust RES, DC and CS pins as needed.
|
||||
// - Adjust SPI frequency as needed.
|
||||
// - Adjust display size as needed.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func newSSD1306Display() *ssd1306.Device {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
Frequency: 50 * machine.MHz,
|
||||
})
|
||||
display := ssd1306.NewSPI(machine.SPI0, machine.D5, machine.D4, machine.D6)
|
||||
display.Configure(ssd1306.Config{
|
||||
Width: 128,
|
||||
Height: 64,
|
||||
})
|
||||
return display
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.SPI0.Configure(machine.SPIConfig{
|
||||
Frequency: 8000000,
|
||||
})
|
||||
display := ssd1306.NewSPI(machine.SPI0, machine.P8, machine.P7, machine.P9)
|
||||
display.Configure(ssd1306.Config{
|
||||
Width: 128,
|
||||
Height: 64,
|
||||
})
|
||||
|
||||
display.ClearDisplay()
|
||||
|
||||
x := int16(64)
|
||||
y := int16(32)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
for {
|
||||
pixel := display.GetPixel(x, y)
|
||||
c := color.RGBA{255, 255, 255, 255}
|
||||
if pixel {
|
||||
c = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
display.SetPixel(x, y, c)
|
||||
display.Display()
|
||||
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == 127 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == 63 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// This example using the SSD1306 OLED display over SPI on the Thumby board
|
||||
// A very tiny 72x40 display.
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.SPI0.Configure(machine.SPIConfig{})
|
||||
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
||||
display.Configure(ssd1306.Config{
|
||||
Width: 72,
|
||||
Height: 40,
|
||||
ResetCol: ssd1306.ResetValue{28, 99},
|
||||
ResetPage: ssd1306.ResetValue{0, 5},
|
||||
})
|
||||
|
||||
display.ClearDisplay()
|
||||
|
||||
x := int16(36)
|
||||
y := int16(20)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
for {
|
||||
pixel := display.GetPixel(x, y)
|
||||
c := color.RGBA{255, 255, 255, 255}
|
||||
if pixel {
|
||||
c = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
display.SetPixel(x, y, c)
|
||||
display.Display()
|
||||
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == 71 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == 39 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
@@ -46,10 +46,6 @@ var DefaultDeviceIdentifier = DeviceIdentifierFunc(func(id JedecID) Attrs {
|
||||
return GD25Q16C()
|
||||
case 0xC84017:
|
||||
return GD25Q64C()
|
||||
case 0x856015:
|
||||
return P25Q16H()
|
||||
case 0xEF4014:
|
||||
return W25Q80DV()
|
||||
case 0xEF4015:
|
||||
return W25Q16JVIQ()
|
||||
case 0xEF4016:
|
||||
@@ -243,24 +239,6 @@ func GD25Q64C() Attrs {
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Puya P25Q16H 2MiB SPI flash.
|
||||
// Datasheet: https://files.seeedstudio.com/wiki/github_weiruanexample/Flash_P25Q16H-UXH-IR_Datasheet.pdf
|
||||
func P25Q16H() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0x85, 0x60, 0x15},
|
||||
MaxClockSpeedMHz: 55,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: true,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: true,
|
||||
WriteStatusSplit: true,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a
|
||||
// different .memory_type (0x70) Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
||||
@@ -402,25 +380,6 @@ func W25Q80DL() Attrs {
|
||||
TotalSize: 1 << 20, // 1 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x60, 0x14},
|
||||
MaxClockSpeedMHz: 80,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
SupportsFastRead: true,
|
||||
SupportsQSPI: true,
|
||||
SupportsQSPIWrites: false,
|
||||
WriteStatusSplit: false,
|
||||
SingleStatusByte: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Settings for the Winbond W25Q80DV 2MiB SPI flash.
|
||||
// Datasheet:
|
||||
// https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
|
||||
func W25Q80DV() Attrs {
|
||||
return Attrs{
|
||||
TotalSize: 1 << 21, // 2 MiB
|
||||
StartUp: 5000 * time.Microsecond,
|
||||
JedecID: JedecID{0xEF, 0x40, 0x14},
|
||||
MaxClockSpeedMHz: 104,
|
||||
QuadEnableBitMask: 0x02,
|
||||
HasSectorProtection: false,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// Guarded because still unsure of how to deal with interrupt drivers.
|
||||
//go:build tinygo
|
||||
|
||||
// Package ft6336 provides a driver for the FT6336 I2C Self-Capacitive touch
|
||||
// panel controller.
|
||||
//
|
||||
|
||||
+15
-16
@@ -5,13 +5,12 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01"
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// Rotation controls the rotation used by the display.
|
||||
@@ -23,10 +22,10 @@ type FrameRate uint8
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
dcPin pin.OutputFunc
|
||||
resetPin pin.OutputFunc
|
||||
csPin pin.OutputFunc
|
||||
blPin pin.OutputFunc
|
||||
dcPin machine.Pin
|
||||
resetPin machine.Pin
|
||||
csPin machine.Pin
|
||||
blPin machine.Pin
|
||||
width int16
|
||||
height int16
|
||||
columnOffsetCfg int16
|
||||
@@ -53,17 +52,17 @@ type Config struct {
|
||||
}
|
||||
|
||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
|
||||
legacy.ConfigurePinOut(resetPin)
|
||||
legacy.ConfigurePinOut(dcPin)
|
||||
legacy.ConfigurePinOut(csPin)
|
||||
legacy.ConfigurePinOut(blPin)
|
||||
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})
|
||||
return Device{
|
||||
bus: bus,
|
||||
resetPin: resetPin.Set,
|
||||
dcPin: dcPin.Set,
|
||||
csPin: csPin.Set,
|
||||
blPin: blPin.Set,
|
||||
resetPin: resetPin,
|
||||
dcPin: dcPin,
|
||||
csPin: csPin,
|
||||
blPin: blPin,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +226,7 @@ func (d *Device) Data(data uint8) {
|
||||
|
||||
// Tx sends data to the display
|
||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||
d.dcPin(!isCommand)
|
||||
d.dcPin.Set(!isCommand)
|
||||
d.bus.Tx(data, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
module tinygo.org/x/drivers
|
||||
|
||||
|
||||
go 1.22.1
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/frankban/quicktest v1.10.2
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/orsinium-labs/tinymath v1.1.0
|
||||
github.com/soypat/natiu-mqtt v0.5.1
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/net v0.7.0
|
||||
tinygo.org/x/tinyfont v0.3.0
|
||||
tinygo.org/x/tinyterm v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-cmp v0.5.2 // indirect
|
||||
github.com/kr/pretty v0.2.1 // indirect
|
||||
github.com/kr/text v0.1.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
)
|
||||
|
||||
@@ -3,9 +3,8 @@ 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=
|
||||
@@ -13,15 +12,12 @@ 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/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/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/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=
|
||||
|
||||
+1
-7
@@ -69,16 +69,10 @@ 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: i2cAddress,
|
||||
address: I2C_ADDRESS,
|
||||
buffer: make([]byte, bufferSize),
|
||||
bufIdx: bufferSize,
|
||||
sentence: strings.Builder{},
|
||||
|
||||
+1
-4
@@ -96,10 +96,7 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
|
||||
fix.Speed = findSpeed(fields[7])
|
||||
fix.Heading = findHeading(fields[8])
|
||||
date := findDate(fields[9])
|
||||
fix.Time = date.Add(time.Duration(fix.Time.Hour())*time.Hour +
|
||||
time.Duration(fix.Time.Minute())*time.Minute +
|
||||
time.Duration(fix.Time.Second())*time.Second +
|
||||
time.Duration(fix.Time.Nanosecond())*time.Nanosecond)
|
||||
fix.Time = fix.Time.AddDate(date.Year(), int(date.Month()), date.Day())
|
||||
|
||||
return fix, nil
|
||||
}
|
||||
|
||||
@@ -70,15 +70,15 @@ func TestParseRMC(t *testing.T) {
|
||||
t.Error("should have errInvalidRMCSentence error")
|
||||
}
|
||||
|
||||
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,010622,0.0,E,D*2B"
|
||||
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,130522,0.0,E,D*2B"
|
||||
fix, err := p.Parse(val)
|
||||
if err != nil {
|
||||
t.Error("should have parsed")
|
||||
}
|
||||
|
||||
c.Assert(fix.Time.Year(), qt.Equals, 2022)
|
||||
c.Assert(fix.Time.Month(), qt.Equals, time.June)
|
||||
c.Assert(fix.Time.Day(), qt.Equals, 1)
|
||||
c.Assert(fix.Time.Month(), qt.Equals, time.May)
|
||||
c.Assert(fix.Time.Day(), qt.Equals, 13)
|
||||
c.Assert(fix.Time.Hour(), qt.Equals, 20)
|
||||
c.Assert(fix.Time.Minute(), qt.Equals, 35)
|
||||
c.Assert(fix.Time.Second(), qt.Equals, 22)
|
||||
|
||||
+1
-5
@@ -4,11 +4,7 @@ package gps
|
||||
|
||||
// The I2C address which this device listens to.
|
||||
const (
|
||||
// To ensure backward compatibility
|
||||
I2C_ADDRESS = UBLOX_I2C_ADDRESS
|
||||
|
||||
UBLOX_I2C_ADDRESS = 0x42
|
||||
PA1010D_I2C_ADDRESS = 0x10
|
||||
I2C_ADDRESS = 0x42
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+10
-19
@@ -5,39 +5,30 @@
|
||||
package hcsr04
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
const TIMEOUT = 23324 // max sensing distance (4m)
|
||||
|
||||
// Device holds the pins
|
||||
type Device struct {
|
||||
trigger pin.OutputFunc
|
||||
echo pin.InputFunc
|
||||
configurePins func()
|
||||
trigger machine.Pin
|
||||
echo machine.Pin
|
||||
}
|
||||
|
||||
// New returns a new ultrasonic driver given 2 pins
|
||||
func New(trigger pin.Output, echo pin.Input) Device {
|
||||
func New(trigger, echo machine.Pin) Device {
|
||||
return Device{
|
||||
trigger: trigger.Set,
|
||||
echo: echo.Get,
|
||||
configurePins: func() {
|
||||
legacy.ConfigurePinOut(trigger)
|
||||
legacy.ConfigurePinInput(echo)
|
||||
},
|
||||
trigger: trigger,
|
||||
echo: echo,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the pins of the Device
|
||||
func (d *Device) Configure() {
|
||||
if d.configurePins == nil {
|
||||
panic(legacy.ErrConfigBeforeInstantiated)
|
||||
}
|
||||
d.configurePins()
|
||||
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
|
||||
@@ -61,7 +52,7 @@ func (d *Device) ReadPulse() int32 {
|
||||
d.trigger.Low()
|
||||
i := uint8(0)
|
||||
for {
|
||||
if d.echo() {
|
||||
if d.echo.Get() {
|
||||
t = time.Now()
|
||||
break
|
||||
}
|
||||
@@ -75,7 +66,7 @@ func (d *Device) ReadPulse() int32 {
|
||||
}
|
||||
i = 0
|
||||
for {
|
||||
if !d.echo() {
|
||||
if !d.echo.Get() {
|
||||
return int32(time.Since(t).Microseconds())
|
||||
}
|
||||
i++
|
||||
|
||||
+5
-5
@@ -210,7 +210,7 @@ func (d *Device) SendCommand(command byte) {
|
||||
d.bus.SetCommandMode(true)
|
||||
d.bus.Write([]byte{command})
|
||||
|
||||
for d.isBusy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
|
||||
for d.busy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func (d *Device) sendData(data byte) {
|
||||
d.bus.SetCommandMode(false)
|
||||
d.bus.Write([]byte{data})
|
||||
|
||||
for d.isBusy(false) {
|
||||
for d.busy(false) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,9 +231,9 @@ func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// isBusy returns true when hd447890 is isBusy
|
||||
// busy returns true when hd447890 is busy
|
||||
// or after the timeout specified
|
||||
func (d *Device) isBusy(longDelay bool) bool {
|
||||
func (d *Device) busy(longDelay bool) bool {
|
||||
if d.bus.WriteOnly() {
|
||||
// Can't read busy flag if write only, so sleep a bit then return
|
||||
if longDelay {
|
||||
@@ -261,7 +261,7 @@ func (d *Device) isBusy(longDelay bool) bool {
|
||||
|
||||
// Busy returns true when hd447890 is busy
|
||||
func (d *Device) Busy() bool {
|
||||
return d.isBusy(false)
|
||||
return d.busy(false)
|
||||
}
|
||||
|
||||
// Size returns the current size of the display.
|
||||
|
||||
-191
@@ -1,191 +0,0 @@
|
||||
package honeyhsc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
var (
|
||||
errSensorMissing = errors.New("hsc: not connected")
|
||||
errDiagnostic = errors.New("hsc: diagnostic error")
|
||||
)
|
||||
|
||||
const (
|
||||
measuremask = drivers.Pressure | drivers.Temperature
|
||||
statusMask = 0b1100_0000
|
||||
statusOffset = 6
|
||||
)
|
||||
|
||||
// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric
|
||||
// analog or digital output for reading pressure over the specified full scale pressure span and temperature range.
|
||||
type DevI2C struct {
|
||||
bus drivers.I2C
|
||||
dev
|
||||
addr uint8
|
||||
buf [6]byte
|
||||
}
|
||||
|
||||
// NewDevI2C creates and returns a new DevI2C that communicates with an HSC device over the provided I2C bus.
|
||||
// Parameters:
|
||||
// - bus: the I2C bus to use.
|
||||
// - addr: the 7-bit I2C address of the sensor.
|
||||
// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model.
|
||||
// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model.
|
||||
//
|
||||
// The returned DevI2C will use these calibration parameters to convert raw bridge counts to pressure.
|
||||
func NewDevI2C(bus drivers.I2C, addr, outMin, outMax uint16, pMin, pMax int32) *DevI2C {
|
||||
h := &DevI2C{
|
||||
bus: bus,
|
||||
addr: uint8(addr),
|
||||
dev: dev{
|
||||
cmin: outMin,
|
||||
cmax: outMax,
|
||||
pmin: pMin,
|
||||
pmax: pMax,
|
||||
},
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the I2C-attached HSC device.
|
||||
// It performs an Update internally to get the latest temperature value.
|
||||
func (h *DevI2C) ReadTemperature() (int32, error) {
|
||||
err := h.Update(drivers.Temperature)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return h.Temperature(), nil
|
||||
}
|
||||
|
||||
// Update reads both temperature and pressure data from the I2C-attached HSC device when
|
||||
// the requested measurement mask includes pressure or temperature.
|
||||
// If neither pressure nor temperature is requested, Update is a no-op.
|
||||
func (d *DevI2C) Update(which drivers.Measurement) error {
|
||||
// Update performs an I2C transaction to read 4 bytes, parses the status bits, 14-bit bridge data and
|
||||
// temperature bits, and forwards them to the internal update routine. Any I2C transport error is returned,
|
||||
// as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic).
|
||||
if which&measuremask == 0 {
|
||||
return nil
|
||||
}
|
||||
rbuf := d.buf[:4]
|
||||
wbuf := d.buf[4:6]
|
||||
const reg = 0
|
||||
value := (d.addr << 1) | 1
|
||||
wbuf[0] = reg
|
||||
wbuf[1] = value
|
||||
err := d.bus.Tx(uint16(d.addr), wbuf, rbuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status := (rbuf[0] & statusMask) >> statusOffset
|
||||
bridgeData := (uint16(rbuf[0]&^statusMask) << 8) | uint16(rbuf[1])
|
||||
tempData := uint16(rbuf[2])<<8 | uint16(rbuf[3]&0xe0)>>5
|
||||
return d.dev.update(status, bridgeData, tempData)
|
||||
}
|
||||
|
||||
type pinout func(level bool)
|
||||
|
||||
// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric
|
||||
// analog or digital output for reading pressure over the specified full scale pressure span and temperature range.
|
||||
type DevSPI struct {
|
||||
spi drivers.SPI
|
||||
cs pinout
|
||||
dev
|
||||
buf [4]byte
|
||||
}
|
||||
|
||||
// NewDevSPI creates and returns a new DevSPI that communicates with an HSC device over SPI.
|
||||
// Parameters:
|
||||
// - conn: the SPI connection to use.
|
||||
// - cs: a chip-select function that drives the device select line low/high.
|
||||
// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model.
|
||||
// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model.
|
||||
//
|
||||
// The function returns the constructed DevSPI and an error value (currently always nil).
|
||||
func NewDevSPI(conn drivers.SPI, cs pinout, outMin, outMax uint16, pMin, pMax int32) (*DevSPI, error) {
|
||||
h := &DevSPI{
|
||||
spi: conn,
|
||||
cs: cs,
|
||||
dev: dev{
|
||||
cmin: outMin,
|
||||
cmax: outMax,
|
||||
pmin: pMin,
|
||||
pmax: pMax,
|
||||
},
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the SPI-attached HSC device.
|
||||
// It performs an Update internally to get the latest temperature value.
|
||||
func (h *DevSPI) ReadTemperature() (int32, error) {
|
||||
err := h.Update(drivers.Temperature)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return h.Temperature(), nil
|
||||
}
|
||||
|
||||
// Update reads pressure and temperature data from the SPI-attached HSC device when the requested measurement mask includes
|
||||
// pressure or temperature. If neither pressure nor temperature is requested, Update is a no-op.
|
||||
func (h *DevSPI) Update(which drivers.Measurement) error {
|
||||
// It toggles the provided chip-select, performs an SPI transfer to read 4 bytes, parses the status bits,
|
||||
// 14-bit bridge data and temperature bits, and forwards them to the internal update routine. Any SPI
|
||||
// transport error is returned, as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic).
|
||||
if which&measuremask == 0 {
|
||||
return nil
|
||||
}
|
||||
buf := &h.buf
|
||||
h.cs(false)
|
||||
err := h.spi.Tx(nil, buf[:4])
|
||||
h.cs(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// First two bits are status bits.
|
||||
status := (buf[0] & statusMask) >> statusOffset
|
||||
bridgeData := (uint16(buf[0]&^statusMask) << 8) | uint16(buf[1])
|
||||
|
||||
tempData := uint16(buf[2])<<8 | uint16(buf[3]&0xe0)>>5
|
||||
return h.dev.update(status, bridgeData, tempData)
|
||||
}
|
||||
|
||||
type dev struct {
|
||||
pressure int32
|
||||
temp int32
|
||||
cmin, cmax uint16
|
||||
pmin, pmax int32
|
||||
}
|
||||
|
||||
// Pressure returns the most recently computed pressure value in millipascals (mPa).
|
||||
// The value is taken from the last successful Update.
|
||||
func (d *dev) Pressure() int32 {
|
||||
return d.pressure
|
||||
}
|
||||
|
||||
// Temperature returns the most recently read temperature value in milliKelvin (mC).
|
||||
// The value is taken from the last successful Update.
|
||||
func (d *dev) Temperature() int32 {
|
||||
return d.temp + 273_150
|
||||
}
|
||||
|
||||
// update interprets raw sensor fields (status, bridgeData, tempData) and updates the dev's stored
|
||||
// pressure and temperature. It returns errSensorMissing when the temperature raw value indicates no sensor
|
||||
// (tempData == math.MaxUint16), errDiagnostic when the status indicates a device diagnostic condition
|
||||
// (status == 3), or nil on success. Pressure is computed with integer arithmetic using the configured
|
||||
// cmin/cmax -> pmin/pmax linear mapping in order to avoid overflows.
|
||||
func (d *dev) update(status uint8, bridgeData, tempData uint16) error {
|
||||
if tempData == math.MaxUint16 {
|
||||
return errSensorMissing
|
||||
} else if status == 3 {
|
||||
return errDiagnostic
|
||||
}
|
||||
|
||||
// Take care not to overflow here.
|
||||
p := (int32(bridgeData)-int32(d.cmin))*(d.pmax-d.pmin)/int32(d.cmax-d.cmin) + d.pmin
|
||||
d.temp = int32(tempData)
|
||||
d.pressure = p
|
||||
return nil
|
||||
}
|
||||
+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] &^= 1 << bitSelect
|
||||
d.buffer[c][offsetR] = 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,
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// The pingconfig group of files serve to abstract away
|
||||
// pin configuration calls on the machine.Pin type.
|
||||
// It was observed this way of developing drivers was
|
||||
// non-portable and unusable on "big" Go projects so
|
||||
// future projects should NOT configure pins in driver code.
|
||||
// Users must configure pins before passing them as arguments
|
||||
// to drivers.
|
||||
|
||||
// ConfigurePinOut is a legacy function used to configure pins as outputs.
|
||||
//
|
||||
// Deprecated: Do not configure pins in drivers.
|
||||
// This is a legacy feature and should only be used by drivers that
|
||||
// previously configured pins in initialization to avoid breaking users.
|
||||
func ConfigurePinOut(po pin.Output) {
|
||||
configurePinOut(po)
|
||||
}
|
||||
|
||||
// ConfigurePinInput is a legacy function used to configure pins as inputs.
|
||||
//
|
||||
// Deprecated: Do not configure pins in drivers.
|
||||
// This is a legacy feature and should only be used by drivers that
|
||||
// previously configured pins in initialization to avoid breaking users.
|
||||
func ConfigurePinInputPulldown(pi pin.Input) {
|
||||
configurePinInputPulldown(pi)
|
||||
}
|
||||
|
||||
// ConfigurePinInput is a legacy function used to configure pins as inputs.
|
||||
//
|
||||
// Deprecated: Do not configure pins in drivers.
|
||||
// This is a legacy feature and should only be used by drivers that
|
||||
// previously configured pins in initialization to avoid breaking users.
|
||||
func ConfigurePinInput(pi pin.Input) {
|
||||
configurePinInput(pi)
|
||||
}
|
||||
|
||||
// ConfigurePinInput is a legacy function used to configure pins as inputs.
|
||||
//
|
||||
// Deprecated: Do not configure pins in drivers.
|
||||
// This is a legacy feature and should only be used by drivers that
|
||||
// previously configured pins in initialization to avoid breaking users.
|
||||
func ConfigurePinInputPullup(pi pin.Input) {
|
||||
configurePinInputPullup(pi)
|
||||
}
|
||||
|
||||
// PinIsNoPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
|
||||
//
|
||||
// Deprecated: Drivers do not require pin knowledge from now on.
|
||||
func PinIsNoPin(pin any) bool {
|
||||
return pinIsNoPin(pin)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build !tinygo
|
||||
|
||||
package legacy
|
||||
|
||||
import "tinygo.org/x/drivers/internal/pin"
|
||||
|
||||
// This file compiles for non-tinygo builds
|
||||
// for use with "big" or "upstream" Go where
|
||||
// there is no machine package.
|
||||
|
||||
func configurePinOut(p pin.Output) {}
|
||||
func configurePinInput(p pin.Input) {}
|
||||
func configurePinInputPulldown(p pin.Input) {}
|
||||
func configurePinInputPullup(p pin.Input) {}
|
||||
func pinIsNoPin(a any) bool { return false }
|
||||
@@ -1,10 +0,0 @@
|
||||
//go:build baremetal && fe310
|
||||
|
||||
package legacy
|
||||
|
||||
import "machine"
|
||||
|
||||
const (
|
||||
pulldown = machine.PinInput
|
||||
pullup = machine.PinInput
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
//go:build baremetal && !fe310
|
||||
|
||||
package legacy
|
||||
|
||||
import "machine"
|
||||
|
||||
// If you are getting a build error here you then we missed adding
|
||||
// your CPU build tag to the list of CPUs that do not have pulldown/pullups.
|
||||
// Add it above and in pinhal_nopulls! You should also add a smoketest for it :)
|
||||
const (
|
||||
pulldown = machine.PinInputPulldown
|
||||
pullup = machine.PinInputPullup
|
||||
)
|
||||
@@ -1,37 +0,0 @@
|
||||
//go:build baremetal
|
||||
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
func configurePinOut(po pin.Output) {
|
||||
configurePin(po, machine.PinOutput)
|
||||
}
|
||||
|
||||
func configurePinInputPulldown(pi pin.Input) {
|
||||
configurePin(pi, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput.
|
||||
}
|
||||
|
||||
func configurePinInput(pi pin.Input) {
|
||||
configurePin(pi, machine.PinInput)
|
||||
}
|
||||
|
||||
func configurePinInputPullup(pi pin.Input) {
|
||||
configurePin(pi, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput.
|
||||
}
|
||||
|
||||
func pinIsNoPin(a any) bool {
|
||||
p, ok := a.(machine.Pin)
|
||||
return ok && p == machine.NoPin
|
||||
}
|
||||
|
||||
func configurePin(p any, mode machine.PinMode) {
|
||||
machinePin, ok := p.(machine.Pin)
|
||||
if ok {
|
||||
machinePin.Configure(machine.PinConfig{Mode: mode})
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// package pin implements a TinyGo Pin HAL.
|
||||
// It serves to eliminate machine.Pin from driver constructors
|
||||
// so that drivers can be used in "big" Go projects where
|
||||
// there is no machine package.
|
||||
// This file contains both function and interface-style Pin HAL definitions.
|
||||
package pin
|
||||
|
||||
// OutputFunc is hardware abstraction for a pin which outputs a
|
||||
// digital signal (high or low level).
|
||||
//
|
||||
// // Code conversion demo: from machine.Pin to pin.OutputFunc
|
||||
// led := machine.LED
|
||||
// led.Configure(machine.PinConfig{Mode: machine.Output})
|
||||
// var pin pin.OutputFunc = led.Set // Going from a machine.Pin to a pin.OutputFunc
|
||||
//
|
||||
// This is an alternative to [Output] which is an interface type.
|
||||
type OutputFunc func(level bool)
|
||||
|
||||
// High sets the underlying pin's level to high. This is equivalent to calling PinOutput(true).
|
||||
func (setPin OutputFunc) High() {
|
||||
setPin(true)
|
||||
}
|
||||
|
||||
// Low sets the underlying pin's level to low. This is equivalent to calling PinOutput(false).
|
||||
func (setPin OutputFunc) Low() {
|
||||
setPin(false)
|
||||
}
|
||||
|
||||
// InputFunc is hardware abstraction for a pin which receives a
|
||||
// digital signal and reads it (high or low level).
|
||||
//
|
||||
// // Code conversion demo: from machine.Pin to pin.InputFunc
|
||||
// input := machine.LED
|
||||
// input.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) // or use machine.PinInputPullup or machine.Input
|
||||
// var pin pin.InputFunc = input.Get // Going from a machine.Pin to a pin.InputFunc
|
||||
//
|
||||
// This is an alternative to [Input] which is an interface type.
|
||||
type InputFunc func() (level bool)
|
||||
|
||||
// // Below is an example on how to define a input/output pin HAL for a
|
||||
// // pin that must switch between input and output mode:
|
||||
//
|
||||
// var pinIsOutput bool
|
||||
// var po PinOutputFunc = func(b bool) {
|
||||
// if !pinIsOutput {
|
||||
// pin.Configure(outputMode)
|
||||
// pinIsOutput = true
|
||||
// }
|
||||
// pin.Set(b)
|
||||
// }
|
||||
//
|
||||
// var pi PinInputFunc = func() bool {
|
||||
// if pinIsOutput {
|
||||
// pin.Configure(inputMode)
|
||||
// pinIsOutput = false
|
||||
// }
|
||||
// return pin.Get()
|
||||
// }
|
||||
|
||||
// Output interface represents a pin hardware abstraction layer for a pin that can output a digital signal.
|
||||
//
|
||||
// This is an alternative to [OutputFunc] abstraction which is a function type.
|
||||
type Output interface {
|
||||
Set(level bool)
|
||||
}
|
||||
|
||||
// Input interface represents a pin hardware abstraction layer for a pin that can read a digital signal.
|
||||
//
|
||||
// This is an alternative to [InputFunc] abstraction which is a function type.
|
||||
type Input interface {
|
||||
Get() (level bool)
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package regmap
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device8 implements common logic to most 8-bit peripherals with an I2C or SPI bus.
|
||||
// All methods expect the target to support conventional register read and write operations
|
||||
// where the first byte sent is the register address being accessed.
|
||||
//
|
||||
// All methods use an internal buffer and perform no dynamic memory allocation.
|
||||
type Device8 struct {
|
||||
buf [10]byte
|
||||
}
|
||||
|
||||
// clear zeroes Device8's buffers.
|
||||
func (d *Device8) clear() {
|
||||
d.buf = [10]byte{}
|
||||
}
|
||||
|
||||
// I2C methods.
|
||||
|
||||
// Read8I2C reads a single byte from register addr of the device at i2cAddr using the provided I2C bus.
|
||||
func (d *Device8) Read8I2C(bus drivers.I2C, i2cAddr uint16, addr uint8) (byte, error) {
|
||||
d.buf[0] = addr
|
||||
err := bus.Tx(i2cAddr, d.buf[0:1], d.buf[1:2])
|
||||
return d.buf[1], err
|
||||
}
|
||||
|
||||
// Read16I2C reads a 16-bit value from register addr of the device at i2cAddr using the provided I2C bus.
|
||||
// The byte order is specified by order.
|
||||
func (d *Device8) Read16I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, order binary.ByteOrder) (uint16, error) {
|
||||
d.buf[0] = addr
|
||||
err := bus.Tx(i2cAddr, d.buf[0:1], d.buf[1:3])
|
||||
return order.Uint16(d.buf[1:3]), err
|
||||
}
|
||||
|
||||
// Read32I2C reads a 32-bit value from register addr of the device at i2cAddr using the provided I2C bus.
|
||||
// The byte order is specified by order.
|
||||
func (d *Device8) Read32I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, order binary.ByteOrder) (uint32, error) {
|
||||
d.buf[0] = addr
|
||||
err := bus.Tx(i2cAddr, d.buf[0:1], d.buf[1:5])
|
||||
return order.Uint32(d.buf[1:5]), err
|
||||
}
|
||||
|
||||
// ReadDataI2C reads dataLength bytes from register addr of the device at i2cAddr using the provided I2C bus.
|
||||
// The data is stored in dataDestination.
|
||||
func (d *Device8) ReadDataI2C(bus drivers.I2C, i2cAddr uint16, addr uint8, dataDestination []byte) error {
|
||||
d.buf[0] = addr
|
||||
return bus.Tx(i2cAddr, d.buf[:1], dataDestination)
|
||||
}
|
||||
|
||||
// Write8I2C writes a single byte value to register addr of the device at i2cAddr using the provided I2C bus.
|
||||
func (d *Device8) Write8I2C(bus drivers.I2C, i2cAddr uint16, addr, value uint8) error {
|
||||
d.buf[0] = addr
|
||||
d.buf[1] = value
|
||||
return bus.Tx(i2cAddr, d.buf[:2], nil)
|
||||
}
|
||||
|
||||
// Write16I2C writes a 16-bit value to register addr of the device at i2cAddr using the provided I2C bus.
|
||||
// The byte order is specified by order.
|
||||
func (d *Device8) Write16I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, value uint16, order binary.ByteOrder) error {
|
||||
d.buf[0] = addr
|
||||
order.PutUint16(d.buf[1:3], value)
|
||||
return bus.Tx(i2cAddr, d.buf[0:3], nil)
|
||||
}
|
||||
|
||||
// Write32I2C writes a 32-bit value to register addr of the device at i2cAddr using the provided I2C bus.
|
||||
// The byte order is specified by order.
|
||||
func (d *Device8) Write32I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, value uint32, order binary.ByteOrder) error {
|
||||
d.buf[0] = addr
|
||||
order.PutUint32(d.buf[1:5], value)
|
||||
return bus.Tx(i2cAddr, d.buf[0:5], nil)
|
||||
}
|
||||
|
||||
// SPI methods.
|
||||
|
||||
// Read8SPI reads a single byte from register addr using the provided SPI bus.
|
||||
func (d *Device8) Read8SPI(bus drivers.SPI, addr uint8) (byte, error) {
|
||||
d.clear()
|
||||
d.buf[0] = addr
|
||||
err := bus.Tx(d.buf[0:1], d.buf[1:2]) // We suppose data is returned after first byte in SPI.
|
||||
return d.buf[1], err
|
||||
}
|
||||
|
||||
// Read16SPI reads a 16-bit value from register addr using the provided SPI bus. The byte order is specified by order.
|
||||
func (d *Device8) Read16SPI(bus drivers.SPI, addr uint8, order binary.ByteOrder) (uint16, error) {
|
||||
d.clear()
|
||||
d.buf[0] = addr
|
||||
err := bus.Tx(d.buf[0:3], d.buf[3:6]) // We suppose data is returned after first byte in SPI.
|
||||
return order.Uint16(d.buf[4:6]), err
|
||||
}
|
||||
|
||||
// Read32SPI reads a 32-bit value from register addr using the provided SPI bus. The byte order is specified by order.
|
||||
func (d *Device8) Read32SPI(bus drivers.SPI, addr uint8, order binary.ByteOrder) (uint32, error) {
|
||||
d.clear()
|
||||
d.buf[0] = addr
|
||||
err := bus.Tx(d.buf[0:5], d.buf[5:10]) // We suppose data is returned after first byte in SPI.
|
||||
return order.Uint32(d.buf[6:10]), err
|
||||
}
|
||||
|
||||
// ReadDataSPI reads data from a 8bit device address. It assumes data at register address is sent back
|
||||
// from device after first byte is written as address.
|
||||
// It needs the auxiliary buffer length to be large enough to contain both the write and read portions of buffer,
|
||||
// so 2*(dataLength+1) < len(auxiliaryBuf) must hold.
|
||||
func (d *Device8) ReadDataSPI(bus drivers.SPI, addr uint8, dataLength int, auxiliaryBuf []byte) ([]byte, error) {
|
||||
split := len(auxiliaryBuf) / 2
|
||||
if split < dataLength+1 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
wbuf, rbuf := auxiliaryBuf[:split], auxiliaryBuf[split:]
|
||||
wbuf[0] = addr
|
||||
err := bus.Tx(wbuf, rbuf)
|
||||
return rbuf[1:], err
|
||||
}
|
||||
|
||||
// Write8SPI writes a single byte value to register addr using the provided SPI bus.
|
||||
func (d *Device8) Write8SPI(bus drivers.SPI, addr, value uint8) error {
|
||||
d.clear()
|
||||
d.buf[0] = addr
|
||||
d.buf[1] = value
|
||||
return bus.Tx(d.buf[:2], nil)
|
||||
}
|
||||
|
||||
// Write16SPI writes a 16-bit value to register addr using the provided SPI bus. The byte order is specified by order.
|
||||
func (d *Device8) Write16SPI(bus drivers.SPI, addr uint8, value uint16, order binary.ByteOrder) error {
|
||||
d.clear()
|
||||
d.buf[0] = addr
|
||||
order.PutUint16(d.buf[1:3], value)
|
||||
return bus.Tx(d.buf[:3], nil)
|
||||
}
|
||||
|
||||
// Write32SPI writes a 32-bit value to register addr using the provided SPI bus. The byte order is specified by order.
|
||||
func (d *Device8) Write32SPI(bus drivers.SPI, addr uint8, value uint32, order binary.ByteOrder) error {
|
||||
d.clear()
|
||||
d.buf[0] = addr
|
||||
order.PutUint32(d.buf[1:5], value)
|
||||
return bus.Tx(d.buf[:5], nil)
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package regmap
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device8SPI implements common logic to most 8-bit peripherals with an SPI bus.
|
||||
// All methods expect the target to support conventional register read and write operations
|
||||
// where the first byte sent is the register address being accessed.
|
||||
//
|
||||
// All methods use an internal buffer and perform no dynamic memory allocation.
|
||||
type Device8SPI struct {
|
||||
bus drivers.SPI
|
||||
order binary.ByteOrder
|
||||
d Device8
|
||||
}
|
||||
|
||||
// SetBus sets the SPI bus and byte order for the Device8SPI.
|
||||
//
|
||||
// As a hint, most SPI devices use big-endian (MSB) byte order.
|
||||
// - Big endian: A value of 0x1234 is transmitted as 0x12 followed by 0x34.
|
||||
// - Little endian: A value of 0x1234 is transmitted as 0x34 followed by 0x12.
|
||||
func (d *Device8SPI) SetBus(bus drivers.SPI, order binary.ByteOrder) {
|
||||
d.bus = bus
|
||||
d.order = order
|
||||
}
|
||||
|
||||
// Read8 reads a single byte from register addr.
|
||||
func (d *Device8SPI) Read8(addr uint8) (byte, error) {
|
||||
return d.d.Read8SPI(d.bus, addr)
|
||||
}
|
||||
|
||||
// Read16 reads a 16-bit value from register addr.
|
||||
func (d *Device8SPI) Read16(addr uint8) (uint16, error) {
|
||||
return d.d.Read16SPI(d.bus, addr, d.order)
|
||||
}
|
||||
|
||||
// Read32 reads a 32-bit value from register addr.
|
||||
func (d *Device8SPI) Read32(addr uint8) (uint32, error) {
|
||||
return d.d.Read32SPI(d.bus, addr, d.order)
|
||||
}
|
||||
|
||||
// ReadData reads dataLength bytes from register addr. Due to the internal functioning of
|
||||
// SPI, an auxiliary buffer must be provided to perform the operation and avoid memory allocation.
|
||||
// The returned slice is a subslice of auxBuffer containing the read data.
|
||||
func (d *Device8SPI) ReadData(addr uint8, datalength int, auxBuffer []byte) ([]byte, error) {
|
||||
return d.d.ReadDataSPI(d.bus, addr, datalength, auxBuffer)
|
||||
}
|
||||
|
||||
// Write8 writes a single byte value to register addr.
|
||||
func (d *Device8SPI) Write8(addr, value uint8) error {
|
||||
return d.d.Write8SPI(d.bus, addr, value)
|
||||
}
|
||||
|
||||
// Write16 writes a 16-bit value to register addr.
|
||||
func (d *Device8SPI) Write16(addr uint8, value uint16) error {
|
||||
return d.d.Write16SPI(d.bus, addr, value, d.order)
|
||||
}
|
||||
|
||||
// Write32 writes a 32-bit value to register addr.
|
||||
func (d *Device8SPI) Write32(addr uint8, value uint32) error {
|
||||
return d.d.Write32SPI(d.bus, addr, value, d.order)
|
||||
}
|
||||
|
||||
// Device8I2C implements common logic to most 8-bit peripherals with an I2C bus.
|
||||
// All methods expect the target to support conventional register read and write operations
|
||||
// where the first byte sent is the register address being accessed.
|
||||
//
|
||||
// All methods use an internal buffer and perform no dynamic memory allocation.
|
||||
type Device8I2C struct {
|
||||
bus drivers.I2C
|
||||
i2cAddr uint16
|
||||
order binary.ByteOrder
|
||||
d Device8
|
||||
}
|
||||
|
||||
// SetBus sets the I2C bus, device address, and byte order for the Device8I2C.
|
||||
//
|
||||
// As a hint, most I2C devices use big-endian (MSB) byte order.
|
||||
// - Big endian: A value of 0x1234 is transmitted as 0x12 followed by 0x34.
|
||||
// - Little endian: A value of 0x1234 is transmitted as 0x34 followed by 0x12.
|
||||
func (d *Device8I2C) SetBus(bus drivers.I2C, i2cAddr uint16, order binary.ByteOrder) {
|
||||
d.bus = bus
|
||||
d.i2cAddr = i2cAddr
|
||||
d.order = order
|
||||
}
|
||||
|
||||
// Read8 reads a single byte from register addr.
|
||||
func (d *Device8I2C) Read8(addr uint8) (byte, error) {
|
||||
return d.d.Read8I2C(d.bus, d.i2cAddr, addr)
|
||||
}
|
||||
|
||||
// Read16 reads a 16-bit value from register addr.
|
||||
func (d *Device8I2C) Read16(addr uint8) (uint16, error) {
|
||||
return d.d.Read16I2C(d.bus, d.i2cAddr, addr, d.order)
|
||||
}
|
||||
|
||||
// Read32 reads a 32-bit value from register addr.
|
||||
func (d *Device8I2C) Read32(addr uint8) (uint32, error) {
|
||||
return d.d.Read32I2C(d.bus, d.i2cAddr, addr, d.order)
|
||||
}
|
||||
|
||||
// ReadData reads dataLength bytes from register addr.
|
||||
func (d *Device8I2C) ReadData(addr uint8, dataDestination []byte) error {
|
||||
return d.d.ReadDataI2C(d.bus, d.i2cAddr, addr, dataDestination)
|
||||
}
|
||||
|
||||
// Write8 writes a single byte value to register addr.
|
||||
func (d *Device8I2C) Write8(addr, value uint8) error {
|
||||
return d.d.Write8I2C(d.bus, d.i2cAddr, addr, value)
|
||||
}
|
||||
|
||||
// Write16 writes a 16-bit value to register addr.
|
||||
func (d *Device8I2C) Write16(addr uint8, value uint16) error {
|
||||
return d.d.Write16I2C(d.bus, d.i2cAddr, addr, value, d.order)
|
||||
}
|
||||
|
||||
// Write32 writes a 32-bit value to register addr.
|
||||
func (d *Device8I2C) Write32(addr uint8, value uint32) error {
|
||||
return d.d.Write32I2C(d.bus, d.i2cAddr, addr, value, d.order)
|
||||
}
|
||||
+36
-115
@@ -11,57 +11,37 @@ import (
|
||||
// Device wraps an I2C connection to a LIS3DH device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
address uint16
|
||||
r Range
|
||||
accel [6]byte // stored acceleration data (from the Update call)
|
||||
}
|
||||
|
||||
// Driver configuration, used for the Configure call. All fields are optional.
|
||||
type Config struct {
|
||||
Address uint16
|
||||
r Range
|
||||
}
|
||||
|
||||
// New creates a new LIS3DH connection. The I2C bus must already be configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{bus: bus, address: Address0}
|
||||
return Device{bus: bus, Address: Address0}
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication
|
||||
func (d *Device) Configure(config Config) error {
|
||||
if config.Address != 0 {
|
||||
d.address = config.Address
|
||||
}
|
||||
|
||||
func (d *Device) Configure() {
|
||||
// enable all axes, normal mode
|
||||
err := legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL1, []byte{0x07})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL1, []byte{0x07})
|
||||
|
||||
// 400Hz rate
|
||||
err = d.SetDataRate(DATARATE_400_HZ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.SetDataRate(DATARATE_400_HZ)
|
||||
|
||||
// High res & BDU enabled
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL4, []byte{0x88})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL4, []byte{0x88})
|
||||
|
||||
// get current range
|
||||
d.r, err = d.ReadRange()
|
||||
return err
|
||||
d.r = d.ReadRange()
|
||||
}
|
||||
|
||||
// Connected returns whether a LIS3DH has been found.
|
||||
// It does a "who am I" request and checks the response.
|
||||
func (d *Device) Connected() bool {
|
||||
data := []byte{0}
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.address), WHO_AM_I, data)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -69,51 +49,46 @@ func (d *Device) Connected() bool {
|
||||
}
|
||||
|
||||
// SetDataRate sets the speed of data collected by the LIS3DH.
|
||||
func (d *Device) SetDataRate(rate DataRate) error {
|
||||
func (d *Device) SetDataRate(rate DataRate) {
|
||||
ctl1 := []byte{0}
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.address), REG_CTRL1, ctl1)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL1, ctl1)
|
||||
if err != nil {
|
||||
return err
|
||||
println(err.Error())
|
||||
}
|
||||
// mask off bits
|
||||
ctl1[0] &^= 0xf0
|
||||
ctl1[0] |= (byte(rate) << 4)
|
||||
return legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL1, ctl1)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL1, ctl1)
|
||||
}
|
||||
|
||||
// SetRange sets the G range for LIS3DH.
|
||||
func (d *Device) SetRange(r Range) error {
|
||||
func (d *Device) SetRange(r Range) {
|
||||
ctl := []byte{0}
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.address), REG_CTRL4, ctl)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||
if err != nil {
|
||||
return err
|
||||
println(err.Error())
|
||||
}
|
||||
// mask off bits
|
||||
ctl[0] &^= 0x30
|
||||
ctl[0] |= (byte(r) << 4)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL4, ctl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||
|
||||
// store the new range
|
||||
d.r = r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadRange returns the current G range for LIS3DH.
|
||||
func (d *Device) ReadRange() (r Range, err error) {
|
||||
func (d *Device) ReadRange() (r Range) {
|
||||
ctl := []byte{0}
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.address), REG_CTRL4, ctl)
|
||||
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
println(err.Error())
|
||||
}
|
||||
// mask off bits
|
||||
r = Range(ctl[0] >> 4)
|
||||
r &= 0x03
|
||||
|
||||
return r, nil
|
||||
return r
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
@@ -121,17 +96,28 @@ func (d *Device) ReadRange() (r Range, err error) {
|
||||
// and the sensor is not moving the returned value will be around 1000000 or
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (int32, int32, int32, error) {
|
||||
rawX, rawY, rawZ := d.ReadRawAcceleration()
|
||||
x, y, z := normalizeRange(rawX, rawY, rawZ, d.r)
|
||||
return x, y, z, nil
|
||||
x, y, z := d.ReadRawAcceleration()
|
||||
divider := float32(1)
|
||||
switch d.r {
|
||||
case RANGE_16_G:
|
||||
divider = 1365
|
||||
case RANGE_8_G:
|
||||
divider = 4096
|
||||
case RANGE_4_G:
|
||||
divider = 8190
|
||||
case RANGE_2_G:
|
||||
divider = 16380
|
||||
}
|
||||
|
||||
return int32(float32(x) / divider * 1000000), int32(float32(y) / divider * 1000000), int32(float32(z) / divider * 1000000), nil
|
||||
}
|
||||
|
||||
// ReadRawAcceleration returns the raw x, y and z axis from the LIS3DH
|
||||
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||
legacy.WriteRegister(d.bus, uint8(d.address), REG_OUT_X_L|0x80, nil)
|
||||
legacy.WriteRegister(d.bus, uint8(d.Address), REG_OUT_X_L|0x80, nil)
|
||||
|
||||
data := []byte{0, 0, 0, 0, 0, 0}
|
||||
d.bus.Tx(d.address, nil, data)
|
||||
d.bus.Tx(d.Address, nil, data)
|
||||
|
||||
x = int16((uint16(data[1]) << 8) | uint16(data[0]))
|
||||
y = int16((uint16(data[3]) << 8) | uint16(data[2]))
|
||||
@@ -139,68 +125,3 @@ func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Update the sensor values of the 'which' parameter. Only acceleration is
|
||||
// supported at the moment.
|
||||
func (d *Device) Update(which drivers.Measurement) error {
|
||||
if which&drivers.Acceleration != 0 {
|
||||
// Read raw acceleration values and store them in the driver.
|
||||
err := legacy.WriteRegister(d.bus, uint8(d.address), REG_OUT_X_L|0x80, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.bus.Tx(d.address, nil, d.accel[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Acceleration returns the last read acceleration in µg (micro-gravity).
|
||||
// When one of the axes is pointing straight to Earth and the sensor is not
|
||||
// moving the returned value will be around 1000000 or -1000000.
|
||||
func (d *Device) Acceleration() (x, y, z int32) {
|
||||
// Extract the raw 16-bit values.
|
||||
rawX := int16((uint16(d.accel[1]) << 8) | uint16(d.accel[0]))
|
||||
rawY := int16((uint16(d.accel[3]) << 8) | uint16(d.accel[2]))
|
||||
rawZ := int16((uint16(d.accel[5]) << 8) | uint16(d.accel[4]))
|
||||
|
||||
// Normalize these values, to be in µg (micro-gravity).
|
||||
return normalizeRange(rawX, rawY, rawZ, d.r)
|
||||
}
|
||||
|
||||
// Convert raw 16-bit values to normalized 32-bit values while avoiding floats
|
||||
// and divisions.
|
||||
func normalizeRange(rawX, rawY, rawZ int16, r Range) (x, y, z int32) {
|
||||
// We're going to convert the 16-bit raw values to values in the range
|
||||
// -1000_000..1000_000. For now we're going to assume a range of 16G, we'll
|
||||
// adjust that range later.
|
||||
// The formula is derived as follows, and carefully selected to avoid
|
||||
// overflow and integer divisions (the division will be optimized to a
|
||||
// bitshift):
|
||||
// x = x * 1000_000 / 2048
|
||||
// x = x * (1000_000/64) / (2048/64)
|
||||
// x = x * 15625 / 32
|
||||
x = int32(rawX) * 15625 / 32
|
||||
y = int32(rawY) * 15625 / 32
|
||||
z = int32(rawZ) * 15625 / 32
|
||||
|
||||
// Now we need to normalize the three values, since we assumed 16G before.
|
||||
shift := uint32(0)
|
||||
switch r {
|
||||
case RANGE_16_G:
|
||||
shift = 0
|
||||
case RANGE_8_G:
|
||||
shift = 1
|
||||
case RANGE_4_G:
|
||||
shift = 2
|
||||
case RANGE_2_G:
|
||||
shift = 3
|
||||
}
|
||||
x >>= shift
|
||||
y >>= shift
|
||||
z >>= shift
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ type Configuration struct {
|
||||
MagDataRate uint8
|
||||
}
|
||||
|
||||
var errNotConnected = errors.New("lsm303agr: failed to communicate with either accel or magnet sensor")
|
||||
var errNotConnected = errors.New("lsm303agr: failed to communicate with either acel or magnet sensor")
|
||||
|
||||
// New creates a new LSM303AGR connection. The I2C bus must already be configured.
|
||||
//
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
// Package lsm303dlhc implements a driver for the LSM303dlhc,
|
||||
// a 3 axis accelerometer/magnetic sensor typically available on breakout boards.
|
||||
//
|
||||
// Datasheet: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf
|
||||
|
||||
package lsm303dlhc // import "tinygo.org/x/drivers/lsm303dlhc"
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a LSM303dlhc device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
AccelAddress uint8
|
||||
MagAddress uint8
|
||||
AccelPowerMode uint8
|
||||
AccelRange uint8
|
||||
AccelDataRate uint8
|
||||
MagPowerMode uint8
|
||||
MagSystemMode uint8
|
||||
MagDataRate uint8
|
||||
buf [6]uint8
|
||||
}
|
||||
|
||||
// Configuration for LSM303dlhc device.
|
||||
type Configuration struct {
|
||||
AccelPowerMode uint8
|
||||
AccelRange uint8
|
||||
AccelDataRate uint8
|
||||
MagPowerMode uint8
|
||||
MagSystemMode uint8
|
||||
MagDataRate uint8
|
||||
}
|
||||
|
||||
// New creates a new LSM303DLHC connection. The I2C bus must already be configured.
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
AccelAddress: ACCEL_ADDRESS,
|
||||
MagAddress: MAG_ADDRESS,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure sets up the LSM303dlhc device for communication.
|
||||
func (d *Device) Configure(cfg Configuration) (err error) {
|
||||
|
||||
if cfg.AccelDataRate != 0 {
|
||||
d.AccelDataRate = cfg.AccelDataRate
|
||||
} else {
|
||||
d.AccelDataRate = ACCEL_DATARATE_100HZ
|
||||
}
|
||||
|
||||
if cfg.AccelPowerMode != 0 {
|
||||
d.AccelPowerMode = cfg.AccelPowerMode
|
||||
} else {
|
||||
d.AccelPowerMode = ACCEL_POWER_NORMAL
|
||||
}
|
||||
|
||||
if cfg.AccelRange != 0 {
|
||||
d.AccelRange = cfg.AccelRange
|
||||
} else {
|
||||
d.AccelRange = ACCEL_RANGE_2G
|
||||
}
|
||||
|
||||
if cfg.MagPowerMode != 0 {
|
||||
d.MagPowerMode = cfg.MagPowerMode
|
||||
} else {
|
||||
d.MagPowerMode = MAG_POWER_NORMAL
|
||||
}
|
||||
|
||||
if cfg.MagDataRate != 0 {
|
||||
d.MagDataRate = cfg.MagDataRate
|
||||
} else {
|
||||
d.MagDataRate = MAG_DATARATE_10HZ
|
||||
}
|
||||
|
||||
if cfg.MagSystemMode != 0 {
|
||||
d.MagSystemMode = cfg.MagSystemMode
|
||||
} else {
|
||||
d.MagSystemMode = MAG_SYSTEM_CONTINUOUS
|
||||
}
|
||||
|
||||
data := d.buf[:1]
|
||||
|
||||
data[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data[0] = byte(0x80 | d.AccelRange<<4)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data[0] = byte(0xC0)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), CRA_REG_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Temperature compensation is on for magnetic sensor
|
||||
data[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
// it in µg (micro-gravity). When one of the axes is pointing straight to Earth
|
||||
// and the sensor is not moving the returned value will be around 1000000 or
|
||||
// -1000000.
|
||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), ACCEL_OUT_AUTO_INC, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rangeFactor := int16(0)
|
||||
switch d.AccelRange {
|
||||
case ACCEL_RANGE_2G:
|
||||
rangeFactor = 1
|
||||
case ACCEL_RANGE_4G:
|
||||
rangeFactor = 2
|
||||
case ACCEL_RANGE_8G:
|
||||
rangeFactor = 4
|
||||
case ACCEL_RANGE_16G:
|
||||
rangeFactor = 12 // the readings in 16G are a bit lower
|
||||
}
|
||||
|
||||
x = int32(int32(int16((uint16(data[1])<<8|uint16(data[0])))>>4*rangeFactor) * 1000000 / 1024)
|
||||
y = int32(int32(int16((uint16(data[3])<<8|uint16(data[2])))>>4*rangeFactor) * 1000000 / 1024)
|
||||
z = int32(int32(int16((uint16(data[5])<<8|uint16(data[4])))>>4*rangeFactor) * 1000000 / 1024)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadPitchRoll reads the current pitch and roll angles from the device and
|
||||
// returns it in micro-degrees. When the z axis is pointing straight to Earth
|
||||
// the returned values of pitch and roll would be zero.
|
||||
func (d *Device) ReadPitchRoll() (pitch, roll int32, err error) {
|
||||
|
||||
x, y, z, err := d.ReadAcceleration()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xf, yf, zf := float64(x), float64(y), float64(z)
|
||||
pitch = int32((math.Round(math.Atan2(yf, math.Sqrt(math.Pow(xf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
|
||||
roll = int32((math.Round(math.Atan2(xf, math.Sqrt(math.Pow(yf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// ReadMagneticField reads the current magnetic field from the device and returns
|
||||
// it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
|
||||
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||
|
||||
if d.MagSystemMode == MAG_SYSTEM_SINGLE {
|
||||
cmd := d.buf[:1]
|
||||
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, cmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := d.buf[0:6]
|
||||
legacy.ReadRegister(d.bus, uint8(d.MagAddress), MAG_OUT_AUTO_INC, data)
|
||||
|
||||
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
|
||||
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
|
||||
z = int32(int16((uint16(data[5])<<8 | uint16(data[4]))))
|
||||
return
|
||||
}
|
||||
|
||||
// ReadCompass reads the current compass heading from the device and returns
|
||||
// it in micro-degrees. When the z axis is pointing straight to Earth and
|
||||
// the y axis is pointing to North, the heading would be zero.
|
||||
//
|
||||
// However, the heading may be off due to electronic compasses would be effected
|
||||
// by strong magnetic fields and require constant calibration.
|
||||
func (d *Device) ReadCompass() (h int32, err error) {
|
||||
|
||||
x, y, _, err := d.ReadMagneticField()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
xf, yf := float64(x), float64(y)
|
||||
h = int32(float32((180/math.Pi)*math.Atan2(yf, xf)) * 1000000)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
|
||||
data := d.buf[:2]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), TEMP_OUT_AUTO_INC, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r := int16((uint16(data[1])<<8 | uint16(data[0]))) >> 4 // temperature offset from 25 °C
|
||||
t = 25000 + int32((float32(r)/8)*1000)
|
||||
return
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package lsm303dlhc
|
||||
|
||||
const (
|
||||
|
||||
// Constants/addresses used for I2C.
|
||||
ACCEL_ADDRESS = 0x19
|
||||
MAG_ADDRESS = 0x1E
|
||||
|
||||
// i2C 8-bit subaddress (SUB): the 7 LSb represent the actual register address
|
||||
// while the MSB enables address auto increment.
|
||||
// If the MSb of the SUB field is 1, the SUB (register address) is
|
||||
// automatically increased to allow multiple data read/writes.
|
||||
ADDR_AUTO_INC_MASK = 0x80
|
||||
|
||||
// accelerometer registers.
|
||||
ACCEL_CTRL_REG1_A = 0x20
|
||||
ACCEL_CTRL_REG4_A = 0x23
|
||||
ACCEL_OUT_X_L_A = 0x28
|
||||
ACCEL_OUT_X_H_A = 0x29
|
||||
ACCEL_OUT_Y_L_A = 0x2A
|
||||
ACCEL_OUT_Y_H_A = 0x2B
|
||||
ACCEL_OUT_Z_L_A = 0x2C
|
||||
ACCEL_OUT_Z_H_A = 0x2D
|
||||
ACCEL_OUT_AUTO_INC = ACCEL_OUT_X_L_A | ADDR_AUTO_INC_MASK
|
||||
|
||||
// magnetic sensor registers.
|
||||
MAG_MR_REG_M = 0x02
|
||||
MAG_OUT_X_L_M = 0x68
|
||||
MAG_OUT_X_H_M = 0x69
|
||||
MAG_OUT_Y_L_M = 0x6A
|
||||
MAG_OUT_Y_H_M = 0x6B
|
||||
MAG_OUT_Z_L_M = 0x6C
|
||||
MAG_OUT_Z_H_M = 0x6D
|
||||
MAG_OUT_AUTO_INC = MAG_OUT_X_L_M | ADDR_AUTO_INC_MASK
|
||||
|
||||
// temperature sensor registers.
|
||||
CRA_REG_M = 0x80
|
||||
TEMP_OUT_L_M = 0x32
|
||||
TEMP_OUT_H_M = 0x31
|
||||
TEMP_OUT_AUTO_INC = TEMP_OUT_L_M | ADDR_AUTO_INC_MASK
|
||||
|
||||
// accelerometer power mode.
|
||||
ACCEL_POWER_NORMAL = 0x00 // default
|
||||
ACCEL_POWER_LOW = 0x08
|
||||
|
||||
// accelerometer range.
|
||||
ACCEL_RANGE_2G = 0x00 // default
|
||||
ACCEL_RANGE_4G = 0x01
|
||||
ACCEL_RANGE_8G = 0x02
|
||||
ACCEL_RANGE_16G = 0x03
|
||||
|
||||
// accelerometer data rate.
|
||||
ACCEL_DATARATE_1HZ = 0x01
|
||||
ACCEL_DATARATE_10HZ = 0x02
|
||||
ACCEL_DATARATE_25HZ = 0x03
|
||||
ACCEL_DATARATE_50HZ = 0x04
|
||||
ACCEL_DATARATE_100HZ = 0x05 // default
|
||||
ACCEL_DATARATE_200HZ = 0x06
|
||||
ACCEL_DATARATE_400HZ = 0x07
|
||||
ACCEL_DATARATE_1344HZ = 0x09 // 5376Hz in low-power mode
|
||||
|
||||
// magnetic sensor power mode.
|
||||
MAG_POWER_NORMAL = 0x00 // default
|
||||
MAG_POWER_LOW = 0x01
|
||||
|
||||
// magnetic sensor operate mode.
|
||||
MAG_SYSTEM_CONTINUOUS = 0x00 // default
|
||||
MAG_SYSTEM_SINGLE = 0x01
|
||||
|
||||
// magnetic sensor data rate
|
||||
MAG_DATARATE_10HZ = 0x00 // default
|
||||
MAG_DATARATE_20HZ = 0x01
|
||||
MAG_DATARATE_50HZ = 0x02
|
||||
MAG_DATARATE_100HZ = 0x03
|
||||
)
|
||||
+24
-35
@@ -8,6 +8,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -25,7 +26,7 @@ type Device struct {
|
||||
accelSampleRate AccelSampleRate
|
||||
gyroRange GyroRange
|
||||
gyroSampleRate GyroSampleRate
|
||||
buf [7]uint8 // up to 6 bytes for read + 1 byte for the register address
|
||||
buf [6]uint8
|
||||
}
|
||||
|
||||
// Configuration for LSM6DS3TR device.
|
||||
@@ -83,20 +84,30 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
d.gyroSampleRate = GYRO_SR_104
|
||||
}
|
||||
|
||||
data := d.buf[:1]
|
||||
|
||||
// Configure accelerometer
|
||||
err = d.writeByte(CTRL1_XL, uint8(d.accelRange)|uint8(d.accelSampleRate))
|
||||
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Enable ODR scaling
|
||||
err = d.setBits(CTRL4_C, BW_SCAL_ODR_ENABLED)
|
||||
// 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)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure gyroscope
|
||||
err = d.writeByte(CTRL2_G, uint8(d.gyroRange)|uint8(d.gyroSampleRate))
|
||||
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
|
||||
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -107,10 +118,8 @@ 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, err := d.readBytes(WHO_AM_I, 1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
data := d.buf[:1]
|
||||
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||
return data[0] == 0x6A
|
||||
}
|
||||
|
||||
@@ -119,7 +128,8 @@ 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, err := d.readBytes(OUTX_L_XL, 6)
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -143,7 +153,8 @@ 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, err := d.readBytes(OUTX_L_G, 6)
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -166,7 +177,8 @@ 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, err := d.readBytes(OUT_TEMP_L, 2)
|
||||
data := d.buf[:2]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -175,26 +187,3 @@ 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)
|
||||
}
|
||||
|
||||
+28
-35
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
type AccelRange uint8
|
||||
@@ -27,7 +28,7 @@ type Device struct {
|
||||
accelMultiplier int32
|
||||
gyroMultiplier int32
|
||||
magMultiplier int32
|
||||
buf [7]uint8 // up to 6 bytes for read + 1 byte for the register address
|
||||
buf [6]uint8
|
||||
}
|
||||
|
||||
// Configuration for LSM9DS1 device.
|
||||
@@ -60,15 +61,10 @@ func New(bus drivers.I2C) *Device {
|
||||
// Case of boolean false and error nil means I2C is up,
|
||||
// but "who am I" responses have unexpected values.
|
||||
func (d *Device) Connected() bool {
|
||||
data, err := d.readBytes(d.AccelAddress, WHO_AM_I, 1)
|
||||
if err != nil || data[0] != 0x68 {
|
||||
return false
|
||||
}
|
||||
data, err = d.readBytes(d.MagAddress, WHO_AM_I_M, 1)
|
||||
if err != nil || data[0] != 0x3D {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
data1, data2 := d.buf[:1], d.buf[1:2]
|
||||
legacy.ReadRegister(d.bus, d.AccelAddress, WHO_AM_I, data1)
|
||||
legacy.ReadRegister(d.bus, d.MagAddress, WHO_AM_I_M, data2)
|
||||
return data1[0] == 0x68 && data2[0] == 0x3D
|
||||
}
|
||||
|
||||
// ReadAcceleration reads the current acceleration from the device and returns
|
||||
@@ -76,7 +72,8 @@ 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, err := d.readBytes(d.AccelAddress, OUT_X_L_XL, 6)
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -91,7 +88,8 @@ 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, err := d.readBytes(d.AccelAddress, OUT_X_L_G, 6)
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -104,7 +102,8 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||
// ReadMagneticField reads the current magnetic field from the device and returns
|
||||
// it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
|
||||
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||
data, err := d.readBytes(d.MagAddress, OUT_X_L_M, 6)
|
||||
data := d.buf[:6]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), OUT_X_L_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -116,7 +115,8 @@ func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||
|
||||
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
|
||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||
data, err := d.readBytes(d.AccelAddress, OUT_TEMP_L, 2)
|
||||
data := d.buf[:2]
|
||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_TEMP_L, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -167,16 +167,20 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
d.magMultiplier = 58
|
||||
}
|
||||
|
||||
data := d.buf[:1]
|
||||
|
||||
// Configure accelerometer
|
||||
// Sample rate & measurement range
|
||||
err = d.writeByte(d.AccelAddress, CTRL_REG6_XL, uint8(cfg.AccelSampleRate)<<5|uint8(cfg.AccelRange)<<3)
|
||||
data[0] = uint8(cfg.AccelSampleRate)<<5 | uint8(cfg.AccelRange)<<3
|
||||
err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG6_XL, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure gyroscope
|
||||
// Sample rate & measurement range
|
||||
err = d.writeByte(d.AccelAddress, CTRL_REG1_G, uint8(cfg.GyroSampleRate)<<5|uint8(cfg.GyroRange)<<3)
|
||||
data[0] = uint8(cfg.GyroSampleRate)<<5 | uint8(cfg.GyroRange)<<3
|
||||
err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG1_G, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -186,44 +190,33 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
||||
// Temperature compensation enabled
|
||||
// High-performance mode XY axis
|
||||
// Sample rate
|
||||
err = d.writeByte(d.MagAddress, CTRL_REG1_M, 0b10000000|0b01000000|uint8(cfg.MagSampleRate)<<2)
|
||||
data[0] = 0b10000000 | 0b01000000 | uint8(cfg.MagSampleRate)<<2
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG1_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Measurement range
|
||||
err = d.writeByte(d.MagAddress, CTRL_REG2_M, uint8(cfg.MagRange)<<5)
|
||||
data[0] = uint8(cfg.MagRange) << 5
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG2_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Continuous-conversion mode
|
||||
// https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode
|
||||
err = d.writeByte(d.MagAddress, CTRL_REG3_M, 0b00000000)
|
||||
data[0] = 0b00000000
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG3_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// High-performance mode Z axis
|
||||
err = d.writeByte(d.MagAddress, CTRL_REG4_M, 0b00001000)
|
||||
data[0] = 0b00001000
|
||||
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG4_M, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) readBytes(addr, reg, size uint8) ([]byte, error) {
|
||||
d.buf[0] = reg
|
||||
err := d.bus.Tx(uint16(addr), 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(addr, reg, value uint8) error {
|
||||
d.buf[0] = reg
|
||||
d.buf[1] = value
|
||||
return d.bus.Tx(uint16(addr), d.buf[0:2], nil)
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6675.pdf
|
||||
package max6675
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// 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 pin.OutputFunc
|
||||
}
|
||||
|
||||
// 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 pin.Output) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs.Set,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
+8
-15
@@ -3,36 +3,29 @@
|
||||
package max72xx
|
||||
|
||||
import (
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
"machine"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
cs pin.OutputFunc
|
||||
configurePins func()
|
||||
bus machine.SPI
|
||||
cs machine.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 drivers.SPI, cs pin.Output) *Device {
|
||||
func NewDevice(bus machine.SPI, cs machine.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs.Set,
|
||||
configurePins: func() {
|
||||
legacy.ConfigurePinOut(cs)
|
||||
},
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure setups the pins.
|
||||
func (driver *Device) Configure() {
|
||||
if driver.configurePins == nil {
|
||||
panic(legacy.ErrConfigBeforeInstantiated)
|
||||
}
|
||||
driver.configurePins()
|
||||
outPutConfig := machine.PinConfig{Mode: machine.PinOutput}
|
||||
|
||||
driver.cs.Configure(outPutConfig)
|
||||
}
|
||||
|
||||
// SetScanLimit sets the scan limit. Maximum is 8.
|
||||
|
||||
+8
-16
@@ -8,20 +8,18 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// Device wraps MCP2515 SPI CAN Module.
|
||||
type Device struct {
|
||||
spi SPI
|
||||
cs pin.OutputFunc
|
||||
msg *CANMsg
|
||||
mcpMode byte
|
||||
configurePins func()
|
||||
spi SPI
|
||||
cs machine.Pin
|
||||
msg *CANMsg
|
||||
mcpMode byte
|
||||
}
|
||||
|
||||
// CANMsg stores CAN message fields.
|
||||
@@ -38,18 +36,15 @@ const (
|
||||
)
|
||||
|
||||
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
|
||||
func New(b drivers.SPI, csPin pin.Output) *Device {
|
||||
func New(b drivers.SPI, csPin machine.Pin) *Device {
|
||||
d := &Device{
|
||||
spi: SPI{
|
||||
bus: b,
|
||||
tx: make([]byte, 0, bufferSize),
|
||||
rx: make([]byte, 0, bufferSize),
|
||||
},
|
||||
cs: csPin.Set,
|
||||
cs: csPin,
|
||||
msg: &CANMsg{},
|
||||
configurePins: func() {
|
||||
legacy.ConfigurePinOut(csPin)
|
||||
},
|
||||
}
|
||||
|
||||
return d
|
||||
@@ -57,10 +52,7 @@ func New(b drivers.SPI, csPin pin.Output) *Device {
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d *Device) Configure() {
|
||||
if d.configurePins == nil {
|
||||
panic(legacy.ErrConfigBeforeInstantiated)
|
||||
}
|
||||
d.configurePins()
|
||||
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.ReadStereo(d.buf)
|
||||
machine.I2S0.Read(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.ReadStereo(d.buf)
|
||||
machine.I2S0.Read(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 DHCP client")
|
||||
ErrStartingDHCPServer = errors.New("Error starting DHCP server")
|
||||
ErrStartingDHCPClient = errors.New("Error starting DHPC client")
|
||||
ErrStartingDHCPServer = errors.New("Error starting DHPC server")
|
||||
ErrNoMoreSockets = errors.New("No more sockets")
|
||||
ErrClosingSocket = errors.New("Error closing socket")
|
||||
ErrNotSupported = errors.New("Not supported")
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
//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
|
||||
}
|
||||
+16
-24
@@ -5,9 +5,8 @@ package onewire // import "tinygo.org/x/drivers/onewire"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// OneWire ROM commands
|
||||
@@ -20,8 +19,7 @@ const (
|
||||
|
||||
// Device wraps a connection to an 1-Wire devices.
|
||||
type Device struct {
|
||||
set pin.OutputFunc
|
||||
get pin.InputFunc
|
||||
p machine.Pin
|
||||
}
|
||||
|
||||
// Config wraps a configuration to an 1-Wire devices.
|
||||
@@ -34,30 +32,24 @@ var (
|
||||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||||
)
|
||||
|
||||
// NewFromFuncs expects pin setter and getter for DQ line. Ideally this driver should receive
|
||||
// a one-wire bus HAL abstraction, but I was asked to show how this could be done using pin HAL so here goes.
|
||||
func NewFromFuncs(getPinLevel pin.InputFunc, setPinLevel pin.OutputFunc) *Device {
|
||||
return &Device{
|
||||
set: setPinLevel,
|
||||
get: getPinLevel,
|
||||
// New creates a new GPIO 1-Wire connection.
|
||||
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
|
||||
func New(p machine.Pin) Device {
|
||||
return Device{
|
||||
p: p,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure initializes the protocol.
|
||||
func (d *Device) Configure(config Config) {}
|
||||
|
||||
// By setting the pin value one should configure as output and also expect a more
|
||||
// consistent behaviour across all tinygo hosts by pulling DQ line low consistently. Win-win.
|
||||
func (d *Device) cfgOut() { d.set.Low() }
|
||||
func (d *Device) cfgIn() { d.get() }
|
||||
|
||||
// Reset pull DQ line low, then up.
|
||||
func (d Device) Reset() error {
|
||||
d.cfgOut()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
time.Sleep(480 * time.Microsecond)
|
||||
d.cfgIn()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(70 * time.Microsecond)
|
||||
precence := d.get()
|
||||
precence := d.p.Get()
|
||||
time.Sleep(410 * time.Microsecond)
|
||||
if precence {
|
||||
return errNoPresence
|
||||
@@ -67,14 +59,14 @@ func (d Device) Reset() error {
|
||||
|
||||
// WriteBit transmits a bit to 1-Wire bus.
|
||||
func (d Device) WriteBit(data uint8) {
|
||||
d.cfgOut()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
if data&1 == 1 { // Send '1'
|
||||
time.Sleep(5 * time.Microsecond)
|
||||
d.cfgIn()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
} else { // Send '0'
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
d.cfgIn()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(5 * time.Microsecond)
|
||||
}
|
||||
}
|
||||
@@ -89,11 +81,11 @@ func (d Device) Write(data uint8) {
|
||||
|
||||
// ReadBit receives a bit from 1-Wire bus.
|
||||
func (d Device) ReadBit() (data uint8) {
|
||||
d.cfgOut()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
time.Sleep(3 * time.Microsecond)
|
||||
d.cfgIn()
|
||||
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
time.Sleep(8 * time.Microsecond)
|
||||
if d.get() {
|
||||
if d.p.Get() {
|
||||
data = 1
|
||||
}
|
||||
time.Sleep(60 * time.Microsecond)
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
//go:build tinygo
|
||||
|
||||
package onewire
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/internal/legacy"
|
||||
)
|
||||
|
||||
// New creates a new GPIO 1-Wire connection.
|
||||
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
|
||||
func New(p machine.Pin) Device {
|
||||
isOut := false
|
||||
return Device{
|
||||
set: func(level bool) {
|
||||
if !isOut {
|
||||
legacy.ConfigurePinOut(p)
|
||||
isOut = true
|
||||
}
|
||||
p.Set(level)
|
||||
},
|
||||
get: func() (level bool) {
|
||||
if isOut {
|
||||
legacy.ConfigurePinInputPullup(p)
|
||||
isOut = false
|
||||
}
|
||||
return p.Get()
|
||||
},
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -14,8 +14,7 @@ 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.
|
||||
|
||||
+8
-8
@@ -6,18 +6,18 @@ package pcd8544 // import "tinygo.org/x/drivers/pcd8544"
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/internal/pin"
|
||||
)
|
||||
|
||||
// Device wraps an SPI connection.
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
dcPin pin.OutputFunc
|
||||
rstPin pin.OutputFunc
|
||||
scePin pin.OutputFunc
|
||||
dcPin machine.Pin
|
||||
rstPin machine.Pin
|
||||
scePin machine.Pin
|
||||
buffer []byte
|
||||
width int16
|
||||
height int16
|
||||
@@ -30,12 +30,12 @@ type Config struct {
|
||||
}
|
||||
|
||||
// New creates a new PCD8544 connection. The SPI bus must already be configured.
|
||||
func New(bus drivers.SPI, dcPin, rstPin, scePin pin.Output) *Device {
|
||||
func New(bus drivers.SPI, dcPin, rstPin, scePin machine.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
dcPin: dcPin.Set,
|
||||
rstPin: rstPin.Set,
|
||||
scePin: scePin.Set,
|
||||
dcPin: dcPin,
|
||||
rstPin: rstPin,
|
||||
scePin: scePin,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ func (p ADCPin) Get() uint16 {
|
||||
p.d.bus.Tx(p.d.Address, tx, rx)
|
||||
|
||||
// scale result to 16bit value like other ADCs
|
||||
return uint16(rx[1]) << 8
|
||||
return uint16(rx[1] << 8)
|
||||
}
|
||||
|
||||
// Configure here just for interface compatibility.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user