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
107 changed files with 475 additions and 7590 deletions
-66
View File
@@ -1,69 +1,3 @@
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**
+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 -7
View File
@@ -5,10 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
const (
@@ -38,11 +37,8 @@ func New(b drivers.SPI) *Device {
// NewSoftwareSPI returns a new APA102 driver that will use a software based
// implementation of the SPI protocol.
func NewSoftwareSPI(sckPin, sdoPin pin.Output, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() {
legacy.ConfigurePinOut(sckPin)
legacy.ConfigurePinOut(sdoPin)
}})
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
+6 -12
View File
@@ -1,9 +1,6 @@
package apa102
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
import "machine"
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
@@ -11,18 +8,15 @@ import (
// most purposes other than the APA102 package. It might be desirable to make
// this more generic and include it in the TinyGo "machine" package instead.
type bbSPI struct {
SCK drivers.PinOutput
SDO drivers.PinOutput
Delay uint32
configurePins func()
SCK machine.Pin
SDO machine.Pin
Delay uint32
}
// Configure sets up the SCK and SDO pins as outputs and sets them low
func (s *bbSPI) Configure() {
if s.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
s.configurePins()
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SCK.Low()
s.SDO.Low()
if s.Delay == 0 {
+23 -31
View File
@@ -1,36 +1,31 @@
package bmi160
import (
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
// also an I2C interface, but it is not yet supported.
type DeviceSPI struct {
// Chip select pin
csb drivers.PinOutput
CSB machine.Pin
buf [7]byte
// SPI bus (requires chip select to be usable).
bus drivers.SPI
configurePins func()
Bus drivers.SPI
}
// NewSPI returns a new device driver. The pin and SPI interface are not
// touched, provide a fully configured SPI object and call Configure to start
// using this device.
func NewSPI(csb pin.Output, spi drivers.SPI) *DeviceSPI {
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
return &DeviceSPI{
csb: csb.Set, // chip select
bus: spi,
configurePins: func() {
legacy.ConfigurePinOut(csb)
},
CSB: csb, // chip select
Bus: spi,
}
}
@@ -38,11 +33,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 legacy.ErrConfigBeforeInstantiated
}
d.configurePins()
d.csb.High()
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.CSB.High()
// The datasheet recommends doing a register read from address 0x7F to get
// SPI communication going:
@@ -94,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
}
@@ -131,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
}
@@ -161,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
}
@@ -209,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]
}
@@ -225,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()
}
+7 -8
View File
@@ -2,23 +2,22 @@
package buzzer // import "tinygo.org/x/drivers/buzzer"
import (
"time"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
"time"
)
// Device wraps a GPIO connection to a buzzer.
type Device struct {
pin drivers.PinOutput
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,
}
@@ -26,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
}
+67 -4
View File
@@ -2,9 +2,9 @@
package easystepper // import "tinygo.org/x/drivers/easystepper"
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
)
// StepMode determines the coil sequence used to perform a single step
@@ -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]drivers.PinOutput
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) {
-26
View File
@@ -1,26 +0,0 @@
package easystepper
import (
"errors"
"time"
"tinygo.org/x/drivers"
)
func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]drivers.PinOutput) (*Device, error) {
if stepcount == 0 || rpm == 0 {
return nil, errors.New("zero rpm and/or stepcount")
}
for i := range pins {
if pins[i] == nil {
return nil, errors.New("nil pin")
}
}
d := &Device{
pins: pins,
stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)),
stepMode: mode,
config: func() {},
}
return d, nil
}
-83
View File
@@ -1,83 +0,0 @@
//go:build baremetal
package easystepper
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// 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]drivers.PinOutput{config.Pin1.Set, config.Pin2.Set, config.Pin3.Set, config.Pin4.Set},
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
stepMode: config.Mode,
config: func() {
legacy.ConfigurePinOut(config.Pin1)
legacy.ConfigurePinOut(config.Pin2)
legacy.ConfigurePinOut(config.Pin3)
legacy.ConfigurePinOut(config.Pin4)
},
}, nil
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
if d.config == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
d.config()
}
// Configure configures the pins of the DualDevice
func (d *DualDevice) Configure() {
d.devices[0].Configure()
d.devices[1].Configure()
}
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
// Create the first device
dev1, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
// Create the second device
config.DeviceConfig.Pin1 = config.Pin5
config.DeviceConfig.Pin2 = config.Pin6
config.DeviceConfig.Pin3 = config.Pin7
config.DeviceConfig.Pin4 = config.Pin8
dev2, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
// Return composite dual device
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
}
// DeviceConfig contains the configuration data for a single easystepper driver
type DeviceConfig struct {
// Pin1 ... Pin4 determines the pins to configure and use for the device
Pin1, Pin2, Pin3, Pin4 machine.Pin
// StepCount is the number of steps required to perform a full revolution of the stepper motor
StepCount uint
// RPM determines the speed of the stepper motor in 'Revolutions per Minute'
RPM uint
// Mode determines the coil sequence used to perform a single step
Mode StepMode
}
// DualDeviceConfig contains the configuration data for a dual easystepper driver
type DualDeviceConfig struct {
DeviceConfig
// Pin5 ... Pin8 determines the pins to configure and use for the second device
Pin5, Pin6, Pin7, Pin8 machine.Pin
}
+1 -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,
-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
-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)
}
}
}
-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() {
legacy.ConfigurePinInputPulldown(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 legacy.ErrConfigBeforeInstantiated
}
d.write1Byte(0xA4, 0x00)
d.configurePins()
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
return nil
}
+15 -16
View File
@@ -5,13 +5,12 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01"
import (
"image/color"
"machine"
"time"
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
// Rotation controls the rotation used by the display.
@@ -23,10 +22,10 @@ type FrameRate uint8
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin drivers.PinOutput
resetPin drivers.PinOutput
csPin drivers.PinOutput
blPin drivers.PinOutput
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
width int16
height int16
columnOffsetCfg int16
@@ -53,17 +52,17 @@ type Config struct {
}
// New creates a new ST7789 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
legacy.ConfigurePinOut(resetPin)
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(csPin)
legacy.ConfigurePinOut(blPin)
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: bus,
resetPin: resetPin.Set,
dcPin: dcPin.Set,
csPin: csPin.Set,
blPin: blPin.Set,
resetPin: resetPin,
dcPin: dcPin,
csPin: csPin,
blPin: blPin,
}
}
@@ -227,7 +226,7 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.dcPin(!isCommand)
d.dcPin.Set(!isCommand)
d.bus.Tx(data, nil)
}
+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=
+10 -20
View File
@@ -5,40 +5,30 @@
package hcsr04
import (
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
const TIMEOUT = 23324 // max sensing distance (4m)
// Device holds the pins
type Device struct {
trigger drivers.PinOutput
echo drivers.PinInput
configurePins func()
trigger machine.Pin
echo machine.Pin
}
// New returns a new ultrasonic driver given 2 pins
func New(trigger pin.Output, echo pin.Input) Device {
func New(trigger, echo machine.Pin) Device {
return Device{
trigger: trigger.Set,
echo: echo.Get,
configurePins: func() {
legacy.ConfigurePinOut(trigger)
legacy.ConfigurePinInput(echo)
},
trigger: trigger,
echo: echo,
}
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
if d.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
d.configurePins()
d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.echo.Configure(machine.PinConfig{Mode: machine.PinInput})
}
// ReadDistance returns the distance of the object in mm
@@ -62,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
}
@@ -76,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.
+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,
-54
View File
@@ -1,54 +0,0 @@
package legacy
import (
"errors"
"tinygo.org/x/drivers/internal/pin"
)
// ConfigurePinOut is a legacy function used to configure pins as outputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinOut(po pin.Output) {
configurePinOut(po)
}
// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInputPulldown(pi pin.Input) {
configurePinInputPulldown(pi)
}
// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInput(pi pin.Input) {
configurePinInput(pi)
}
// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInputPullup(pi pin.Input) {
configurePinInputPullup(pi)
}
// PinIsNoPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
//
// Deprecated: Drivers do not require pin knowledge from now on.
func PinIsNoPin(pin any) bool {
return pinIsNoPin(pin)
}
var (
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
)
-11
View File
@@ -1,11 +0,0 @@
//go:build !baremetal
package legacy
import "tinygo.org/x/drivers/internal/pin"
func configurePinOut(p pin.Output) {}
func configurePinInput(p pin.Input) {}
func configurePinInputPulldown(p pin.Input) {}
func configurePinInputPullup(p pin.Input) {}
func pinIsNoPin(a any) bool { return false }
-10
View File
@@ -1,10 +0,0 @@
//go:build baremetal && fe310
package legacy
import "machine"
const (
pulldown = machine.PinInput
pullup = machine.PinInput
)
-13
View File
@@ -1,13 +0,0 @@
//go:build baremetal && !fe310
package legacy
import "machine"
// If you are getting a build error here you then we missed adding
// your CPU build tag to the list of CPUs that do not have pulldown/pullups.
// Add it above and in pinhal_nopulls! You should also add a smoketest for it :)
const (
pulldown = machine.PinInputPulldown
pullup = machine.PinInputPullup
)
-37
View File
@@ -1,37 +0,0 @@
//go:build baremetal
package legacy
import (
"machine"
"tinygo.org/x/drivers/internal/pin"
)
func configurePinOut(po pin.Output) {
configurePin(po, machine.PinOutput)
}
func configurePinInputPulldown(pi pin.Input) {
configurePin(pi, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput.
}
func configurePinInput(pi pin.Input) {
configurePin(pi, machine.PinInput)
}
func configurePinInputPullup(pi pin.Input) {
configurePin(pi, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput.
}
func pinIsNoPin(a any) bool {
p, ok := a.(machine.Pin)
return ok && p == machine.NoPin
}
func configurePin(p any, mode machine.PinMode) {
machinePin, ok := p.(machine.Pin)
if ok {
machinePin.Configure(machine.PinConfig{Mode: mode})
}
}
-23
View File
@@ -1,23 +0,0 @@
package pin
import "tinygo.org/x/drivers"
// Here to aid relevant documentation links of [drivers.PinOutput] and [drivers.PinInput].
var _ drivers.PinOutput
// Output represents a pin hardware abstraction layer for a pin that can output a digital signal.
//
// This is an alternative to [drivers.PinOutput] abstraction which is a function type and has
// not been standardized as of yet as a standard HAL in the drivers package,
// [discussion ongoing here].
//
// [discussion ongoing here]: https://github.com/orgs/tinygo-org/discussions/5043
type Output interface {
Set(level bool)
}
// Input represents a pin hardware abstraction layer.
// See [Output] for more information on why this type exists separate to drivers.
type Input interface {
Get() (level bool)
}
-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 drivers.PinOutput
}
// Create a new Device to read from a MAX6675 thermocouple.
// Pins must be configured before use. Frequency for SPI
// should be 4.3MHz maximum.
func NewDevice(bus drivers.SPI, cs pin.Output) *Device {
return &Device{
bus: bus,
cs: cs.Set,
}
}
// Read and return the temperature in celsius
func (d *Device) Read() (float32, error) {
var (
read []byte = []byte{0, 0}
value uint16
)
d.cs.Low()
if err := d.bus.Tx([]byte{0, 0}, read); err != nil {
return 0, err
}
d.cs.High()
// datasheet: Bit D2 is normally low and goes high if the thermocouple input is open.
if read[1]&0x04 == 0x04 {
return 0, ErrThermocoupleOpen
}
// data is 12 bits, split across the two bytes
// -XXXXXXX XXXXX---
value = (uint16(read[0]) << 5) | (uint16(read[1]) >> 3)
return float32(value) * 0.25, nil
}
+8 -15
View File
@@ -3,36 +3,29 @@
package max72xx
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
"machine"
)
type Device struct {
bus drivers.SPI
cs drivers.PinOutput
configurePins func()
bus machine.SPI
cs machine.Pin
}
// NewDriver creates a new max7219 connection. The SPI wire must already be configured
// The SPI frequency must not be higher than 10MHz.
// parameter cs: the datasheet also refers to this pin as "load" pin.
func NewDevice(bus drivers.SPI, cs pin.Output) *Device {
func NewDevice(bus machine.SPI, cs machine.Pin) *Device {
return &Device{
bus: bus,
cs: cs.Set,
configurePins: func() {
legacy.ConfigurePinOut(cs)
},
cs: cs,
}
}
// Configure setups the pins.
func (driver *Device) Configure() {
if driver.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
driver.configurePins()
outPutConfig := machine.PinConfig{Mode: machine.PinOutput}
driver.cs.Configure(outPutConfig)
}
// SetScanLimit sets the scan limit. Maximum is 8.
+8 -16
View File
@@ -8,20 +8,18 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
import (
"errors"
"fmt"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
// Device wraps MCP2515 SPI CAN Module.
type Device struct {
spi SPI
cs drivers.PinOutput
msg *CANMsg
mcpMode byte
configurePins func()
spi SPI
cs machine.Pin
msg *CANMsg
mcpMode byte
}
// CANMsg stores CAN message fields.
@@ -38,18 +36,15 @@ const (
)
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
func New(b drivers.SPI, csPin pin.Output) *Device {
func New(b drivers.SPI, csPin machine.Pin) *Device {
d := &Device{
spi: SPI{
bus: b,
tx: make([]byte, 0, bufferSize),
rx: make([]byte, 0, bufferSize),
},
cs: csPin.Set,
cs: csPin,
msg: &CANMsg{},
configurePins: func() {
legacy.ConfigurePinOut(csPin)
},
}
return d
@@ -57,10 +52,7 @@ func New(b drivers.SPI, csPin pin.Output) *Device {
// Configure sets up the device for communication.
func (d *Device) Configure() {
if d.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
d.configurePins()
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
}
const beginTimeoutValue int = 10
+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
}
+16 -24
View File
@@ -5,9 +5,8 @@ package onewire // import "tinygo.org/x/drivers/onewire"
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
)
// OneWire ROM commands
@@ -20,8 +19,7 @@ const (
// Device wraps a connection to an 1-Wire devices.
type Device struct {
set drivers.PinOutput
get drivers.PinInput
p machine.Pin
}
// Config wraps a configuration to an 1-Wire devices.
@@ -34,30 +32,24 @@ var (
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
)
// NewFromFuncs expects pin setter and getter for DQ line. Ideally this driver should receive
// a one-wire bus HAL abstraction, but I was asked to show how this could be done using pin HAL so here goes.
func NewFromFuncs(getPinLevel drivers.PinInput, setPinLevel drivers.PinOutput) *Device {
return &Device{
set: setPinLevel,
get: getPinLevel,
// New creates a new GPIO 1-Wire connection.
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
func New(p machine.Pin) Device {
return Device{
p: p,
}
}
// Configure initializes the protocol.
func (d *Device) Configure(config Config) {}
// By setting the pin value one should configure as output and also expect a more
// consistent behaviour across all tinygo hosts by pulling DQ line low consistently. Win-win.
func (d *Device) cfgOut() { d.set.Low() }
func (d *Device) cfgIn() { d.get() }
// Reset pull DQ line low, then up.
func (d Device) Reset() error {
d.cfgOut()
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
time.Sleep(480 * time.Microsecond)
d.cfgIn()
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
time.Sleep(70 * time.Microsecond)
precence := d.get()
precence := d.p.Get()
time.Sleep(410 * time.Microsecond)
if precence {
return errNoPresence
@@ -67,14 +59,14 @@ func (d Device) Reset() error {
// WriteBit transmits a bit to 1-Wire bus.
func (d Device) WriteBit(data uint8) {
d.cfgOut()
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
if data&1 == 1 { // Send '1'
time.Sleep(5 * time.Microsecond)
d.cfgIn()
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
time.Sleep(60 * time.Microsecond)
} else { // Send '0'
time.Sleep(60 * time.Microsecond)
d.cfgIn()
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
time.Sleep(5 * time.Microsecond)
}
}
@@ -89,11 +81,11 @@ func (d Device) Write(data uint8) {
// ReadBit receives a bit from 1-Wire bus.
func (d Device) ReadBit() (data uint8) {
d.cfgOut()
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
time.Sleep(3 * time.Microsecond)
d.cfgIn()
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
time.Sleep(8 * time.Microsecond)
if d.get() {
if d.p.Get() {
data = 1
}
time.Sleep(60 * time.Microsecond)
-29
View File
@@ -1,29 +0,0 @@
package onewire
import (
"machine"
"tinygo.org/x/drivers/internal/legacy"
)
// New creates a new GPIO 1-Wire connection.
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
func New(p machine.Pin) Device {
isOut := false
return Device{
set: func(level bool) {
if !isOut {
legacy.ConfigurePinOut(p)
isOut = true
}
p.Set(level)
},
get: func() (level bool) {
if isOut {
legacy.ConfigurePinInputPullup(p)
isOut = false
}
return p.Get()
},
}
}
+1 -2
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 drivers.PinOutput
rstPin drivers.PinOutput
scePin drivers.PinOutput
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.
-29
View File
@@ -1,29 +0,0 @@
package drivers
// PinOutput is hardware abstraction for a pin which outputs a
// digital signal (high or low level).
//
// // Code conversion demo: from machine.Pin to drivers.PinOutput
// led := machine.LED
// led.Configure(machine.PinConfig{Mode: machine.PinOutput})
// var pin drivers.PinOutput = led.Set // Going from a machine.Pin to a drivers.PinOutput
type PinOutput func(level bool)
// High sets the underlying pin's level to high. This is equivalent to calling PinOutput(true).
func (po PinOutput) High() {
po(true)
}
// Low sets the underlying pin's level to low. This is equivalent to calling PinOutput(false).
func (po PinOutput) Low() {
po(false)
}
// PinInput 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 drivers.PinInput
// input := machine.LED
// input.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) // or use machine.PinInputPullup or machine.PinInput
// var pin drivers.PinInput = input.Get // Going from a machine.Pin to a drivers.PinInput
type PinInput func() (level bool)
+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
-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
}
+12 -20
View File
@@ -2,9 +2,7 @@
package shiftregister
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
"machine"
)
type NumberBit int8
@@ -18,10 +16,9 @@ const (
// Device holds pin number
type Device struct {
latch, clock, out drivers.PinOutput // 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.
@@ -32,25 +29,20 @@ 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() {
legacy.ConfigurePinOut(Latch)
legacy.ConfigurePinOut(Clock)
legacy.ConfigurePinOut(Out)
},
}
}
// Configure set hardware configuration
func (d *Device) Configure() {
if d.config == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
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()
}
@@ -61,7 +53,7 @@ func (d *Device) WriteMask(mask uint32) {
d.latch.Low()
for i := 0; i < int(d.bits); i++ {
d.clock.Low()
d.out(mask&1 != 0)
d.out.Set(mask&1 != 0)
mask = mask >> 1
d.clock.High()
}
-7
View File
@@ -136,10 +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
# network examples (espat)
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
# network examples (wifinina)
@@ -154,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"
+16 -18
View File
@@ -7,9 +7,6 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type Bus interface {
@@ -17,10 +14,10 @@ type Bus interface {
}
type Device struct {
rs drivers.PinOutput
wr drivers.PinOutput
cs drivers.PinOutput
rst drivers.PinOutput
rs machine.Pin
wr machine.Pin
cs machine.Pin
rst machine.Pin
bus Bus
}
@@ -29,20 +26,21 @@ const height = int16(320)
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,
}
legacy.ConfigurePinOut(rs)
legacy.ConfigurePinOut(wr)
legacy.ConfigurePinOut(cs)
legacy.ConfigurePinOut(rst)
cs.Set(true)
rst.Set(true)
wr.Set(true)
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
}
-28
View File
@@ -396,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
}
+10 -11
View File
@@ -11,7 +11,6 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type Model uint8
@@ -20,9 +19,9 @@ type Rotation uint8
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin drivers.PinOutput
resetPin drivers.PinOutput
csPin drivers.PinOutput
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
width int16
height int16
batchLength int16
@@ -38,14 +37,14 @@ type Config struct {
// New creates a new SSD1331 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin machine.Pin) Device {
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(resetPin)
legacy.ConfigurePinOut(csPin)
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: bus,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: csPin.Set,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
}
}
@@ -252,7 +251,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)
}
+24 -32
View File
@@ -6,11 +6,10 @@ package ssd1351 // import "tinygo.org/x/drivers/ssd1351"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
var (
@@ -20,18 +19,17 @@ var (
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin drivers.PinOutput
resetPin drivers.PinOutput
csPin drivers.PinOutput
enPin drivers.PinOutput
rwPin drivers.PinOutput
configurePins func()
width int16
height int16
rowOffset int16
columnOffset int16
bufferLength int16
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
enPin machine.Pin
rwPin machine.Pin
width int16
height int16
rowOffset int16
columnOffset int16
bufferLength int16
}
// Config is the configuration for the display
@@ -43,29 +41,19 @@ type Config struct {
}
// New creates a new SSD1351 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin pin.Output) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device {
return Device{
bus: bus,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: csPin.Set,
enPin: enPin.Set,
rwPin: rwPin.Set,
configurePins: func() {
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(resetPin)
legacy.ConfigurePinOut(csPin)
legacy.ConfigurePinOut(enPin)
legacy.ConfigurePinOut(rwPin)
},
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
enPin: enPin,
rwPin: rwPin,
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
if d.configurePins == nil {
panic(legacy.ErrConfigBeforeInstantiated)
}
if cfg.Width == 0 {
cfg.Width = 128
}
@@ -85,7 +73,11 @@ func (d *Device) Configure(cfg Config) {
}
// configure GPIO pins
d.configurePins()
d.dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.enPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.rwPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
// reset the device
d.resetPin.High()
@@ -286,7 +278,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.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
+16 -17
View File
@@ -5,13 +5,12 @@ package st7735 // import "tinygo.org/x/drivers/st7735"
import (
"image/color"
"machine"
"time"
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
"tinygo.org/x/drivers/pixel"
)
@@ -40,10 +39,10 @@ type Device = DeviceOf[pixel.RGB565BE]
// formats.
type DeviceOf[T Color] struct {
bus drivers.SPI
dcPin drivers.PinOutput
resetPin drivers.PinOutput
csPin drivers.PinOutput
blPin drivers.PinOutput
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
width int16
height int16
columnOffset int16
@@ -66,23 +65,23 @@ type Config struct {
}
// New creates a new ST7735 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
}
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI
// wire must already be configured.
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) DeviceOf[T] {
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(resetPin)
legacy.ConfigurePinOut(csPin)
legacy.ConfigurePinOut(blPin)
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return DeviceOf[T]{
bus: bus,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: csPin.Set,
blPin: blPin.Set,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
blPin: blPin,
}
}
@@ -424,7 +423,7 @@ func (d *DeviceOf[T]) Data(data uint8) {
// Tx sends data to the display
func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) {
d.dcPin(!isCommand)
d.dcPin.Set(!isCommand)
d.bus.Tx(data, nil)
}
+17 -22
View File
@@ -7,14 +7,13 @@ package st7789 // import "tinygo.org/x/drivers/st7789"
import (
"image/color"
"machine"
"math"
"time"
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
"tinygo.org/x/drivers/pixel"
)
@@ -47,10 +46,10 @@ type Device = DeviceOf[pixel.RGB565BE]
// formats.
type DeviceOf[T Color] struct {
bus drivers.SPI
dcPin drivers.PinOutput
resetPin drivers.PinOutput
csPin drivers.PinOutput
blPin drivers.PinOutput
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
width int16
height int16
columnOffsetCfg int16
@@ -84,27 +83,23 @@ 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 {
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
}
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
// wire must already be configured.
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) DeviceOf[T] {
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(resetPin)
legacy.ConfigurePinOut(csPin)
legacy.ConfigurePinOut(blPin)
var cs drivers.PinOutput
if !legacy.PinIsNoPin(csPin) {
cs = csPin.Set
}
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return DeviceOf[T]{
bus: bus,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: cs,
blPin: blPin.Set,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
blPin: blPin,
}
}
@@ -234,7 +229,7 @@ func (d *DeviceOf[T]) sendCommand(command uint8, data []byte) error {
// startWrite must be called at the beginning of all exported methods to set the
// chip select pin low.
func (d *DeviceOf[T]) startWrite() {
if d.csPin != nil {
if d.csPin != machine.NoPin {
d.csPin.Low()
}
}
@@ -242,7 +237,7 @@ func (d *DeviceOf[T]) startWrite() {
// endWrite must be called at the end of all exported methods to set the chip
// select pin high.
func (d *DeviceOf[T]) endWrite() {
if d.csPin != nil {
if d.csPin != machine.NoPin {
d.csPin.High()
}
}
+4 -4
View File
@@ -6,10 +6,10 @@ package sx127x
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
"tinygo.org/x/drivers/lora"
)
@@ -22,7 +22,7 @@ const (
// Device wraps an SPI connection to a SX127x device.
type Device struct {
spi drivers.SPI // SPI bus for module communication
rstPin drivers.PinOutput // GPIO for reset
rstPin machine.Pin // GPIO for reset
radioEventChan chan lora.RadioEvent // Channel for Receiving events
loraConf lora.Config // Current Lora configuration
controller RadioController // to manage interactions with the radio
@@ -43,10 +43,10 @@ func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
}
// New creates a new SX127x connection. The SPI bus must already be configured.
func New(spi drivers.SPI, rstPin pin.Output) *Device {
func New(spi machine.SPI, rstPin machine.Pin) *Device {
k := Device{
spi: spi,
rstPin: rstPin.Set,
rstPin: rstPin,
radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
spiTxBuf: make([]byte, SPI_BUFFER_SIZE),
spiRxBuf: make([]byte, SPI_BUFFER_SIZE),
-163
View File
@@ -1,163 +0,0 @@
# TMC2209 Go Package
This package provides a lightweight interface to communicate with the TMC2209 stepper motor driver via UART. It is designed to be used with microcontrollers and supports both default UART communication and custom interface implementations for flexibility.
Currently this package only implements the communications with TMC2209. Functions to handle specific operations such as EnableStealthChop() etc. will need to be implemented by the user.
## Features
- **UART Communication:** Standard communication through UART for controlling the TMC2209 driver.
- **Custom Communication Interfaces:** Allows custom implementations of communication interfaces (e.g., USB, SPI, etc.) via the `RegisterComm` interface. The usecase for this is if you have host system that can "talk" to the microcontroller connected to the TMC2209
- **Error Handling:** Lightweight error handling for TinyGo compatibility.
- **Register Access:** Provides methods to read and write registers on the TMC2209.
## Setup
### Prerequisites
- **TinyGo**: This package is optimized for TinyGo, which is suitable for running Go on microcontrollers and embedded systems.
- **UART Communication**: For microcontrollers with UART support, such as ESP32, STM32, and Raspberry Pi Pico.
## Usage
### Using with Microcontrollers (Default UART)
To use the package with a microcontroller, you need to initialize the UART communication and configure the TMC2209 driver.
Example:
```aiignore
package main
import (
"fmt"
"log"
"github.com/yourusername/tmc2209"
"machine"
)
func main() {
uart := machine.UART0
// Create an instance of the TMC2209 with UART communication
tmc := tmc2209.NewTMC2209(uart, 0x00) // Replace 0x00 with the appropriate address
// Set up the TMC2209 driver
err := tmc.Setup()
if err != nil {
log.Fatalf("Failed to set up TMC2209: %v", err)
}
// Write to a register (example: setting a register value)
err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value
if err != nil {
log.Fatalf("Failed to write register: %v", err)
}
// Read from a register (example: reading a register value)
value, err := tmc.ReadRegister(0x10)
if err != nil {
log.Fatalf("Failed to read register: %v", err)
}
// Output the read value
fmt.Printf("Register value: 0x%X\n", value)
}
```
## Microcontroller Notes
- **TinyGo Support:** This code is optimized for use with TinyGo on supported microcontrollers.
- **UART Configuration:** Ensure the UART instance is configured correctly for your microcontroller.
- The machine.UART0 in the example is for the default UART on TinyGo-compatible devices like the Raspberry Pi Pico. Check your microcontroller's documentation for the correct UART instance and pin configuration.
## 2. Custom Interface Implementation
If you want to use a custom communication interface (e.g., USB, SPI, etc.), you can implement the RegisterComm interface.
Custom Interface Example (e.g., USB Communication):
```
package main
import (
"fmt"
"log"
"github.com/yourusername/tmc2209"
"yourcustompackage" // Custom package for communication
)
type CustomComm struct {
// Implement your custom communication method here
}
func (c *CustomComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) {
// Implement the register read logic using your custom interface (USB, SPI, etc.)
// For example, send the register read command over USB and read the response
return 0, nil
}
func (c *CustomComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error {
// Implement the register write logic using your custom interface (USB, SPI, etc.)
// For example, send the register write command over USB
return nil
}
func main() {
// Create an instance of the TMC2209 with your custom communication interface
customComm := &CustomComm{}
tmc := tmc2209.NewTMC2209(customComm, 0x00) // Replace 0x00 with the appropriate address
// Set up the TMC2209 driver
err := tmc.Setup()
if err != nil {
log.Fatalf("Failed to set up TMC2209: %v", err)
}
// Write to a register (example: setting a register value)
err = tmc.WriteRegister(0x10, 0x12345678) // Replace 0x10 with the register address and 0x12345678 with the value
if err != nil {
log.Fatalf("Failed to write register: %v", err)
}
// Read from a register (example: reading a register value)
value, err := tmc.ReadRegister(0x10)
if err != nil {
log.Fatalf("Failed to read register: %v", err)
}
// Output the read value
fmt.Printf("Register value: 0x%X\n", value)
}
```
### Custom Interface Notes:
- RegisterComm Interface: Your custom communication interface (e.g., USB, SPI) should implement the RegisterComm interface. This ensures that the TMC2209 driver can interact with your custom interface for reading and writing registers.
- Error Handling: Implement proper error handling in your custom interface methods (ReadRegister and WriteRegister).
#### Functions
```
NewTMC2209(comm RegisterComm, address uint8) *TMC2209
```
Creates a new TMC2209 instance with the provided communication interface (comm) and driver address (address).
```Setup() error```
Initializes the communication interface. This is required before interacting with the TMC2209 driver. The method checks if the communication interface is UART and sets it up accordingly.
```WriteRegister(register uint8, value uint32) error```
Writes a value to a specific register on the TMC2209 driver.
```ReadRegister(register uint8) (uint32, error)```
Reads a value from a specific register on the TMC2209 driver.
### Error Handling
This package uses a lightweight custom error type (CustomError) to ensure compatibility with TinyGo and reduce external dependencies.
Example Error:
```CustomError("communication interface not set")```
Created by Amken3d
info@amken3d.us
-1409
View File
File diff suppressed because it is too large Load Diff
-42
View File
@@ -1,42 +0,0 @@
package tmc2209
func Constrain(value, low, high uint32) uint32 {
if value < low {
return low
}
if value > high {
return high
}
return value
}
func SetRunCurrent(percent uint8) {
_ = PercentToCurrentSetting(percent)
// Set the run current register to runCurrent value
}
func SetHoldCurrent(percent uint8) {
_ = PercentToCurrentSetting(percent)
// Set the hold current register to holdCurrent value
}
func PercentToCurrentSetting(percent uint8) uint8 {
constrainedPercent := Constrain(uint32(percent), 0, 100)
return uint8(Map(constrainedPercent, 0, 100, 0, 255))
}
func CurrentSettingToPercent(currentSetting uint8) uint8 {
return uint8(Map(uint32(currentSetting), 0, 255, 0, 100))
}
func PercentToHoldDelaySetting(percent uint8) uint8 {
constrainedPercent := Constrain(uint32(percent), 0, 100)
return uint8(Map(constrainedPercent, 0, 100, 0, 255))
}
func HoldDelaySettingToPercent(holdDelaySetting uint8) uint8 {
return uint8(Map(uint32(holdDelaySetting), 0, 255, 0, 100))
}
func Map(value, fromLow, fromHigh, toLow, toHigh uint32) uint32 {
return (value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow) + toLow
}
-37
View File
@@ -1,37 +0,0 @@
package tmc2209
func SetMicrostepsPerStep(microsteps uint16) uint8 {
exponent := uint8(0)
microstepsShifted := microsteps >> 1
for microstepsShifted > 0 {
microstepsShifted = microstepsShifted >> 1
exponent++
}
SetMicrostepsPerStepPowerOfTwo(exponent)
return exponent
}
func SetMicrostepsPerStepPowerOfTwo(exponent uint8) {
switch exponent {
case 0:
// Set MRES_001
case 1:
// Set MRES_002
case 2:
// Set MRES_004
case 3:
// Set MRES_008
case 4:
// Set MRES_016
case 5:
// Set MRES_032
case 6:
// Set MRES_064
case 7:
// Set MRES_128
default:
// Set MRES_256
}
}
-25
View File
@@ -1,25 +0,0 @@
package tmc2209
func EnableStealthChop() {
// Set StealthChop enabled in the global config register
}
func DisableStealthChop() {
// Set StealthChop disabled in the global config register
}
func EnableCoolStep(lowerThreshold, upperThreshold uint8) {
// Enable CoolStep with specified thresholds
}
func DisableCoolStep() {
// Disable CoolStep feature
}
func EnableAutomaticCurrentScaling() {
// Enable Automatic Current Scaling
}
func DisableAutomaticCurrentScaling() {
// Disable Automatic Current Scaling
}
-56
View File
@@ -1,56 +0,0 @@
//go:build tinygo
package tmc2209
import (
"log"
)
// TMC2209 represents a single TMC2209 stepper motor driver on the communication line.
type TMC2209 struct {
comm RegisterComm
address uint8
}
// NewTMC2209 creates a new instance of the TMC2209 driver for a specific address.
func NewTMC2209(comm RegisterComm, address uint8) *TMC2209 {
return &TMC2209{
comm: comm,
address: address,
}
}
// Setup initializes the communication interface with the TMC2209.
func (driver *TMC2209) Setup() error {
// Check if comm is of type *UARTComm
if uartComm, ok := driver.comm.(*UARTComm); ok {
// Call Setup only if comm is a *UARTComm
err := uartComm.Setup()
if err != nil {
return CustomError("Failed to setup UART communication: " + err.Error())
}
} else {
// If it's not a UARTComm, log that it's using a different communication method
log.Println("Using a non-UART communication method")
}
return nil
}
// WriteRegister sends a register write command to the TMC2209.
func (driver *TMC2209) WriteRegister(reg uint8, value uint32) error {
if driver.comm == nil {
return CustomError("communication interface not set")
}
// Use the communication interface (RegisterComm) to write the register
return driver.comm.WriteRegister(reg, value, driver.address)
}
// ReadRegister sends a register read command to the TMC2209 and returns the read value.
func (driver *TMC2209) ReadRegister(reg uint8) (uint32, error) {
if driver.comm == nil {
return 0, CustomError("communication interface not set")
}
// Use the communication interface (RegisterComm) to read the register
return driver.comm.ReadRegister(reg, driver.address)
}
-120
View File
@@ -1,120 +0,0 @@
//go:build tinygo
package tmc2209
import (
"machine"
"time"
)
// CustomError is a lightweight error type used for TinyGo compatibility.
type CustomError string
func (e CustomError) Error() string {
return string(e)
}
// UARTComm implements RegisterComm for UART-based communication
type UARTComm struct {
uart machine.UART
address uint8
}
// NewUARTComm creates a new UARTComm instance.
func NewUARTComm(uart machine.UART, address uint8) *UARTComm {
return &UARTComm{
uart: uart,
address: address,
}
}
// Setup initializes the UART communication with the TMC2209.
func (comm *UARTComm) Setup() error {
// Check if UART is initialized
if comm.uart == (machine.UART{}) {
return CustomError("UART not initialized")
}
// Configure the UART interface with the desired baud rate and settings
err := comm.uart.Configure(machine.UARTConfig{
BaudRate: 115200,
})
if err != nil {
return CustomError("Failed to configure UART")
}
// No built-in timeout in TinyGo, so timeout will be handled in the read/write methods
return nil
}
// WriteRegister sends a register write command to the TMC2209 with a timeout.
func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error {
buffer := []byte{
0x05, // Sync byte
comm.address, // Slave address
register | 0x80, // Write command (set MSB to 1 for write)
byte((value >> 24) & 0xFF), // MSB of value
byte((value >> 16) & 0xFF), // Middle byte
byte((value >> 8) & 0xFF), // Next byte
byte(value & 0xFF), // LSB of value
}
// Calculate checksum by XORing all bytes
checksum := byte(0)
for _, b := range buffer[:7] {
checksum ^= b
}
buffer[7] = checksum // Set checksum byte
// Write the data to the TMC2209
done := make(chan error, 1)
go func() {
comm.uart.Write(buffer)
done <- nil
}()
// Implementing timeout using a 100ms timer
select {
case err := <-done:
return err
case <-time.After(100 * time.Millisecond): // Timeout after 100ms
return CustomError("write timeout")
}
}
// ReadRegister sends a register read command to the TMC2209 with a timeout.
func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) {
var writeBuffer [4]byte
writeBuffer[0] = 0x05 // Sync byte
writeBuffer[1] = 0x00 // Slave address
writeBuffer[2] = register & 0x7F // Read command (MSB clear for read)
writeBuffer[3] = writeBuffer[0] ^ writeBuffer[1] ^ writeBuffer[2] // Checksum
// Send the read command
done := make(chan []byte, 1)
go func() {
comm.uart.Write(writeBuffer[:])
readBuffer := make([]byte, 8)
comm.uart.Read(readBuffer)
done <- readBuffer
}()
// Implementing timeout using a 100ms timer
select {
case readBuffer := <-done:
// Validate checksum
checksum := byte(0)
for i := 0; i < 7; i++ {
checksum ^= readBuffer[i]
}
if checksum != readBuffer[7] {
return 0, CustomError("checksum error")
}
// Return the value from the register
return uint32(readBuffer[3])<<24 | uint32(readBuffer[4])<<16 | uint32(readBuffer[5])<<8 | uint32(readBuffer[6]), nil
case <-time.After(100 * time.Millisecond): // Timeout after 100ms
return 0, CustomError("read timeout")
}
}
-73
View File
@@ -1,73 +0,0 @@
package tmc2209
import "log"
func CalculateCRC(data []byte) uint8 {
crc := uint8(0)
for _, byte := range data {
for i := 0; i < 8; i++ {
if (crc>>7)^(byte&0x01) == 1 {
crc = (crc << 1) ^ 0x07
} else {
crc = crc << 1
}
byte >>= 1
}
}
return crc
}
// VerifyCommunication checks the communication with the TMC2209 by reading the version register (IOIN).
// It returns true if the communication is successful (i.e., the version matches the expected version).
// VerifyCommunication verifies the communication with the TMC2209 by reading the version register (IOIN).
// It explicitly resets the struct and de-references it after the check to ensure memory is managed manually.
func VerifyCommunication(comm RegisterComm, driverIndex uint8) bool {
var io *Ioin
if io == nil {
io = NewIoin() // Initialize the struct if not already initialized
} else {
*io = Ioin{}
}
_, err := ReadRegister(comm, driverIndex, io.GetAddress())
if err != nil {
return false
}
if io.Version == expectedVersion {
io = nil
return true
}
io = nil
return false
}
// CheckErrorStatus verifies the communication and checks for error flags in the TMC2209 driver status.
// It explicitly resets the struct and de-references it when done to ensure memory is managed manually.
func CheckErrorStatus(comm RegisterComm, driverIndex uint8) bool {
var d *DrvStatus
if d == nil {
d = NewDrvStatus()
} else {
*d = DrvStatus{}
}
_, err := d.Read(comm, driverIndex)
if err != nil {
return false
}
errorFlags := d.Ola | d.S2vsa | d.S2vsb | d.Ot | d.S2ga | d.S2gb | d.Olb
if errorFlags != 0 {
log.Printf("TMC2209 Error Detected: %X", errorFlags)
return false
}
d = nil
return true
}
// GetInterfaceTransmissionCount reads the IFCNT register to check for UART transmission status
func GetInterfaceTransmissionCount(comm RegisterComm, driverIndex uint8) (uint32, error) {
ifcnt := NewIfcnt()
_, err := ReadRegister(comm, driverIndex, ifcnt.GetAddress())
if err != nil {
return 0, err
}
return ifcnt.Bytes, nil
}
-12
View File
@@ -1,12 +0,0 @@
package tmc2209
func MoveAtVelocity(microstepsPerPeriod int32) {
// Write velocity to the relevant register
}
func MoveUsingStepDirInterface() {
// Set the interface to use step/direction interface
}
func GetVelocity() uint32 {
// Read the actual velocity register
return 0 // Placeholder return value
}
-211
View File
@@ -1,211 +0,0 @@
# TMC5160 Driver for Go (TinyGo)
This repository provides a Go-based driver for the **TMC5160** stepper motor driver, implemented for both **SPI** and **UART** communication modes. The driver allows you to easily interface with the TMC5160 to configure and control stepper motors.
## Table of Contents
- [Installation](#installation)
- [Communication Modes](#communication-modes)
- [SPI Mode](#spi-mode)
- [UART Mode](#uart-mode)
- [Usage Example](#usage-example)
- [Setting and Getting Modes](#setting-and-getting-modes)
- [Reading and Writing Registers](#reading-and-writing-registers)
- [API Reference](#api-reference)
- [License](#license)
## Installation
To use the TMC5160 driver, you'll need to have **TinyGo** installed. You can install TinyGo by following the [official installation guide](https://tinygo.org/getting-started/).
### Dependencies
- **machine**: To interface with hardware on platforms like Raspberry Pi, STM32, etc.
- **TinyGo**: A Go compiler for embedded systems.
Add the module
```bash
import "tinygo.org/x/drivers/tmc5160"
```
### Communication Modes
The TMC5160 supports two communication modes for controlling the motor:
**SPI Mode**
To communicate with the TMC5160 in SPI mode, you'll need to configure the SPI bus and the chip-select (CS) pin. This allows full-speed communication between your microcontroller and the TMC5160.
SPI Setup
In SPI mode, you must configure the SPI interface on your microcontroller. Here's how to set up SPI communication for the TMC5160.
```go
spi := machine.SPI1
csPin := machine.GPIO13
spi.Configure(machine.SPIConfig{
SCK: machine.GPIO10,
SDI: machine.GPIO11,
SDO: machine.GPIO12,
Frequency: 5000000,
Mode: 3,
LSBFirst: false,
})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
```
**Sending Commands via SPI**
The driver supports reading and writing registers using the SPIComm interface, which is initialized with the configured SPI bus and CS pins
```go
comm := tmc5160.NewSPIComm(*spi, csPins)
driver := tmc5160.NewTMC5160(comm, driverIndex)
driver.WriteRegister(tmc5160.GCONF, value)
```
**UART Mode**
Alternatively, you can use UART mode to communicate with the TMC5160. UART mode is useful for cases where SPI is not available or when the TMC5160 is used in multi-driver configurations with limited SPI pins.
UART Setup
In UART mode, you will need to configure the UART interface with the appropriate baud rate and settings:
```go
uart := machine.UART0
uart.Configure(machine.UARTConfig{
BaudRate: 115200,
})
```
#### Sending Commands via UART
The UART communication is handled through the UARTComm struct, which wraps the UART interface.
```go
comm := tmc5160.NewUARTComm(uart, 0x01)
driver := tmc5160.NewTMC5160(comm, 0)
driver.WriteRegister(tmc5160.GCONF, 0x01)
```
## Usage Example
Heres a simple example of how to use the TMC5160 driver with SPI and UART modes:
```aiignore
// Connects to SPI1 on a RP2040 (Pico)
package main
import (
"machine"
"tinygo.org/x/drivers/tmc5160"
)
func main() {
// Step 1. Setup your protocol. SPI setup shown below
spi := machine.SPI1
spi.Configure(machine.SPIConfig{
SCK: machine.GPIO10,
SDI: machine.GPIO11,
SDO: machine.GPIO12,
Frequency: 12000000, // Upto 12 MHZ is pretty stable. Reduce to 5 or 6 Mhz if you are experiencing issues
Mode: 3,
LSBFirst: false,
})
// Step 2. Set up all associated Pins
csPin0 := machine.GPIO13
csPin0.Configure(machine.PinConfig{Mode: machine.PinOutput})
enn0 := machine.GPIO18
enn0.Configure(machine.PinConfig{Mode: machine.PinOutput})
// csPins is a map of all chip select pins in a multi driver setup.
//Only one pin csPin0 mapped to "0"is shown in this example, but add more mappings as required
csPins := map[uint8]machine.Pin{0: csPin0}
//bind csPin to driverAdddress
driverAddress := uint8(0) // Let's assume we are working with driver at address 0x01
// Step 3. Bind the communication interface to the protocol
comm := tmc5160.NewSPIComm(*spi, csPins)
// Step 4. Define your stepper like this below
//stepper := tmc5160.NewStepper(angle , gearRatio vSupply rCoil , lCoil , iPeak , rSense , mSteps, fclk )
stepper := tmc5160.NewDefaultStepper() // Default Stepper should be used only for testing.
// Step 5. Instantiate your driver
driver := tmc5160.NewDriver(
comm,
driverAddress,
enn0,
stepper)
// Setting and getting mode
rampMode := tmc5160.NewRAMPMODE(comm, driverAddress)
err := rampMode.SetMode(tmc5160.PositioningMode)
if err != nil {
return
}
mode, err := rampMode.GetMode()
if err != nil {
println("Error getting mode:", err)
} else {
println("Current Mode:", mode)
}
// Read GCONF register
GCONF := tmc5160.NewGCONF()
gconfVal, err := driver.ReadRegister(tmc5160.GCONF)
// Uppack the register to get all the bits and bytes of the register
GCONF.Unpack(gconfVal)
//E.g. MultiStepFlit is retrieved from the GCONF register
println("GCONF:MultiStepFlit:", GCONF.MultistepFilt)
}
```
## Reading and Writing Registers
You can easily read and write registers using the WriteRegister and ReadRegister methods:
```aiignore
// Write a value to a register
err := driver.WriteRegister(tmc5160.GCONF, 0x01)
if err != nil {
fmt.Println("Error writing register:", err)
}
// Read a register
value, err := driver.ReadRegister(tmc5160.GCONF)
if err != nil {
fmt.Println("Error reading register:", err)
} else {
fmt.Println("Read value from GCONF:", value)
}
```
## API Reference
NewSPIComm(spi *machine.SPI, csPins map[uint8]machine.Pin) *SPIComm
Creates a new SPI communication interface for the TMC5160.
NewUARTComm(uart machine.UART, address uint8) *UARTComm
Creates a new UART communication interface for the TMC5160.
NewTMC5160(comm RegisterComm, address uint8) *TMC5160
Creates a new instance of the TMC5160 driver.
WriteRegister(register uint8, value uint32) error
Writes a value to the specified register.
ReadRegister(register uint8) (uint32, error)
Reads a value from the specified register.
## License
This project is licensed under the MIT License
-82
View File
@@ -1,82 +0,0 @@
package tmc5160
// Driver Register addresses
const (
GCONF uint8 = 0x00 // Global configuration flags
GSTAT uint8 = 0x01 // Global status flags
IFCNT = 0x02 // UART transmission counter
SLAVECONF = 0x03 // UART slave configuration
IOIN = 0x04 // Read input / write output pins
X_COMPARE = 0x05 // Position comparison register
OTP_PROG = 0x06 // OTP programming register
OTP_READ = 0x07 // OTP read register
FACTORY_CONF = 0x08 // Factory configuration (clock trim)
SHORT_CONF = 0x09 // Short detector configuration
DRV_CONF = 0x0A // Driver configuration
GLOBAL_SCALER = 0x0B // Global scaling of motor current
OFFSET_READ = 0x0C // Offset calibration results
/* Velocity dependent driver feature control registers */
IHOLD_IRUN = 0x10 // Driver current control
TPOWERDOWN = 0x11 // Delay before power down
TSTEP = 0x12 // Actual time between microsteps
TPWMTHRS = 0x13 // Upper velocity for stealthChop voltage PWM mode
TCOOLTHRS = 0x14 // Lower threshold velocity for switching on smart energy coolStep and stallGuard feature
THIGH = 0x15 // Velocity threshold for switching into a different chopper mode and fullstepping
/* Ramp generator motion control registers */
RAMPMODE = 0x20 // Driving mode (Velocity, Positioning, Hold)
XACTUAL = 0x21 // Actual motor position
VACTUAL = 0x22 // Actual motor velocity from ramp generator
VSTART = 0x23 // Motor start velocity
A_1 = 0x24 // First acceleration between VSTART and V1
V_1 = 0x25 // First acceleration/deceleration phase target velocity
AMAX = 0x26 // Second acceleration between V1 and VMAX
VMAX = 0x27 // Target velocity in velocity mode
DMAX = 0x28 // Deceleration between VMAX and V1
D_1 = 0x2A // Deceleration between V1 and VSTOP
//Attention: Do not set 0 in positioning mode, even if V1=0!
VSTOP = 0x2B // Motor stop velocity
//Attention: Set VSTOP > VSTART!
//Attention: Do not set 0 in positioning mode, minimum 10 recommend!
TZEROWAIT = 0x2C // Waiting time after ramping down to zero velocity before next movement or direction inversion can start.
XTARGET = 0x2D // Target position for ramp mode
/* Ramp generator driver feature control registers */
VDCMIN = 0x33 // Velocity threshold for enabling automatic commutation dcStep
SW_MODE = 0x34 // Switch mode configuration
RAMP_STAT = 0x35 // Ramp status and switch event status
XLATCH = 0x36 // Ramp generator latch position upon programmable switch event
/* Encoder registers */
ENCMODE = 0x38 // Encoder configuration and use of N channel
X_ENC = 0x39 // Actual encoder position
ENC_CONST = 0x3A // Accumulation constant
ENC_STATUS = 0x3B // Encoder status information
ENC_LATCH = 0x3C // Encoder position latched on N event
ENC_DEVIATION = 0x3D // Maximum number of steps deviation between encoder counter and XACTUAL for deviation warning
/* Motor driver registers */
MSLUT0 uint8 = 0x60 // 32 bits
MSLUT1 uint8 = 0x61 // 32 bits
MSLUT2 uint8 = 0x62 // 32 bits
MSLUT3 uint8 = 0x63 // 32 bits
MSLUT4 uint8 = 0x64 // 32 bits
MSLUT5 uint8 = 0x65 // 32 bits
MSLUT6 uint8 = 0x66 // 32 bits
MSLUT7 uint8 = 0x67 // 32 bits
MSLUTSEL = 0x68 // Look up table segmentation definition
MSLUTSTART = 0x69 // Absolute current at microstep table entries 0 and 256
MSCNT = 0x6A // Actual position in the microstep table
MSCURACT = 0x6B // Actual microstep current
CHOPCONF = 0x6C // Chopper and driver configuration
COOLCONF = 0x6D // coolStep smart current control register and stallGuard2 configuration
DCCTRL = 0x6E // dcStep automatic commutation configuration register
DRV_STATUS = 0x6F // stallGuard2 value and driver error flags
PWMCONF = 0x70 // stealthChop voltage PWM mode chopper configuration
PWM_SCALE = 0x71 // Results of stealthChop amplitude regulator.
PWM_AUTO = 0x72 // Automatically determined PWM config values
LOST_STEPS = 0x73 // Number of input steps skipped due to dcStep. only with SD_MODE = 1
expectedVersion = 0x03
DEFAULT_F_CLK = 12000000
)
-56
View File
@@ -1,56 +0,0 @@
package tmc5160
import (
"github.com/orsinium-labs/tinymath"
"golang.org/x/exp/constraints"
)
// VelocityToVMAX calculates the VMAX register value from the current stepper velocity which is in microsteps per tRef (i.e 1/clock speed)
func (stepper *Stepper) CurrentVelocityToVMAX() uint32 {
tref := float32(16777216) / (float32(stepper.Fclk) * 1000000)
r := stepper.VelocitySPS * stepper.GearRatio * float32(tref)
return constrain(uint32(r), 0, maxVMAX) // VMAX register value cannot exceed maxVMAX
}
func (stepper *Stepper) DesiredVelocityToVMAX(v float32) uint32 {
tref := 16777216 / (float32(stepper.Fclk) * 1000000)
r := tinymath.Round(v * stepper.GearRatio * tref)
return constrain(uint32(r), 0, maxVMAX) // VMAX register value cannot exceed maxVMAX
}
func (stepper *Stepper) DesiredAccelToAMAX(dacc float32, dVel float32) uint32 {
dVelToVMAX := stepper.DesiredVelocityToVMAX(dVel)
_a := uint64(dVelToVMAX) * 131072
_b := float32(_a) / dacc
_c := _b / float32(uint32(stepper.Fclk)*1000000)
return uint32(_c)
}
// Convert threshold speed (Hz) to internal TSTEP value
func (stepper *Stepper) DesiredSpeedToTSTEP(thrsSpeed uint32) uint32 {
if thrsSpeed < 0 {
return 0
}
_a := stepper.DesiredVelocityToVMAX(float32(thrsSpeed))
_b := float32(16777216 / _a)
_c := float32(stepper.MSteps) / float32(256)
_d := uint32(_b * _c)
return constrain(_d, 0, 1048575)
}
func (stepper *Stepper) VMAXToTSTEP(vmax uint32) uint32 {
_b := float32(16777216 / vmax)
_c := float32(stepper.MSteps) / float32(256)
_d := tinymath.Round(_b * _c)
return constrain(uint32(_d), 0, 1048575)
}
// Constrain function to limit values to a specific range (supports multiple types).
func constrain[T constraints.Ordered](value, min, max T) T {
if value < min {
return min
} else if value > max {
return max
}
return value
}
-2080
View File
File diff suppressed because it is too large Load Diff
-134
View File
@@ -1,134 +0,0 @@
//go:build tinygo
package tmc5160
import (
"machine"
"time"
)
// CustomError is a lightweight error type used for TinyGo compatibility.
type CustomError string
func (e CustomError) Error() string {
return string(e)
}
// SPIComm implements RegisterComm for SPI-based communication
type SPIComm struct {
spi *machine.SPI
CsPins map[uint8]machine.Pin // Map to store CS pin for each Driver by its address
}
// NewSPIComm creates a new SPIComm instance.
func NewSPIComm(spi *machine.SPI, csPins map[uint8]machine.Pin) *SPIComm {
return &SPIComm{
spi: spi,
CsPins: csPins,
}
}
// Setup initializes the SPI communication with the Driver and configures all CS pins.
func (comm *SPIComm) Setup() error {
// Check if SPI is initialized
if comm.spi == nil {
return CustomError("SPI not initialized")
}
// Configure all CS pins (make them output and set them high)
for _, csPin := range comm.CsPins {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.High() // Set all CS pins high initially
}
// Configure the SPI interface with the desired settings
err := comm.spi.Configure(machine.SPIConfig{
LSBFirst: false,
Mode: 3,
})
if err != nil {
return CustomError("Failed to configure SPI")
}
return nil
}
// WriteRegister sends a register write command to the TMC5160.
func (comm *SPIComm) WriteRegister(register uint8, value uint32, driverAddress uint8) error {
// Assert the chip select pin (set CS low to start communication)
csPin, exists := comm.CsPins[driverAddress]
if !exists {
return CustomError("Invalid driver address")
}
csPin.Low()
// Set the register address with WRITE_ACCESS (0x80)
addressWithWriteAccess := register | 0x80
// Send the address and the data to write (split into 4 bytes)
_, err := spiTransfer40(comm.spi, addressWithWriteAccess, value)
if err != nil {
csPin.High()
return CustomError("Failed to write register")
}
// Deassert the chip select pin (set CS high to end communication)
csPin.High()
return nil
}
// ReadRegister sends a register read command to the TMC5160.
func (comm *SPIComm) ReadRegister(register uint8, driverAddress uint8) (uint32, error) {
// Assert the chip select pin (set CS low to start communication)
csPin, exists := comm.CsPins[driverAddress]
if !exists {
return 0, CustomError("Invalid driver address")
}
csPin.Low()
// Step 1: Send a dummy write operation to begin the read sequence
_, err := spiTransfer40(comm.spi, register, 0x00) // Send dummy data
if err != nil {
csPin.High()
return 0, CustomError("Failed to send dummy write")
}
csPin.High()
time.Sleep(176 * time.Nanosecond)
csPin.Low()
// Step 2: Send the register read request again to get the actual value
response, err := spiTransfer40(comm.spi, register, 0x00) // Send again to get actual register data
if err != nil {
csPin.High()
return 0, CustomError("Failed to read register")
}
// Deassert the chip select pin (set CS high to end communication)
csPin.High()
return response, nil
}
func spiTransfer40(spi *machine.SPI, register uint8, txData uint32) (uint32, error) {
// Prepare the 5-byte buffer for transmission (1 byte address + 4 bytes data)
tx := []byte{
register, // Address byte
byte(txData >> 24), // Upper 8 bits of data
byte(txData >> 16), // Middle 8 bits of data
byte(txData >> 8), // Next 8 bits of data
byte(txData), // Lower 8 bits of data
}
rx := make([]byte, 5)
// Perform the SPI transaction
err := spi.Tx(tx, rx)
if err != nil {
return 0, err
}
// Combine the received bytes into a 32-bit response, ignore the address byte
rxData := uint32(rx[1])<<24 | uint32(rx[2])<<16 | uint32(rx[3])<<8 | uint32(rx[4])
return rxData, nil
}
-105
View File
@@ -1,105 +0,0 @@
package tmc5160
const maxVMAX = 8388096
// PowerStageParameters represents the power stage parameters
type PowerStageParameters struct {
drvStrength uint8
bbmTime uint8
bbmClks uint8
}
// MotorParameters represents the motor parameters
type MotorParameters struct {
globalScaler uint16
ihold uint8
irun uint8
iholddelay uint8
pwmGradInitial uint16
pwmOfsInitial uint16
freewheeling uint8
}
// MotorDirection defines motor direction constants
type MotorDirection uint8
const (
Clockwise MotorDirection = iota
CounterClockwise
)
const (
// Common stepper motor angles
StepAngle_1_8 = 1.8
StepAngle_0_9 = 0.9
StepAngle_0_72 = 0.72
StepAngle_1_2 = 1.2
StepAngle_0_48 = 0.48
// Common microstepping options
Step_1 uint8 = 1
Step_2 uint8 = 2
Step_4 uint8 = 4
Step_8 uint8 = 8
Step_16 uint8 = 16
Step_32 uint8 = 32
Step_64 uint8 = 64
Step_128 uint8 = 128
)
const (
DefaultAngle float32 = StepAngle_1_8
DefaultGearRatio float32 = 1.0
DefaultVSupply float32 = 12.0
DefaultRCoil float32 = 1.2
DefaultLCoil float32 = 0.005
DefaultIPeak float32 = 2.0
DefaultRSense float32 = 0.1
DefaultFclk uint8 = 12
DefaultStep_256 = 256
)
type Stepper struct {
Angle float32
GearRatio float32
VelocitySPS float32 // Velocity in Steps per sec
VSupply float32
RCoil float32
LCoil float32
IPeak float32
RSense float32
MSteps uint8
Fclk uint8 //Clock in Mhz
}
// NewStepper function initializes a Stepper with default values used for testing
func NewDefaultStepper() Stepper {
return Stepper{
Angle: StepAngle_1_8, // Default to 1.8 degrees
GearRatio: 1.0, // Default to no reduction (1:1)
VSupply: 12.0, // Default 12V supply
RCoil: 1.2, // Default coil resistance (1.2 ohms)
LCoil: 0.005, // Default coil inductance (5 mH)
IPeak: 2.0, // Default peak current (2A)
RSense: 0.1, // Default sense resistance (0.1 ohms)
MSteps: Step_16, // Default 16 Microsteps
Fclk: DefaultFclk,
}
}
// NewStepper initializes a Stepper with user-defined values
func NewStepper(angle float32, gearRatio, vSupply, rCoil, lCoil, iPeak, rSense float32, mSteps uint8, fclk uint8) Stepper {
return Stepper{
Angle: angle, // User-defined stepper angle (e.g., StepAngle_1_8)
GearRatio: gearRatio, // User-defined gear ratio
VSupply: vSupply, // User-defined supply voltage
RCoil: rCoil, // User-defined coil resistance
LCoil: lCoil, // User-defined coil inductance
IPeak: iPeak, // User-defined peak current
RSense: rSense, // User-defined sense resistance
MSteps: mSteps, // User-defined microstepping setting
Fclk: fclk, // User-defined clock frequency in MHz
}
}
-194
View File
@@ -1,194 +0,0 @@
//go:build tinygo
package tmc5160
import (
"github.com/orsinium-labs/tinymath"
"machine"
)
type Driver struct {
comm RegisterComm
address uint8
enablePin machine.Pin
stepper Stepper
}
func NewDriver(comm RegisterComm, address uint8, enablePin machine.Pin, stepper Stepper) *Driver {
return &Driver{
comm: comm,
address: address,
enablePin: enablePin,
stepper: stepper,
}
}
// WriteRegister sends a register write command to the Driver.
func (driver *Driver) WriteRegister(reg uint8, value uint32) error {
if driver.comm == nil {
return CustomError("communication interface not set")
}
// Use the communication interface (RegisterComm) to write the register
return driver.comm.WriteRegister(reg, value, driver.address)
}
// ReadRegister sends a register read command to the Driver and returns the read value.
func (driver *Driver) ReadRegister(reg uint8) (uint32, error) {
if driver.comm == nil {
return 0, CustomError("communication interface not set")
}
// Use the communication interface (RegisterComm) to read the register
return driver.comm.ReadRegister(reg, driver.address)
}
// Begin initializes the Driver driver with power and motor parameters
func (driver *Driver) Begin(powerParams PowerStageParameters, motorParams MotorParameters, stepperDirection MotorDirection) bool {
// Clear the reset and charge pump undervoltage flags
gstat := NewGSTAT()
gstat.Reset = true
gstat.UvCp = true
err := driver.WriteRegister(GSTAT, gstat.Pack())
if err != nil {
return false
}
// Configure driver settings
drvConf := NewDRV_CONF()
drvConf.DrvStrength = constrain(powerParams.drvStrength, 0, 3)
drvConf.BBMTime = constrain(powerParams.bbmTime, 0, 24)
drvConf.BBMClks = constrain(powerParams.bbmClks, 0, 15)
err = driver.WriteRegister(DRV_CONF, drvConf.Pack())
if err != nil {
return false
}
// Set global scaler
err = driver.WriteRegister(GLOBAL_SCALER, uint32(constrain(motorParams.globalScaler, 32, 256)))
if err != nil {
return false
}
// Set initial currents and delay
iholdrun := NewIHOLD_IRUN()
iholdrun.Ihold = constrain(motorParams.ihold, 0, 31)
iholdrun.Ihold = constrain(motorParams.irun, 0, 31)
iholdrun.IholdDelay = 7
err = driver.WriteRegister(IHOLD_IRUN, iholdrun.Pack())
if err != nil {
return false
}
// Set PWM configuration values
pwmconf := NewPWMCONF()
err = driver.WriteRegister(PWMCONF, 0xC40C001E)
if err != nil {
return false
} // Reset default value pwm_ofs = 196,pwm_grad = 12,pwm_freq = 0, pwm_autoscale = false, pwm_autograd = false,freewheel = 3
pwmconf.PwmAutoscale = false // Temporarily set to false for setting OFS and GRAD values
_fclk := int(driver.stepper.Fclk) * 1000000
if _fclk > DEFAULT_F_CLK {
pwmconf.PwmFreq = 0
} else {
pwmconf.PwmFreq = 0b01 // Recommended: 35kHz with internal 12MHz clock
}
pwmconf.PwmGrad = uint8(motorParams.pwmGradInitial)
pwmconf.PwmOfs = uint8(motorParams.pwmOfsInitial)
pwmconf.Freewheel = motorParams.freewheeling
err = driver.WriteRegister(PWMCONF, pwmconf.Pack())
if err != nil {
return false
}
// Enable PWM auto-scaling and gradient adjustment
pwmconf.PwmAutoscale = true
pwmconf.PwmAutograd = true
err = driver.WriteRegister(PWMCONF, pwmconf.Pack())
if err != nil {
return false
}
// Recommended chop configuration settings
_chopConf := NewCHOPCONF()
_chopConf.Toff = 5
_chopConf.Tbl = 2
_chopConf.HstrtTfd = 4
_chopConf.HendOffset = 0
err = driver.WriteRegister(CHOPCONF, _chopConf.Pack())
if err != nil {
return false
}
rampMode := NewRAMPMODE(driver.comm, driver.address)
rampMode.SetMode(PositioningMode)
gconf := NewGCONF()
gconf.EnPwmMode = true // Enable stealthChop PWM mode
gconf.Shaft = stepperDirection == Clockwise
err = driver.WriteRegister(GCONF, gconf.Pack())
if err != nil {
return false
}
// Set default start, stop, threshold speeds
driver.setRampSpeeds(0.0, 0.1, 0.0) // Start, stop, threshold speeds
// Set default D1 (must not be = 0 in positioning mode even with V1=0)
err = driver.WriteRegister(D_1, 100)
if err != nil {
return false
}
return false
}
func (driver *Driver) setRampSpeeds(startSpeed float32, stopSpeed float32, transitionSpeed float32) {
str := driver.stepper.DesiredSpeedToTSTEP(uint32(startSpeed))
stp := driver.stepper.DesiredSpeedToTSTEP(uint32(stopSpeed))
ts := driver.stepper.DesiredSpeedToTSTEP(uint32(transitionSpeed))
driver.WriteRegister(VSTART, uint32(tinymath.Min(0x3FFFF, float32(str))))
driver.WriteRegister(VSTOP, uint32(tinymath.Min(0x3FFFF, float32(stp))))
driver.WriteRegister(V_1, uint32(tinymath.Min(0xFFFFF, float32(ts))))
println("Ramp set to: startSpeed:", startSpeed, "stopSpeed:", stopSpeed, "transitionSpeed:", transitionSpeed)
}
// setMaxSpeed sets the maximum speed to 0 (placeholder function)
func setMaxSpeed(speed uint32) {
// This is a placeholder function that sets the speed
// Implement the actual logic to set the maximum speed register value
println("Max Speed set to:", speed)
}
// Dump_TMC reads multiple registers from the Driver and logs their values with their names.
func (driver *Driver) Dump_TMC() error {
registers := []uint8{
GCONF, CHOPCONF, GSTAT, DRV_STATUS, FACTORY_CONF, IOIN, LOST_STEPS, MSCNT,
MSCURACT, OTP_READ, PWM_SCALE, PWM_AUTO, TSTEP,
}
registerNames := map[uint8]string{
GCONF: "GCONF",
CHOPCONF: "CHOPCONF",
GSTAT: "GSTAT",
DRV_STATUS: "DRV_STATUS",
FACTORY_CONF: "FACTORY_CONF",
IOIN: "IOIN",
LOST_STEPS: "LOST_STEPS",
MSCNT: "MSCNT",
MSCURACT: "MSCURACT",
OTP_READ: "OTP_READ",
PWM_SCALE: "PWM_SCALE",
PWM_AUTO: "PWM_AUTO",
TSTEP: "TSTEP",
}
for _, reg := range registers {
// Fetch the register name from the map
regName, exists := registerNames[reg]
if !exists {
regName = "Unknown Register"
}
val, err := driver.ReadRegister(reg)
if err != nil {
println("Error reading register", regName, err)
return err
}
println("Register", regName, "Value:", val)
}
return nil
}
-101
View File
@@ -1,101 +0,0 @@
//go:build uart
package tmc5160
import (
"machine"
"time"
)
// UARTComm implements RegisterComm for UART-based communication with Driver.
type UARTComm struct {
uart machine.UART
address uint8
}
// NewUARTComm creates a new UARTComm instance.
func NewUARTComm(uart machine.UART, address uint8) *UARTComm {
return &UARTComm{
uart: uart,
address: address,
}
}
// Setup initializes the UART communication with the Driver.
func (comm *UARTComm) Setup() error {
if comm.uart == (machine.UART{}) {
return CustomError("UART not initialized")
}
err := comm.uart.Configure(machine.UARTConfig{
BaudRate: 115200,
})
if err != nil {
return CustomError("Failed to configure UART")
}
return nil
}
// WriteRegister sends a register write command to the Driver.
// Prepare the data packet (sync byte + address + register + data + checksum)
func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex uint8) error {
buffer := []byte{
0x05, // Sync byte
comm.address, // Slave address
register | 0x80, // Write command (MSB set to 1 for write)
byte((value >> 24) & 0xFF), // MSB of value
byte((value >> 16) & 0xFF), // Middle byte
byte((value >> 8) & 0xFF), // Next byte
byte(value & 0xFF), // LSB of value
}
checksum := byte(0)
for _, b := range buffer[:7] {
checksum ^= b
}
buffer[7] = checksum // Set checksum byte
// Write the data to the Driver
done := make(chan error, 1)
go func() {
comm.uart.Write(buffer)
done <- nil
}()
select {
case err := <-done:
return err
case <-time.After(100 * time.Millisecond): // Timeout after 100ms
return CustomError("write timeout")
}
}
// ReadRegister sends a register read command to the Driver.
func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, error) {
// Prepare the read command (sync byte + address + register + checksum)
var writeBuffer [4]byte
writeBuffer[0] = 0x05 // Sync byte
writeBuffer[1] = comm.address // Slave address
writeBuffer[2] = register & 0x7F // Read command (MSB clear for read)
writeBuffer[3] = writeBuffer[0] ^ writeBuffer[1] ^ writeBuffer[2] // Checksum
done := make(chan []byte, 1)
go func() {
comm.uart.Write(writeBuffer[:])
readBuffer := make([]byte, 8) // Prepare the buffer to read 8 bytes
comm.uart.Read(readBuffer)
done <- readBuffer
}()
select {
case readBuffer := <-done:
checksum := byte(0)
for i := 0; i < 7; i++ {
checksum ^= readBuffer[i]
}
if checksum != readBuffer[7] {
return 0, CustomError("checksum error")
}
return uint32(readBuffer[3])<<24 | uint32(readBuffer[4])<<16 | uint32(readBuffer[5])<<8 | uint32(readBuffer[6]), nil
case <-time.After(100 * time.Millisecond): // Timeout after 100ms
return 0, CustomError("read timeout")
}
}
-13
View File
@@ -1,13 +0,0 @@
package tmc5160
func ToHex(value uint32) string {
hexChars := "0123456789ABCDEF"
result := ""
for i := 0; i < 8; i++ { // 8 nibbles for a 32-bit number
nibble := (value >> (28 - i*4)) & 0xF
result += string(hexChars[nibble])
}
return "0x" + result
}
+17 -18
View File
@@ -8,11 +8,10 @@ package uc8151 // import "tinygo.org/x/drivers/uc8151"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
"tinygo.org/x/drivers/pixel"
)
@@ -32,10 +31,10 @@ type Config struct {
type Device struct {
bus drivers.SPI
cs drivers.PinOutput
dc drivers.PinOutput
rst drivers.PinOutput
isBusy drivers.PinInput
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
width int16
height int16
buffer []uint8
@@ -50,17 +49,17 @@ type Device struct {
type Speed uint8
// New returns a new uc8151 driver. Pass in a fully configured SPI bus.
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
legacy.ConfigurePinOut(csPin)
legacy.ConfigurePinOut(dcPin)
legacy.ConfigurePinOut(rstPin)
legacy.ConfigurePinInput(busyPin)
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
busyPin.Configure(machine.PinConfig{Mode: machine.PinInput})
return Device{
bus: bus,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
}
}
@@ -314,14 +313,14 @@ func (d *Device) ClearDisplay() {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for !d.isBusy() {
for !d.busy.Get() {
time.Sleep(10 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.isBusy()
return d.busy.Get()
}
// ClearBuffer sets the buffer to 0xFF (white)
+1 -1
View File
@@ -2,4 +2,4 @@ package drivers
// Version returns a user-readable string showing the version of the drivers package for support purposes.
// Update this value before release of new version of software.
const Version = "0.30.0"
const Version = "0.28.0"

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