mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-11 02:13:42 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3d0697dbc | |||
| 9b5840e3cd |
+1
-112
@@ -236,83 +236,16 @@ func (d *Device) getMode() (byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) configRate(speed, clock byte) error {
|
func (d *Device) configRate(speed, clock byte) error {
|
||||||
|
// TODO: add another baudrate
|
||||||
var cfg1, cfg2, cfg3 byte
|
var cfg1, cfg2, cfg3 byte
|
||||||
set := true
|
set := true
|
||||||
switch clock {
|
switch clock {
|
||||||
case Clock16MHz:
|
case Clock16MHz:
|
||||||
switch speed {
|
switch speed {
|
||||||
case CAN5kBps:
|
|
||||||
cfg1 = mcp16mHz5kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz5kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz5kBpsCfg3
|
|
||||||
case CAN10kBps:
|
|
||||||
cfg1 = mcp16mHz10kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz10kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz10kBpsCfg3
|
|
||||||
case CAN20kBps:
|
|
||||||
cfg1 = mcp16mHz20kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz20kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz20kBpsCfg3
|
|
||||||
case CAN25kBps:
|
|
||||||
cfg1 = mcp16mHz25kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz25kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz25kBpsCfg3
|
|
||||||
case CAN31k25Bps:
|
|
||||||
cfg1 = mcp16mHz31k25BpsCfg1
|
|
||||||
cfg2 = mcp16mHz31k25BpsCfg2
|
|
||||||
cfg3 = mcp16mHz31k25BpsCfg3
|
|
||||||
case CAN33kBps:
|
|
||||||
cfg1 = mcp16mHz33kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz33kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz33kBpsCfg3
|
|
||||||
case CAN40kBps:
|
|
||||||
cfg1 = mcp16mHz40kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz40kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz40kBpsCfg3
|
|
||||||
case CAN47kBps:
|
|
||||||
cfg1 = mcp16mHz47kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz47kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz47kBpsCfg3
|
|
||||||
case CAN50kBps:
|
|
||||||
cfg1 = mcp16mHz50kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz50kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz50kBpsCfg3
|
|
||||||
case CAN80kBps:
|
|
||||||
cfg1 = mcp16mHz80kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz80kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz80kBpsCfg3
|
|
||||||
case CAN83k3Bps:
|
|
||||||
cfg1 = mcp16mHz83k3BpsCfg1
|
|
||||||
cfg2 = mcp16mHz83k3BpsCfg2
|
|
||||||
cfg3 = mcp16mHz83k3BpsCfg3
|
|
||||||
case CAN95kBps:
|
|
||||||
cfg1 = mcp16mHz95kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz95kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz95kBpsCfg3
|
|
||||||
case CAN100kBps:
|
|
||||||
cfg1 = mcp16mHz100kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz100kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz100kBpsCfg3
|
|
||||||
case CAN125kBps:
|
|
||||||
cfg1 = mcp16mHz125kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz125kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz125kBpsCfg3
|
|
||||||
case CAN200kBps:
|
|
||||||
cfg1 = mcp16mHz200kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz200kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz200kBpsCfg3
|
|
||||||
case CAN250kBps:
|
|
||||||
cfg1 = mcp16mHz250kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz250kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz250kBpsCfg3
|
|
||||||
case CAN500kBps:
|
case CAN500kBps:
|
||||||
cfg1 = mcp16mHz500kBpsCfg1
|
cfg1 = mcp16mHz500kBpsCfg1
|
||||||
cfg2 = mcp16mHz500kBpsCfg2
|
cfg2 = mcp16mHz500kBpsCfg2
|
||||||
cfg3 = mcp16mHz500kBpsCfg3
|
cfg3 = mcp16mHz500kBpsCfg3
|
||||||
case CAN666kBps:
|
|
||||||
cfg1 = mcp16mHz666kBpsCfg1
|
|
||||||
cfg2 = mcp16mHz666kBpsCfg2
|
|
||||||
cfg3 = mcp16mHz666kBpsCfg3
|
|
||||||
case CAN1000kBps:
|
case CAN1000kBps:
|
||||||
cfg1 = mcp16mHz1000kBpsCfg1
|
cfg1 = mcp16mHz1000kBpsCfg1
|
||||||
cfg2 = mcp16mHz1000kBpsCfg2
|
cfg2 = mcp16mHz1000kBpsCfg2
|
||||||
@@ -322,50 +255,6 @@ func (d *Device) configRate(speed, clock byte) error {
|
|||||||
}
|
}
|
||||||
case Clock8MHz:
|
case Clock8MHz:
|
||||||
switch speed {
|
switch speed {
|
||||||
case CAN5kBps:
|
|
||||||
cfg1 = mcp8mHz5kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz5kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz5kBpsCfg3
|
|
||||||
case CAN10kBps:
|
|
||||||
cfg1 = mcp8mHz10kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz10kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz10kBpsCfg3
|
|
||||||
case CAN20kBps:
|
|
||||||
cfg1 = mcp8mHz20kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz20kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz20kBpsCfg3
|
|
||||||
case CAN31k25Bps:
|
|
||||||
cfg1 = mcp8mHz31k25BpsCfg1
|
|
||||||
cfg2 = mcp8mHz31k25BpsCfg2
|
|
||||||
cfg3 = mcp8mHz31k25BpsCfg3
|
|
||||||
case CAN40kBps:
|
|
||||||
cfg1 = mcp8mHz40kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz40kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz40kBpsCfg3
|
|
||||||
case CAN50kBps:
|
|
||||||
cfg1 = mcp8mHz50kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz50kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz50kBpsCfg3
|
|
||||||
case CAN80kBps:
|
|
||||||
cfg1 = mcp8mHz80kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz80kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz80kBpsCfg3
|
|
||||||
case CAN100kBps:
|
|
||||||
cfg1 = mcp8mHz100kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz100kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz100kBpsCfg3
|
|
||||||
case CAN125kBps:
|
|
||||||
cfg1 = mcp8mHz125kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz125kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz125kBpsCfg3
|
|
||||||
case CAN200kBps:
|
|
||||||
cfg1 = mcp8mHz200kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz200kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz200kBpsCfg3
|
|
||||||
case CAN250kBps:
|
|
||||||
cfg1 = mcp8mHz250kBpsCfg1
|
|
||||||
cfg2 = mcp8mHz250kBpsCfg2
|
|
||||||
cfg3 = mcp8mHz250kBpsCfg3
|
|
||||||
case CAN500kBps:
|
case CAN500kBps:
|
||||||
cfg1 = mcp8mHz500kBpsCfg1
|
cfg1 = mcp8mHz500kBpsCfg1
|
||||||
cfg2 = mcp8mHz500kBpsCfg2
|
cfg2 = mcp8mHz500kBpsCfg2
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
package netif
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AutoProbe(timeout time.Duration) (Stack, error) {
|
|
||||||
// This function automatically gets the first available network device
|
|
||||||
// and returns a stack for it.
|
|
||||||
// It is intended to be used by the user as a convenience function.
|
|
||||||
// Will be guarded by build tags and specific for whether the device
|
|
||||||
// is a StackWifi, InterfaceEthPollWifi or InterfaceEthPoller.
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProbeStackWifi() StackWifi {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func ProbeEthPollWifi() InterfaceEthPollWifi {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func ProbeEthPoller() InterfaceEthPoller {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func StackForEthPoll(dev InterfaceEthPoller) Stack {
|
|
||||||
// Use seqs to generate a stack.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OSI layer 4 enabled WIFI chip. i.e.: ESP32
|
|
||||||
func ExampleProbeStackWifi() {
|
|
||||||
dev := ProbeStackWifi()
|
|
||||||
wifiparams := WifiParams{
|
|
||||||
SSID: "myssid",
|
|
||||||
ConnectMode: ConnectModeSTA,
|
|
||||||
Passphrase: "mypassphrase",
|
|
||||||
Auth: AuthTypeWPA2,
|
|
||||||
CountryCode: "US",
|
|
||||||
}
|
|
||||||
err := StartWifiAutoconnect(dev, WifiAutoconnectParams{
|
|
||||||
WifiParams: wifiparams,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for dev.NetFlags()&net.FlagRunning == 0 {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
UseStack(dev)
|
|
||||||
net.Dial("tcp", "192.168.1.1:33")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplest case, OSI layer 2 enabled WIFI chip, i.e: CYW43439
|
|
||||||
func ExampleProbeEthPollWifi() {
|
|
||||||
dev := ProbeEthPollWifi()
|
|
||||||
wifiparams := WifiParams{
|
|
||||||
SSID: "myssid",
|
|
||||||
ConnectMode: ConnectModeSTA,
|
|
||||||
Passphrase: "mypassphrase",
|
|
||||||
Auth: AuthTypeWPA2,
|
|
||||||
CountryCode: "US",
|
|
||||||
}
|
|
||||||
err := StartWifiAutoconnect(dev, WifiAutoconnectParams{
|
|
||||||
WifiParams: wifiparams,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for dev.NetFlags()&net.FlagRunning == 0 {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
}
|
|
||||||
stack := StackForEthPoll(dev)
|
|
||||||
UseStack(stack)
|
|
||||||
net.Dial("tcp", "192.168.1.1:33")
|
|
||||||
}
|
|
||||||
|
|
||||||
// OSI level 2 chip with wired connection, i.e. ENC28J60.
|
|
||||||
func ExampleProbeEthPoller() {
|
|
||||||
dev := ProbeEthPoller()
|
|
||||||
if dev.NetFlags()&net.FlagRunning == 0 {
|
|
||||||
panic("ethernet not connected")
|
|
||||||
}
|
|
||||||
stack := StackForEthPoll(dev)
|
|
||||||
UseStack(stack)
|
|
||||||
net.Dial("tcp", "192.168.1.1:33")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleAutoProbe() {
|
|
||||||
stack, err := AutoProbe(10 * time.Second)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
UseStack(stack)
|
|
||||||
net.Dial("tcp", "192.168.1.1:33")
|
|
||||||
}
|
|
||||||
-211
@@ -1,211 +0,0 @@
|
|||||||
package netif
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"time"
|
|
||||||
_ "unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:linkname UseStack net.useNetdev
|
|
||||||
func UseStack(stack Stack)
|
|
||||||
|
|
||||||
// Socket errors.
|
|
||||||
var (
|
|
||||||
ErrProtocolNotSupported = errors.New("socket protocol/type not supported")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wifi errors.
|
|
||||||
var (
|
|
||||||
ErrAuthFailure = errors.New("wifi authentication failure")
|
|
||||||
ErrAuthUnsupported = errors.New("wifi authorization type not supported")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
_AF_INET = 0x2
|
|
||||||
_SOCK_STREAM = 0x1
|
|
||||||
_SOCK_DGRAM = 0x2
|
|
||||||
_SOL_SOCKET = 0x1
|
|
||||||
_SO_KEEPALIVE = 0x9
|
|
||||||
_SOL_TCP = 0x6
|
|
||||||
_TCP_KEEPINTVL = 0x5
|
|
||||||
_IPPROTO_TCP = 0x6
|
|
||||||
_IPPROTO_UDP = 0x11
|
|
||||||
// Made up, not a real IP protocol number. This is used to create a
|
|
||||||
// TLS socket on the device, assuming the device supports mbed TLS.
|
|
||||||
_IPPROTO_TLS = 0xFE
|
|
||||||
_F_SETFL = 0x4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Interface is the minimum interface that need be implemented by any network
|
|
||||||
// device driver and is based on [net.Interface].
|
|
||||||
type Interface interface {
|
|
||||||
// HardwareAddr6 returns the device's 6-byte [MAC address].
|
|
||||||
//
|
|
||||||
// [MAC address]: https://en.wikipedia.org/wiki/MAC_address
|
|
||||||
HardwareAddr6() ([6]byte, error)
|
|
||||||
// NetFlags returns the net.Flag values for the interface. It includes state of connection.
|
|
||||||
NetFlags() net.Flags
|
|
||||||
// MTU returns the maximum transmission unit size.
|
|
||||||
MTU() int
|
|
||||||
// Notify to register callback for network events. May not be supported for certain devices.
|
|
||||||
NetNotify(cb func(Event)) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// InterfaceEthPoller is implemented by devices that send/receive ethernet packets.
|
|
||||||
type InterfaceEthPoller interface {
|
|
||||||
Interface
|
|
||||||
// SendEth sends an Ethernet packet
|
|
||||||
SendEth(pkt []byte) error
|
|
||||||
// RecvEthHandle sets recieve Ethernet packet callback function
|
|
||||||
RecvEthHandle(func(pkt []byte) error)
|
|
||||||
// PollOne tries to receive one Ethernet packet and returns true if one was
|
|
||||||
PollOne() (bool, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InterfaceWifi is implemented by a interface device that has the capacity
|
|
||||||
// to connect to wifi networks.
|
|
||||||
type InterfaceWifi interface {
|
|
||||||
Interface
|
|
||||||
// Connect device to network
|
|
||||||
NetConnect(params WifiParams) error
|
|
||||||
// Disconnect device from network
|
|
||||||
NetDisconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// InterfaceEthPollWifi is implemented by devices that connect to wifi networks
|
|
||||||
// and send/receive ethernet packets (OSI level 2).
|
|
||||||
type InterfaceEthPollWifi interface {
|
|
||||||
InterfaceWifi
|
|
||||||
InterfaceEthPoller
|
|
||||||
}
|
|
||||||
|
|
||||||
type Stack interface {
|
|
||||||
// GetHostByName returns the IP address of either a hostname or IPv4
|
|
||||||
// address in standard dot notation
|
|
||||||
// GetHostByName(name string) (netip.Addr, error)
|
|
||||||
|
|
||||||
// Addr returns IP address assigned to the interface, either by
|
|
||||||
// DHCP or statically
|
|
||||||
Addr() (netip.Addr, error)
|
|
||||||
|
|
||||||
// Berkely Sockets-like interface, Go-ified. See man page for socket(2), etc.
|
|
||||||
Socket(domain int, stype int, protocol int) (int, error)
|
|
||||||
Bind(sockfd int, ip netip.AddrPort) error
|
|
||||||
Connect(sockfd int, host string, ip netip.AddrPort) error
|
|
||||||
Listen(sockfd int, backlog int) error
|
|
||||||
Accept(sockfd int) (int, netip.AddrPort, error)
|
|
||||||
Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
|
||||||
Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error)
|
|
||||||
Close(sockfd int) error
|
|
||||||
SetSockOpt(sockfd int, level int, opt int, value interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolver is implemented by DNS resolvers, notably Go's [net.DefaultResolver].
|
|
||||||
type Resolver interface {
|
|
||||||
// LookupNetIP looks up host using the local resolver.
|
|
||||||
// It returns a slice of that host's IP addresses of the type specified by
|
|
||||||
// network.
|
|
||||||
// The network must be one of "ip", "ip4" or "ip6".
|
|
||||||
LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StackWifi is returned by `Probe` function for devices that communicate
|
|
||||||
// on the OSI level 4 (transport) layer.
|
|
||||||
type StackWifi interface {
|
|
||||||
InterfaceWifi
|
|
||||||
Stack
|
|
||||||
}
|
|
||||||
|
|
||||||
type WifiParams struct {
|
|
||||||
// Connect mode
|
|
||||||
ConnectMode ConnectMode
|
|
||||||
|
|
||||||
// SSID of Wifi AP
|
|
||||||
SSID string
|
|
||||||
|
|
||||||
// Passphrase of Wifi AP
|
|
||||||
Passphrase string
|
|
||||||
|
|
||||||
// Wifi authorization type
|
|
||||||
Auth AuthType
|
|
||||||
|
|
||||||
// Wifi country code as two-char string. E.g. "XX" for world-wide,
|
|
||||||
// "US" for USA, etc.
|
|
||||||
CountryCode string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Event uint8
|
|
||||||
|
|
||||||
// Network events
|
|
||||||
const (
|
|
||||||
// The device's network connection is now UP
|
|
||||||
EventNetUp Event = iota
|
|
||||||
// The device's network connection is now DOWN
|
|
||||||
EventNetDown
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnectMode uint8
|
|
||||||
|
|
||||||
// Connect modes
|
|
||||||
const (
|
|
||||||
ConnectModeSTA = iota // Connect as Wifi station (default)
|
|
||||||
ConnectModeAP // Connect as Wifi Access Point
|
|
||||||
)
|
|
||||||
|
|
||||||
type AuthType uint8
|
|
||||||
|
|
||||||
// Wifi authorization types. Used when setting up an access point, or
|
|
||||||
// connecting to an access point
|
|
||||||
const (
|
|
||||||
AuthTypeWPA2 = iota // WPA2 authorization (default)
|
|
||||||
AuthTypeOpen // No authorization required (open)
|
|
||||||
AuthTypeWPA // WPA authorization
|
|
||||||
AuthTypeWPA2Mixed // WPA2/WPA mixed authorization
|
|
||||||
)
|
|
||||||
|
|
||||||
type WifiAutoconnectParams struct {
|
|
||||||
WifiParams
|
|
||||||
|
|
||||||
// Retries is how many attempts to connect before returning with a
|
|
||||||
// "Connect failed" error. Zero means infinite retries.
|
|
||||||
// Retries int // Probably should be implemented as a function
|
|
||||||
|
|
||||||
// Timeout duration for each connection attempt. The default zero
|
|
||||||
// value means 10sec.
|
|
||||||
ConnectTimeout time.Duration
|
|
||||||
|
|
||||||
// Watchdog ticker duration. On tick, the watchdog will check for
|
|
||||||
// downed connection or hardware fault and try to recover the
|
|
||||||
// connection. Set to zero to disable watchodog.
|
|
||||||
WatchdogTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func StartWifiAutoconnect(dev InterfaceWifi, cfg WifiAutoconnectParams) error {
|
|
||||||
if dev == nil {
|
|
||||||
return errors.New("nil device")
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
// Wifi autoconnect algorithm in one place,
|
|
||||||
// no need to implement for every single netdever.
|
|
||||||
RECONNECT:
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
err := dev.NetConnect(cfg.WifiParams)
|
|
||||||
if err != nil {
|
|
||||||
time.Sleep(cfg.ConnectTimeout)
|
|
||||||
goto RECONNECT
|
|
||||||
}
|
|
||||||
// Once connected reset the retry counter.
|
|
||||||
i = 0
|
|
||||||
for cfg.WatchdogTimeout != 0 {
|
|
||||||
time.Sleep(cfg.WatchdogTimeout)
|
|
||||||
if dev.NetFlags()&net.FlagRunning == 0 {
|
|
||||||
goto RECONNECT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -70,6 +70,54 @@ func (img Image[T]) LimitHeight(height int) Image[T] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split the buffer into two buffers that can be used independently.
|
||||||
|
// The top half is split just like LimitHeight. The bottom half is made out of
|
||||||
|
// the remaining buffer area and can be zero. The topHeight parameter must not
|
||||||
|
// be larger than the height of the buffer.
|
||||||
|
//
|
||||||
|
// Always check the height of the bottom half: it may be zero due to alignment
|
||||||
|
// issues.
|
||||||
|
func (img Image[T]) Split(topHeight int) (top, bottom Image[T]) {
|
||||||
|
if topHeight < 0 || topHeight > int(img.height) {
|
||||||
|
panic("Image.Split: out of bounds")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The top half of the buffer, the same as LimitHeight.
|
||||||
|
top = Image[T]{
|
||||||
|
width: img.width,
|
||||||
|
height: int16(topHeight),
|
||||||
|
data: img.data,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the bottom half of the buffer.
|
||||||
|
// This is a bit more complicated since it's possible that the bottom half
|
||||||
|
// can't have all the other bytes: the top half pixels might cross a byte
|
||||||
|
// boundary (for example with RGB444). So instead we calculate the size of
|
||||||
|
// the buffer we have, the size of the buffer that the top half will use
|
||||||
|
// (which is rounded up to a byte boundary), and then calculate the
|
||||||
|
// remaining bytes at the bottom.
|
||||||
|
// In practice, I expect it's unlikely that the top half will cross a byte
|
||||||
|
// boundary since a typical split buffer will have a width that's a nice
|
||||||
|
// round number, but it's possible so we have to avoid this edge case.
|
||||||
|
var zeroColor T
|
||||||
|
dataBytes := (int(img.width)*int(img.height)*zeroColor.BitsPerPixel() + 7) / 8
|
||||||
|
topDataBytes := (int(img.width)*int(topHeight)*zeroColor.BitsPerPixel() + 7) / 8
|
||||||
|
bottomDataBytes := dataBytes - topDataBytes
|
||||||
|
if bottomDataBytes < 0 {
|
||||||
|
// No buffer remaining (not sure whether this is possible in practice
|
||||||
|
// but guarding just in case).
|
||||||
|
bottomDataBytes = 0
|
||||||
|
}
|
||||||
|
bottomHeight := (bottomDataBytes * 8 / zeroColor.BitsPerPixel()) / int(img.width)
|
||||||
|
bottom = Image[T]{
|
||||||
|
width: img.width,
|
||||||
|
height: int16(bottomHeight),
|
||||||
|
data: unsafe.Add(img.data, topDataBytes),
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Len returns the number of pixels in this image buffer.
|
// Len returns the number of pixels in this image buffer.
|
||||||
func (img Image[T]) Len() int {
|
func (img Image[T]) Len() int {
|
||||||
return int(img.width) * int(img.height)
|
return int(img.width) * int(img.height)
|
||||||
|
|||||||
@@ -11,3 +11,12 @@ type SPI interface {
|
|||||||
// If you want to transfer multiple bytes, it is more efficient to use Tx instead.
|
// If you want to transfer multiple bytes, it is more efficient to use Tx instead.
|
||||||
Transfer(b byte) (byte, error)
|
Transfer(b byte) (byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsyncSPI is a SPI bus that also implements async operations (using DMA,
|
||||||
|
// probably).
|
||||||
|
type AsyncSPI interface {
|
||||||
|
SPI
|
||||||
|
IsAsync() bool
|
||||||
|
StartTx(tx, rx []byte) error
|
||||||
|
Wait() error
|
||||||
|
}
|
||||||
|
|||||||
+48
-3
@@ -45,7 +45,7 @@ type Device = DeviceOf[pixel.RGB565BE]
|
|||||||
// DeviceOf is a generic version of Device. It supports multiple different pixel
|
// DeviceOf is a generic version of Device. It supports multiple different pixel
|
||||||
// formats.
|
// formats.
|
||||||
type DeviceOf[T Color] struct {
|
type DeviceOf[T Color] struct {
|
||||||
bus drivers.SPI
|
bus drivers.AsyncSPI
|
||||||
dcPin machine.Pin
|
dcPin machine.Pin
|
||||||
resetPin machine.Pin
|
resetPin machine.Pin
|
||||||
csPin machine.Pin
|
csPin machine.Pin
|
||||||
@@ -83,13 +83,13 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
||||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
func New(bus drivers.AsyncSPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
|
||||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
|
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
|
||||||
// wire must already be configured.
|
// wire must already be configured.
|
||||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
|
func NewOf[T Color](bus drivers.AsyncSPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
|
||||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
@@ -404,6 +404,51 @@ func (d *DeviceOf[T]) DrawBitmap(x, y int16, bitmap pixel.Image[T]) error {
|
|||||||
return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height))
|
return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAsync returns whether the underlying SPI bus supports async operations.
|
||||||
|
func (d *DeviceOf[T]) IsAsync() bool {
|
||||||
|
return d.bus.IsAsync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartDrawBitmap starts sending the given bitmap to the screen.
|
||||||
|
// After calling StartDrawBitmap, you can only call Wait() or another
|
||||||
|
// StartDrawBitmap. Calling any other method may result in incorrect behavior.
|
||||||
|
// The bitmap passed to StartDrawBitmap may not be written to until Wait() has
|
||||||
|
// been called.
|
||||||
|
func (d *DeviceOf[T]) StartDrawBitmap(x, y int16, bitmap pixel.Image[T]) error {
|
||||||
|
// Check that the provided buffer is drawn entirely inside the image.
|
||||||
|
width, height := bitmap.Size()
|
||||||
|
displayWidth, displayHeight := d.Size()
|
||||||
|
if uint(int(x)+width) > uint(int(displayWidth)) || uint(int(y)+height) > uint(int(displayHeight)) {
|
||||||
|
return errOutOfBounds
|
||||||
|
}
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
return nil // no bitmap to send
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the previous buffer has been fully sent.
|
||||||
|
err := d.bus.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the next buffer.
|
||||||
|
d.startWrite()
|
||||||
|
d.setWindow(x, y, int16(width), int16(height))
|
||||||
|
d.bus.StartTx(bitmap.RawBuffer(), nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all previous transfers have completed. After this call, the bitmap
|
||||||
|
// passed to StartDrawBitmap can be reused.
|
||||||
|
func (d *DeviceOf[T]) Wait() error {
|
||||||
|
err := d.bus.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.endWrite()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FillRectangleWithBuffer fills buffer with a rectangle at a given coordinates.
|
// FillRectangleWithBuffer fills buffer with a rectangle at a given coordinates.
|
||||||
func (d *DeviceOf[T]) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
|
func (d *DeviceOf[T]) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
|
||||||
i, j := d.Size()
|
i, j := d.Size()
|
||||||
|
|||||||
Reference in New Issue
Block a user