Compare commits

...

28 Commits

Author SHA1 Message Date
deadprogram dcf53ac04a revision: modify proposed pin HAL.
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-09-16 20:33:50 +02:00
deadprogram f55c0b1853 fix: correct smoke tests for Adafruit Seesaw
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-09-16 19:16:01 +02:00
JP Hastings-Spital acc0d47b72 feat: add support for seesaw encoders
Adds the necessary function addresses for reading and writing encoders on a seesaw.
Also provides two helper functions to make this easier.
2025-09-16 19:16:01 +02:00
Russel Hunter Yukawa 9e1d8f6998 Fix gps time calculation (#785)
* Change test case to match the date patterns where the bug reproduces
* Fix RMC date and time calculation
2025-09-16 19:16:01 +02:00
Bryan Souza 4d81aa4787 added support for LSM303DLHC e-Compass; (#783)
fixed the spelling in the Connection error message; Initial support for LSM303DLHC added;
Added LSM303DLHC to smoketest and added an example;
Removed unnecessary comments;
fixed format error;
squashed and ready for merge;
2025-09-16 19:16:01 +02:00
Artur Nasyrov 24a9d7dcc9 Add ens160 i2c driver
Driver for ENS160 sensor:
https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
2025-09-16 19:16:01 +02:00
Ayke van Laethem 9717f365d9 ws2812: add RP2350 support
Adding 150MHz support for the RP2350
2025-09-16 19:16:01 +02:00
Yurii Soldak e61269cdc0 ssd1306: avoid unnecessary heap allocations (#767)
* ssd1306: avoid unnecessary heap allocations

* ssd1306: extract i2c and spi bus implementations

* ssd1306: refactor tests -- show fps and heap usage

* ssd1306: bring back the lost exported methods

* Adjust examples

* Fix smoketests for ssd1306
2025-09-16 19:16:01 +02:00
JP Hastings-Spital 6e1e9b75b8 feat: allow gps init with address
Adafruit's Mini GPS PA1010D Module works with this device driver, but requires 0x10 as the address, rather than 0x42.

This change allows the device to be initialised with whatever i2c address is needed, while maintaining backward compatibility.

Adds new constants to allow easy configuration of both the ublox device and the PA1010D.
2025-09-16 19:16:01 +02:00
Yurii Soldak b7be85a96a lsm6ds3tr: avoid unnecessary heap allocations (#766)
* lsm6ds3tr: avoid unnecessary heap allocations
* lsm6ds3tr: use helper functions, for readability
* lsm6ds3tr: return slice of the internal buffer on readBytes
2025-09-16 19:16:01 +02:00
Ron Evans 1d618b4729 Revert "add regmap package to facilitate heapless driver development (#768)" (#776)
This reverts commit 80356fd9d9.
2025-09-16 19:16:01 +02:00
Patricio Whittingslow 40784df446 add regmap package to facilitate heapless driver development (#768) 2025-09-16 19:16:01 +02:00
deadprogram c8788ee4a1 all: updates for drivers release v0.32.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2025-09-16 19:16:01 +02:00
soypat adfc8def7b add driver design pointer to CONTRIBUTING.md 2025-09-16 19:16:01 +02:00
Hikmatulloh Hari Mukti 5b9be516c5 bmp280: remove alloc on read sensor data 2025-09-16 19:16:01 +02:00
Leon Matthews 462a0de10a ws2812: add 200MHz support for the Cortex-M0/rp2040 2025-09-16 19:16:01 +02:00
Mateusz Nowak ad49c0d0ca fix: remove time.Sleep from SSD1306 SPI transfer code 2025-09-16 19:16:01 +02:00
Craig Swank 673367c317 tmc2209 bug fixes (#755)
* Make write buffer big enough for crc
* crc according to datasheet
* fix build
* a correct crc func already exists
2025-09-16 19:16:01 +02:00
Patricio Whittingslow 8b6e8c81e3 rename busy fields to isBusy 2025-09-16 09:49:45 -03:00
Patricio Whittingslow 1f37a1398c missed high or low ~voltage~ level 2025-09-16 09:31:11 -03:00
Patricio Whittingslow 8d3cac8322 apply @gen2thomas suggestions 2025-09-16 09:26:00 -03:00
Patricio Whittingslow 3cd6afb84c pins.go -> pin.go 2025-09-07 17:13:50 -03:00
soypat db544f4ca7 bugfix: output misconfigured 2025-07-09 12:14:14 -03:00
soypat 9fb57e22fb fix pullups not available on fe310 2025-07-09 12:04:37 -03:00
soypat 3681452097 finish rewrites! 2025-07-08 21:11:46 -03:00
soypat 8f0fbaa945 more drivers are now cross platform 2025-07-02 22:03:58 -03:00
soypat f3945b1d58 apply suggestions from team 2025-04-30 15:18:21 -03:00
soypat 06e4c0267f add PinInput and PinOutput HAL 2025-04-08 17:08:08 -03:00
75 changed files with 2811 additions and 837 deletions
+27
View File
@@ -1,3 +1,30 @@
0.32.0
---
- **enhancements**
- **bmp280**
- remove alloc on read sensor data
- **ws2812**
- add 200MHz support for the Cortex-M0/rp2040
- **bugfixes**
- **ssd1306**
- remove time.Sleep from SSD1306 SPI transfer code
- **tmc2209**
- tmc2209 bug fixes (#755)
- **docs**
- **contributing**
- add driver design pointer to CONTRIBUTING.md
0.31.0
---
---
- **enhancements**
- **spi**
- update all SPI usage to use either *machine.SPI or drivers.SPI
0.30.0
---
- **new devices**
+3
View File
@@ -8,6 +8,9 @@ We would like your help to make this project better, so we appreciate any contri
We'd love to get your feedback on getting started with TinyGo. Run into any difficulty, confusion, or anything else? You are not alone. We want to know about your experience, so we can help the next people. Please open a Github issue with your questions, or you can also get in touch directly with us on our Slack channel at [https://gophers.slack.com/messages/CDJD3SUP6](https://gophers.slack.com/messages/CDJD3SUP6).
### Driver design
Before porting or writing a driver from scratch please read **[Driver Design for TinyGo](https://tinygo.org/docs/guides/driver-design)**.
### One of the TinyGo drivers is not working as you expect
Please open a Github issue with your problem, and we will be happy to assist.
+6 -3
View File
@@ -5,9 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
const (
@@ -37,8 +37,11 @@ func New(b drivers.SPI) *Device {
// NewSoftwareSPI returns a new APA102 driver that will use a software based
// implementation of the SPI protocol.
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
func NewSoftwareSPI(sckPin, sdoPin pin.Output, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() {
pin.ConfigureOutput(sckPin)
pin.ConfigureOutput(sdoPin)
}})
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
+11 -6
View File
@@ -1,6 +1,8 @@
package apa102
import "machine"
import (
"tinygo.org/x/drivers/internal/pin"
)
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
// to mode 0 and ignores trying to receive data. Just enough for the APA102.
@@ -8,15 +10,18 @@ import "machine"
// most purposes other than the APA102 package. It might be desirable to make
// this more generic and include it in the TinyGo "machine" package instead.
type bbSPI struct {
SCK machine.Pin
SDO machine.Pin
Delay uint32
SCK pin.OutputFunc
SDO pin.OutputFunc
Delay uint32
configurePins func()
}
// Configure sets up the SCK and SDO pins as outputs and sets them low
func (s *bbSPI) Configure() {
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput})
if s.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
s.configurePins()
s.SCK.Low()
s.SDO.Low()
if s.Delay == 0 {
+30 -23
View File
@@ -1,31 +1,35 @@
package bmi160
import (
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
// also an I2C interface, but it is not yet supported.
type DeviceSPI struct {
// Chip select pin
CSB machine.Pin
csb pin.OutputFunc
buf [7]byte
// SPI bus (requires chip select to be usable).
Bus drivers.SPI
bus drivers.SPI
configurePins func()
}
// NewSPI returns a new device driver. The pin and SPI interface are not
// touched, provide a fully configured SPI object and call Configure to start
// using this device.
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
func NewSPI(csb pin.Output, spi drivers.SPI) *DeviceSPI {
return &DeviceSPI{
CSB: csb, // chip select
Bus: spi,
csb: csb.Set, // chip select
bus: spi,
configurePins: func() {
pin.ConfigureOutput(csb)
},
}
}
@@ -33,8 +37,11 @@ func NewSPI(csb machine.Pin, 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 {
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.CSB.High()
if d.configurePins == nil {
return pin.ErrConfigBeforeInstantiated
}
d.configurePins()
d.csb.High()
// The datasheet recommends doing a register read from address 0x7F to get
// SPI communication going:
@@ -86,9 +93,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
}
@@ -123,9 +130,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
}
@@ -153,9 +160,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
}
@@ -201,9 +208,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]
}
@@ -217,7 +224,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
@@ -23,6 +23,7 @@ type Filter uint
type Device struct {
bus drivers.I2C
Address uint16
buf [6]byte
cali calibrationCoefficients
Temperature Oversampling
Pressure Oversampling
@@ -134,8 +135,8 @@ func (d *Device) PrintCali() {
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
data, err := d.readData(REG_TEMP, 3)
if err != nil {
data := d.buf[:3]
if err = d.readData(REG_TEMP, data); err != nil {
return
}
@@ -158,8 +159,8 @@ func (d *Device) ReadTemperature() (temperature int32, err error) {
// ReadPressure returns the pressure in milli pascals (mPa).
func (d *Device) ReadPressure() (pressure int32, err error) {
// First 3 bytes are Pressure, last 3 bytes are Temperature
data, err := d.readData(REG_PRES, 6)
if err != nil {
data := d.buf[:6]
if err = d.readData(REG_PRES, data); err != nil {
return
}
@@ -203,7 +204,7 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
}
// readData reads n number of bytes of the specified register
func (d *Device) readData(register int, n int) ([]byte, error) {
func (d *Device) readData(register int, data []byte) error {
// If not in normal mode, set the mode to FORCED mode, to prevent incorrect measurements
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
if d.Mode != MODE_NORMAL {
@@ -218,9 +219,7 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
}
// Read the requested register
data := make([]byte, n)
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
return data, err
return legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
}
// convert3Bytes converts three bytes to int32
+7 -7
View File
@@ -2,22 +2,22 @@
package buzzer // import "tinygo.org/x/drivers/buzzer"
import (
"machine"
"time"
"tinygo.org/x/drivers/internal/pin"
)
// Device wraps a GPIO connection to a buzzer.
type Device struct {
pin machine.Pin
pin pin.OutputFunc
High bool
BPM float64
}
// New returns a new buzzer driver given which pin to use
func New(pin machine.Pin) Device {
func New(pin pin.Output) Device {
return Device{
pin: pin,
pin: pin.Set,
High: false,
BPM: 96.0,
}
@@ -25,14 +25,14 @@ func New(pin machine.Pin) Device {
// On sets the buzzer to a high state.
func (l *Device) On() (err error) {
l.pin.Set(true)
l.pin.High()
l.High = true
return
}
// Off sets the buzzer to a low state.
func (l *Device) Off() (err error) {
l.pin.Set(false)
l.pin.Low()
l.High = false
return
}
+6 -69
View File
@@ -2,9 +2,9 @@
package easystepper // import "tinygo.org/x/drivers/easystepper"
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers/internal/pin"
)
// StepMode determines the coil sequence used to perform a single step
@@ -30,28 +30,10 @@ 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]machine.Pin
pins [4]pin.OutputFunc
config func()
stepDelay time.Duration
stepNumber uint8
stepMode StepMode
@@ -62,51 +44,6 @@ 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) {
@@ -125,8 +62,8 @@ func (d *Device) Move(steps int32) {
// Off turns off all motor pins
func (d *Device) Off() {
for _, pin := range d.pins {
pin.Low()
for _, p := range d.pins {
p.Low()
}
}
+29
View File
@@ -0,0 +1,29 @@
package easystepper
import (
"errors"
"time"
"tinygo.org/x/drivers/internal/pin"
)
func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]pin.Output) (*Device, error) {
if stepcount == 0 || rpm == 0 {
return nil, errors.New("zero rpm and/or stepcount")
}
var ps [4]pin.OutputFunc
for i := range pins {
if pins[i] == nil {
return nil, errors.New("nil pin")
}
ps[i] = pins[i].Set
}
d := &Device{
pins: ps,
stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)),
stepMode: mode,
config: func() {},
}
return d, nil
}
+82
View File
@@ -0,0 +1,82 @@
//go:build baremetal
package easystepper
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers/internal/pin"
)
// New returns a new single easystepper driver given a DeviceConfig
func New(config DeviceConfig) (*Device, error) {
if config.StepCount == 0 || config.RPM == 0 {
return nil, errors.New("config.StepCount and config.RPM must be > 0")
}
return &Device{
pins: [4]pin.OutputFunc{config.Pin1.Set, config.Pin2.Set, config.Pin3.Set, config.Pin4.Set},
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
stepMode: config.Mode,
config: func() {
pin.ConfigureOutput(config.Pin1)
pin.ConfigureOutput(config.Pin2)
pin.ConfigureOutput(config.Pin3)
pin.ConfigureOutput(config.Pin4)
},
}, nil
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
if d.config == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.config()
}
// Configure configures the pins of the DualDevice
func (d *DualDevice) Configure() {
d.devices[0].Configure()
d.devices[1].Configure()
}
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
// Create the first device
dev1, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
// Create the second device
config.DeviceConfig.Pin1 = config.Pin5
config.DeviceConfig.Pin2 = config.Pin6
config.DeviceConfig.Pin3 = config.Pin7
config.DeviceConfig.Pin4 = config.Pin8
dev2, err := New(config.DeviceConfig)
if err != nil {
return nil, err
}
// Return composite dual device
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
}
// DeviceConfig contains the configuration data for a single easystepper driver
type DeviceConfig struct {
// Pin1 ... Pin4 determines the pins to configure and use for the device
Pin1, Pin2, Pin3, Pin4 machine.Pin
// StepCount is the number of steps required to perform a full revolution of the stepper motor
StepCount uint
// RPM determines the speed of the stepper motor in 'Revolutions per Minute'
RPM uint
// Mode determines the coil sequence used to perform a single step
Mode StepMode
}
// DualDeviceConfig contains the configuration data for a dual easystepper driver
type DualDeviceConfig struct {
DeviceConfig
// Pin5 ... Pin8 determines the pins to configure and use for the second device
Pin5, Pin6, Pin7, Pin8 machine.Pin
}
+225
View File
@@ -0,0 +1,225 @@
// Package ens160 provides a driver for the ScioSense ENS160 digital gas sensor.
//
// Datasheet: https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
package ens160
import (
"encoding/binary"
"errors"
"time"
"tinygo.org/x/drivers"
)
const (
defaultTimeout = 30 * time.Millisecond
shortTimeout = 1 * time.Millisecond
)
// Conversion constants for environment data compensation.
const (
kelvinOffsetMilli = 273150 // 273.15 K in milli-units
tempRawFactor = 64 // As per datasheet for TEMP_IN
humRawFactor = 512 // As per datasheet for RH_IN
milliFactor = 1000 // For converting from milli-units
roundingTerm = milliFactor / 2 // For rounding before integer division
)
// validityStrings provides human-readable descriptions for validity flags.
var validityStrings = [...]string{
ValidityNormalOperation: "normal operation",
ValidityWarmUpPhase: "warm-up phase, wait ~3 minutes for valid data",
ValidityInitialStartUpPhase: "initial start-up phase, wait ~1 hour for valid data",
ValidityInvalidOutput: "invalid output",
}
// Device wraps an I2C connection to an ENS160 device.
type Device struct {
bus drivers.I2C // I²C implementation
addr uint16 // 7bit bus address, promoted to uint16 per drivers.I2C
// shadow registers / last measurements
lastTvocPPB uint16
lastEco2PPM uint16
lastAqiUBA uint8
lastValidity uint8 // Store the latest validity status
// preallocated buffers
wbuf [5]byte // longest write: reg + 4bytes (TEMP+RH)
rbuf [5]byte // longest read: DATA burst (5bytes)
}
// New returns a new ENS160 driver.
func New(bus drivers.I2C, addr uint16) *Device {
if addr == 0 {
addr = DefaultAddress
}
return &Device{
bus: bus,
addr: addr,
lastValidity: ValidityInvalidOutput,
}
}
// Connected returns whether a ENS160 has been found.
func (d *Device) Connected() bool {
d.wbuf[0] = regPartID
err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:2])
return err == nil && d.rbuf[0] == LowPartID && d.rbuf[1] == HighPartID
}
// Configure sets up the device for reading.
func (d *Device) Configure() error {
// 1. Soft-reset. The device will automatically enter IDLE mode.
if err := d.write1(regOpMode, ModeReset); err != nil {
return err
}
time.Sleep(defaultTimeout)
// 2. Clear GPR registers, then go to STANDARD mode.
if err := d.write1(regCommand, cmdClrGPR); err != nil {
return err
}
time.Sleep(defaultTimeout)
if err := d.write1(regOpMode, ModeStandard); err != nil {
return err
}
time.Sleep(defaultTimeout)
return nil
}
// calculateTempRaw converts temperature from milli-degrees Celsius to the sensor's raw format.
func calculateTempRaw(tempMilliC int32) uint16 {
// Clip temperature
const (
minC = -40 * 1000
maxC = 85 * 1000
)
if tempMilliC < minC {
tempMilliC = minC
} else if tempMilliC > maxC {
tempMilliC = maxC
}
// Integer fixed-point conversion to format required by the sensor.
// Formula from datasheet: T_IN = (T_ambient_C + 273.15) * 64
return uint16((((tempMilliC + kelvinOffsetMilli) * tempRawFactor) + roundingTerm) / milliFactor)
}
// calculateHumRaw converts relative humidity from milli-percent to the sensor's raw format.
func calculateHumRaw(rhMilliPct int32) uint16 {
// Clip humidity
if rhMilliPct < 0 {
rhMilliPct = 0
} else if rhMilliPct > 100*1000 {
rhMilliPct = 100 * 1000
}
// Integer fixed-point conversion to format required by the sensor.
// Formula from datasheet: RH_IN = (RH_ambient_% * 512)
return uint16(((rhMilliPct * humRawFactor) + roundingTerm) / milliFactor)
}
// SetEnvDataMilli sets the ambient temperature and humidity for compensation.
//
// tempMilliC is the temperature in milli-degrees Celsius.
// rhMilliPct is the relative humidity in milli-percent.
func (d *Device) SetEnvDataMilli(tempMilliC, rhMilliPct int32) error {
tempRaw := calculateTempRaw(tempMilliC)
humRaw := calculateHumRaw(rhMilliPct)
d.wbuf[0] = regTempIn // start address (autoincrement)
binary.LittleEndian.PutUint16(d.wbuf[1:3], tempRaw)
binary.LittleEndian.PutUint16(d.wbuf[3:5], humRaw)
return d.bus.Tx(d.addr, d.wbuf[:5], nil)
}
// Update refreshes the concentration measurements.
func (d *Device) Update(which drivers.Measurement) error {
if which&drivers.Concentration == 0 {
return nil // nothing requested
}
const maxTries = 1000
var (
status uint8
validity uint8
)
var gotData bool
// Poll DEVICE_STATUS until NEWDAT or timeout
for range maxTries {
var err error
status, err = d.read1(regStatus)
if err != nil {
return err
}
if status&statusSTATER != 0 {
return errors.New("ENS160: error (STATER set)")
}
validity = (status & statusValidityMask) >> statusValidityShift
if status&statusNEWDAT != 0 {
gotData = true
break // Always break when data available
}
time.Sleep(shortTimeout)
}
if !gotData {
return errors.New("ENS160: timeout waiting for NEWDAT")
}
// Burst-read data regardless of validity state
d.wbuf[0] = regAQI
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:5]); err != nil {
return errors.New("ENS160: burst read failed")
}
d.lastAqiUBA = d.rbuf[0]
d.lastTvocPPB = binary.LittleEndian.Uint16(d.rbuf[1:3])
d.lastEco2PPM = binary.LittleEndian.Uint16(d.rbuf[3:5])
d.lastValidity = validity // Store the validity status
return nil
}
// TVOC returns the last totalVOC concentration in partsperbillion.
func (d *Device) TVOC() uint16 { return d.lastTvocPPB }
// ECO2 returns the last equivalent CO₂ concentration in partspermillion.
func (d *Device) ECO2() uint16 { return d.lastEco2PPM }
// AQI returns the last AirQuality Index according to UBA (15).
func (d *Device) AQI() uint8 { return d.lastAqiUBA }
// Validity returns the current operating state of the sensor.
func (d *Device) Validity() uint8 {
return d.lastValidity
}
// ValidityString returns a human-readable string describing the current validity status.
func (d *Device) ValidityString() string {
if int(d.lastValidity) < len(validityStrings) {
return validityStrings[d.lastValidity]
}
return "unknown"
}
// write1 writes a single byte to a register.
func (d *Device) write1(reg, val uint8) error {
d.wbuf[0] = reg
d.wbuf[1] = val
return d.bus.Tx(d.addr, d.wbuf[:2], nil)
}
// read1 reads a single byte from a register.
func (d *Device) read1(reg uint8) (uint8, error) {
d.wbuf[0] = reg
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:1]); err != nil {
return 0, err
}
return d.rbuf[0], nil
}
+54
View File
@@ -0,0 +1,54 @@
package ens160
import (
"testing"
)
func TestCalculateTempRaw(t *testing.T) {
testCases := []struct {
name string
tempMilliC int32
expectedRaw uint16
}{
{"25°C", 25000, 19082},
{"-10.5°C", -10500, 16810},
{"Min temp", -40000, 14922},
{"Below min", -50000, 14922},
{"Max temp", 85000, 22922},
{"Above max", 90000, 22922},
{"Zero", 0, 17482},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
raw := calculateTempRaw(tc.tempMilliC)
if raw != tc.expectedRaw {
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
}
})
}
}
func TestCalculateHumRaw(t *testing.T) {
testCases := []struct {
name string
rhMilliPct int32
expectedRaw uint16
}{
{"50%", 50000, 25600},
{"0%", 0, 0},
{"100%", 100000, 51200},
{"Below 0%", -10000, 0},
{"Above 100%", 110000, 51200},
{"33.3%", 33300, 17050},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
raw := calculateHumRaw(tc.rhMilliPct)
if raw != tc.expectedRaw {
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
}
})
}
}
+65
View File
@@ -0,0 +1,65 @@
package ens160
// DefaultAddress is the default I2C address for the ENS160 when the ADDR pin is
// connected to high (3.3V). When connected to low (GND), the address is 0x52.
const DefaultAddress = 0x53
// Registers
const (
regPartID = 0x00
regOpMode = 0x10
regConfig = 0x11
regCommand = 0x12
regTempIn = 0x13
regRhIn = 0x15
regStatus = 0x20
regAQI = 0x21
regTVOC = 0x22
regECO2 = 0x24
regDataT = 0x30
regDataRH = 0x32
regMISR = 0x38
regGPRWrite = 0x40
regGPRRead = 0x48
)
// Operating modes
const (
ModeDeepSleep = 0x00
ModeIdle = 0x01
ModeStandard = 0x02
ModeReset = 0xF0
)
// Status register bits
const (
statusSTATAS = 1 << 7
statusSTATER = 1 << 6
statusValidityMask = 0x0C
statusValidityShift = 2
statusNEWDAT = 1 << 1
statusNEWGPR = 1 << 0
)
// Validity flags
const (
ValidityNormalOperation = 0x00
ValidityWarmUpPhase = 0x01 // need ~3 minutes until valid data
ValidityInitialStartUpPhase = 0x02 // need ~1 hour until valid data
ValidityInvalidOutput = 0x03
)
// Commands
const (
cmdNOP = 0x00
cmdGetAppVer = 0x0E
cmdClrGPR = 0xCC
)
// Part IDs
const (
LowPartID = 0x60
HighPartID = 0x01
)
+56
View File
@@ -0,0 +1,56 @@
// This example demonstrates ENS160 usage.
//
// Wiring:
// - VCC to 3.3V, GND to ground
// - SDA to board SDA, SCL to board SCL
package main
import (
"time"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/ens160"
)
func main() {
err := machine.I2C0.Configure(machine.I2CConfig{
Frequency: 400 * machine.KHz,
})
if err != nil {
println("Failed to configure I2C:", err)
}
dev := ens160.New(machine.I2C0, ens160.DefaultAddress)
connected := dev.Connected()
if !connected {
println("ENS160 not detected")
return
}
println("ENS160 detected")
if err := dev.Configure(); err != nil {
println("Failed to configure ENS160:", err)
}
for {
err := dev.Update(drivers.Concentration)
if err != nil {
println("Error reading ENS160: %v\n", err)
time.Sleep(5 * time.Second)
continue
}
println(
"AQI:", dev.AQI(),
"TVOC:", dev.TVOC(),
"eCO2:", dev.ECO2(),
"Validity:", dev.ValidityString(),
)
time.Sleep(2 * time.Second)
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func main() {
println("GPS I2C Example")
machine.I2C0.Configure(machine.I2CConfig{})
ublox := gps.NewI2C(machine.I2C0)
ublox := gps.NewI2CWithAddress(machine.I2C0, gps.UBLOX_I2C_ADDRESS)
parser := gps.NewParser()
var fix gps.Fix
for {
+58
View File
@@ -0,0 +1,58 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/lsm303dlhc"
)
func main() {
// LSM303DLHC is connected to the I2C0 bus on Adafruit Feather M4 via pins: 20(SDA) and 21(SCL).
machine.I2C0.Configure(machine.I2CConfig{})
sensor := lsm303dlhc.New(machine.I2C0)
//default settings
err := sensor.Configure(lsm303dlhc.Configuration{
AccelPowerMode: lsm303dlhc.ACCEL_POWER_NORMAL,
AccelRange: lsm303dlhc.ACCEL_RANGE_2G,
AccelDataRate: lsm303dlhc.ACCEL_DATARATE_100HZ,
MagPowerMode: lsm303dlhc.MAG_POWER_NORMAL,
MagSystemMode: lsm303dlhc.MAG_SYSTEM_CONTINUOUS,
MagDataRate: lsm303dlhc.MAG_DATARATE_10HZ,
})
if err != nil {
for {
println("Failed to configure", err.Error())
time.Sleep(time.Second)
}
}
for {
accel_x, accel_y, accel_z, err := sensor.ReadAcceleration()
if err != nil {
println("Failed to read accel", err.Error())
}
println("ACCEL_X:", accel_x, " ACCEL_Y:", accel_y, " ACCEL_Z:", accel_z)
mag_x, mag_y, mag_z, err := sensor.ReadMagneticField()
if err != nil {
println("Failed to read mag", err.Error())
}
println("MAG_X:", mag_x, " MAG_Y:", mag_y, " MAG_Z:", mag_z)
pitch, roll, _ := sensor.ReadPitchRoll()
println("Pitch:", float32(pitch), " Roll:", float32(roll))
heading, _ := sensor.ReadCompass()
println("Heading:", float32(heading), "degrees")
temp, _ := sensor.ReadTemperature()
println("Temperature:", float32(temp)/1000, "*C")
println("\n")
time.Sleep(time.Millisecond * 250)
}
}
+35
View File
@@ -0,0 +1,35 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/seesaw"
)
// example reading the position of a rotary encoder (4991) powered by a seesaw
// https://learn.adafruit.com/adafruit-i2c-qt-rotary-encoder/arduino
func main() {
// This assumes you are using an Adafruit QT Py RP2040 for its Stemma QT connector
// https://www.adafruit.com/product/4900
i2c := machine.I2C1
i2c.Configure(machine.I2CConfig{
SCL: machine.I2C1_QT_SCL_PIN,
SDA: machine.I2C1_QT_SDA_PIN,
})
dev := seesaw.New(i2c)
dev.Address = 0x36
for {
time.Sleep(time.Second)
pos, err := dev.GetEncoderPosition(0, false)
if err != nil {
println(err)
continue
}
println(pos)
}
}
-51
View File
@@ -1,51 +0,0 @@
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: ssd1306.Address_128_32,
Width: 128,
Height: 32,
})
display.ClearDisplay()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 31 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
-60
View File
@@ -1,60 +0,0 @@
// This example shows how to use 128x64 display over I2C
// Tested on Seeeduino XIAO Expansion Board https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
//
// According to manual, I2C address of the display is 0x78, but that's 8-bit address.
// TinyGo operates on 7-bit addresses and respective 7-bit address would be 0x3C, which we use below.
//
// To learn more about different types of I2C addresses, please see following page
// https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
package main
import (
"machine"
"image/color"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: 0x3C,
Width: 128,
Height: 64,
})
display.ClearDisplay()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 63 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+59
View File
@@ -0,0 +1,59 @@
package main
// This example shows how to use SSD1306 OLED display driver over I2C and SPI.
//
// Check the `newSSD1306Display()` functions for I2C and SPI initializations.
import (
"runtime"
"image/color"
"time"
)
func main() {
display := newSSD1306Display()
display.ClearDisplay()
w, h := display.Size()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)
traceTime := time.Now().UnixMilli() + 1000
frames := 0
ms := runtime.MemStats{}
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == w-1 {
deltaX = -deltaX
}
if y == 0 || y == h-1 {
deltaY = -deltaY
}
frames++
now := time.Now().UnixMilli()
if now >= traceTime {
runtime.ReadMemStats(&ms)
println("TS", now, "| FPS", frames, "| HeapInuse", ms.HeapInuse)
traceTime = now + 1000
frames = 0
}
}
}
+38
View File
@@ -0,0 +1,38 @@
//go:build xiao_ble
// This initializes SSD1306 OLED display driver over I2C.
//
// Seeed XIAO BLE board + SSD1306 128x32 I2C OLED display.
//
// Wiring:
// - XIAO GND -> OLED GND
// - XIAO 3v3 -> OLED VCC
// - XIAO D4 (SDA) -> OLED SDA
// - XIAO D5 (SCL) -> OLED SCK
//
// For your case:
// - Connect the display to I2C pins on your board.
// - Adjust I2C address and display size as needed.
package main
import (
"machine"
"tinygo.org/x/drivers/ssd1306"
)
func newSSD1306Display() *ssd1306.Device {
machine.I2C0.Configure(machine.I2CConfig{
Frequency: 400 * machine.KHz,
SDA: machine.SDA0_PIN,
SCL: machine.SCL0_PIN,
})
display := ssd1306.NewI2C(machine.I2C0)
display.Configure(ssd1306.Config{
Address: ssd1306.Address_128_32, // or ssd1306.Address
Width: 128,
Height: 32, // or 64
})
return display
}
+27
View File
@@ -0,0 +1,27 @@
//go:build thumby
// This initializes SSD1306 OLED display driver over SPI.
//
// Thumby board has a tiny built-in 72x40 display.
//
// As the display is built-in, no wiring is needed.
package main
import (
"machine"
"tinygo.org/x/drivers/ssd1306"
)
func newSSD1306Display() *ssd1306.Device {
machine.SPI0.Configure(machine.SPIConfig{})
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
display.Configure(ssd1306.Config{
Width: 72,
Height: 40,
ResetCol: ssd1306.ResetValue{28, 99},
ResetPage: ssd1306.ResetValue{0, 5},
})
return display
}
+40
View File
@@ -0,0 +1,40 @@
//go:build xiao_rp2040
// This initializes SSD1306 OLED display driver over SPI.
//
// Seeed XIAO RP2040 board + SSD1306 128x64 SPI OLED display.
//
// Wiring:
// - XIAO GND -> OLED GND
// - XIAO 3v3 -> OLED VCC
// - XIAO D8 (SCK) -> OLED D0
// - XIAO D10 (SDO) -> OLED D1
// - XIAO D4 -> OLED RES
// - XIAO D5 -> OLED DC
// - XIAO D6 -> OLED CS
//
// For your case:
// - Connect the display to SPI pins on your board.
// - Adjust RES, DC and CS pins as needed.
// - Adjust SPI frequency as needed.
// - Adjust display size as needed.
package main
import (
"machine"
"tinygo.org/x/drivers/ssd1306"
)
func newSSD1306Display() *ssd1306.Device {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 50 * machine.MHz,
})
display := ssd1306.NewSPI(machine.SPI0, machine.D5, machine.D4, machine.D6)
display.Configure(ssd1306.Config{
Width: 128,
Height: 64,
})
return display
}
-48
View File
@@ -1,48 +0,0 @@
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{
Frequency: 8000000,
})
display := ssd1306.NewSPI(machine.SPI0, machine.P8, machine.P7, machine.P9)
display.Configure(ssd1306.Config{
Width: 128,
Height: 64,
})
display.ClearDisplay()
x := int16(64)
y := int16(32)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 127 {
deltaX = -deltaX
}
if y == 0 || y == 63 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
-50
View File
@@ -1,50 +0,0 @@
// This example using the SSD1306 OLED display over SPI on the Thumby board
// A very tiny 72x40 display.
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ssd1306"
)
func main() {
machine.SPI0.Configure(machine.SPIConfig{})
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
display.Configure(ssd1306.Config{
Width: 72,
Height: 40,
ResetCol: ssd1306.ResetValue{28, 99},
ResetPage: ssd1306.ResetValue{0, 5},
})
display.ClearDisplay()
x := int16(36)
y := int16(20)
deltaX := int16(1)
deltaY := int16(1)
for {
pixel := display.GetPixel(x, y)
c := color.RGBA{255, 255, 255, 255}
if pixel {
c = color.RGBA{0, 0, 0, 255}
}
display.SetPixel(x, y, c)
display.Display()
x += deltaX
y += deltaY
if x == 0 || x == 71 {
deltaX = -deltaX
}
if y == 0 || y == 39 {
deltaY = -deltaY
}
time.Sleep(1 * time.Millisecond)
}
}
+13 -9
View File
@@ -5,28 +5,29 @@
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
intPin machine.Pin
bus drivers.I2C
buf []byte
Address uint8
configurePins func()
}
// New returns FT6336 device for the provided I2C bus using default address.
func New(i2c drivers.I2C, intPin machine.Pin) *Device {
func New(i2c drivers.I2C, intPin pin.Input) *Device {
return &Device{
bus: i2c,
buf: make([]byte, 11),
Address: Address,
intPin: intPin,
configurePins: func() {
pin.ConfigureInputPulldown(intPin)
},
}
}
@@ -36,8 +37,11 @@ type Config struct {
// Configure the FT6336 device.
func (d *Device) Configure(config Config) error {
if d.configurePins == nil {
return pin.ErrConfigBeforeInstantiated
}
d.write1Byte(0xA4, 0x00)
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
d.configurePins()
return nil
}
+15 -15
View File
@@ -5,12 +5,12 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01"
import (
"image/color"
"machine"
"time"
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
// Rotation controls the rotation used by the display.
@@ -22,10 +22,10 @@ type FrameRate uint8
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
dcPin pin.OutputFunc
resetPin pin.OutputFunc
csPin pin.OutputFunc
blPin pin.OutputFunc
width int16
height int16
columnOffsetCfg int16
@@ -52,17 +52,17 @@ type Config struct {
}
// New creates a new ST7789 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin 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})
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
pin.ConfigureOutput(resetPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(blPin)
return Device{
bus: bus,
resetPin: resetPin,
dcPin: dcPin,
csPin: csPin,
blPin: blPin,
resetPin: resetPin.Set,
dcPin: dcPin.Set,
csPin: csPin.Set,
blPin: blPin.Set,
}
}
@@ -226,7 +226,7 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.dcPin.Set(!isCommand)
d.dcPin(!isCommand)
d.bus.Tx(data, nil)
}
+7 -1
View File
@@ -69,10 +69,16 @@ func NewUART(uart drivers.UART) Device {
}
// NewI2C creates a new I2C GPS connection.
// Uses the default i2c address (0x42) for backward compatibility reasons.
func NewI2C(bus drivers.I2C) Device {
return NewI2CWithAddress(bus, I2C_ADDRESS)
}
// NewI2CWithAddress creates a new I2C GPS connection on the provided address
func NewI2CWithAddress(bus drivers.I2C, i2cAddress uint16) Device {
return Device{
bus: bus,
address: I2C_ADDRESS,
address: i2cAddress,
buffer: make([]byte, bufferSize),
bufIdx: bufferSize,
sentence: strings.Builder{},
+4 -1
View File
@@ -96,7 +96,10 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
fix.Speed = findSpeed(fields[7])
fix.Heading = findHeading(fields[8])
date := findDate(fields[9])
fix.Time = fix.Time.AddDate(date.Year(), int(date.Month()), date.Day())
fix.Time = date.Add(time.Duration(fix.Time.Hour())*time.Hour +
time.Duration(fix.Time.Minute())*time.Minute +
time.Duration(fix.Time.Second())*time.Second +
time.Duration(fix.Time.Nanosecond())*time.Nanosecond)
return fix, nil
}
+3 -3
View File
@@ -70,15 +70,15 @@ func TestParseRMC(t *testing.T) {
t.Error("should have errInvalidRMCSentence error")
}
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,130522,0.0,E,D*2B"
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,010622,0.0,E,D*2B"
fix, err := p.Parse(val)
if err != nil {
t.Error("should have parsed")
}
c.Assert(fix.Time.Year(), qt.Equals, 2022)
c.Assert(fix.Time.Month(), qt.Equals, time.May)
c.Assert(fix.Time.Day(), qt.Equals, 13)
c.Assert(fix.Time.Month(), qt.Equals, time.June)
c.Assert(fix.Time.Day(), qt.Equals, 1)
c.Assert(fix.Time.Hour(), qt.Equals, 20)
c.Assert(fix.Time.Minute(), qt.Equals, 35)
c.Assert(fix.Time.Second(), qt.Equals, 22)
+5 -1
View File
@@ -4,7 +4,11 @@ package gps
// The I2C address which this device listens to.
const (
I2C_ADDRESS = 0x42
// To ensure backward compatibility
I2C_ADDRESS = UBLOX_I2C_ADDRESS
UBLOX_I2C_ADDRESS = 0x42
PA1010D_I2C_ADDRESS = 0x10
)
const (
+18 -10
View File
@@ -5,30 +5,38 @@
package hcsr04
import (
"machine"
"time"
"tinygo.org/x/drivers/internal/pin"
)
const TIMEOUT = 23324 // max sensing distance (4m)
// Device holds the pins
type Device struct {
trigger machine.Pin
echo machine.Pin
trigger pin.OutputFunc
echo pin.InputFunc
configurePins func()
}
// New returns a new ultrasonic driver given 2 pins
func New(trigger, echo machine.Pin) Device {
func New(trigger pin.Output, echo pin.Input) Device {
return Device{
trigger: trigger,
echo: echo,
trigger: trigger.Set,
echo: echo.Get,
configurePins: func() {
pin.ConfigureOutput(trigger)
pin.ConfigureInput(echo)
},
}
}
// Configure configures the pins of the Device
func (d *Device) Configure() {
d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.echo.Configure(machine.PinConfig{Mode: machine.PinInput})
if d.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.configurePins()
}
// ReadDistance returns the distance of the object in mm
@@ -52,7 +60,7 @@ func (d *Device) ReadPulse() int32 {
d.trigger.Low()
i := uint8(0)
for {
if d.echo.Get() {
if d.echo() {
t = time.Now()
break
}
@@ -66,7 +74,7 @@ func (d *Device) ReadPulse() int32 {
}
i = 0
for {
if !d.echo.Get() {
if !d.echo() {
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.busy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
for d.isBusy(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.busy(false) {
for d.isBusy(false) {
}
}
@@ -231,9 +231,9 @@ func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
}
}
// busy returns true when hd447890 is busy
// isBusy returns true when hd447890 is isBusy
// or after the timeout specified
func (d *Device) busy(longDelay bool) bool {
func (d *Device) isBusy(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) busy(longDelay bool) bool {
// Busy returns true when hd447890 is busy
func (d *Device) Busy() bool {
return d.busy(false)
return d.isBusy(false)
}
// Size returns the current size of the display.
-2
View File
@@ -2,8 +2,6 @@
package hts221
import "tinygo.org/x/drivers"
// Configure sets up the HTS221 device for communication.
func (d *Device) Configure() {
// read calibration data
+98
View File
@@ -0,0 +1,98 @@
package pin
import "errors"
// OutputFunc is hardware abstraction for a pin which outputs a
// digital signal (high or low level).
//
// // Code conversion demo: from machine.Pin to pin.OutputFunc
// led := machine.LED
// led.Configure(machine.PinConfig{Mode: machine.PinOutput})
// var p pin.OutputFuncFunc = led.Set // Going from a machine.Pin to a pin.OutputFunc
type OutputFunc func(level bool)
func (o OutputFunc) High() {
o(true)
}
func (o OutputFunc) Low() {
o(false)
}
// InputFunc is hardware abstraction for a pin which receives a
// digital signal and reads it (high or low level).
//
// // Code conversion demo: from machine.Pin to pin.InputFunc
// input := machine.LED
// input.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) // or use machine.PinInputPullup or machine.PinInput
// var p pin.InputFunc = input.Get // Going from a machine.Pin to a drivers.PinInput
type InputFunc func() (level bool)
// PinOutput represents a pin hardware abstraction layer for a pin that can output a digital signal.
// This is an wrapper to pin.OutputFunc abstraction which is a function type.
//
// func New(p1, p2, p3 pin.Output) *Device {
// return NewWithPinfuncs(p1.Set, p2.Set, p3.Set)
// }
//
// func NewWithPinfuncs(p1, p2, p3 pin.OutputFunc) *Device {
// return &Device{p1:p1, p2:p2, p3:p3}
// }
//
// [relevant issue]: https://github.com/tinygo-org/drivers/pull/749/files
type Output interface {
Set(level bool)
}
// PinInput represents a pin hardware abstraction layer. See [PinOutput] for
// more information on why this is "legacy".
type Input interface {
Get() (level bool)
}
// ConfigureOutput is a legacy function used to configure pins as outputs.
//
// Deprecated: You should not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigureOutput(po Output) {
configureOutput(po)
}
// ConfigureInput is a legacy function used to configure pins as inputs.
//
// Deprecated: You should not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigureInputPulldown(pi Input) {
configureInputPulldown(pi)
}
// ConfigureInput is a legacy function used to configure pins as inputs.
//
// Deprecated: You should not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigureInput(pi Input) {
configureInput(pi)
}
// ConfigureInputPullup is a legacy function used to configure pins as inputs.
//
// Deprecated: You should not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigureInputPullup(pi Input) {
configureInputPullup(pi)
}
// IsNotPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
//
// Deprecated: Drivers should not require pin knowledge.
func IsNotPin(pin any) bool {
return isNotPin(pin)
}
var (
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
)
+9
View File
@@ -0,0 +1,9 @@
//go:build !baremetal
package pin
func configureOutput(p Output) {}
func configureInput(p Input) {}
func configureInputPulldown(p Input) {}
func configureInputPullup(p Input) {}
func isNotPin(a any) bool { return false }
+10
View File
@@ -0,0 +1,10 @@
//go:build baremetal && fe310
package pin
import "machine"
const (
pulldown = machine.PinInput
pullup = machine.PinInput
)
+13
View File
@@ -0,0 +1,13 @@
//go:build baremetal && !fe310
package pin
import "machine"
// If you are getting a build error here you then we missed adding
// your CPU build tag to the list of CPUs that do not have pulldown/pullups.
// Add it above and in pinhal_nopulls! You should also add a smoketest for it :)
const (
pulldown = machine.PinInputPulldown
pullup = machine.PinInputPullup
)
+33
View File
@@ -0,0 +1,33 @@
//go:build baremetal
package pin
import "machine"
func configureOutput(p Output) {
configure(p, machine.PinOutput)
}
func configureInputPulldown(p Input) {
configure(p, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput.
}
func configureInput(p Input) {
configure(p, machine.PinInput)
}
func configureInputPullup(p Input) {
configure(p, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput.
}
func isNotPin(a any) bool {
p, ok := a.(machine.Pin)
return ok && p == machine.NoPin
}
func configure(p any, mode machine.PinMode) {
machinePin, ok := p.(machine.Pin)
if ok {
machinePin.Configure(machine.PinConfig{Mode: mode})
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ type Configuration struct {
MagDataRate uint8
}
var errNotConnected = errors.New("lsm303agr: failed to communicate with either acel or magnet sensor")
var errNotConnected = errors.New("lsm303agr: failed to communicate with either accel or magnet sensor")
// New creates a new LSM303AGR connection. The I2C bus must already be configured.
//
+214
View File
@@ -0,0 +1,214 @@
// Package lsm303dlhc implements a driver for the LSM303dlhc,
// a 3 axis accelerometer/magnetic sensor typically available on breakout boards.
//
// Datasheet: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf
package lsm303dlhc // import "tinygo.org/x/drivers/lsm303dlhc"
import (
"math"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// Device wraps an I2C connection to a LSM303dlhc device.
type Device struct {
bus drivers.I2C
AccelAddress uint8
MagAddress uint8
AccelPowerMode uint8
AccelRange uint8
AccelDataRate uint8
MagPowerMode uint8
MagSystemMode uint8
MagDataRate uint8
buf [6]uint8
}
// Configuration for LSM303dlhc device.
type Configuration struct {
AccelPowerMode uint8
AccelRange uint8
AccelDataRate uint8
MagPowerMode uint8
MagSystemMode uint8
MagDataRate uint8
}
// New creates a new LSM303DLHC connection. The I2C bus must already be configured.
// This function only creates the Device object, it does not touch the device.
func New(bus drivers.I2C) *Device {
return &Device{
bus: bus,
AccelAddress: ACCEL_ADDRESS,
MagAddress: MAG_ADDRESS,
}
}
// Configure sets up the LSM303dlhc device for communication.
func (d *Device) Configure(cfg Configuration) (err error) {
if cfg.AccelDataRate != 0 {
d.AccelDataRate = cfg.AccelDataRate
} else {
d.AccelDataRate = ACCEL_DATARATE_100HZ
}
if cfg.AccelPowerMode != 0 {
d.AccelPowerMode = cfg.AccelPowerMode
} else {
d.AccelPowerMode = ACCEL_POWER_NORMAL
}
if cfg.AccelRange != 0 {
d.AccelRange = cfg.AccelRange
} else {
d.AccelRange = ACCEL_RANGE_2G
}
if cfg.MagPowerMode != 0 {
d.MagPowerMode = cfg.MagPowerMode
} else {
d.MagPowerMode = MAG_POWER_NORMAL
}
if cfg.MagDataRate != 0 {
d.MagDataRate = cfg.MagDataRate
} else {
d.MagDataRate = MAG_DATARATE_10HZ
}
if cfg.MagSystemMode != 0 {
d.MagSystemMode = cfg.MagSystemMode
} else {
d.MagSystemMode = MAG_SYSTEM_CONTINUOUS
}
data := d.buf[:1]
data[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, data)
if err != nil {
return
}
data[0] = byte(0x80 | d.AccelRange<<4)
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, data)
if err != nil {
return
}
data[0] = byte(0xC0)
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), CRA_REG_M, data)
if err != nil {
return
}
// Temperature compensation is on for magnetic sensor
data[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, data)
if err != nil {
return
}
return nil
}
// ReadAcceleration reads the current acceleration from the device and returns
// it in µg (micro-gravity). When one of the axes is pointing straight to Earth
// and the sensor is not moving the returned value will be around 1000000 or
// -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6]
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), ACCEL_OUT_AUTO_INC, data)
if err != nil {
return
}
rangeFactor := int16(0)
switch d.AccelRange {
case ACCEL_RANGE_2G:
rangeFactor = 1
case ACCEL_RANGE_4G:
rangeFactor = 2
case ACCEL_RANGE_8G:
rangeFactor = 4
case ACCEL_RANGE_16G:
rangeFactor = 12 // the readings in 16G are a bit lower
}
x = int32(int32(int16((uint16(data[1])<<8|uint16(data[0])))>>4*rangeFactor) * 1000000 / 1024)
y = int32(int32(int16((uint16(data[3])<<8|uint16(data[2])))>>4*rangeFactor) * 1000000 / 1024)
z = int32(int32(int16((uint16(data[5])<<8|uint16(data[4])))>>4*rangeFactor) * 1000000 / 1024)
return
}
// ReadPitchRoll reads the current pitch and roll angles from the device and
// returns it in micro-degrees. When the z axis is pointing straight to Earth
// the returned values of pitch and roll would be zero.
func (d *Device) ReadPitchRoll() (pitch, roll int32, err error) {
x, y, z, err := d.ReadAcceleration()
if err != nil {
return
}
xf, yf, zf := float64(x), float64(y), float64(z)
pitch = int32((math.Round(math.Atan2(yf, math.Sqrt(math.Pow(xf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
roll = int32((math.Round(math.Atan2(xf, math.Sqrt(math.Pow(yf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
return
}
// ReadMagneticField reads the current magnetic field from the device and returns
// it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
if d.MagSystemMode == MAG_SYSTEM_SINGLE {
cmd := d.buf[:1]
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, cmd)
if err != nil {
return
}
}
data := d.buf[0:6]
legacy.ReadRegister(d.bus, uint8(d.MagAddress), MAG_OUT_AUTO_INC, data)
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
z = int32(int16((uint16(data[5])<<8 | uint16(data[4]))))
return
}
// ReadCompass reads the current compass heading from the device and returns
// it in micro-degrees. When the z axis is pointing straight to Earth and
// the y axis is pointing to North, the heading would be zero.
//
// However, the heading may be off due to electronic compasses would be effected
// by strong magnetic fields and require constant calibration.
func (d *Device) ReadCompass() (h int32, err error) {
x, y, _, err := d.ReadMagneticField()
if err != nil {
return
}
xf, yf := float64(x), float64(y)
h = int32(float32((180/math.Pi)*math.Atan2(yf, xf)) * 1000000)
return
}
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2]
err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), TEMP_OUT_AUTO_INC, data)
if err != nil {
return
}
r := int16((uint16(data[1])<<8 | uint16(data[0]))) >> 4 // temperature offset from 25 °C
t = 25000 + int32((float32(r)/8)*1000)
return
}
+75
View File
@@ -0,0 +1,75 @@
package lsm303dlhc
const (
// Constants/addresses used for I2C.
ACCEL_ADDRESS = 0x19
MAG_ADDRESS = 0x1E
// i2C 8-bit subaddress (SUB): the 7 LSb represent the actual register address
// while the MSB enables address auto increment.
// If the MSb of the SUB field is 1, the SUB (register address) is
// automatically increased to allow multiple data read/writes.
ADDR_AUTO_INC_MASK = 0x80
// accelerometer registers.
ACCEL_CTRL_REG1_A = 0x20
ACCEL_CTRL_REG4_A = 0x23
ACCEL_OUT_X_L_A = 0x28
ACCEL_OUT_X_H_A = 0x29
ACCEL_OUT_Y_L_A = 0x2A
ACCEL_OUT_Y_H_A = 0x2B
ACCEL_OUT_Z_L_A = 0x2C
ACCEL_OUT_Z_H_A = 0x2D
ACCEL_OUT_AUTO_INC = ACCEL_OUT_X_L_A | ADDR_AUTO_INC_MASK
// magnetic sensor registers.
MAG_MR_REG_M = 0x02
MAG_OUT_X_L_M = 0x68
MAG_OUT_X_H_M = 0x69
MAG_OUT_Y_L_M = 0x6A
MAG_OUT_Y_H_M = 0x6B
MAG_OUT_Z_L_M = 0x6C
MAG_OUT_Z_H_M = 0x6D
MAG_OUT_AUTO_INC = MAG_OUT_X_L_M | ADDR_AUTO_INC_MASK
// temperature sensor registers.
CRA_REG_M = 0x80
TEMP_OUT_L_M = 0x32
TEMP_OUT_H_M = 0x31
TEMP_OUT_AUTO_INC = TEMP_OUT_L_M | ADDR_AUTO_INC_MASK
// accelerometer power mode.
ACCEL_POWER_NORMAL = 0x00 // default
ACCEL_POWER_LOW = 0x08
// accelerometer range.
ACCEL_RANGE_2G = 0x00 // default
ACCEL_RANGE_4G = 0x01
ACCEL_RANGE_8G = 0x02
ACCEL_RANGE_16G = 0x03
// accelerometer data rate.
ACCEL_DATARATE_1HZ = 0x01
ACCEL_DATARATE_10HZ = 0x02
ACCEL_DATARATE_25HZ = 0x03
ACCEL_DATARATE_50HZ = 0x04
ACCEL_DATARATE_100HZ = 0x05 // default
ACCEL_DATARATE_200HZ = 0x06
ACCEL_DATARATE_400HZ = 0x07
ACCEL_DATARATE_1344HZ = 0x09 // 5376Hz in low-power mode
// magnetic sensor power mode.
MAG_POWER_NORMAL = 0x00 // default
MAG_POWER_LOW = 0x01
// magnetic sensor operate mode.
MAG_SYSTEM_CONTINUOUS = 0x00 // default
MAG_SYSTEM_SINGLE = 0x01
// magnetic sensor data rate
MAG_DATARATE_10HZ = 0x00 // default
MAG_DATARATE_20HZ = 0x01
MAG_DATARATE_50HZ = 0x02
MAG_DATARATE_100HZ = 0x03
)
+35 -24
View File
@@ -8,7 +8,6 @@ import (
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type AccelRange uint8
@@ -26,7 +25,7 @@ type Device struct {
accelSampleRate AccelSampleRate
gyroRange GyroRange
gyroSampleRate GyroSampleRate
buf [6]uint8
buf [7]uint8 // up to 6 bytes for read + 1 byte for the register address
}
// Configuration for LSM6DS3TR device.
@@ -84,30 +83,20 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
d.gyroSampleRate = GYRO_SR_104
}
data := d.buf[:1]
// Configure accelerometer
data[0] = uint8(d.accelRange) | uint8(d.accelSampleRate)
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL1_XL, data)
err = d.writeByte(CTRL1_XL, uint8(d.accelRange)|uint8(d.accelSampleRate))
if err != nil {
return
}
// Set ODR bit
err = legacy.ReadRegister(d.bus, uint8(d.Address), CTRL4_C, data)
if err != nil {
return
}
data[0] = data[0] &^ BW_SCAL_ODR_ENABLED
data[0] |= BW_SCAL_ODR_ENABLED
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL4_C, data)
// Enable ODR scaling
err = d.setBits(CTRL4_C, BW_SCAL_ODR_ENABLED)
if err != nil {
return
}
// Configure gyroscope
data[0] = uint8(d.gyroRange) | uint8(d.gyroSampleRate)
err = legacy.WriteRegister(d.bus, uint8(d.Address), CTRL2_G, data)
err = d.writeByte(CTRL2_G, uint8(d.gyroRange)|uint8(d.gyroSampleRate))
if err != nil {
return
}
@@ -118,8 +107,10 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
// Connected returns whether a LSM6DS3TR has been found.
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := d.buf[:1]
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
data, err := d.readBytes(WHO_AM_I, 1)
if err != nil {
return false
}
return data[0] == 0x6A
}
@@ -128,8 +119,7 @@ func (d *Device) Connected() bool {
// and the sensor is not moving the returned value will be around 1000000 or
// -1000000.
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
data := d.buf[:6]
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_XL, data)
data, err := d.readBytes(OUTX_L_XL, 6)
if err != nil {
return
}
@@ -153,8 +143,7 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
// rotation along one axis and while doing so integrate all values over time,
// you would get a value close to 360000000.
func (d *Device) ReadRotation() (x, y, z int32, err error) {
data := d.buf[:6]
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_G, data)
data, err := d.readBytes(OUTX_L_G, 6)
if err != nil {
return
}
@@ -177,8 +166,7 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d *Device) ReadTemperature() (t int32, err error) {
data := d.buf[:2]
err = legacy.ReadRegister(d.bus, uint8(d.Address), OUT_TEMP_L, data)
data, err := d.readBytes(OUT_TEMP_L, 2)
if err != nil {
return
}
@@ -187,3 +175,26 @@ func (d *Device) ReadTemperature() (t int32, err error) {
t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/32
return
}
func (d *Device) readBytes(reg, size uint8) ([]byte, error) {
d.buf[0] = reg
err := d.bus.Tx(d.Address, d.buf[0:1], d.buf[1:size+1])
if err != nil {
return nil, err
}
return d.buf[1 : size+1], nil
}
func (d *Device) writeByte(reg, value uint8) error {
d.buf[0] = reg
d.buf[1] = value
return d.bus.Tx(d.Address, d.buf[0:2], nil)
}
func (d *Device) setBits(reg, bits uint8) error {
data, err := d.readBytes(reg, 1)
if err != nil {
return err
}
return d.writeByte(reg, (data[0]&^bits)|bits)
}
+4 -4
View File
@@ -3,9 +3,9 @@ package max6675
import (
"errors"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
// ErrThermocoupleOpen is returned when the thermocouple input is open.
@@ -14,16 +14,16 @@ var ErrThermocoupleOpen = errors.New("thermocouple input open")
type Device struct {
bus drivers.SPI
cs machine.Pin
cs pin.OutputFunc
}
// Create a new Device to read from a MAX6675 thermocouple.
// Pins must be configured before use. Frequency for SPI
// should be 4.3MHz maximum.
func NewDevice(bus drivers.SPI, cs machine.Pin) *Device {
func NewDevice(bus drivers.SPI, cs pin.Output) *Device {
return &Device{
bus: bus,
cs: cs,
cs: cs.Set,
}
}
+15 -11
View File
@@ -3,31 +3,35 @@
package max72xx
import (
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Device struct {
bus drivers.SPI
cs machine.Pin
bus drivers.SPI
cs pin.OutputFunc
configurePins func()
}
// 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 machine.Pin) *Device {
func NewDevice(bus drivers.SPI, cs pin.Output) *Device {
return &Device{
bus: bus,
cs: cs,
cs: cs.Set,
configurePins: func() {
pin.ConfigureOutput(cs)
},
}
}
// Configure setups the pins.
func (driver *Device) Configure() {
outPutConfig := machine.PinConfig{Mode: machine.PinOutput}
driver.cs.Configure(outPutConfig)
if driver.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
driver.configurePins()
}
// SetScanLimit sets the scan limit. Maximum is 8.
@@ -89,8 +93,8 @@ func (driver *Device) writeByte(data byte) {
// WriteCommand write data to a given register.
func (driver *Device) WriteCommand(register, data byte) {
driver.cs.Low()
driver.cs(false)
driver.writeByte(register)
driver.writeByte(data)
driver.cs.High()
driver.cs(true)
}
+15 -8
View File
@@ -8,18 +8,19 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
import (
"errors"
"fmt"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
// Device wraps MCP2515 SPI CAN Module.
type Device struct {
spi SPI
cs machine.Pin
msg *CANMsg
mcpMode byte
spi SPI
cs pin.OutputFunc
msg *CANMsg
mcpMode byte
configurePins func()
}
// CANMsg stores CAN message fields.
@@ -36,15 +37,18 @@ const (
)
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
func New(b drivers.SPI, csPin machine.Pin) *Device {
func New(b drivers.SPI, csPin pin.Output) *Device {
d := &Device{
spi: SPI{
bus: b,
tx: make([]byte, 0, bufferSize),
rx: make([]byte, 0, bufferSize),
},
cs: csPin,
cs: csPin.Set,
msg: &CANMsg{},
configurePins: func() {
pin.ConfigureOutput(csPin)
},
}
return d
@@ -52,7 +56,10 @@ func New(b drivers.SPI, csPin machine.Pin) *Device {
// Configure sets up the device for communication.
func (d *Device) Configure() {
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
if d.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.configurePins()
}
const beginTimeoutValue int = 10
+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 machine.Pin
rstPin machine.Pin
scePin machine.Pin
dcPin pin.OutputFunc
rstPin pin.OutputFunc
scePin pin.OutputFunc
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 machine.Pin) *Device {
func New(bus drivers.SPI, dcPin, rstPin, scePin pin.Output) *Device {
return &Device{
bus: bus,
dcPin: dcPin,
rstPin: rstPin,
scePin: scePin,
dcPin: dcPin.Set,
rstPin: rstPin.Set,
scePin: scePin.Set,
}
}
+49
View File
@@ -0,0 +1,49 @@
package seesaw
import (
"errors"
)
var errInvalidEncoderNumber = errors.New("invalid encoder choice, 0-15 are supported")
// GetEncoderPosition returns the absolute position (or delta since the previous call) of the specified rotary encoder.
func (d *Device) GetEncoderPosition(encoder uint, asDelta bool) (int32, error) {
if encoder >= 16 {
return 0, errInvalidEncoderNumber
}
// The function address' upper nibble is the function, the lower nibble selects which encoder to communicate with
fnAddr := FunctionAddress(encoder)
if asDelta {
fnAddr |= FunctionEncoderDelta
} else {
fnAddr |= FunctionEncoderPosition
}
var buf [4]byte
err := d.Read(ModuleEncoderBase, fnAddr, buf[:])
if err != nil {
return 0, err
}
return int32(buf[0])<<24 | int32(buf[1])<<16 | int32(buf[2])<<8 | int32(buf[3]), nil
}
// SetEncoderPosition calibrate's the encoder's current absolute position to be whatever the provided position is.
func (d *Device) SetEncoderPosition(encoder uint, position int32) error {
if encoder >= 16 {
return errInvalidEncoderNumber
}
// The function address' upper nibble is the function, the lower nibble selects which encoder to communicate with
fnAddr := FunctionEncoderPosition | FunctionAddress(encoder)
buf := [4]byte{
byte(position >> 24),
byte(position >> 16),
byte(position >> 8),
byte(position),
}
return d.Write(ModuleEncoderBase, fnAddr, buf[:])
}
+10
View File
@@ -98,3 +98,13 @@ const (
FunctionKeypadCount FunctionAddress = 0x04
FunctionKeypadFifo FunctionAddress = 0x10
)
// encoder module function address registers
// these are the defaults for encoder 0, change the lower nibble to address other encoders
// see the Device.GetEncoderPosition and SetEncoderPosition methods for examples.
const (
FunctionEncoderIntenset FunctionAddress = 0x10
FunctionEncoderIntenclr FunctionAddress = 0x20
FunctionEncoderPosition FunctionAddress = 0x30
FunctionEncoderDelta FunctionAddress = 0x40
)
+23 -17
View File
@@ -2,7 +2,7 @@
package shiftregister
import (
"machine"
"tinygo.org/x/drivers/internal/pin"
)
type NumberBit int8
@@ -16,9 +16,10 @@ const (
// Device holds pin number
type Device struct {
latch, clock, out machine.Pin // IC wiring
bits NumberBit // Pin number
mask uint32 // keep all pins state
latch, clock, out pin.OutputFunc // IC wiring
config func()
bits NumberBit // Pin number
mask uint32 // keep all pins state
}
// ShiftPin is the implementation of the ShiftPin interface.
@@ -29,35 +30,40 @@ type ShiftPin struct {
}
// New returns a new shift output register device
func New(Bits NumberBit, Latch, Clock, Out machine.Pin) *Device {
func New(Bits NumberBit, Latch, Clock, Out pin.Output) *Device {
return &Device{
latch: Latch,
clock: Clock,
out: Out,
latch: Latch.Set,
clock: Clock.Set,
out: Out.Set,
bits: Bits,
config: func() {
pin.ConfigureOutput(Latch)
pin.ConfigureOutput(Clock)
pin.ConfigureOutput(Out)
},
}
}
// Configure set hardware configuration
func (d *Device) Configure() {
d.latch.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.clock.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.out.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.latch.High()
if d.config == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.latch(true)
}
// WriteMask applies mask's bits to register's outputs pin
// mask's MSB set Q1, LSB set Q8 (for 8 bits mask)
func (d *Device) WriteMask(mask uint32) {
d.mask = mask // Keep the mask for individual addressing
d.latch.Low()
d.latch(false)
for i := 0; i < int(d.bits); i++ {
d.clock.Low()
d.out.Set(mask&1 != 0)
d.clock(false)
d.out(mask&1 != 0)
mask = mask >> 1
d.clock.High()
d.clock(true)
}
d.latch.High()
d.latch(true)
}
// GetShiftPin return an individually addressable pin
+7 -3
View File
@@ -44,6 +44,7 @@ tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/lsm303dlhc/main.go
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
@@ -58,15 +59,17 @@ tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/ma
tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw/soil-sensor
tinygo build -size short -o ./build/test.hex -target=qtpy-rp2040 ./examples/seesaw/rotary-encoder
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
tinygo build -size short -o ./build/test.hex -target=pico ./examples/sgp30
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht4x/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
tinygo build -size short -o ./build/test.hex -target=xiao-ble ./examples/ssd1306/
tinygo build -size short -o ./build/test.hex -target=xiao-rp2040 ./examples/ssd1306/
tinygo build -size short -o ./build/test.hex -target=thumby ./examples/ssd1306/
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
@@ -140,6 +143,7 @@ tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/tmc2209/mai
tinygo build -size short -o ./build/test.hex -target=pico ./examples/tmc5160/main.go
tinygo build -size short -o ./build/test.uf2 -target=nicenano ./examples/sharpmem/main.go
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/max6675/main.go
tinygo build -size short -o ./build/test.hex -target=pico ./examples/ens160/main.go
# network examples (espat)
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
# network examples (wifinina)
+2
View File
@@ -1,3 +1,5 @@
//go:build baremetal
package ssd1289
import "machine"
+18 -18
View File
@@ -5,8 +5,9 @@ package ssd1289
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/internal/pin"
)
type Bus interface {
@@ -14,33 +15,32 @@ type Bus interface {
}
type Device struct {
rs machine.Pin
wr machine.Pin
cs machine.Pin
rst machine.Pin
rs pin.OutputFunc
wr pin.OutputFunc
cs pin.OutputFunc
rst pin.OutputFunc
bus Bus
}
const width = int16(240)
const height = int16(320)
func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bus) Device {
func New(rs pin.Output, wr pin.Output, cs pin.Output, rst pin.Output, bus Bus) Device {
d := Device{
rs: rs,
wr: wr,
cs: cs,
rst: rst,
rs: rs.Set,
wr: wr.Set,
cs: cs.Set,
rst: rst.Set,
bus: bus,
}
pin.ConfigureOutput(rs)
pin.ConfigureOutput(wr)
pin.ConfigureOutput(cs)
pin.ConfigureOutput(rst)
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()
d.cs.High()
d.rst.High()
d.wr.High()
return d
}
+29 -139
View File
@@ -6,11 +6,9 @@ package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/pixel"
)
@@ -23,16 +21,15 @@ type ResetValue [2]byte
// Device wraps I2C or SPI connection.
type Device struct {
bus Buser
buffer []byte
width int16
height int16
bufferSize int16
vccState VccMode
canReset bool
resetCol ResetValue
resetPage ResetValue
rotation drivers.Rotation
bus Buser
buffer []byte
width int16
height int16
vccState VccMode
canReset bool
resetCol ResetValue
resetPage ResetValue
rotation drivers.Rotation
}
// Config is the configuration for the display
@@ -51,51 +48,15 @@ type Config struct {
Rotation drivers.Rotation
}
type I2CBus struct {
wire drivers.I2C
Address uint16
}
type SPIBus struct {
wire drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
}
type Buser interface {
configure() error
tx(data []byte, isCommand bool) error
setAddress(address uint16) error
configure(address uint16, size int16) []byte // configure the bus and return the image buffer to use
command(cmd uint8) error // send a command to the display
flush() error // send the image to the display, faster than "tx()" in i2c case since avoids slice copy
tx(data []byte, isCommand bool) error // generic transmit function
}
type VccMode uint8
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
func NewI2C(bus drivers.I2C) Device {
return Device{
bus: &I2CBus{
wire: bus,
Address: Address,
},
}
}
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return Device{
bus: &SPIBus{
wire: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
},
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
var zeroReset ResetValue
@@ -109,9 +70,6 @@ func (d *Device) Configure(cfg Config) {
} else {
d.height = 64
}
if cfg.Address != 0 {
d.bus.setAddress(cfg.Address)
}
if cfg.VccState != 0 {
d.vccState = cfg.VccState
} else {
@@ -127,11 +85,9 @@ func (d *Device) Configure(cfg Config) {
} else {
d.resetPage = ResetValue{0, uint8(d.height/8) - 1}
}
d.bufferSize = d.width * d.height / 8
d.buffer = make([]byte, d.bufferSize)
d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
d.bus.configure()
d.buffer = d.bus.configure(cfg.Address, d.width*d.height/8)
time.Sleep(100 * time.Nanosecond)
d.Command(DISPLAYOFF)
@@ -193,11 +149,22 @@ func (d *Device) Configure(cfg Config) {
d.Command(NORMALDISPLAY)
d.Command(DEACTIVATE_SCROLL)
d.Command(DISPLAYON)
}
// Command sends a command to the display
func (d *Device) Command(command uint8) {
d.bus.command(command)
}
// Tx sends data to the display; if isCommand is false, this also updates the image buffer.
func (d *Device) Tx(data []byte, isCommand bool) error {
return d.bus.tx(data, isCommand)
}
// ClearBuffer clears the image buffer
func (d *Device) ClearBuffer() {
for i := int16(0); i < d.bufferSize; i++ {
for i := 0; i < len(d.buffer); i++ {
d.buffer[i] = 0
}
}
@@ -223,7 +190,7 @@ func (d *Device) Display() error {
d.Command(d.resetPage[1])
}
return d.Tx(d.buffer, false)
return d.bus.flush()
}
// SetPixel enables or disables a pixel in the buffer
@@ -252,12 +219,10 @@ func (d *Device) GetPixel(x int16, y int16) bool {
// SetBuffer changes the whole buffer at once
func (d *Device) SetBuffer(buffer []byte) error {
if int16(len(buffer)) != d.bufferSize {
if len(buffer) != len(d.buffer) {
return errBufferSize
}
for i := int16(0); i < d.bufferSize; i++ {
d.buffer[i] = buffer[i]
}
copy(d.buffer, buffer)
return nil
}
@@ -266,81 +231,6 @@ func (d *Device) GetBuffer() []byte {
return d.buffer
}
// Command sends a command to the display
func (d *Device) Command(command uint8) {
d.bus.tx([]byte{command}, true)
}
// setAddress sets the address to the I2C bus
func (b *I2CBus) setAddress(address uint16) error {
b.Address = address
return nil
}
// setAddress does nothing, but it's required to avoid reflection
func (b *SPIBus) setAddress(address uint16) error {
// do nothing
println("trying to Configure an address on a SPI device")
return nil
}
// configure does nothing, but it's required to avoid reflection
func (b *I2CBus) configure() error { return nil }
// configure configures some pins with the SPI bus
func (b *SPIBus) configure() error {
b.csPin.Low()
b.dcPin.Low()
b.resetPin.Low()
b.resetPin.High()
time.Sleep(1 * time.Millisecond)
b.resetPin.Low()
time.Sleep(10 * time.Millisecond)
b.resetPin.High()
return nil
}
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) error {
return d.bus.tx(data, isCommand)
}
// tx sends data to the display (I2CBus implementation)
func (b *I2CBus) tx(data []byte, isCommand bool) error {
if isCommand {
return legacy.WriteRegister(b.wire, uint8(b.Address), 0x00, data)
} else {
return legacy.WriteRegister(b.wire, uint8(b.Address), 0x40, data)
}
}
// tx sends data to the display (SPIBus implementation)
func (b *SPIBus) tx(data []byte, isCommand bool) error {
var err error
if isCommand {
b.csPin.High()
time.Sleep(1 * time.Millisecond)
b.dcPin.Low()
b.csPin.Low()
err = b.wire.Tx(data, nil)
b.csPin.High()
} else {
b.csPin.High()
time.Sleep(1 * time.Millisecond)
b.dcPin.High()
b.csPin.Low()
err = b.wire.Tx(data, nil)
b.csPin.High()
}
return err
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
+52
View File
@@ -0,0 +1,52 @@
package ssd1306
import (
"tinygo.org/x/drivers"
)
type I2CBus struct {
wire drivers.I2C
address uint16
buffer []byte // buffer to avoid heap allocations
}
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
func NewI2C(bus drivers.I2C) *Device {
return &Device{
bus: &I2CBus{
wire: bus,
address: Address,
},
}
}
// configure address for the I2C bus and allocate the buffer
func (b *I2CBus) configure(address uint16, size int16) []byte {
if address != 0 {
b.address = address
}
b.buffer = make([]byte, size+2) // +1 for the mode and +1 for a command
return b.buffer[2:] // return the image buffer
}
// command sends a command to the display
func (b *I2CBus) command(cmd uint8) error {
b.buffer[0] = 0x00 // Command mode
b.buffer[1] = cmd
return b.wire.Tx(b.address, b.buffer[:2], nil)
}
// flush sends the image to the display
func (b *I2CBus) flush() error {
b.buffer[1] = 0x40 // Data mode
return b.wire.Tx(b.address, b.buffer[1:], nil)
}
// tx sends data to the display
func (b *I2CBus) tx(data []byte, isCommand bool) error {
if isCommand {
return b.command(data[0])
}
copy(b.buffer[2:], data)
return b.flush()
}
+68
View File
@@ -0,0 +1,68 @@
package ssd1306
import (
"machine"
"time"
"tinygo.org/x/drivers"
)
type SPIBus struct {
wire drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
buffer []byte // buffer to avoid heap allocations
}
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) *Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return &Device{
bus: &SPIBus{
wire: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
},
}
}
// configure pins with the SPI bus and allocate the buffer
func (b *SPIBus) configure(address uint16, size int16) []byte {
b.csPin.Low()
b.dcPin.Low()
b.resetPin.Low()
b.resetPin.High()
time.Sleep(1 * time.Millisecond)
b.resetPin.Low()
time.Sleep(10 * time.Millisecond)
b.resetPin.High()
b.buffer = make([]byte, size+1) // +1 for a command
return b.buffer[1:] // return the image buffer
}
// command sends a command to the display
func (b *SPIBus) command(cmd uint8) error {
b.buffer[0] = cmd
return b.tx(b.buffer[:1], true)
}
// flush sends the image to the display
func (b *SPIBus) flush() error {
return b.tx(b.buffer[1:], false)
}
// tx sends data to the display
func (b *SPIBus) tx(data []byte, isCommand bool) error {
b.csPin.High()
b.dcPin.Set(!isCommand)
b.csPin.Low()
err := b.wire.Tx(data, nil)
b.csPin.High()
return err
}
+11 -10
View File
@@ -11,6 +11,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Model uint8
@@ -19,9 +20,9 @@ type Rotation uint8
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
dcPin pin.OutputFunc
resetPin pin.OutputFunc
csPin pin.OutputFunc
width int16
height int16
batchLength int16
@@ -37,14 +38,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 {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(resetPin)
pin.ConfigureOutput(csPin)
return Device{
bus: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: csPin.Set,
}
}
@@ -251,7 +252,7 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.dcPin.Set(!isCommand)
d.dcPin(!isCommand)
d.bus.Tx(data, nil)
}
+31 -24
View File
@@ -6,10 +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/pin"
)
var (
@@ -19,17 +19,18 @@ var (
// Device wraps an SPI connection.
type Device struct {
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
enPin machine.Pin
rwPin machine.Pin
width int16
height int16
rowOffset int16
columnOffset int16
bufferLength int16
bus drivers.SPI
dcPin pin.OutputFunc
resetPin pin.OutputFunc
csPin pin.OutputFunc
enPin pin.OutputFunc
rwPin pin.OutputFunc
configurePins func()
width int16
height int16
rowOffset int16
columnOffset int16
bufferLength int16
}
// Config is the configuration for the display
@@ -41,19 +42,29 @@ type Config struct {
}
// New creates a new SSD1351 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin pin.Output) Device {
return Device{
bus: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
enPin: enPin,
rwPin: rwPin,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: csPin.Set,
enPin: enPin.Set,
rwPin: rwPin.Set,
configurePins: func() {
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(resetPin)
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(enPin)
pin.ConfigureOutput(rwPin)
},
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure(cfg Config) {
if d.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
if cfg.Width == 0 {
cfg.Width = 128
}
@@ -73,11 +84,7 @@ func (d *Device) Configure(cfg Config) {
}
// configure GPIO pins
d.dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.enPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.rwPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.configurePins()
// reset the device
d.resetPin.High()
@@ -278,7 +285,7 @@ func (d *Device) Data(data uint8) {
// Tx sends data to the display
func (d *Device) Tx(data []byte, isCommand bool) {
d.dcPin.Set(!isCommand)
d.dcPin(!isCommand)
d.csPin.Low()
d.bus.Tx(data, nil)
d.csPin.High()
+18 -18
View File
@@ -5,12 +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/pin"
"tinygo.org/x/drivers/pixel"
)
@@ -39,10 +39,10 @@ type Device = DeviceOf[pixel.RGB565BE]
// formats.
type DeviceOf[T Color] struct {
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
dcPin pin.OutputFunc
resetPin pin.OutputFunc
csPin pin.OutputFunc
blPin pin.OutputFunc
width int16
height int16
columnOffset int16
@@ -65,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 machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
}
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI
// wire must already be configured.
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) DeviceOf[T] {
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(resetPin)
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(blPin)
return DeviceOf[T]{
bus: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
blPin: blPin,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: csPin.Set,
blPin: blPin.Set,
}
}
@@ -423,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.Set(!isCommand)
d.dcPin(!isCommand)
d.bus.Tx(data, nil)
}
@@ -438,9 +438,9 @@ func (d *DeviceOf[T]) Size() (w, h int16) {
// EnableBacklight enables or disables the backlight
func (d *DeviceOf[T]) EnableBacklight(enable bool) {
if enable {
d.blPin.High()
d.blPin(true)
} else {
d.blPin.Low()
d.blPin(false)
}
}
+21 -17
View File
@@ -7,13 +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/pin"
"tinygo.org/x/drivers/pixel"
)
@@ -46,10 +46,10 @@ type Device = DeviceOf[pixel.RGB565BE]
// formats.
type DeviceOf[T Color] struct {
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
blPin machine.Pin
dcPin pin.OutputFunc
resetPin pin.OutputFunc
csPin pin.OutputFunc
blPin pin.OutputFunc
width int16
height int16
columnOffsetCfg int16
@@ -83,23 +83,27 @@ type Config struct {
}
// New creates a new ST7789 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
}
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
// wire must already be configured.
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) DeviceOf[T] {
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(resetPin)
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(blPin)
var cs pin.OutputFunc
if !pin.IsNotPin(csPin) {
cs = csPin.Set
}
return DeviceOf[T]{
bus: bus,
dcPin: dcPin,
resetPin: resetPin,
csPin: csPin,
blPin: blPin,
dcPin: dcPin.Set,
resetPin: resetPin.Set,
csPin: cs,
blPin: blPin.Set,
}
}
@@ -229,7 +233,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 != machine.NoPin {
if d.csPin != nil {
d.csPin.Low()
}
}
@@ -237,7 +241,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 != machine.NoPin {
if d.csPin != nil {
d.csPin.High()
}
}
+6 -6
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 machine.Pin // GPIO for reset
rstPin pin.OutputFunc // 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 machine.Pin) *Device {
func New(spi drivers.SPI, rstPin pin.Output) *Device {
k := Device{
spi: spi,
rstPin: rstPin,
rstPin: rstPin.Set,
radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
spiTxBuf: make([]byte, SPI_BUFFER_SIZE),
spiRxBuf: make([]byte, SPI_BUFFER_SIZE),
@@ -67,9 +67,9 @@ func (d *Device) SetRadioController(rc RadioController) error {
// Reset re-initialize the sx127x device
func (d *Device) Reset() {
d.rstPin.Low()
d.rstPin(false)
time.Sleep(100 * time.Millisecond)
d.rstPin.High()
d.rstPin(true)
time.Sleep(100 * time.Millisecond)
}
+7 -15
View File
@@ -57,14 +57,10 @@ func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex ui
byte((value >> 16) & 0xFF), // Middle byte
byte((value >> 8) & 0xFF), // Next byte
byte(value & 0xFF), // LSB of value
0, // CRC
}
// Calculate checksum by XORing all bytes
checksum := byte(0)
for _, b := range buffer[:7] {
checksum ^= b
}
buffer[7] = checksum // Set checksum byte
buffer[7] = CalculateCRC(buffer[:7])
// Write the data to the TMC2209
done := make(chan error, 1)
@@ -86,10 +82,10 @@ func (comm *UARTComm) WriteRegister(register uint8, value uint32, driverIndex ui
// 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
writeBuffer[0] = 0x05 // Sync byte
writeBuffer[1] = 0x00 // Slave address
writeBuffer[2] = register & 0x7F // Read command (MSB clear for read)
writeBuffer[3] = CalculateCRC(writeBuffer[:3])
// Send the read command
done := make(chan []byte, 1)
@@ -103,11 +99,7 @@ func (comm *UARTComm) ReadRegister(register uint8, driverIndex uint8) (uint32, e
// Implementing timeout using a 100ms timer
select {
case readBuffer := <-done:
// Validate checksum
checksum := byte(0)
for i := 0; i < 7; i++ {
checksum ^= readBuffer[i]
}
checksum := CalculateCRC(readBuffer[:7])
if checksum != readBuffer[7] {
return 0, CustomError("checksum error")
}
+17 -17
View File
@@ -8,10 +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/pin"
"tinygo.org/x/drivers/pixel"
)
@@ -31,10 +31,10 @@ type Config struct {
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
width int16
height int16
buffer []uint8
@@ -49,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, 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})
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(rstPin)
pin.ConfigureInput(busyPin)
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
bus: bus,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
}
}
@@ -313,14 +313,14 @@ func (d *Device) ClearDisplay() {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for !d.busy.Get() {
for !d.isBusy() {
time.Sleep(10 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
return d.isBusy()
}
// 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.32.0"
+32 -23
View File
@@ -12,6 +12,9 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Config struct {
@@ -22,14 +25,14 @@ type Config struct {
}
type Device struct {
bus *machine.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
buffer []uint8
rotation Rotation
bus *machine.SPI
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
configurePins func()
buffer []uint8
rotation Rotation
}
type Rotation uint8
@@ -79,22 +82,28 @@ var partialRefresh = [159]uint8{
}
// New returns a new epd1in54 driver. Pass in a fully configured SPI bus.
func New(bus *machine.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
func New(bus *machine.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
return Device{
buffer: make([]uint8, (uint32(Width)*uint32(Height))/8),
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
configurePins: func() {
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(rstPin)
pin.ConfigureInput(busyPin)
},
}
}
func (d *Device) LDirInit(cfg Config) {
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.busy.Configure(machine.PinConfig{Mode: machine.PinInput})
if d.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.configurePins()
d.bus.Configure(machine.SPIConfig{
Frequency: 2000000,
@@ -150,10 +159,10 @@ func (d *Device) LDirInit(cfg Config) {
}
func (d *Device) HDirInit(cfg Config) {
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.busy.Configure(machine.PinConfig{Mode: machine.PinInput})
if d.configurePins == nil {
panic(pin.ErrConfigBeforeInstantiated)
}
d.configurePins()
d.bus.Configure(machine.SPIConfig{
Frequency: 2000000,
@@ -369,7 +378,7 @@ func (d *Device) Clear() {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for d.busy.Get() {
for d.isBusy() {
time.Sleep(100 * time.Millisecond)
}
time.Sleep(200 * time.Millisecond)
@@ -377,7 +386,7 @@ func (d *Device) WaitUntilIdle() {
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
return d.isBusy()
}
// ClearBuffer sets the buffer to 0xFF (white)
+17 -17
View File
@@ -6,10 +6,10 @@ package epd2in13 // import "tinygo.org/x/drivers/waveshare-epd/epd2in13"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Config struct {
@@ -21,10 +21,10 @@ type Config struct {
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
logicalWidth int16
width int16
height int16
@@ -53,17 +53,17 @@ var lutPartialUpdate = [30]uint8{
}
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
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})
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(rstPin)
pin.ConfigureInput(busyPin)
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
bus: bus,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
}
}
@@ -298,14 +298,14 @@ func (d *Device) setMemoryPointer(x int16, y int16) {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for d.busy.Get() {
for d.isBusy() {
time.Sleep(100 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
return d.isBusy()
}
// ClearBuffer sets the buffer to 0xFF (white)
+17 -17
View File
@@ -6,10 +6,10 @@ package epd2in13x // import "tinygo.org/x/drivers/waveshare-epd/epd2in13x"
import (
"errors"
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Config struct {
@@ -20,10 +20,10 @@ type Config struct {
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
width int16
height int16
buffer [][]uint8
@@ -33,17 +33,17 @@ type Device struct {
type Color uint8
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
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})
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(rstPin)
pin.ConfigureInput(busyPin)
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
bus: bus,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
}
}
@@ -277,14 +277,14 @@ func (d *Device) ClearDisplay() {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for !d.busy.Get() {
for !d.isBusy() {
time.Sleep(100 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
return d.isBusy()
}
// ClearBuffer sets the buffer to 0xFF (white)
+7 -29
View File
@@ -5,10 +5,10 @@ package epd2in66b
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
const (
@@ -18,19 +18,12 @@ const (
const Baudrate = 4_000_000 // 4 MHz
type Config struct {
ResetPin machine.Pin
DataPin machine.Pin
ChipSelectPin machine.Pin
BusyPin machine.Pin
}
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
bus drivers.SPI
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
blackBuffer []byte
redBuffer []byte
@@ -50,21 +43,6 @@ func New(bus drivers.SPI) Device {
}
}
// Configure configures the device and its pins.
func (d *Device) Configure(c Config) error {
d.cs = c.ChipSelectPin
d.dc = c.DataPin
d.rst = c.ResetPin
d.busy = c.BusyPin
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.busy.Configure(machine.PinConfig{Mode: machine.PinInput})
return nil
}
func (d *Device) Size() (x, y int16) {
return displayWidth, displayHeight
}
@@ -229,7 +207,7 @@ func (d *Device) WaitUntilIdle() {
// give it some time to get busy
time.Sleep(50 * time.Millisecond)
for d.busy.Get() { // high = busy
for d.isBusy() { // high = busy
time.Sleep(10 * time.Millisecond)
}
+30
View File
@@ -0,0 +1,30 @@
//go:build baremetal
package epd2in66b
import "machine"
type Config struct {
ResetPin machine.Pin
DataPin machine.Pin
ChipSelectPin machine.Pin
BusyPin machine.Pin
}
// Configure configures the device and its pins.
func (d *Device) Configure(c Config) error {
cs := c.ChipSelectPin
dc := c.DataPin
rst := c.ResetPin
busy := c.BusyPin
cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
dc.Configure(machine.PinConfig{Mode: machine.PinOutput})
rst.Configure(machine.PinConfig{Mode: machine.PinOutput})
busy.Configure(machine.PinConfig{Mode: machine.PinInput})
d.cs = cs.Set
d.dc = dc.Set
d.rst = rst.Set
d.isBusy = busy.Get
return nil
}
+18 -18
View File
@@ -13,10 +13,10 @@ package epd2in9 // import "tinygo.org/x/drivers/waveshare-epd/epd2in9"
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Config struct {
@@ -28,10 +28,10 @@ type Config struct {
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
logicalWidth int16
width int16
height int16
@@ -61,17 +61,17 @@ var lutPartialUpdate = [30]uint8{
}
// New returns a new epd2in9 driver. Pass in a fully configured SPI bus.
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})
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(rstPin)
pin.ConfigureInput(busyPin)
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
bus: bus,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
}
}
@@ -122,7 +122,7 @@ func (d *Device) Configure(cfg Config) {
d.SendCommand(DATA_ENTRY_MODE_SETTING)
d.SendData(0x03) // X increment; Y increment
d.SetLUT(true)
d.SetLUT.High()
}
// Reset resets the device
@@ -245,14 +245,14 @@ func (d *Device) setMemoryPointer(x int16, y int16) {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for d.busy.Get() {
for d.isBusy() {
time.Sleep(100 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
return d.isBusy()
}
// ClearBuffer sets the buffer to 0xFF (white)
+17 -17
View File
@@ -10,10 +10,10 @@ package epd4in2
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/pin"
)
type Config struct {
@@ -25,10 +25,10 @@ type Config struct {
type Device struct {
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
busy machine.Pin
cs pin.OutputFunc
dc pin.OutputFunc
rst pin.OutputFunc
isBusy drivers.PinInput
logicalWidth int16
width int16
height int16
@@ -40,17 +40,17 @@ type Device struct {
type Rotation uint8
// New returns a new epd4in2 driver. Pass in a fully configured SPI bus.
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})
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
pin.ConfigureOutput(csPin)
pin.ConfigureOutput(dcPin)
pin.ConfigureOutput(rstPin)
pin.ConfigureInput(busyPin)
return Device{
bus: bus,
cs: csPin,
dc: dcPin,
rst: rstPin,
busy: busyPin,
bus: bus,
cs: csPin.Set,
dc: dcPin.Set,
rst: rstPin.Set,
isBusy: busyPin.Get,
}
}
@@ -311,14 +311,14 @@ func (d *Device) ClearDisplay() {
// WaitUntilIdle waits until the display is ready
func (d *Device) WaitUntilIdle() {
for d.busy.Get() {
for d.isBusy() {
time.Sleep(100 * time.Millisecond)
}
}
// IsBusy returns the busy status of the display
func (d *Device) IsBusy() bool {
return d.busy.Get()
return d.isBusy()
}
// ClearBuffer sets the buffer to 0xFF (white)
+829
View File
@@ -931,6 +931,356 @@ void ws2812_writeByte125(char c, uint32_t *portSet, uint32_t *portClear, uint32_
[portClear]"m"(*portClear));
}
__attribute__((always_inline))
void ws2812_writeByte150(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
// Timings:
// T0H: 53 - 55 cycles or 353.3ns - 366.7ns
// T1H: 158 - 160 cycles or 1053.3ns - 1066.7ns
// TLD: 173 - cycles or 1153.3ns -
uint32_t value = (uint32_t)c << 24;
char i = 8;
__asm__ __volatile__(
"1: @ send_bit\n"
"\t str %[maskSet], %[portSet] @ [2] T0H and T0L start here\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t lsls %[value], #1 @ [1]\n"
"\t bcs.n 2f @ [1/3] skip_store\n"
"\t str %[maskClear], %[portClear] @ [2] T0H -> T0L transition\n"
"\t2: @ skip_store\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t str %[maskClear], %[portClear] @ [2] T1H -> T1L transition\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t subs %[i], #1 @ [1]\n"
"\t beq.n 3f @ [1/3] end\n"
"\t b 1b @ [1/3] send_bit\n"
"\t3: @ end\n"
: [value]"+r"(value),
[i]"+r"(i)
: [maskSet]"r"(maskSet),
[portSet]"m"(*portSet),
[maskClear]"r"(maskClear),
[portClear]"m"(*portClear));
}
__attribute__((always_inline))
void ws2812_writeByte168(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
// Timings:
@@ -1320,6 +1670,465 @@ void ws2812_writeByte168(char c, uint32_t *portSet, uint32_t *portClear, uint32_
[maskClear]"r"(maskClear),
[portClear]"m"(*portClear));
}
__attribute__((always_inline))
void ws2812_writeByte200(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
// Timings:
// T0H: 70 - 72 cycles or 350.0ns - 360.0ns
// T1H: 210 - 212 cycles or 1050.0ns - 1060.0ns
// TLD: 230 - cycles or 1150.0ns -
uint32_t value = (uint32_t)c << 24;
char i = 8;
__asm__ __volatile__(
"1: @ send_bit\n"
"\t str %[maskSet], %[portSet] @ [2] T0H and T0L start here\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t lsls %[value], #1 @ [1]\n"
"\t bcs.n 2f @ [1/3] skip_store\n"
"\t str %[maskClear], %[portClear] @ [2] T0H -> T0L transition\n"
"\t2: @ skip_store\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t str %[maskClear], %[portClear] @ [2] T1H -> T1L transition\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t subs %[i], #1 @ [1]\n"
"\t beq.n 3f @ [1/3] end\n"
"\t b 1b @ [1/3] send_bit\n"
"\t3: @ end\n"
: [value]"+r"(value),
[i]"+r"(i)
: [maskSet]"r"(maskSet),
[portSet]"m"(*portSet),
[maskClear]"r"(maskClear),
[portClear]"m"(*portClear));
}
*/
import "C"
@@ -1373,6 +2182,16 @@ func (d Device) writeByte125(c byte) {
interrupt.Restore(mask)
}
func (d Device) writeByte150(c byte) {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
mask := interrupt.Disable()
C.ws2812_writeByte150(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear))
interrupt.Restore(mask)
}
func (d Device) writeByte168(c byte) {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
@@ -1382,3 +2201,13 @@ func (d Device) writeByte168(c byte) {
interrupt.Restore(mask)
}
func (d Device) writeByte200(c byte) {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
mask := interrupt.Disable()
C.ws2812_writeByte200(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear))
interrupt.Restore(mask)
}
+1 -1
View File
@@ -1,7 +1,7 @@
// Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips.
package ws2812 // import "tinygo.org/x/drivers/ws2812"
//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 125 168
//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 125 150 168 200
//go:generate go run gen-ws2812.go -arch=tinygoriscv 160 320
import (
+7 -1
View File
@@ -28,12 +28,18 @@ func (d Device) WriteByte(c byte) error {
case 120_000_000: // 120MHz
d.writeByte120(c)
return nil
case 125_000_000: // 125 MHz e.g. rp2040
case 125_000_000: // 125 MHz e.g. rp2040 originally
d.writeByte125(c)
return nil
case 150_000_000: // 150MHz, e.g. rp2350
d.writeByte150(c)
return nil
case 168_000_000: // 168MHz, e.g. stm32f405
d.writeByte168(c)
return nil
case 200_000_000: // 200MHz, e.g. rp2040 starting with TinyGo v0.37
d.writeByte200(c)
return nil
default:
return errUnknownClockSpeed
}