Compare commits

..

3 Commits

Author SHA1 Message Date
deadprogram 71eef4196f uc8151: add FillRectangle() and SetScroll() functions to satisfy tinyterm.Displayer interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-27 18:48:52 +01:00
deadprogram 6301627338 pixel: correct and clarify code for monochrome get/set pixels
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-27 17:04:12 +01:00
deadprogram b7bbecc456 pixel: add NewImageFromBytes() function to allow creating image from existing slice
Signed-off-by: deadprogram <ron@hybridgroup.com>
2024-10-27 13:03:54 +01:00
137 changed files with 870 additions and 9603 deletions
-93
View File
@@ -1,96 +1,3 @@
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**
-3
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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 -6
View File
@@ -5,9 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
const (
@@ -37,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() {
pin.ConfigureOutput(sckPin)
pin.ConfigureOutput(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 -11
View File
@@ -1,8 +1,6 @@
package apa102
import (
"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.
@@ -10,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(pin.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 {
+23 -30
View File
@@ -1,35 +1,31 @@
package bmi160
import (
"machine"
"time"
"tinygo.org/x/drivers"
"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() {
pin.ConfigureOutput(csb)
},
CSB: csb, // chip select
Bus: spi,
}
}
@@ -37,11 +33,8 @@ 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 pin.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:
@@ -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
View File
@@ -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
View File
@@ -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
}
-711
View File
@@ -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
}
-86
View File
@@ -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
}
+69 -6
View File
@@ -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) {
@@ -62,8 +125,8 @@ func (d *Device) Move(steps int32) {
// Off turns off all motor pins
func (d *Device) Off() {
for _, p := range d.pins {
p.Low()
for _, pin := range d.pins {
pin.Low()
}
}
-29
View File
@@ -1,29 +0,0 @@
package easystepper
import (
"errors"
"time"
"tinygo.org/x/drivers/internal/pin"
)
func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]pin.Output) (*Device, error) {
if stepcount == 0 || rpm == 0 {
return nil, errors.New("zero rpm and/or stepcount")
}
var ps [4]pin.OutputFunc
for i := range pins {
if pins[i] == nil {
return nil, errors.New("nil pin")
}
ps[i] = pins[i].Set
}
d := &Device{
pins: ps,
stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)),
stepMode: mode,
config: func() {},
}
return d, nil
}
-82
View File
@@ -1,82 +0,0 @@
//go:build baremetal
package easystepper
import (
"errors"
"machine"
"time"
"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() {
pin.ConfigureOutput(config.Pin1)
pin.ConfigureOutput(config.Pin2)
pin.ConfigureOutput(config.Pin3)
pin.ConfigureOutput(config.Pin4)
},
}, nil
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
if d.config == nil {
panic(pin.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
}
-225
View File
@@ -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 // 7bit 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
// preallocated buffers
wbuf [5]byte // longest write: reg + 4bytes (TEMP+RH)
rbuf [5]byte // longest read: DATA burst (5bytes)
}
// 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 (autoincrement)
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 totalVOC concentration in partsperbillion.
func (d *Device) TVOC() uint16 { return d.lastTvocPPB }
// ECO2 returns the last equivalent CO₂ concentration in partspermillion.
func (d *Device) ECO2() uint16 { return d.lastEco2PPM }
// AQI returns the last AirQuality Index according to UBA (15).
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
}
-54
View File
@@ -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)
}
})
}
}
-65
View File
@@ -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
)
-56
View File
@@ -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)
}
}
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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 {
-58
View File
@@ -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)
}
}
-34
View File
@@ -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)
}
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//go:build ninafw || wioterminal || comboat_fw
//go:build ninafw || wioterminal
package main
+1 -1
View File
@@ -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
+1 -1
View File
@@ -4,7 +4,7 @@
//
// nc -lk 8080
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
//go:build ninafw || wioterminal || challenger_rp2040
package main
+1 -1
View File
@@ -5,7 +5,7 @@
//
// nc -lk 8080
//go:build ninafw || wioterminal || challenger_rp2040 || comboat_fw
//go:build ninafw || wioterminal || challenger_rp2040 || pico
package main
+1 -1
View File
@@ -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
+1 -1
View File
@@ -17,7 +17,7 @@
// }
// ---------------------------------------------------------------------------
//go:build ninafw || wioterminal || comboat_fw
//go:build ninafw || wioterminal
package main
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func init() {
spi = machine.SPI2
spi = &machine.SPI2
sckPin = machine.SCK2
sdoPin = machine.SDO2
sdiPin = machine.SDI2
-35
View File
@@ -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)
}
}
-90
View File
@@ -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)
}
}
}
+51
View File
@@ -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)
}
}
+60
View File
@@ -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)
}
}
-59
View File
@@ -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
}
}
}
-38
View File
@@ -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
}
-27
View File
@@ -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
}
-40
View File
@@ -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
}
+48
View File
@@ -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)
}
}
+50
View File
@@ -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)
}
}
-35
View File
@@ -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)
}
-61
View File
@@ -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)
}
+9 -13
View File
@@ -5,29 +5,28 @@
package ft6336
import (
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
"tinygo.org/x/drivers/touch"
)
// Device wraps FT6336 I2C Self-Capacitive touch
type Device struct {
bus drivers.I2C
buf []byte
Address uint8
configurePins func()
bus drivers.I2C
buf []byte
Address uint8
intPin machine.Pin
}
// New returns FT6336 device for the provided I2C bus using default address.
func New(i2c drivers.I2C, intPin pin.Input) *Device {
func New(i2c drivers.I2C, intPin machine.Pin) *Device {
return &Device{
bus: i2c,
buf: make([]byte, 11),
Address: Address,
configurePins: func() {
pin.ConfigureInputPulldown(intPin)
},
intPin: intPin,
}
}
@@ -37,11 +36,8 @@ type Config struct {
// Configure the FT6336 device.
func (d *Device) Configure(config Config) error {
if d.configurePins == nil {
return pin.ErrConfigBeforeInstantiated
}
d.write1Byte(0xA4, 0x00)
d.configurePins()
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
return nil
}
+15 -15
View File
@@ -5,12 +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/pin"
)
// Rotation controls the rotation used by the display.
@@ -22,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
@@ -52,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 {
pin.ConfigureOutput(resetPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(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,
}
}
@@ -226,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)
}
+4 -9
View File
@@ -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
)
+4 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+3 -3
View File
@@ -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
View File
@@ -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 -18
View File
@@ -5,38 +5,30 @@
package hcsr04
import (
"machine"
"time"
"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() {
pin.ConfigureOutput(trigger)
pin.ConfigureInput(echo)
},
trigger: trigger,
echo: echo,
}
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
if d.configurePins == nil {
panic(pin.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
@@ -60,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
}
@@ -74,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
View File
@@ -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.
+2
View File
@@ -2,6 +2,8 @@
package hts221
import "tinygo.org/x/drivers"
// Configure sets up the HTS221 device for communication.
func (d *Device) Configure() {
// read calibration data
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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,
-98
View File
@@ -1,98 +0,0 @@
package pin
import "errors"
// 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.PinOutput})
// var p pin.OutputFuncFunc = led.Set // Going from a machine.Pin to a pin.OutputFunc
type OutputFunc func(level bool)
func (o OutputFunc) High() {
o(true)
}
func (o OutputFunc) Low() {
o(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.PinInput
// var p pin.InputFunc = input.Get // Going from a machine.Pin to a drivers.PinInput
type InputFunc func() (level bool)
// PinOutput represents a pin hardware abstraction layer for a pin that can output a digital signal.
// This is an wrapper to pin.OutputFunc abstraction which is a function type.
//
// func New(p1, p2, p3 pin.Output) *Device {
// return NewWithPinfuncs(p1.Set, p2.Set, p3.Set)
// }
//
// func NewWithPinfuncs(p1, p2, p3 pin.OutputFunc) *Device {
// return &Device{p1:p1, p2:p2, p3:p3}
// }
//
// [relevant issue]: https://github.com/tinygo-org/drivers/pull/749/files
type Output interface {
Set(level bool)
}
// PinInput represents a pin hardware abstraction layer. See [PinOutput] for
// more information on why this is "legacy".
type Input interface {
Get() (level bool)
}
// ConfigureOutput is a legacy function used to configure pins as outputs.
//
// Deprecated: You should 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 ConfigureOutput(po Output) {
configureOutput(po)
}
// ConfigureInput is a legacy function used to configure pins as inputs.
//
// Deprecated: You should 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 ConfigureInputPulldown(pi Input) {
configureInputPulldown(pi)
}
// ConfigureInput is a legacy function used to configure pins as inputs.
//
// Deprecated: You should 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 ConfigureInput(pi Input) {
configureInput(pi)
}
// ConfigureInputPullup is a legacy function used to configure pins as inputs.
//
// Deprecated: You should 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 ConfigureInputPullup(pi Input) {
configureInputPullup(pi)
}
// IsNotPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
//
// Deprecated: Drivers should not require pin knowledge.
func IsNotPin(pin any) bool {
return isNotPin(pin)
}
var (
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
)
-9
View File
@@ -1,9 +0,0 @@
//go:build !baremetal
package pin
func configureOutput(p Output) {}
func configureInput(p Input) {}
func configureInputPulldown(p Input) {}
func configureInputPullup(p Input) {}
func isNotPin(a any) bool { return false }
-10
View File
@@ -1,10 +0,0 @@
//go:build baremetal && fe310
package pin
import "machine"
const (
pulldown = machine.PinInput
pullup = machine.PinInput
)
-13
View File
@@ -1,13 +0,0 @@
//go:build baremetal && !fe310
package pin
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
)
-33
View File
@@ -1,33 +0,0 @@
//go:build baremetal
package pin
import "machine"
func configureOutput(p Output) {
configure(p, machine.PinOutput)
}
func configureInputPulldown(p Input) {
configure(p, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput.
}
func configureInput(p Input) {
configure(p, machine.PinInput)
}
func configureInputPullup(p Input) {
configure(p, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput.
}
func isNotPin(a any) bool {
p, ok := a.(machine.Pin)
return ok && p == machine.NoPin
}
func configure(p any, mode machine.PinMode) {
machinePin, ok := p.(machine.Pin)
if ok {
machinePin.Configure(machine.PinConfig{Mode: mode})
}
}
+1 -1
View File
@@ -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.
//
-214
View File
@@ -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
}
-75
View File
@@ -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
View File
@@ -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)
}
-53
View File
@@ -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
}
+10 -16
View File
@@ -3,35 +3,29 @@
package max72xx
import (
"tinygo.org/x/drivers"
"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() {
pin.ConfigureOutput(cs)
},
cs: cs,
}
}
// Configure setups the pins.
func (driver *Device) Configure() {
if driver.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
driver.configurePins()
outPutConfig := machine.PinConfig{Mode: machine.PinOutput}
driver.cs.Configure(outPutConfig)
}
// SetScanLimit sets the scan limit. Maximum is 8.
@@ -93,8 +87,8 @@ func (driver *Device) writeByte(data byte) {
// WriteCommand write data to a given register.
func (driver *Device) WriteCommand(register, data byte) {
driver.cs(false)
driver.cs.Low()
driver.writeByte(register)
driver.writeByte(data)
driver.cs(true)
driver.cs.High()
}
+8 -15
View File
@@ -8,19 +8,18 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
import (
"errors"
"fmt"
"machine"
"time"
"tinygo.org/x/drivers"
"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.
@@ -37,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() {
pin.ConfigureOutput(csPin)
},
}
return d
@@ -56,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(pin.ErrConfigBeforeInstantiated)
}
d.configurePins()
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
const beginTimeoutValue int = 10
+2 -2
View File
@@ -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
View File
@@ -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")
-26
View File
@@ -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
}
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+4 -4
View File
@@ -138,14 +138,14 @@ func (img Image[T]) setPixel(index int, c T) {
switch {
case zeroColor.BitsPerPixel() == 1:
// Monochrome.
x := index % int(img.width)
offset := index / 8
bits := index % 8
ptr := (*byte)(unsafe.Add(img.data, offset))
if c != zeroColor {
*((*byte)(ptr)) |= (1 << (7 - uint8(bits)))
*((*byte)(ptr)) |= (1 << (7 - uint8(x%8)))
} else {
*((*byte)(ptr)) &^= (1 << (7 - uint8(bits)))
*((*byte)(ptr)) &^= (1 << (7 - uint8(x%8)))
}
return
@@ -202,7 +202,7 @@ func (img Image[T]) Get(x, y int) T {
// Monochrome.
var c Monochrome
offset := index / 8
bits := index % 8
bits := index - (offset * 8)
ptr := (*byte)(unsafe.Add(img.data, offset))
c = ((*ptr >> (7 - uint8(bits))) & 0x1) > 0
return any(c).(T)
+5 -13
View File
@@ -183,30 +183,22 @@ func TestImageFromBytesMonochrome(t *testing.T) {
// contain the same data afterwards.
func TestImageNoise(t *testing.T) {
t.Run("RGB888", func(t *testing.T) {
testImageNoiseN[pixel.RGB888](t)
testImageNoise[pixel.RGB888](t)
})
t.Run("RGB565BE", func(t *testing.T) {
testImageNoiseN[pixel.RGB565BE](t)
testImageNoise[pixel.RGB565BE](t)
})
t.Run("RGB555", func(t *testing.T) {
testImageNoiseN[pixel.RGB555](t)
testImageNoise[pixel.RGB555](t)
})
t.Run("RGB444BE", func(t *testing.T) {
testImageNoiseN[pixel.RGB444BE](t)
testImageNoise[pixel.RGB444BE](t)
})
t.Run("Monochrome", func(t *testing.T) {
testImageNoiseN[pixel.Monochrome](t)
testImageNoise[pixel.Monochrome](t)
})
}
// Run the testImageNoise multiple times, because a single test might not catch
// all bugs (since the test uses random data).
func testImageNoiseN[T pixel.Color](t *testing.T) {
for i := 0; i < 10; i++ {
testImageNoise[T](t)
}
}
func testImageNoise[T pixel.Color](t *testing.T) {
// Create an image of a random width/height for extra testing.
width := rand.Int()%500 + 10
-49
View File
@@ -1,49 +0,0 @@
package seesaw
import (
"errors"
)
var errInvalidEncoderNumber = errors.New("invalid encoder choice, 0-15 are supported")
// GetEncoderPosition returns the absolute position (or delta since the previous call) of the specified rotary encoder.
func (d *Device) GetEncoderPosition(encoder uint, asDelta bool) (int32, error) {
if encoder >= 16 {
return 0, errInvalidEncoderNumber
}
// The function address' upper nibble is the function, the lower nibble selects which encoder to communicate with
fnAddr := FunctionAddress(encoder)
if asDelta {
fnAddr |= FunctionEncoderDelta
} else {
fnAddr |= FunctionEncoderPosition
}
var buf [4]byte
err := d.Read(ModuleEncoderBase, fnAddr, buf[:])
if err != nil {
return 0, err
}
return int32(buf[0])<<24 | int32(buf[1])<<16 | int32(buf[2])<<8 | int32(buf[3]), nil
}
// SetEncoderPosition calibrate's the encoder's current absolute position to be whatever the provided position is.
func (d *Device) SetEncoderPosition(encoder uint, position int32) error {
if encoder >= 16 {
return errInvalidEncoderNumber
}
// The function address' upper nibble is the function, the lower nibble selects which encoder to communicate with
fnAddr := FunctionEncoderPosition | FunctionAddress(encoder)
buf := [4]byte{
byte(position >> 24),
byte(position >> 16),
byte(position >> 8),
byte(position),
}
return d.Write(ModuleEncoderBase, fnAddr, buf[:])
}
-10
View File
@@ -98,13 +98,3 @@ const (
FunctionKeypadCount FunctionAddress = 0x04
FunctionKeypadFifo FunctionAddress = 0x10
)
// encoder module function address registers
// these are the defaults for encoder 0, change the lower nibble to address other encoders
// see the Device.GetEncoderPosition and SetEncoderPosition methods for examples.
const (
FunctionEncoderIntenset FunctionAddress = 0x10
FunctionEncoderIntenclr FunctionAddress = 0x20
FunctionEncoderPosition FunctionAddress = 0x30
FunctionEncoderDelta FunctionAddress = 0x40
)
-374
View File
@@ -1,374 +0,0 @@
package sharpmem
import (
"errors"
"image/color"
"tinygo.org/x/drivers"
)
const (
bitWriteCmd uint8 = 0b00000001
bitVcom uint8 = 0b00000010
bitClear uint8 = 0b00000100
)
var (
ConfigLS010B7DH04 = Config{Width: 128, Height: 128}
ConfigLS011B7DH03 = Config{Width: 160, Height: 68}
ConfigLS012B7DD01 = Config{Width: 184, Height: 38}
ConfigLS013B7DH03 = ConfigLS010B7DH04
ConfigLS013B7DH05 = Config{Width: 144, Height: 168}
ConfigLS018B7DH02 = Config{Width: 230, Height: 303}
ConfigLS027B7DH01 = Config{Width: 400, Height: 240}
ConfigLS027B7DH01A = ConfigLS027B7DH01
ConfigLS032B7DD02 = Config{Width: 336, Height: 536}
ConfigLS044Q7DH01 = Config{Width: 320, Height: 240}
)
type Pin interface {
High()
Low()
}
// Device represents a Sharp Memory Display device. This driver implementation
// concerns the 1-bit color versions only (black and white memory displays).
//
// Supported SKUs include:
// LS010B7DH04, LS011B7DH03, LS012B7DD01, LS013B7DH03, LS013B7DH05,
// LS018B7DH02, LS027B7DH01, LS027B7DH01A, LS032B7DD02, LS044Q7DH01
//
// Note: Only SKU LS011B7DH03 (160x68) has been tested as of writing.
//
// The driver includes optimizations (frame and per-line invalidation) that
// only transmit the changed lines to the display. These optimizations are on
// by default, and they can be disabled with the respective config option.
type Device struct {
bus drivers.SPI
csPin Pin
buffer []byte
txBuf []byte
lineDiff []byte
width int16
height int16
bufferSize int16
bytesPerLine int16
vcom uint8
diffing bool
}
type Config struct {
Width int16
Height int16
// DisableOptimizations disables frame and line invalidation optimizations.
// Useful if constant frame times are desired.
DisableOptimizations bool
}
// New creates a new device connection.
// The SPI bus must have already been configured.
func New(bus drivers.SPI, csPin Pin) Device {
d := Device{
bus: bus,
csPin: csPin,
}
return d
}
// Configure initializes the display with specified configuration. It can be
// called multiple times on the same display, resetting its internal state.
func (d *Device) Configure(cfg Config) {
if cfg.Width == 0 {
cfg.Width = 160
}
if cfg.Height == 0 {
cfg.Height = 68
}
d.width = cfg.Width
d.height = cfg.Height
d.diffing = !cfg.DisableOptimizations
d.initialize()
}
// initialize properly initializes the display and the in-memory image buffers.
func (d *Device) initialize() {
d.csPin.Low()
// initialize VCOM as high
d.vcom = bitVcom
// bytesPerLine has to be 16-bit aligned, as some resolutions require
// padding to the nearest 2nd byte.
d.bytesPerLine = ceilDiv(d.width, 16) * 2
// preallocate a contiguous byte buffer for all lines, including
// protocol-required padding for each line apriori (easier to transfer).
d.bufferSize = d.bytesPerLine * d.height
d.buffer = make([]byte, d.bufferSize)
// A bit being 1 is white (reflective), 0 is black (less reflective).
for i := range d.buffer {
d.buffer[i] = 0xff
}
// auxiliary buffer for SPI transfers to avoid dynamic allocations
d.txBuf = make([]byte, 2)
if d.diffing {
// buffer to store the changed lines. First bit is whether any line has
// changed at all (i.e. the frame is invalid), followed by N bits,
// one for each line.
d.lineDiff = make([]byte, bitfieldBufLen(1+d.height))
}
}
// SetPixel enables or disables a pixel in the buffer.
// color.RGBA{0, 0, 0, 255} is considered transparent (reflective, white),
// anything else will enable a pixel on the screen (make it appear less
// reflective, black).
func (d *Device) SetPixel(x, y int16, c color.RGBA) {
if d.width == 0 {
return
}
// bounds check
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
offset := y * d.bytesPerLine
div := offset + x/8
mod := uint8(x % 8)
prev := hasBit(d.buffer[div], mod)
curr := c.R == 0 && c.G == 0 && c.B == 0 && c.A == 255
if prev == curr {
return
}
if curr {
d.buffer[div] = setBit(d.buffer[div], mod)
} else {
d.buffer[div] = unsetBit(d.buffer[div], mod)
}
if d.diffing {
d.invalidateLine(y)
}
}
// Size returns the current size of the display.
func (d *Device) Size() (x, y int16) {
return d.width, d.height
}
// Display renders the buffer to the screen. It only transmits changed lines if
// optimizations are enabled. It should be called at >=1hz, even if the
// buffer hasn't been modified.
func (d *Device) Display() error {
if d.width == 0 {
return errors.New("display not configured")
}
if d.diffing {
if !hasBit(d.lineDiff[0], 0) {
// no pixels have been modified, simply toggle VCOM
return d.holdDisplay()
}
defer func() {
for i := 0; i < len(d.lineDiff); i++ {
d.lineDiff[i] = 0x00
}
}()
}
cmd := bitWriteCmd | d.vcom
d.toggleVcom()
// Padding to use for high bits of line numbers that overflow 8 bits.
var hiPad = uint8(0)
if d.height >= 512 {
hiPad = 3 + 3 // 3 mode bits + 3 low bits
} else if d.height >= 256 {
hiPad = 3 + 4 // 3 mode bits + 4 low bits
}
// start transfer
d.csPin.High()
for i := int16(0); i < d.height; i++ {
if d.diffing {
// Skip rendering lines that haven't changed.
linediv := (i + 1) / 8
linemod := uint8((i + 1) % 8)
if !hasBit(d.lineDiff[linediv], linemod) {
continue
}
}
// The first 5 bits are either dummy or part of the current line
// (1-indexed) if it overflows 8-bits.
// The last 3 bits are the command for the first line and dummy bits
// for subsequent lines (set as command for simplicity)
hi := uint8((i + 1) >> 8)
hi = hi << hiPad
d.txBuf[0] = cmd | hi
// The second byte is the low bits of the current line (1-indexed).
// for <8 bits cases, the high bits are dummy, so we leave them as 0.
d.txBuf[1] = uint8(i + 1)
// send the first two bytes
err := d.bus.Tx(d.txBuf, nil)
if err != nil {
return err
}
// send the line data
err = d.bus.Tx(d.buffer[i*d.bytesPerLine:(i+1)*d.bytesPerLine], nil)
if err != nil {
return err
}
}
// Trailer 16 bits (low)
d.txBuf[0] = 0x00
d.txBuf[1] = 0x00
err := d.bus.Tx(d.txBuf, nil)
if err != nil {
return err
}
// end transfer
d.csPin.Low()
return nil
}
// holdDisplay simply toggles VCOM without updating any lines.
func (d *Device) holdDisplay() error {
d.txBuf[0] = d.vcom
d.txBuf[1] = 0x00
d.toggleVcom()
// begin transaction
d.csPin.High()
err := d.bus.Tx(d.txBuf, nil)
if err != nil {
return err
}
// end transaction
d.csPin.Low()
return nil
}
// Clear clears both the in-memory buffer and the display.
func (d *Device) Clear() error {
if d.width == 0 {
return errors.New("display not configured")
}
d.ClearBuffer()
return d.ClearDisplay()
}
// ClearBuffer clears the in-memory buffer. The display is not updated.
func (d *Device) ClearBuffer() {
if d.width == 0 {
return
}
if d.diffing {
// detect what rows need to be reset on the next render
d.invalidateModifiedLines()
}
// reset the in-memory buffer
for i := 0; i < len(d.buffer); i++ {
d.buffer[i] = 0xff
}
}
// invalidateModifiedLines marks any line that has at least a single black pixel
// as invalidated. Padding bits, if any, are always 1.
func (d *Device) invalidateModifiedLines() {
for y := int16(0); y < d.height; y++ {
offset := y * d.bytesPerLine
updateLine := false
for x := int16(0); x < d.width; x++ {
div := offset + x/8
mod := uint8(x % 8)
if !hasBit(d.buffer[div], mod) {
updateLine = true
break
}
}
if updateLine {
d.invalidateLine(y)
}
}
}
// ClearDisplay clears the display. The in-memory buffer is not updated. A
// subsequent call to Display() will re-render the content as it was before
// clearing.
func (d *Device) ClearDisplay() error {
if d.width == 0 {
return errors.New("display not configured")
}
d.txBuf[0] = d.vcom | bitClear
d.txBuf[1] = 0x00
d.toggleVcom()
// begin transaction
d.csPin.High()
err := d.bus.Tx(d.txBuf, nil)
if err != nil {
return err
}
// end transaction
d.csPin.Low()
return nil
}
// invalidateLine marks a line and the frame itself as invalidated.
func (d *Device) invalidateLine(line int16) {
// mark the frame as invalidated
d.lineDiff[0] = setBit(d.lineDiff[0], 0)
// mark the line as invalidated
linediv := (line + 1) / 8
linemod := uint8((line + 1) % 8)
d.lineDiff[linediv] = setBit(d.lineDiff[linediv], linemod)
}
// toggleVcom toggles the VCOM, as is instructed by the datasheet.
// Toggling VCOM can help maintain the display's longevity. It should ideally
// be called at least once per second, preferably at 4-100 Hz.
// Toggling VCOM causes a tiny bit of flicker, but without it the pixels can
// be permanently damaged by the DC bias accumulating over time.
func (d *Device) toggleVcom() {
if d.vcom != 0 {
d.vcom = 0x00
} else {
d.vcom = bitVcom
}
}
-225
View File
@@ -1,225 +0,0 @@
package sharpmem
import (
"image/color"
"math/rand/v2"
"testing"
qt "github.com/frankban/quicktest"
)
func Test_setBit(t *testing.T) {
c := qt.New(t)
for i := uint8(0); i < 8; i++ {
v := uint8(1) << i
c.Assert(setBit(0x00, i), qt.Equals, v)
c.Assert(setBit(0x00, (i+1)%8), qt.Not(qt.Equals), v)
}
}
func Test_unsetBit(t *testing.T) {
c := qt.New(t)
for i := uint8(0); i < 8; i++ {
v := uint8(1) << i
c.Assert(unsetBit(v, i), qt.Equals, uint8(0x00))
c.Assert(unsetBit(v, (i+1)%8), qt.Not(qt.Equals), uint8(0x00))
}
}
func Test_hasBit(t *testing.T) {
c := qt.New(t)
for i := uint8(0); i < 8; i++ {
v := uint8(1) << i
c.Assert(hasBit(v, i), qt.Equals, true)
c.Assert(hasBit(v, (i+1)%8), qt.Equals, false)
}
}
func Test_bitfieldBufLen(t *testing.T) {
c := qt.New(t)
for i := int16(1); i < 536; i++ {
requiredBufferSize := i / 8
wouldOverflow := i % 8
if wouldOverflow > 0 {
requiredBufferSize += 1
}
c.Assert(bitfieldBufLen(i), qt.Equals, requiredBufferSize)
}
}
type mockBus struct {
b []byte
}
func (m *mockBus) Tx(w, _ []byte) error {
m.b = append(m.b, w...)
return nil
}
func (m *mockBus) Transfer(b byte) (byte, error) {
m.b = append(m.b, b)
return 0x00, nil
}
type mockPin struct{}
func (m mockPin) High() {
}
func (m mockPin) Low() {
}
func Test_Device(t *testing.T) {
c := qt.New(t)
cfgs := []Config{
ConfigLS010B7DH04,
ConfigLS011B7DH03,
ConfigLS012B7DD01,
ConfigLS013B7DH03,
ConfigLS013B7DH05,
ConfigLS018B7DH02,
ConfigLS027B7DH01,
ConfigLS027B7DH01A,
ConfigLS032B7DD02,
ConfigLS044Q7DH01,
}
cfgLen := len(cfgs)
for i := 0; i < cfgLen; i++ {
cfgs = append(cfgs, Config{
Width: cfgs[i].Width,
Height: cfgs[i].Height,
DisableOptimizations: true,
})
}
spi := &mockBus{}
pin := mockPin{}
display := New(spi, pin)
for _, cfg := range cfgs {
display.Configure(cfg)
x, y := display.Size()
c.Assert(x, qt.Equals, cfg.Width)
c.Assert(y, qt.Equals, cfg.Height)
for i := 0; i < 10; i++ {
x := int16(rand.IntN(int(cfg.Width)))
y := int16(rand.IntN(int(cfg.Height)))
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
for i := 0; i < 10; i++ {
x := int16(rand.IntN(int(cfg.Width)))
y := int16(rand.IntN(int(cfg.Height)))
display.SetPixel(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
}
err := display.Display()
c.Assert(err, qt.Equals, nil)
err = display.ClearDisplay()
c.Assert(err, qt.Equals, nil)
display.ClearBuffer()
}
}
func Test_HiPad(t *testing.T) {
c := qt.New(t)
spi := &mockBus{}
pin := mockPin{}
display := New(spi, pin)
t.Run("LS011B7DH03, 8-bit address", func(t *testing.T) {
t.Cleanup(func() {
spi.b = nil
})
display.Configure(ConfigLS011B7DH03)
display.SetPixel(0, display.height-1, color.RGBA{R: 255, G: 255, B: 255, A: 255})
err := display.Display()
c.Assert(err, qt.Equals, nil)
// 160 perfectly divisible by 16, so 20 bytes of pixel data
c.Assert(spi.b, qt.HasLen, 2+20+2)
// line is 1-indexed on the wire (67+1)
// 68 in binary
// 0b01000100
// DDDDDMMM
c.Assert(spi.b[0], qt.Equals, uint8(0b00000011)) // mode 1, vcom is high on first run
c.Assert(spi.b[1], qt.Equals, uint8(0b01000100)) // the actual address
})
t.Run("LS018B7DH02, 9-bit address", func(t *testing.T) {
t.Cleanup(func() {
spi.b = nil
})
display.Configure(ConfigLS018B7DH02)
display.SetPixel(0, display.height-1, color.RGBA{R: 255, G: 255, B: 255, A: 255})
err := display.Display()
c.Assert(err, qt.Equals, nil)
// 2 first bytes command+address
// 230 bits are not divisible by 16, 240 is (15*16), so 30 bytes for line data
// 2 trailing bytes
c.Assert(spi.b, qt.HasLen, 2+30+2)
// line is 1-indexed on the wire (302+1)
// 303 in binary (split in 2 bytes)
// R
// 0b00000001 0b00101111
// ^
// RDDDDMMM
c.Assert(spi.b[0], qt.Equals, uint8(0b10000011)) // mode 1, vcom is high on first run
// ^
c.Assert(spi.b[1], qt.Equals, uint8(0b00101111)) // rest of the address (low 8 bits)
})
t.Run("LS032B7DD02, 10-bit address", func(t *testing.T) {
t.Cleanup(func() {
spi.b = nil
})
display.Configure(ConfigLS032B7DD02)
display.SetPixel(0, display.height-1, color.RGBA{R: 255, G: 255, B: 255, A: 255})
err := display.Display()
c.Assert(err, qt.Equals, nil)
c.Assert(spi.b, qt.HasLen, 2+336/8+2) // 2 command+address, width / 2, 2 trailing bytes
// line is 1-indexed on the wire (535+1)
// 536 in binary (split in 2 bytes)
// RR
// 0b00000010 0b00011000
// ^^
// RRDDDMMM
c.Assert(spi.b[0], qt.Equals, uint8(0b10000011)) // mode 1, vcom is high on first run
// ^^
c.Assert(spi.b[1], qt.Equals, uint8(0b00011000)) // rest of the address (low 8 bits)
})
}
-30
View File
@@ -1,30 +0,0 @@
package sharpmem
// setBit sets the bit at pos in n to 1 and returns the updated number.
func setBit(n uint8, pos uint8) uint8 {
n |= 1 << pos
return n
}
// unsetBit sets the bit at pos in n to 0 and returns the updated number.
func unsetBit(n uint8, pos uint8) uint8 {
n &^= 1 << pos
return n
}
// hasBit returns whether the bit at pos in n is 1.
func hasBit(n uint8, pos uint8) bool {
n = n & (1 << pos)
return n > 0
}
// bitfieldBufLen returns the required buffer size for keeping track of
// changed lines.
func bitfieldBufLen(bits int16) int16 {
return 1 + (bits-1)/8
}
// ceilDiv divides a with b, but it uses the ceiling if modulo is not 0.
func ceilDiv(a, b int16) int16 {
return 1 + (a-1)/b
}
+17 -23
View File
@@ -2,7 +2,7 @@
package shiftregister
import (
"tinygo.org/x/drivers/internal/pin"
"machine"
)
type NumberBit int8
@@ -16,10 +16,9 @@ const (
// Device holds pin number
type Device struct {
latch, clock, out pin.OutputFunc // IC wiring
config func()
bits NumberBit // Pin number
mask uint32 // keep all pins state
latch, clock, out machine.Pin // IC wiring
bits NumberBit // Pin number
mask uint32 // keep all pins state
}
// ShiftPin is the implementation of the ShiftPin interface.
@@ -30,40 +29,35 @@ type ShiftPin struct {
}
// New returns a new shift output register device
func New(Bits NumberBit, Latch, Clock, Out pin.Output) *Device {
func New(Bits NumberBit, Latch, Clock, Out machine.Pin) *Device {
return &Device{
latch: Latch.Set,
clock: Clock.Set,
out: Out.Set,
latch: Latch,
clock: Clock,
out: Out,
bits: Bits,
config: func() {
pin.ConfigureOutput(Latch)
pin.ConfigureOutput(Clock)
pin.ConfigureOutput(Out)
},
}
}
// Configure set hardware configuration
func (d *Device) Configure() {
if d.config == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.latch(true)
d.latch.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.clock.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.out.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.latch.High()
}
// WriteMask applies mask's bits to register's outputs pin
// mask's MSB set Q1, LSB set Q8 (for 8 bits mask)
func (d *Device) WriteMask(mask uint32) {
d.mask = mask // Keep the mask for individual addressing
d.latch(false)
d.latch.Low()
for i := 0; i < int(d.bits); i++ {
d.clock(false)
d.out(mask&1 != 0)
d.clock.Low()
d.out.Set(mask&1 != 0)
mask = mask >> 1
d.clock(true)
d.clock.High()
}
d.latch(true)
d.latch.High()
}
// GetShiftPin return an individually addressable pin
+3 -14
View File
@@ -44,7 +44,6 @@ tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/lsm303dlhc/main.go
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
@@ -59,17 +58,15 @@ tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/ma
tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw/soil-sensor
tinygo build -size short -o ./build/test.hex -target=qtpy-rp2040 ./examples/seesaw/rotary-encoder
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
tinygo build -size short -o ./build/test.hex -target=pico ./examples/sgp30
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht4x/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
tinygo build -size short -o ./build/test.hex -target=xiao-ble ./examples/ssd1306/
tinygo build -size short -o ./build/test.hex -target=xiao-rp2040 ./examples/ssd1306/
tinygo build -size short -o ./build/test.hex -target=thumby ./examples/ssd1306/
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
@@ -139,11 +136,6 @@ tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/mai
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/sh1106/macropad_spi
tinygo build -size short -o ./build/test.hex -target=macropad-rp2040 ./examples/encoders/quadrature-interrupt
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mcp9808/main.go
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/main.go
tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go
tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/max6675/main.go
tinygo build -size short -o ./build/test.hex -target=pico ./examples/ens160/main.go
# network examples (espat)
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
# network examples (wifinina)
@@ -158,6 +150,3 @@ tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb
tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webclient/
tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webserver/
tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/mqttclient/paho/
# network examples (comboat)
tinygo build -size short -o ./build/test.hex -target=elecrow-rp2040 -stack-size 8kb ./examples/net/tlsclient/
tinygo build -size short -o ./build/test.hex -target=elecrow-rp2350 -stack-size 8kb ./examples/net/ntpclient/
-2
View File
@@ -1,5 +1,3 @@
//go:build baremetal
package ssd1289
import "machine"
+18 -18
View File
@@ -5,9 +5,8 @@ package ssd1289
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/internal/pin"
)
type Bus interface {
@@ -15,32 +14,33 @@ type Bus interface {
}
type Device struct {
rs pin.OutputFunc
wr pin.OutputFunc
cs pin.OutputFunc
rst pin.OutputFunc
rs machine.Pin
wr machine.Pin
cs machine.Pin
rst machine.Pin
bus Bus
}
const width = int16(240)
const height = int16(320)
func New(rs pin.Output, wr pin.Output, cs pin.Output, rst pin.Output, bus Bus) Device {
func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bus) Device {
d := Device{
rs: rs.Set,
wr: wr.Set,
cs: cs.Set,
rst: rst.Set,
rs: rs,
wr: wr,
cs: cs,
rst: rst,
bus: bus,
}
pin.ConfigureOutput(rs)
pin.ConfigureOutput(wr)
pin.ConfigureOutput(cs)
pin.ConfigureOutput(rst)
d.cs.High()
d.rst.High()
d.wr.High()
rs.Configure(machine.PinConfig{Mode: machine.PinOutput})
wr.Configure(machine.PinConfig{Mode: machine.PinOutput})
cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
cs.High()
rst.High()
wr.High()
return d
}
+139 -57
View File
@@ -6,9 +6,11 @@ package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/pixel"
)
@@ -21,15 +23,16 @@ type ResetValue [2]byte
// Device wraps I2C or SPI connection.
type Device struct {
bus Buser
buffer []byte
width int16
height int16
vccState VccMode
canReset bool
resetCol ResetValue
resetPage ResetValue
rotation drivers.Rotation
bus Buser
buffer []byte
width int16
height int16
bufferSize int16
vccState VccMode
canReset bool
resetCol ResetValue
resetPage ResetValue
rotation drivers.Rotation
}
// Config is the configuration for the display
@@ -48,15 +51,51 @@ type Config struct {
Rotation drivers.Rotation
}
type I2CBus struct {
wire drivers.I2C
Address uint16
}
type SPIBus struct {
wire drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
}
type Buser interface {
configure(address uint16, size int16) []byte // configure the bus and return the image buffer to use
command(cmd uint8) error // send a command to the display
flush() error // send the image to the display, faster than "tx()" in i2c case since avoids slice copy
tx(data []byte, isCommand bool) error // generic transmit function
configure() error
tx(data []byte, isCommand bool) error
setAddress(address uint16) error
}
type VccMode uint8
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
func NewI2C(bus drivers.I2C) Device {
return Device{
bus: &I2CBus{
wire: bus,
Address: Address,
},
}
}
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: &SPIBus{
wire: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
},
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
var zeroReset ResetValue
@@ -70,6 +109,9 @@ func (d *Device) Configure(cfg Config) {
} else {
d.height = 64
}
if cfg.Address != 0 {
d.bus.setAddress(cfg.Address)
}
if cfg.VccState != 0 {
d.vccState = cfg.VccState
} else {
@@ -85,9 +127,11 @@ func (d *Device) Configure(cfg Config) {
} else {
d.resetPage = ResetValue{0, uint8(d.height/8) - 1}
}
d.bufferSize = d.width * d.height / 8
d.buffer = make([]byte, d.bufferSize)
d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
d.buffer = d.bus.configure(cfg.Address, d.width*d.height/8)
d.bus.configure()
time.Sleep(100 * time.Nanosecond)
d.Command(DISPLAYOFF)
@@ -149,22 +193,11 @@ func (d *Device) Configure(cfg Config) {
d.Command(NORMALDISPLAY)
d.Command(DEACTIVATE_SCROLL)
d.Command(DISPLAYON)
}
// Command sends a command to the display
func (d *Device) Command(command uint8) {
d.bus.command(command)
}
// Tx sends data to the display; if isCommand is false, this also updates the image buffer.
func (d *Device) Tx(data []byte, isCommand bool) error {
return d.bus.tx(data, isCommand)
}
// ClearBuffer clears the image buffer
func (d *Device) ClearBuffer() {
for i := 0; i < len(d.buffer); i++ {
for i := int16(0); i < d.bufferSize; i++ {
d.buffer[i] = 0
}
}
@@ -190,7 +223,7 @@ func (d *Device) Display() error {
d.Command(d.resetPage[1])
}
return d.bus.flush()
return d.Tx(d.buffer, false)
}
// SetPixel enables or disables a pixel in the buffer
@@ -219,10 +252,12 @@ func (d *Device) GetPixel(x int16, y int16) bool {
// SetBuffer changes the whole buffer at once
func (d *Device) SetBuffer(buffer []byte) error {
if len(buffer) != len(d.buffer) {
if int16(len(buffer)) != d.bufferSize {
return errBufferSize
}
copy(d.buffer, buffer)
for i := int16(0); i < d.bufferSize; i++ {
d.buffer[i] = buffer[i]
}
return nil
}
@@ -231,6 +266,81 @@ func (d *Device) GetBuffer() []byte {
return d.buffer
}
// Command sends a command to the display
func (d *Device) Command(command uint8) {
d.bus.tx([]byte{command}, true)
}
// setAddress sets the address to the I2C bus
func (b *I2CBus) setAddress(address uint16) error {
b.Address = address
return nil
}
// setAddress does nothing, but it's required to avoid reflection
func (b *SPIBus) setAddress(address uint16) error {
// do nothing
println("trying to Configure an address on a SPI device")
return nil
}
// configure does nothing, but it's required to avoid reflection
func (b *I2CBus) configure() error { return nil }
// configure configures some pins with the SPI bus
func (b *SPIBus) configure() error {
b.csPin.Low()
b.dcPin.Low()
b.resetPin.Low()
b.resetPin.High()
time.Sleep(1 * time.Millisecond)
b.resetPin.Low()
time.Sleep(10 * time.Millisecond)
b.resetPin.High()
return nil
}
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) error {
return d.bus.tx(data, isCommand)
}
// tx sends data to the display (I2CBus implementation)
func (b *I2CBus) tx(data []byte, isCommand bool) error {
if isCommand {
return legacy.WriteRegister(b.wire, uint8(b.Address), 0x00, data)
} else {
return legacy.WriteRegister(b.wire, uint8(b.Address), 0x40, data)
}
}
// tx sends data to the display (SPIBus implementation)
func (b *SPIBus) tx(data []byte, isCommand bool) error {
var err error
if isCommand {
b.csPin.High()
time.Sleep(1 * time.Millisecond)
b.dcPin.Low()
b.csPin.Low()
err = b.wire.Tx(data, nil)
b.csPin.High()
} else {
b.csPin.High()
time.Sleep(1 * time.Millisecond)
b.dcPin.High()
b.csPin.Low()
err = b.wire.Tx(data, nil)
b.csPin.High()
}
return err
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
@@ -286,31 +396,3 @@ func (d *Device) Sleep(sleepEnabled bool) error {
}
return nil
}
// FillRectangle fills a rectangle at a given coordinates with a color
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
dw, dh := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= d.width || (x+width) > dw || y >= dh || (y+height) > dh {
return errOutOfRange
}
if x+width == dw && y+height == dh && c.R == 0 && c.G == 0 && c.B == 0 {
d.ClearDisplay()
return nil
}
for i := x; i < x+width; i++ {
for j := y; j < y+height; j++ {
d.SetPixel(i, j, c)
}
}
return nil
}
// SetScroll sets the vertical scrolling for the display, which is a NOP for this display.
func (d *Device) SetScroll(line int16) {
return
}

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