mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-05 23:43:41 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38c606d813 | |||
| d02d21ecea | |||
| 805e2a02f8 |
@@ -1,28 +1,3 @@
|
|||||||
0.33.0
|
|
||||||
---
|
|
||||||
- **new devices**
|
|
||||||
- **ens160**
|
|
||||||
- Add ens160 i2c driver
|
|
||||||
- **lsm303dlhc**
|
|
||||||
- added support for LSM303DLHC e-Compass; (#783)
|
|
||||||
- **seesaw**
|
|
||||||
- add support for Adafruit Seesaw encoders
|
|
||||||
|
|
||||||
- **enhancements**
|
|
||||||
- **ws2812**
|
|
||||||
- add RP2350 support
|
|
||||||
- **ssd1306**
|
|
||||||
- avoid unnecessary heap allocations (#767)
|
|
||||||
- **gps**
|
|
||||||
- allow gps init with address
|
|
||||||
- **lsm6ds3tr**
|
|
||||||
- avoid unnecessary heap allocations (#766)
|
|
||||||
|
|
||||||
- **bugfixes**
|
|
||||||
- **gps**
|
|
||||||
- Fix gps time calculation (#785)
|
|
||||||
|
|
||||||
|
|
||||||
0.32.0
|
0.32.0
|
||||||
---
|
---
|
||||||
- **enhancements**
|
- **enhancements**
|
||||||
|
|||||||
+2
-7
@@ -7,8 +7,6 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -38,11 +36,8 @@ func New(b drivers.SPI) *Device {
|
|||||||
|
|
||||||
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
||||||
// implementation of the SPI protocol.
|
// implementation of the SPI protocol.
|
||||||
func NewSoftwareSPI(sckPin, sdoPin pin.Output, delay uint32) *Device {
|
func NewSoftwareSPI(sckPin, sdoPin drivers.Pin, delay uint32) *Device {
|
||||||
return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() {
|
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
|
||||||
legacy.ConfigurePinOut(sckPin)
|
|
||||||
legacy.ConfigurePinOut(sdoPin)
|
|
||||||
}})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
||||||
|
|||||||
+6
-11
@@ -1,8 +1,7 @@
|
|||||||
package apa102
|
package apa102
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
|
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
|
||||||
@@ -11,18 +10,14 @@ import (
|
|||||||
// most purposes other than the APA102 package. It might be desirable to make
|
// 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.
|
// this more generic and include it in the TinyGo "machine" package instead.
|
||||||
type bbSPI struct {
|
type bbSPI struct {
|
||||||
SCK pin.OutputFunc
|
SCK drivers.Pin
|
||||||
SDO pin.OutputFunc
|
SDO drivers.Pin
|
||||||
Delay uint32
|
Delay uint32
|
||||||
configurePins func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the SCK and SDO pins as outputs and sets them low
|
// Configure sets the SCK and SDO pins to low.
|
||||||
|
// Note that the SCK and SDO pins must already be configured as outputs.
|
||||||
func (s *bbSPI) Configure() {
|
func (s *bbSPI) Configure() {
|
||||||
if s.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
s.configurePins()
|
|
||||||
s.SCK.Low()
|
s.SCK.Low()
|
||||||
s.SDO.Low()
|
s.SDO.Low()
|
||||||
if s.Delay == 0 {
|
if s.Delay == 0 {
|
||||||
|
|||||||
+24
-34
@@ -4,45 +4,35 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
|
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
|
||||||
// also an I2C interface, but it is not yet supported.
|
// also an I2C interface, but it is not yet supported.
|
||||||
type DeviceSPI struct {
|
type DeviceSPI struct {
|
||||||
// Chip select pin
|
// Chip select pin
|
||||||
csb pin.OutputFunc
|
CSB drivers.Pin
|
||||||
|
|
||||||
buf [7]byte
|
buf [7]byte
|
||||||
|
|
||||||
// SPI bus (requires chip select to be usable).
|
// 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
|
// 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
|
// touched, provide a fully configured SPI object and call Configure to start
|
||||||
// using this device.
|
// using this device.
|
||||||
func NewSPI(csb pin.Output, spi drivers.SPI) *DeviceSPI {
|
func NewSPI(csb drivers.Pin, spi drivers.SPI) *DeviceSPI {
|
||||||
return &DeviceSPI{
|
return &DeviceSPI{
|
||||||
csb: csb.Set, // chip select
|
CSB: csb, // chip select
|
||||||
bus: spi,
|
Bus: spi,
|
||||||
configurePins: func() {
|
|
||||||
legacy.ConfigurePinOut(csb)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures the BMI160 for use. It configures the CSB pin and
|
// Configure configures the BMI160 for use. The CSB pin anf the SPI interface
|
||||||
// configures the BMI160, but it does not configure the SPI interface (it is
|
// should be configured already. This function configures the BMI160 only.
|
||||||
// assumed to be up and running).
|
|
||||||
func (d *DeviceSPI) Configure() error {
|
func (d *DeviceSPI) Configure() error {
|
||||||
if d.configurePins == nil {
|
d.CSB.High()
|
||||||
return legacy.ErrConfigBeforeInstantiated
|
|
||||||
}
|
|
||||||
d.configurePins()
|
|
||||||
d.csb.High()
|
|
||||||
// The datasheet recommends doing a register read from address 0x7F to get
|
// The datasheet recommends doing a register read from address 0x7F to get
|
||||||
// SPI communication going:
|
// SPI communication going:
|
||||||
// > If CSB sees a rising edge after power-up, the BMI160 interface switches
|
// > If CSB sees a rising edge after power-up, the BMI160 interface switches
|
||||||
@@ -93,9 +83,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
|
|||||||
data[0] = 0x80 | reg_TEMPERATURE_0
|
data[0] = 0x80 | reg_TEMPERATURE_0
|
||||||
data[1] = 0
|
data[1] = 0
|
||||||
data[2] = 0
|
data[2] = 0
|
||||||
d.csb.Low()
|
d.CSB.Low()
|
||||||
err = d.bus.Tx(data, data)
|
err = d.Bus.Tx(data, data)
|
||||||
d.csb.High()
|
d.CSB.High()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -130,9 +120,9 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
|||||||
for i := 1; i < len(data); i++ {
|
for i := 1; i < len(data); i++ {
|
||||||
data[i] = 0
|
data[i] = 0
|
||||||
}
|
}
|
||||||
d.csb.Low()
|
d.CSB.Low()
|
||||||
err = d.bus.Tx(data, data)
|
err = d.Bus.Tx(data, data)
|
||||||
d.csb.High()
|
d.CSB.High()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -160,9 +150,9 @@ func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
|
|||||||
for i := 1; i < len(data); i++ {
|
for i := 1; i < len(data); i++ {
|
||||||
data[i] = 0
|
data[i] = 0
|
||||||
}
|
}
|
||||||
d.csb.Low()
|
d.CSB.Low()
|
||||||
err = d.bus.Tx(data, data)
|
err = d.Bus.Tx(data, data)
|
||||||
d.csb.High()
|
d.CSB.High()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -208,9 +198,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 {
|
|||||||
data := d.buf[:2]
|
data := d.buf[:2]
|
||||||
data[0] = 0x80 | address
|
data[0] = 0x80 | address
|
||||||
data[1] = 0
|
data[1] = 0
|
||||||
d.csb.Low()
|
d.CSB.Low()
|
||||||
d.bus.Tx(data, data)
|
d.Bus.Tx(data, data)
|
||||||
d.csb.High()
|
d.CSB.High()
|
||||||
return data[1]
|
return data[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +214,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) {
|
|||||||
buf[0] = address
|
buf[0] = address
|
||||||
buf[1] = data
|
buf[1] = data
|
||||||
|
|
||||||
d.csb.Low()
|
d.CSB.Low()
|
||||||
d.bus.Tx(buf, buf)
|
d.Bus.Tx(buf, buf)
|
||||||
d.csb.High()
|
d.CSB.High()
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -4,20 +4,20 @@ package buzzer // import "tinygo.org/x/drivers/buzzer"
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
"tinygo.org/x/drivers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps a GPIO connection to a buzzer.
|
// Device wraps a GPIO connection to a buzzer.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
pin pin.OutputFunc
|
pin drivers.Pin
|
||||||
High bool
|
High bool
|
||||||
BPM float64
|
BPM float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new buzzer driver given which pin to use
|
// New returns a new buzzer driver given which pin to use
|
||||||
func New(pin pin.Output) Device {
|
func New(pin drivers.Pin) Device {
|
||||||
return Device{
|
return Device{
|
||||||
pin: pin.Set,
|
pin: pin,
|
||||||
High: false,
|
High: false,
|
||||||
BPM: 96.0,
|
BPM: 96.0,
|
||||||
}
|
}
|
||||||
@@ -25,14 +25,14 @@ func New(pin pin.Output) Device {
|
|||||||
|
|
||||||
// On sets the buzzer to a high state.
|
// On sets the buzzer to a high state.
|
||||||
func (l *Device) On() (err error) {
|
func (l *Device) On() (err error) {
|
||||||
l.pin.High()
|
l.pin.Set(true)
|
||||||
l.High = true
|
l.High = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Off sets the buzzer to a low state.
|
// Off sets the buzzer to a low state.
|
||||||
func (l *Device) Off() (err error) {
|
func (l *Device) Off() (err error) {
|
||||||
l.pin.Low()
|
l.pin.Set(false)
|
||||||
l.High = false
|
l.High = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
package easystepper // import "tinygo.org/x/drivers/easystepper"
|
package easystepper // import "tinygo.org/x/drivers/easystepper"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
"tinygo.org/x/drivers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StepMode determines the coil sequence used to perform a single step
|
// StepMode determines the coil sequence used to perform a single step
|
||||||
@@ -30,10 +31,28 @@ func (sm StepMode) stepCount() uint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeviceConfig contains the configuration data for a single easystepper driver
|
||||||
|
type DeviceConfig struct {
|
||||||
|
// Pin1 ... Pin4 determines the pins to configure and use for the device
|
||||||
|
Pin1, Pin2, Pin3, Pin4 drivers.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 drivers.Pin
|
||||||
|
}
|
||||||
|
|
||||||
// Device holds the pins and the delay between steps
|
// Device holds the pins and the delay between steps
|
||||||
type Device struct {
|
type Device struct {
|
||||||
pins [4]pin.OutputFunc
|
pins [4]drivers.Pin
|
||||||
config func()
|
|
||||||
stepDelay time.Duration
|
stepDelay time.Duration
|
||||||
stepNumber uint8
|
stepNumber uint8
|
||||||
stepMode StepMode
|
stepMode StepMode
|
||||||
@@ -44,6 +63,49 @@ type DualDevice struct {
|
|||||||
devices [2]*Device
|
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]drivers.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4},
|
||||||
|
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
|
||||||
|
stepMode: config.Mode,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure does nothing, as it assumes that the pins of the Device have already
|
||||||
|
// been configured by the user as outputs.
|
||||||
|
func (d *Device) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// Move rotates the motor the number of given steps
|
||||||
// (negative steps will rotate it the opposite direction)
|
// (negative steps will rotate it the opposite direction)
|
||||||
func (d *Device) Move(steps int32) {
|
func (d *Device) Move(steps int32) {
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package easystepper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]pin.OutputFunc) (*Device, error) {
|
|
||||||
if stepcount == 0 || rpm == 0 {
|
|
||||||
return nil, errors.New("zero rpm and/or stepcount")
|
|
||||||
}
|
|
||||||
for i := range pins {
|
|
||||||
if pins[i] == nil {
|
|
||||||
return nil, errors.New("nil pin")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d := &Device{
|
|
||||||
pins: pins,
|
|
||||||
stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)),
|
|
||||||
stepMode: mode,
|
|
||||||
config: func() {},
|
|
||||||
}
|
|
||||||
return d, nil
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
//go:build baremetal
|
|
||||||
|
|
||||||
package easystepper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New returns a new single easystepper driver given a DeviceConfig
|
|
||||||
func New(config DeviceConfig) (*Device, error) {
|
|
||||||
if config.StepCount == 0 || config.RPM == 0 {
|
|
||||||
return nil, errors.New("config.StepCount and config.RPM must be > 0")
|
|
||||||
}
|
|
||||||
return &Device{
|
|
||||||
pins: [4]pin.OutputFunc{config.Pin1.Set, config.Pin2.Set, config.Pin3.Set, config.Pin4.Set},
|
|
||||||
stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)),
|
|
||||||
stepMode: config.Mode,
|
|
||||||
config: func() {
|
|
||||||
legacy.ConfigurePinOut(config.Pin1)
|
|
||||||
legacy.ConfigurePinOut(config.Pin2)
|
|
||||||
legacy.ConfigurePinOut(config.Pin3)
|
|
||||||
legacy.ConfigurePinOut(config.Pin4)
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures the pins of the Device
|
|
||||||
func (d *Device) Configure() {
|
|
||||||
if d.config == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
d.config()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures the pins of the DualDevice
|
|
||||||
func (d *DualDevice) Configure() {
|
|
||||||
d.devices[0].Configure()
|
|
||||||
d.devices[1].Configure()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm
|
|
||||||
func NewDual(config DualDeviceConfig) (*DualDevice, error) {
|
|
||||||
// Create the first device
|
|
||||||
dev1, err := New(config.DeviceConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Create the second device
|
|
||||||
config.DeviceConfig.Pin1 = config.Pin5
|
|
||||||
config.DeviceConfig.Pin2 = config.Pin6
|
|
||||||
config.DeviceConfig.Pin3 = config.Pin7
|
|
||||||
config.DeviceConfig.Pin4 = config.Pin8
|
|
||||||
dev2, err := New(config.DeviceConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Return composite dual device
|
|
||||||
return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceConfig contains the configuration data for a single easystepper driver
|
|
||||||
type DeviceConfig struct {
|
|
||||||
// Pin1 ... Pin4 determines the pins to configure and use for the device
|
|
||||||
Pin1, Pin2, Pin3, Pin4 machine.Pin
|
|
||||||
// StepCount is the number of steps required to perform a full revolution of the stepper motor
|
|
||||||
StepCount uint
|
|
||||||
// RPM determines the speed of the stepper motor in 'Revolutions per Minute'
|
|
||||||
RPM uint
|
|
||||||
// Mode determines the coil sequence used to perform a single step
|
|
||||||
Mode StepMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// DualDeviceConfig contains the configuration data for a dual easystepper driver
|
|
||||||
type DualDeviceConfig struct {
|
|
||||||
DeviceConfig
|
|
||||||
// Pin5 ... Pin8 determines the pins to configure and use for the second device
|
|
||||||
Pin5, Pin6, Pin7, Pin8 machine.Pin
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build tinygo && (rp2040 || rp2350 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
//go:build tinygo && (rp2040 || stm32 || k210 || esp32c3 || nrf || sam || (avr && (atmega328p || atmega328pb)))
|
||||||
|
|
||||||
// Implementation based on:
|
// Implementation based on:
|
||||||
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
|
// https://gist.github.com/aykevl/3fc1683ed77bb0a9c07559dfe857304a
|
||||||
|
|||||||
@@ -1,225 +0,0 @@
|
|||||||
// Package ens160 provides a driver for the ScioSense ENS160 digital gas sensor.
|
|
||||||
//
|
|
||||||
// Datasheet: https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
|
|
||||||
package ens160
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultTimeout = 30 * time.Millisecond
|
|
||||||
shortTimeout = 1 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
// Conversion constants for environment data compensation.
|
|
||||||
const (
|
|
||||||
kelvinOffsetMilli = 273150 // 273.15 K in milli-units
|
|
||||||
tempRawFactor = 64 // As per datasheet for TEMP_IN
|
|
||||||
humRawFactor = 512 // As per datasheet for RH_IN
|
|
||||||
milliFactor = 1000 // For converting from milli-units
|
|
||||||
roundingTerm = milliFactor / 2 // For rounding before integer division
|
|
||||||
)
|
|
||||||
|
|
||||||
// validityStrings provides human-readable descriptions for validity flags.
|
|
||||||
var validityStrings = [...]string{
|
|
||||||
ValidityNormalOperation: "normal operation",
|
|
||||||
ValidityWarmUpPhase: "warm-up phase, wait ~3 minutes for valid data",
|
|
||||||
ValidityInitialStartUpPhase: "initial start-up phase, wait ~1 hour for valid data",
|
|
||||||
ValidityInvalidOutput: "invalid output",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device wraps an I2C connection to an ENS160 device.
|
|
||||||
type Device struct {
|
|
||||||
bus drivers.I2C // I²C implementation
|
|
||||||
addr uint16 // 7‑bit bus address, promoted to uint16 per drivers.I2C
|
|
||||||
|
|
||||||
// shadow registers / last measurements
|
|
||||||
lastTvocPPB uint16
|
|
||||||
lastEco2PPM uint16
|
|
||||||
lastAqiUBA uint8
|
|
||||||
lastValidity uint8 // Store the latest validity status
|
|
||||||
|
|
||||||
// pre‑allocated buffers
|
|
||||||
wbuf [5]byte // longest write: reg + 4 bytes (TEMP+RH)
|
|
||||||
rbuf [5]byte // longest read: DATA burst (5 bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new ENS160 driver.
|
|
||||||
func New(bus drivers.I2C, addr uint16) *Device {
|
|
||||||
if addr == 0 {
|
|
||||||
addr = DefaultAddress
|
|
||||||
}
|
|
||||||
return &Device{
|
|
||||||
bus: bus,
|
|
||||||
addr: addr,
|
|
||||||
lastValidity: ValidityInvalidOutput,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connected returns whether a ENS160 has been found.
|
|
||||||
func (d *Device) Connected() bool {
|
|
||||||
d.wbuf[0] = regPartID
|
|
||||||
err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:2])
|
|
||||||
return err == nil && d.rbuf[0] == LowPartID && d.rbuf[1] == HighPartID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure sets up the device for reading.
|
|
||||||
func (d *Device) Configure() error {
|
|
||||||
// 1. Soft-reset. The device will automatically enter IDLE mode.
|
|
||||||
if err := d.write1(regOpMode, ModeReset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(defaultTimeout)
|
|
||||||
|
|
||||||
// 2. Clear GPR registers, then go to STANDARD mode.
|
|
||||||
if err := d.write1(regCommand, cmdClrGPR); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(defaultTimeout)
|
|
||||||
|
|
||||||
if err := d.write1(regOpMode, ModeStandard); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(defaultTimeout)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculateTempRaw converts temperature from milli-degrees Celsius to the sensor's raw format.
|
|
||||||
func calculateTempRaw(tempMilliC int32) uint16 {
|
|
||||||
// Clip temperature
|
|
||||||
const (
|
|
||||||
minC = -40 * 1000
|
|
||||||
maxC = 85 * 1000
|
|
||||||
)
|
|
||||||
if tempMilliC < minC {
|
|
||||||
tempMilliC = minC
|
|
||||||
} else if tempMilliC > maxC {
|
|
||||||
tempMilliC = maxC
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer fixed-point conversion to format required by the sensor.
|
|
||||||
// Formula from datasheet: T_IN = (T_ambient_C + 273.15) * 64
|
|
||||||
return uint16((((tempMilliC + kelvinOffsetMilli) * tempRawFactor) + roundingTerm) / milliFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculateHumRaw converts relative humidity from milli-percent to the sensor's raw format.
|
|
||||||
func calculateHumRaw(rhMilliPct int32) uint16 {
|
|
||||||
// Clip humidity
|
|
||||||
if rhMilliPct < 0 {
|
|
||||||
rhMilliPct = 0
|
|
||||||
} else if rhMilliPct > 100*1000 {
|
|
||||||
rhMilliPct = 100 * 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer fixed-point conversion to format required by the sensor.
|
|
||||||
// Formula from datasheet: RH_IN = (RH_ambient_% * 512)
|
|
||||||
return uint16(((rhMilliPct * humRawFactor) + roundingTerm) / milliFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEnvDataMilli sets the ambient temperature and humidity for compensation.
|
|
||||||
//
|
|
||||||
// tempMilliC is the temperature in milli-degrees Celsius.
|
|
||||||
// rhMilliPct is the relative humidity in milli-percent.
|
|
||||||
func (d *Device) SetEnvDataMilli(tempMilliC, rhMilliPct int32) error {
|
|
||||||
tempRaw := calculateTempRaw(tempMilliC)
|
|
||||||
humRaw := calculateHumRaw(rhMilliPct)
|
|
||||||
|
|
||||||
d.wbuf[0] = regTempIn // start address (auto‑increment)
|
|
||||||
binary.LittleEndian.PutUint16(d.wbuf[1:3], tempRaw)
|
|
||||||
binary.LittleEndian.PutUint16(d.wbuf[3:5], humRaw)
|
|
||||||
|
|
||||||
return d.bus.Tx(d.addr, d.wbuf[:5], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update refreshes the concentration measurements.
|
|
||||||
func (d *Device) Update(which drivers.Measurement) error {
|
|
||||||
if which&drivers.Concentration == 0 {
|
|
||||||
return nil // nothing requested
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxTries = 1000
|
|
||||||
var (
|
|
||||||
status uint8
|
|
||||||
validity uint8
|
|
||||||
)
|
|
||||||
var gotData bool
|
|
||||||
|
|
||||||
// Poll DEVICE_STATUS until NEWDAT or timeout
|
|
||||||
for range maxTries {
|
|
||||||
var err error
|
|
||||||
status, err = d.read1(regStatus)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if status&statusSTATER != 0 {
|
|
||||||
return errors.New("ENS160: error (STATER set)")
|
|
||||||
}
|
|
||||||
validity = (status & statusValidityMask) >> statusValidityShift
|
|
||||||
|
|
||||||
if status&statusNEWDAT != 0 {
|
|
||||||
gotData = true
|
|
||||||
break // Always break when data available
|
|
||||||
}
|
|
||||||
time.Sleep(shortTimeout)
|
|
||||||
}
|
|
||||||
if !gotData {
|
|
||||||
return errors.New("ENS160: timeout waiting for NEWDAT")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Burst-read data regardless of validity state
|
|
||||||
d.wbuf[0] = regAQI
|
|
||||||
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:5]); err != nil {
|
|
||||||
return errors.New("ENS160: burst read failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
d.lastAqiUBA = d.rbuf[0]
|
|
||||||
d.lastTvocPPB = binary.LittleEndian.Uint16(d.rbuf[1:3])
|
|
||||||
d.lastEco2PPM = binary.LittleEndian.Uint16(d.rbuf[3:5])
|
|
||||||
d.lastValidity = validity // Store the validity status
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TVOC returns the last total‑VOC concentration in parts‑per‑billion.
|
|
||||||
func (d *Device) TVOC() uint16 { return d.lastTvocPPB }
|
|
||||||
|
|
||||||
// ECO2 returns the last equivalent CO₂ concentration in parts‑per‑million.
|
|
||||||
func (d *Device) ECO2() uint16 { return d.lastEco2PPM }
|
|
||||||
|
|
||||||
// AQI returns the last Air‑Quality Index according to UBA (1–5).
|
|
||||||
func (d *Device) AQI() uint8 { return d.lastAqiUBA }
|
|
||||||
|
|
||||||
// Validity returns the current operating state of the sensor.
|
|
||||||
func (d *Device) Validity() uint8 {
|
|
||||||
return d.lastValidity
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidityString returns a human-readable string describing the current validity status.
|
|
||||||
func (d *Device) ValidityString() string {
|
|
||||||
if int(d.lastValidity) < len(validityStrings) {
|
|
||||||
return validityStrings[d.lastValidity]
|
|
||||||
}
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
// write1 writes a single byte to a register.
|
|
||||||
func (d *Device) write1(reg, val uint8) error {
|
|
||||||
d.wbuf[0] = reg
|
|
||||||
d.wbuf[1] = val
|
|
||||||
return d.bus.Tx(d.addr, d.wbuf[:2], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read1 reads a single byte from a register.
|
|
||||||
func (d *Device) read1(reg uint8) (uint8, error) {
|
|
||||||
d.wbuf[0] = reg
|
|
||||||
if err := d.bus.Tx(d.addr, d.wbuf[:1], d.rbuf[:1]); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return d.rbuf[0], nil
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package ens160
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCalculateTempRaw(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
tempMilliC int32
|
|
||||||
expectedRaw uint16
|
|
||||||
}{
|
|
||||||
{"25°C", 25000, 19082},
|
|
||||||
{"-10.5°C", -10500, 16810},
|
|
||||||
{"Min temp", -40000, 14922},
|
|
||||||
{"Below min", -50000, 14922},
|
|
||||||
{"Max temp", 85000, 22922},
|
|
||||||
{"Above max", 90000, 22922},
|
|
||||||
{"Zero", 0, 17482},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
raw := calculateTempRaw(tc.tempMilliC)
|
|
||||||
if raw != tc.expectedRaw {
|
|
||||||
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCalculateHumRaw(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
rhMilliPct int32
|
|
||||||
expectedRaw uint16
|
|
||||||
}{
|
|
||||||
{"50%", 50000, 25600},
|
|
||||||
{"0%", 0, 0},
|
|
||||||
{"100%", 100000, 51200},
|
|
||||||
{"Below 0%", -10000, 0},
|
|
||||||
{"Above 100%", 110000, 51200},
|
|
||||||
{"33.3%", 33300, 17050},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
raw := calculateHumRaw(tc.rhMilliPct)
|
|
||||||
if raw != tc.expectedRaw {
|
|
||||||
t.Errorf("expected %d, got %d", tc.expectedRaw, raw)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package ens160
|
|
||||||
|
|
||||||
// DefaultAddress is the default I2C address for the ENS160 when the ADDR pin is
|
|
||||||
// connected to high (3.3V). When connected to low (GND), the address is 0x52.
|
|
||||||
const DefaultAddress = 0x53
|
|
||||||
|
|
||||||
// Registers
|
|
||||||
const (
|
|
||||||
regPartID = 0x00
|
|
||||||
regOpMode = 0x10
|
|
||||||
regConfig = 0x11
|
|
||||||
regCommand = 0x12
|
|
||||||
regTempIn = 0x13
|
|
||||||
regRhIn = 0x15
|
|
||||||
regStatus = 0x20
|
|
||||||
regAQI = 0x21
|
|
||||||
regTVOC = 0x22
|
|
||||||
regECO2 = 0x24
|
|
||||||
regDataT = 0x30
|
|
||||||
regDataRH = 0x32
|
|
||||||
regMISR = 0x38
|
|
||||||
regGPRWrite = 0x40
|
|
||||||
regGPRRead = 0x48
|
|
||||||
)
|
|
||||||
|
|
||||||
// Operating modes
|
|
||||||
const (
|
|
||||||
ModeDeepSleep = 0x00
|
|
||||||
ModeIdle = 0x01
|
|
||||||
ModeStandard = 0x02
|
|
||||||
ModeReset = 0xF0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Status register bits
|
|
||||||
const (
|
|
||||||
statusSTATAS = 1 << 7
|
|
||||||
statusSTATER = 1 << 6
|
|
||||||
|
|
||||||
statusValidityMask = 0x0C
|
|
||||||
statusValidityShift = 2
|
|
||||||
|
|
||||||
statusNEWDAT = 1 << 1
|
|
||||||
statusNEWGPR = 1 << 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validity flags
|
|
||||||
const (
|
|
||||||
ValidityNormalOperation = 0x00
|
|
||||||
ValidityWarmUpPhase = 0x01 // need ~3 minutes until valid data
|
|
||||||
ValidityInitialStartUpPhase = 0x02 // need ~1 hour until valid data
|
|
||||||
ValidityInvalidOutput = 0x03
|
|
||||||
)
|
|
||||||
|
|
||||||
// Commands
|
|
||||||
const (
|
|
||||||
cmdNOP = 0x00
|
|
||||||
cmdGetAppVer = 0x0E
|
|
||||||
cmdClrGPR = 0xCC
|
|
||||||
)
|
|
||||||
|
|
||||||
// Part IDs
|
|
||||||
const (
|
|
||||||
LowPartID = 0x60
|
|
||||||
HighPartID = 0x01
|
|
||||||
)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
// This example demonstrates ENS160 usage.
|
|
||||||
//
|
|
||||||
// Wiring:
|
|
||||||
// - VCC to 3.3V, GND to ground
|
|
||||||
// - SDA to board SDA, SCL to board SCL
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/ens160"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
err := machine.I2C0.Configure(machine.I2CConfig{
|
|
||||||
Frequency: 400 * machine.KHz,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
println("Failed to configure I2C:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dev := ens160.New(machine.I2C0, ens160.DefaultAddress)
|
|
||||||
|
|
||||||
connected := dev.Connected()
|
|
||||||
if !connected {
|
|
||||||
println("ENS160 not detected")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
println("ENS160 detected")
|
|
||||||
|
|
||||||
if err := dev.Configure(); err != nil {
|
|
||||||
println("Failed to configure ENS160:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
err := dev.Update(drivers.Concentration)
|
|
||||||
if err != nil {
|
|
||||||
println("Error reading ENS160: %v\n", err)
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
println(
|
|
||||||
"AQI:", dev.AQI(),
|
|
||||||
"TVOC:", dev.TVOC(),
|
|
||||||
"eCO2:", dev.ECO2(),
|
|
||||||
"Validity:", dev.ValidityString(),
|
|
||||||
)
|
|
||||||
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/honeyhsc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Data taken from https://github.com/rodan/honeywell_hsc_ssc_i2c/blob/master/hsc_ssc_i2c.cpp
|
|
||||||
// these defaults are valid for the HSCMRNN030PA2A3 chip
|
|
||||||
const (
|
|
||||||
i2cAddress = 0x28
|
|
||||||
// 10%
|
|
||||||
outputMinimum = 0x666
|
|
||||||
// 90% of 2^14 - 1
|
|
||||||
outputMax = 0x399A
|
|
||||||
// min is 0 for sensors that give absolute values
|
|
||||||
pressureMin = 0
|
|
||||||
// 30psi (and we want results in millipascals)
|
|
||||||
// pressureMax = 206842.7
|
|
||||||
pressureMax = 206843 * 1000
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
bus := machine.I2C0
|
|
||||||
err := bus.Configure(machine.I2CConfig{
|
|
||||||
Frequency: 400_000, // 100kHz minimum and 400kHz I2C maximum clock. 50 to 800 for SPI.
|
|
||||||
SDA: machine.I2C0_SDA_PIN,
|
|
||||||
SCL: machine.I2C0_SCL_PIN,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
sensor := honeyhsc.NewDevI2C(bus, i2cAddress, outputMinimum, outputMax, pressureMin, pressureMax)
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
const measuremask = drivers.Pressure | drivers.Temperature
|
|
||||||
err := sensor.Update(measuremask)
|
|
||||||
if err != nil {
|
|
||||||
println("error updating measurements:", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
P := sensor.Pressure()
|
|
||||||
T := sensor.Temperature()
|
|
||||||
println("pressure:", P, "temperature:", T)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-12
@@ -14,18 +14,9 @@ func main() {
|
|||||||
i2c.Configure(machine.I2CConfig{SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN})
|
i2c.Configure(machine.I2CConfig{SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN})
|
||||||
|
|
||||||
accel := lis3dh.New(i2c)
|
accel := lis3dh.New(i2c)
|
||||||
err := accel.Configure(lis3dh.Config{
|
accel.Address = lis3dh.Address1 // address on the Circuit Playground Express
|
||||||
Address: lis3dh.Address1, // address on the Circuit Playground Express
|
accel.Configure()
|
||||||
})
|
accel.SetRange(lis3dh.RANGE_2_G)
|
||||||
for err != nil {
|
|
||||||
println("could not configure LIS3DH:", err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
err = accel.SetRange(lis3dh.RANGE_2_G)
|
|
||||||
for err != nil {
|
|
||||||
println("could not set acceleration range:", err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
println(accel.Connected())
|
println(accel.Connected())
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/lsm303dlhc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// LSM303DLHC is connected to the I2C0 bus on Adafruit Feather M4 via pins: 20(SDA) and 21(SCL).
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{})
|
|
||||||
|
|
||||||
sensor := lsm303dlhc.New(machine.I2C0)
|
|
||||||
//default settings
|
|
||||||
err := sensor.Configure(lsm303dlhc.Configuration{
|
|
||||||
AccelPowerMode: lsm303dlhc.ACCEL_POWER_NORMAL,
|
|
||||||
AccelRange: lsm303dlhc.ACCEL_RANGE_2G,
|
|
||||||
AccelDataRate: lsm303dlhc.ACCEL_DATARATE_100HZ,
|
|
||||||
MagPowerMode: lsm303dlhc.MAG_POWER_NORMAL,
|
|
||||||
MagSystemMode: lsm303dlhc.MAG_SYSTEM_CONTINUOUS,
|
|
||||||
MagDataRate: lsm303dlhc.MAG_DATARATE_10HZ,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
for {
|
|
||||||
println("Failed to configure", err.Error())
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
accel_x, accel_y, accel_z, err := sensor.ReadAcceleration()
|
|
||||||
if err != nil {
|
|
||||||
println("Failed to read accel", err.Error())
|
|
||||||
}
|
|
||||||
println("ACCEL_X:", accel_x, " ACCEL_Y:", accel_y, " ACCEL_Z:", accel_z)
|
|
||||||
|
|
||||||
mag_x, mag_y, mag_z, err := sensor.ReadMagneticField()
|
|
||||||
if err != nil {
|
|
||||||
println("Failed to read mag", err.Error())
|
|
||||||
}
|
|
||||||
println("MAG_X:", mag_x, " MAG_Y:", mag_y, " MAG_Z:", mag_z)
|
|
||||||
|
|
||||||
pitch, roll, _ := sensor.ReadPitchRoll()
|
|
||||||
println("Pitch:", float32(pitch), " Roll:", float32(roll))
|
|
||||||
|
|
||||||
heading, _ := sensor.ReadCompass()
|
|
||||||
println("Heading:", float32(heading), "degrees")
|
|
||||||
|
|
||||||
temp, _ := sensor.ReadTemperature()
|
|
||||||
println("Temperature:", float32(temp)/1000, "*C")
|
|
||||||
|
|
||||||
println("\n")
|
|
||||||
time.Sleep(time.Millisecond * 250)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/seesaw"
|
|
||||||
)
|
|
||||||
|
|
||||||
// example reading the position of a rotary encoder (4991) powered by a seesaw
|
|
||||||
// https://learn.adafruit.com/adafruit-i2c-qt-rotary-encoder/arduino
|
|
||||||
func main() {
|
|
||||||
// This assumes you are using an Adafruit QT Py RP2040 for its Stemma QT connector
|
|
||||||
// https://www.adafruit.com/product/4900
|
|
||||||
i2c := machine.I2C1
|
|
||||||
i2c.Configure(machine.I2CConfig{
|
|
||||||
SCL: machine.I2C1_QT_SCL_PIN,
|
|
||||||
SDA: machine.I2C1_QT_SDA_PIN,
|
|
||||||
})
|
|
||||||
|
|
||||||
dev := seesaw.New(i2c)
|
|
||||||
dev.Address = 0x36
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
pos, err := dev.GetEncoderPosition(0, false)
|
|
||||||
if err != nil {
|
|
||||||
println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
println(pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/si5351"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Simple demo of the SI5351 clock generator.
|
|
||||||
// This is like the Arduino library example:
|
|
||||||
// https://github.com/adafruit/Adafruit_Si5351_Library/blob/master/examples/si5351/si5351.ino
|
|
||||||
// Which will configure the chip with:
|
|
||||||
// - PLL A at 900mhz
|
|
||||||
// - PLL B at 616.66667mhz
|
|
||||||
// - Clock 0 at 112.5mhz, using PLL A as a source divided by 8
|
|
||||||
// - Clock 1 at 13.5531mhz, using PLL B as a source divided by 45.5
|
|
||||||
// - Clock 2 at 10.76khz, using PLL B as a source divided by 900 and further divided with an R divider of 64.
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
println("Si5351 Clockgen Test")
|
|
||||||
println()
|
|
||||||
|
|
||||||
// Configure I2C bus
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{})
|
|
||||||
|
|
||||||
// Create driver instance
|
|
||||||
clockgen := si5351.New(machine.I2C0)
|
|
||||||
|
|
||||||
// Verify device wired properly
|
|
||||||
connected, err := clockgen.Connected()
|
|
||||||
if err != nil {
|
|
||||||
println("Unable to read device status")
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
if !connected {
|
|
||||||
for {
|
|
||||||
println("Unable to detect si5351 device")
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise device
|
|
||||||
clockgen.Configure()
|
|
||||||
|
|
||||||
// Now configue the PLLs and clock outputs.
|
|
||||||
// The PLLs can be configured with a multiplier and division of the on-board
|
|
||||||
// 25mhz reference crystal. For example configure PLL A to 900mhz by multiplying
|
|
||||||
// by 36. This uses an integer multiplier which is more accurate over time
|
|
||||||
// but allows less of a range of frequencies compared to a fractional
|
|
||||||
// multiplier shown next.
|
|
||||||
clockgen.ConfigurePLL(si5351.PLL_A, 36, 0, 1) // Multiply 25mhz by 36
|
|
||||||
println("PLL A frequency: 900mhz")
|
|
||||||
|
|
||||||
// And next configure PLL B to 616.6667mhz by multiplying 25mhz by 24.667 using
|
|
||||||
// the fractional multiplier configuration. Notice you specify the integer
|
|
||||||
// multiplier and then a numerator and denominator as separate values, i.e.
|
|
||||||
// numerator 2 and denominator 3 means 2/3 or 0.667. This fractional
|
|
||||||
// configuration is susceptible to some jitter over time but can set a larger
|
|
||||||
// range of frequencies.
|
|
||||||
clockgen.ConfigurePLL(si5351.PLL_B, 24, 2, 3) // Multiply 25mhz by 24.667 (24 2/3)
|
|
||||||
println("PLL B frequency: 616.6667mhz")
|
|
||||||
|
|
||||||
// Now configure the clock outputs. Each is driven by a PLL frequency as input
|
|
||||||
// and then further divides that down to a specific frequency.
|
|
||||||
// Configure clock 0 output to be driven by PLL A divided by 8, so an output
|
|
||||||
// of 112.5mhz (900mhz / 8). Again this uses the most precise integer division
|
|
||||||
// but can't set as wide a range of values.
|
|
||||||
clockgen.ConfigureMultisynth(0, si5351.PLL_A, 8, 0, 1) // Divide by 8 (8 0/1)
|
|
||||||
println("Clock 0: 112.5mhz")
|
|
||||||
|
|
||||||
// Next configure clock 1 to be driven by PLL B divided by 45.5 to get
|
|
||||||
// 13.5531mhz (616.6667mhz / 45.5). This uses fractional division and again
|
|
||||||
// notice the numerator and denominator are explicitly specified. This is less
|
|
||||||
// precise but allows a large range of frequencies.
|
|
||||||
clockgen.ConfigureMultisynth(1, si5351.PLL_B, 45, 1, 2) // Divide by 45.5 (45 1/2)
|
|
||||||
println("Clock 1: 13.5531mhz")
|
|
||||||
|
|
||||||
// Finally configure clock 2 to be driven by PLL B divided once by 900 to get
|
|
||||||
// down to 685.15 khz and then further divided by a special R divider that
|
|
||||||
// divides 685.15 khz by 64 to get a final output of 10.706khz.
|
|
||||||
clockgen.ConfigureMultisynth(2, si5351.PLL_B, 900, 0, 1) // Divide by 900 (900 0/1)
|
|
||||||
// Set the R divider, this can be a value of:
|
|
||||||
// - R_DIV_1: divider of 1
|
|
||||||
// - R_DIV_2: divider of 2
|
|
||||||
// - R_DIV_4: divider of 4
|
|
||||||
// - R_DIV_8: divider of 8
|
|
||||||
// - R_DIV_16: divider of 16
|
|
||||||
// - R_DIV_32: divider of 32
|
|
||||||
// - R_DIV_64: divider of 64
|
|
||||||
// - R_DIV_128: divider of 128
|
|
||||||
clockgen.ConfigureRdiv(2, si5351.R_DIV_64)
|
|
||||||
println("Clock 2: 10.706khz")
|
|
||||||
|
|
||||||
// After configuring PLLs and clocks, enable the outputs.
|
|
||||||
clockgen.EnableOutputs()
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
println()
|
|
||||||
println("Clock 0: 112.5mhz")
|
|
||||||
println("Clock 1: 13.5531mhz")
|
|
||||||
println("Clock 2: 10.706khz")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"image/color"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.I2C0.Configure(machine.I2CConfig{
|
||||||
|
Frequency: machine.TWI_FREQ_400KHZ,
|
||||||
|
})
|
||||||
|
|
||||||
|
display := ssd1306.NewI2C(machine.I2C0)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Address: ssd1306.Address_128_32,
|
||||||
|
Width: 128,
|
||||||
|
Height: 32,
|
||||||
|
})
|
||||||
|
|
||||||
|
display.ClearDisplay()
|
||||||
|
|
||||||
|
x := int16(0)
|
||||||
|
y := int16(0)
|
||||||
|
deltaX := int16(1)
|
||||||
|
deltaY := int16(1)
|
||||||
|
for {
|
||||||
|
pixel := display.GetPixel(x, y)
|
||||||
|
c := color.RGBA{255, 255, 255, 255}
|
||||||
|
if pixel {
|
||||||
|
c = color.RGBA{0, 0, 0, 255}
|
||||||
|
}
|
||||||
|
display.SetPixel(x, y, c)
|
||||||
|
display.Display()
|
||||||
|
|
||||||
|
x += deltaX
|
||||||
|
y += deltaY
|
||||||
|
|
||||||
|
if x == 0 || x == 127 {
|
||||||
|
deltaX = -deltaX
|
||||||
|
}
|
||||||
|
|
||||||
|
if y == 0 || y == 31 {
|
||||||
|
deltaY = -deltaY
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// This example shows how to use 128x64 display over I2C
|
||||||
|
// Tested on Seeeduino XIAO Expansion Board https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
|
||||||
|
//
|
||||||
|
// According to manual, I2C address of the display is 0x78, but that's 8-bit address.
|
||||||
|
// TinyGo operates on 7-bit addresses and respective 7-bit address would be 0x3C, which we use below.
|
||||||
|
//
|
||||||
|
// To learn more about different types of I2C addresses, please see following page
|
||||||
|
// https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"image/color"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.I2C0.Configure(machine.I2CConfig{
|
||||||
|
Frequency: machine.TWI_FREQ_400KHZ,
|
||||||
|
})
|
||||||
|
|
||||||
|
display := ssd1306.NewI2C(machine.I2C0)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Address: 0x3C,
|
||||||
|
Width: 128,
|
||||||
|
Height: 64,
|
||||||
|
})
|
||||||
|
|
||||||
|
display.ClearDisplay()
|
||||||
|
|
||||||
|
x := int16(0)
|
||||||
|
y := int16(0)
|
||||||
|
deltaX := int16(1)
|
||||||
|
deltaY := int16(1)
|
||||||
|
for {
|
||||||
|
pixel := display.GetPixel(x, y)
|
||||||
|
c := color.RGBA{255, 255, 255, 255}
|
||||||
|
if pixel {
|
||||||
|
c = color.RGBA{0, 0, 0, 255}
|
||||||
|
}
|
||||||
|
display.SetPixel(x, y, c)
|
||||||
|
display.Display()
|
||||||
|
|
||||||
|
x += deltaX
|
||||||
|
y += deltaY
|
||||||
|
|
||||||
|
if x == 0 || x == 127 {
|
||||||
|
deltaX = -deltaX
|
||||||
|
}
|
||||||
|
|
||||||
|
if y == 0 || y == 63 {
|
||||||
|
deltaY = -deltaY
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// This example shows how to use SSD1306 OLED display driver over I2C and SPI.
|
|
||||||
//
|
|
||||||
// Check the `newSSD1306Display()` functions for I2C and SPI initializations.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"image/color"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
display := newSSD1306Display()
|
|
||||||
display.ClearDisplay()
|
|
||||||
|
|
||||||
w, h := display.Size()
|
|
||||||
x := int16(0)
|
|
||||||
y := int16(0)
|
|
||||||
deltaX := int16(1)
|
|
||||||
deltaY := int16(1)
|
|
||||||
|
|
||||||
traceTime := time.Now().UnixMilli() + 1000
|
|
||||||
frames := 0
|
|
||||||
ms := runtime.MemStats{}
|
|
||||||
|
|
||||||
for {
|
|
||||||
pixel := display.GetPixel(x, y)
|
|
||||||
c := color.RGBA{255, 255, 255, 255}
|
|
||||||
if pixel {
|
|
||||||
c = color.RGBA{0, 0, 0, 255}
|
|
||||||
}
|
|
||||||
display.SetPixel(x, y, c)
|
|
||||||
display.Display()
|
|
||||||
|
|
||||||
x += deltaX
|
|
||||||
y += deltaY
|
|
||||||
|
|
||||||
if x == 0 || x == w-1 {
|
|
||||||
deltaX = -deltaX
|
|
||||||
}
|
|
||||||
|
|
||||||
if y == 0 || y == h-1 {
|
|
||||||
deltaY = -deltaY
|
|
||||||
}
|
|
||||||
|
|
||||||
frames++
|
|
||||||
now := time.Now().UnixMilli()
|
|
||||||
if now >= traceTime {
|
|
||||||
runtime.ReadMemStats(&ms)
|
|
||||||
println("TS", now, "| FPS", frames, "| HeapInuse", ms.HeapInuse)
|
|
||||||
traceTime = now + 1000
|
|
||||||
frames = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
//go:build xiao_ble
|
|
||||||
|
|
||||||
// This initializes SSD1306 OLED display driver over I2C.
|
|
||||||
//
|
|
||||||
// Seeed XIAO BLE board + SSD1306 128x32 I2C OLED display.
|
|
||||||
//
|
|
||||||
// Wiring:
|
|
||||||
// - XIAO GND -> OLED GND
|
|
||||||
// - XIAO 3v3 -> OLED VCC
|
|
||||||
// - XIAO D4 (SDA) -> OLED SDA
|
|
||||||
// - XIAO D5 (SCL) -> OLED SCK
|
|
||||||
//
|
|
||||||
// For your case:
|
|
||||||
// - Connect the display to I2C pins on your board.
|
|
||||||
// - Adjust I2C address and display size as needed.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newSSD1306Display() *ssd1306.Device {
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{
|
|
||||||
Frequency: 400 * machine.KHz,
|
|
||||||
SDA: machine.SDA0_PIN,
|
|
||||||
SCL: machine.SCL0_PIN,
|
|
||||||
})
|
|
||||||
display := ssd1306.NewI2C(machine.I2C0)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Address: ssd1306.Address_128_32, // or ssd1306.Address
|
|
||||||
Width: 128,
|
|
||||||
Height: 32, // or 64
|
|
||||||
})
|
|
||||||
return display
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//go:build thumby
|
|
||||||
|
|
||||||
// This initializes SSD1306 OLED display driver over SPI.
|
|
||||||
//
|
|
||||||
// Thumby board has a tiny built-in 72x40 display.
|
|
||||||
//
|
|
||||||
// As the display is built-in, no wiring is needed.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newSSD1306Display() *ssd1306.Device {
|
|
||||||
machine.SPI0.Configure(machine.SPIConfig{})
|
|
||||||
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Width: 72,
|
|
||||||
Height: 40,
|
|
||||||
ResetCol: ssd1306.ResetValue{28, 99},
|
|
||||||
ResetPage: ssd1306.ResetValue{0, 5},
|
|
||||||
})
|
|
||||||
return display
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
//go:build xiao_rp2040
|
|
||||||
|
|
||||||
// This initializes SSD1306 OLED display driver over SPI.
|
|
||||||
//
|
|
||||||
// Seeed XIAO RP2040 board + SSD1306 128x64 SPI OLED display.
|
|
||||||
//
|
|
||||||
// Wiring:
|
|
||||||
// - XIAO GND -> OLED GND
|
|
||||||
// - XIAO 3v3 -> OLED VCC
|
|
||||||
// - XIAO D8 (SCK) -> OLED D0
|
|
||||||
// - XIAO D10 (SDO) -> OLED D1
|
|
||||||
// - XIAO D4 -> OLED RES
|
|
||||||
// - XIAO D5 -> OLED DC
|
|
||||||
// - XIAO D6 -> OLED CS
|
|
||||||
//
|
|
||||||
// For your case:
|
|
||||||
// - Connect the display to SPI pins on your board.
|
|
||||||
// - Adjust RES, DC and CS pins as needed.
|
|
||||||
// - Adjust SPI frequency as needed.
|
|
||||||
// - Adjust display size as needed.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newSSD1306Display() *ssd1306.Device {
|
|
||||||
machine.SPI0.Configure(machine.SPIConfig{
|
|
||||||
Frequency: 50 * machine.MHz,
|
|
||||||
})
|
|
||||||
display := ssd1306.NewSPI(machine.SPI0, machine.D5, machine.D4, machine.D6)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Width: 128,
|
|
||||||
Height: 64,
|
|
||||||
})
|
|
||||||
return display
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.SPI0.Configure(machine.SPIConfig{
|
||||||
|
Frequency: 8000000,
|
||||||
|
})
|
||||||
|
display := ssd1306.NewSPI(machine.SPI0, machine.P8, machine.P7, machine.P9)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Width: 128,
|
||||||
|
Height: 64,
|
||||||
|
})
|
||||||
|
|
||||||
|
display.ClearDisplay()
|
||||||
|
|
||||||
|
x := int16(64)
|
||||||
|
y := int16(32)
|
||||||
|
deltaX := int16(1)
|
||||||
|
deltaY := int16(1)
|
||||||
|
for {
|
||||||
|
pixel := display.GetPixel(x, y)
|
||||||
|
c := color.RGBA{255, 255, 255, 255}
|
||||||
|
if pixel {
|
||||||
|
c = color.RGBA{0, 0, 0, 255}
|
||||||
|
}
|
||||||
|
display.SetPixel(x, y, c)
|
||||||
|
display.Display()
|
||||||
|
|
||||||
|
x += deltaX
|
||||||
|
y += deltaY
|
||||||
|
|
||||||
|
if x == 0 || x == 127 {
|
||||||
|
deltaX = -deltaX
|
||||||
|
}
|
||||||
|
|
||||||
|
if y == 0 || y == 63 {
|
||||||
|
deltaY = -deltaY
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// This example using the SSD1306 OLED display over SPI on the Thumby board
|
||||||
|
// A very tiny 72x40 display.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.SPI0.Configure(machine.SPIConfig{})
|
||||||
|
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Width: 72,
|
||||||
|
Height: 40,
|
||||||
|
ResetCol: ssd1306.ResetValue{28, 99},
|
||||||
|
ResetPage: ssd1306.ResetValue{0, 5},
|
||||||
|
})
|
||||||
|
|
||||||
|
display.ClearDisplay()
|
||||||
|
|
||||||
|
x := int16(36)
|
||||||
|
y := int16(20)
|
||||||
|
deltaX := int16(1)
|
||||||
|
deltaY := int16(1)
|
||||||
|
for {
|
||||||
|
pixel := display.GetPixel(x, y)
|
||||||
|
c := color.RGBA{255, 255, 255, 255}
|
||||||
|
if pixel {
|
||||||
|
c = color.RGBA{0, 0, 0, 255}
|
||||||
|
}
|
||||||
|
display.SetPixel(x, y, c)
|
||||||
|
display.Display()
|
||||||
|
|
||||||
|
x += deltaX
|
||||||
|
y += deltaY
|
||||||
|
|
||||||
|
if x == 0 || x == 71 {
|
||||||
|
deltaX = -deltaX
|
||||||
|
}
|
||||||
|
|
||||||
|
if y == 0 || y == 39 {
|
||||||
|
deltaY = -deltaY
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,10 +46,6 @@ var DefaultDeviceIdentifier = DeviceIdentifierFunc(func(id JedecID) Attrs {
|
|||||||
return GD25Q16C()
|
return GD25Q16C()
|
||||||
case 0xC84017:
|
case 0xC84017:
|
||||||
return GD25Q64C()
|
return GD25Q64C()
|
||||||
case 0x856015:
|
|
||||||
return P25Q16H()
|
|
||||||
case 0xEF4014:
|
|
||||||
return W25Q80DV()
|
|
||||||
case 0xEF4015:
|
case 0xEF4015:
|
||||||
return W25Q16JVIQ()
|
return W25Q16JVIQ()
|
||||||
case 0xEF4016:
|
case 0xEF4016:
|
||||||
@@ -243,24 +239,6 @@ func GD25Q64C() Attrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings for the Puya P25Q16H 2MiB SPI flash.
|
|
||||||
// Datasheet: https://files.seeedstudio.com/wiki/github_weiruanexample/Flash_P25Q16H-UXH-IR_Datasheet.pdf
|
|
||||||
func P25Q16H() Attrs {
|
|
||||||
return Attrs{
|
|
||||||
TotalSize: 1 << 21, // 2 MiB
|
|
||||||
StartUp: 5000 * time.Microsecond,
|
|
||||||
JedecID: JedecID{0x85, 0x60, 0x15},
|
|
||||||
MaxClockSpeedMHz: 55,
|
|
||||||
QuadEnableBitMask: 0x02,
|
|
||||||
HasSectorProtection: true,
|
|
||||||
SupportsFastRead: true,
|
|
||||||
SupportsQSPI: true,
|
|
||||||
SupportsQSPIWrites: true,
|
|
||||||
WriteStatusSplit: true,
|
|
||||||
SingleStatusByte: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a
|
// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a
|
||||||
// different .memory_type (0x70) Datasheet:
|
// different .memory_type (0x70) Datasheet:
|
||||||
// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf
|
||||||
@@ -402,25 +380,6 @@ func W25Q80DL() Attrs {
|
|||||||
TotalSize: 1 << 20, // 1 MiB
|
TotalSize: 1 << 20, // 1 MiB
|
||||||
StartUp: 5000 * time.Microsecond,
|
StartUp: 5000 * time.Microsecond,
|
||||||
JedecID: JedecID{0xEF, 0x60, 0x14},
|
JedecID: JedecID{0xEF, 0x60, 0x14},
|
||||||
MaxClockSpeedMHz: 80,
|
|
||||||
QuadEnableBitMask: 0x02,
|
|
||||||
HasSectorProtection: false,
|
|
||||||
SupportsFastRead: true,
|
|
||||||
SupportsQSPI: true,
|
|
||||||
SupportsQSPIWrites: false,
|
|
||||||
WriteStatusSplit: false,
|
|
||||||
SingleStatusByte: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings for the Winbond W25Q80DV 2MiB SPI flash.
|
|
||||||
// Datasheet:
|
|
||||||
// https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
|
|
||||||
func W25Q80DV() Attrs {
|
|
||||||
return Attrs{
|
|
||||||
TotalSize: 1 << 21, // 2 MiB
|
|
||||||
StartUp: 5000 * time.Microsecond,
|
|
||||||
JedecID: JedecID{0xEF, 0x40, 0x14},
|
|
||||||
MaxClockSpeedMHz: 104,
|
MaxClockSpeedMHz: 104,
|
||||||
QuadEnableBitMask: 0x02,
|
QuadEnableBitMask: 0x02,
|
||||||
HasSectorProtection: false,
|
HasSectorProtection: false,
|
||||||
|
|||||||
+4
-9
@@ -1,6 +1,3 @@
|
|||||||
// Guarded because still unsure of how to deal with interrupt drivers.
|
|
||||||
//go:build tinygo
|
|
||||||
|
|
||||||
// Package ft6336 provides a driver for the FT6336 I2C Self-Capacitive touch
|
// Package ft6336 provides a driver for the FT6336 I2C Self-Capacitive touch
|
||||||
// panel controller.
|
// panel controller.
|
||||||
//
|
//
|
||||||
@@ -8,8 +5,6 @@
|
|||||||
package ft6336
|
package ft6336
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
"tinygo.org/x/drivers/touch"
|
"tinygo.org/x/drivers/touch"
|
||||||
@@ -20,11 +15,11 @@ type Device struct {
|
|||||||
bus drivers.I2C
|
bus drivers.I2C
|
||||||
buf []byte
|
buf []byte
|
||||||
Address uint8
|
Address uint8
|
||||||
intPin machine.Pin
|
intPin drivers.Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns FT6336 device for the provided I2C bus using default address.
|
// 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 drivers.Pin) *Device {
|
||||||
return &Device{
|
return &Device{
|
||||||
bus: i2c,
|
bus: i2c,
|
||||||
buf: make([]byte, 11),
|
buf: make([]byte, 11),
|
||||||
@@ -37,10 +32,10 @@ func New(i2c drivers.I2C, intPin machine.Pin) *Device {
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the FT6336 device.
|
// Configure the FT6336 device. Note that the interrupt pin must be configured
|
||||||
|
// separately as an input.
|
||||||
func (d *Device) Configure(config Config) error {
|
func (d *Device) Configure(config Config) error {
|
||||||
d.write1Byte(0xA4, 0x00)
|
d.write1Byte(0xA4, 0x00)
|
||||||
d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-17
@@ -10,8 +10,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rotation controls the rotation used by the display.
|
// Rotation controls the rotation used by the display.
|
||||||
@@ -23,10 +21,10 @@ type FrameRate uint8
|
|||||||
// Device wraps an SPI connection.
|
// Device wraps an SPI connection.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
dcPin pin.OutputFunc
|
dcPin drivers.Pin
|
||||||
resetPin pin.OutputFunc
|
resetPin drivers.Pin
|
||||||
csPin pin.OutputFunc
|
csPin drivers.Pin
|
||||||
blPin pin.OutputFunc
|
blPin drivers.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
columnOffsetCfg int16
|
columnOffsetCfg int16
|
||||||
@@ -52,18 +50,15 @@ type Config struct {
|
|||||||
Height int16
|
Height int16
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
// New creates a new ST7789 connection. The SPI wire must already be configured, along with the
|
||||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
|
// data/command and reset pins as outputs.
|
||||||
legacy.ConfigurePinOut(resetPin)
|
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
legacy.ConfigurePinOut(blPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
resetPin: resetPin.Set,
|
resetPin: resetPin,
|
||||||
dcPin: dcPin.Set,
|
dcPin: dcPin,
|
||||||
csPin: csPin.Set,
|
csPin: csPin,
|
||||||
blPin: blPin.Set,
|
blPin: blPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +222,7 @@ func (d *Device) Data(data uint8) {
|
|||||||
|
|
||||||
// Tx sends data to the display
|
// Tx sends data to the display
|
||||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||||
d.dcPin(!isCommand)
|
d.dcPin.Set(!isCommand)
|
||||||
d.bus.Tx(data, nil)
|
d.bus.Tx(data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -96,10 +96,7 @@ func (parser *Parser) Parse(sentence string) (Fix, error) {
|
|||||||
fix.Speed = findSpeed(fields[7])
|
fix.Speed = findSpeed(fields[7])
|
||||||
fix.Heading = findHeading(fields[8])
|
fix.Heading = findHeading(fields[8])
|
||||||
date := findDate(fields[9])
|
date := findDate(fields[9])
|
||||||
fix.Time = date.Add(time.Duration(fix.Time.Hour())*time.Hour +
|
fix.Time = fix.Time.AddDate(date.Year(), int(date.Month()), date.Day())
|
||||||
time.Duration(fix.Time.Minute())*time.Minute +
|
|
||||||
time.Duration(fix.Time.Second())*time.Second +
|
|
||||||
time.Duration(fix.Time.Nanosecond())*time.Nanosecond)
|
|
||||||
|
|
||||||
return fix, nil
|
return fix, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,15 +70,15 @@ func TestParseRMC(t *testing.T) {
|
|||||||
t.Error("should have errInvalidRMCSentence error")
|
t.Error("should have errInvalidRMCSentence error")
|
||||||
}
|
}
|
||||||
|
|
||||||
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,010622,0.0,E,D*2B"
|
val = "$GPRMC,203522.00,A,5109.0262308,N,11401.8407342,W,0.004,133.4,130522,0.0,E,D*2B"
|
||||||
fix, err := p.Parse(val)
|
fix, err := p.Parse(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("should have parsed")
|
t.Error("should have parsed")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(fix.Time.Year(), qt.Equals, 2022)
|
c.Assert(fix.Time.Year(), qt.Equals, 2022)
|
||||||
c.Assert(fix.Time.Month(), qt.Equals, time.June)
|
c.Assert(fix.Time.Month(), qt.Equals, time.May)
|
||||||
c.Assert(fix.Time.Day(), qt.Equals, 1)
|
c.Assert(fix.Time.Day(), qt.Equals, 13)
|
||||||
c.Assert(fix.Time.Hour(), qt.Equals, 20)
|
c.Assert(fix.Time.Hour(), qt.Equals, 20)
|
||||||
c.Assert(fix.Time.Minute(), qt.Equals, 35)
|
c.Assert(fix.Time.Minute(), qt.Equals, 35)
|
||||||
c.Assert(fix.Time.Second(), qt.Equals, 22)
|
c.Assert(fix.Time.Second(), qt.Equals, 22)
|
||||||
|
|||||||
+11
-19
@@ -7,37 +7,29 @@ package hcsr04
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const TIMEOUT = 23324 // max sensing distance (4m)
|
const TIMEOUT = 23324 // max sensing distance (4m)
|
||||||
|
|
||||||
// Device holds the pins
|
// Device holds the pins
|
||||||
type Device struct {
|
type Device struct {
|
||||||
trigger pin.OutputFunc
|
trigger drivers.Pin
|
||||||
echo pin.InputFunc
|
echo drivers.Pin
|
||||||
configurePins func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new ultrasonic driver given 2 pins
|
// New returns a new ultrasonic driver given 2 pins
|
||||||
func New(trigger pin.Output, echo pin.Input) Device {
|
func New(trigger, echo drivers.Pin) Device {
|
||||||
return Device{
|
return Device{
|
||||||
trigger: trigger.Set,
|
trigger: trigger,
|
||||||
echo: echo.Get,
|
echo: echo,
|
||||||
configurePins: func() {
|
|
||||||
legacy.ConfigurePinOut(trigger)
|
|
||||||
legacy.ConfigurePinInput(echo)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures the pins of the Device
|
// Configure expects that the pins of the Device have already
|
||||||
|
// been configured by the user. Trigger pin should be an output
|
||||||
|
// and echo pin should be an input.
|
||||||
func (d *Device) Configure() {
|
func (d *Device) Configure() {
|
||||||
if d.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
d.configurePins()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadDistance returns the distance of the object in mm
|
// ReadDistance returns the distance of the object in mm
|
||||||
@@ -61,7 +53,7 @@ func (d *Device) ReadPulse() int32 {
|
|||||||
d.trigger.Low()
|
d.trigger.Low()
|
||||||
i := uint8(0)
|
i := uint8(0)
|
||||||
for {
|
for {
|
||||||
if d.echo() {
|
if d.echo.Get() {
|
||||||
t = time.Now()
|
t = time.Now()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -75,7 +67,7 @@ func (d *Device) ReadPulse() int32 {
|
|||||||
}
|
}
|
||||||
i = 0
|
i = 0
|
||||||
for {
|
for {
|
||||||
if !d.echo() {
|
if !d.echo.Get() {
|
||||||
return int32(time.Since(t).Microseconds())
|
return int32(time.Since(t).Microseconds())
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
|
|||||||
+5
-5
@@ -210,7 +210,7 @@ func (d *Device) SendCommand(command byte) {
|
|||||||
d.bus.SetCommandMode(true)
|
d.bus.SetCommandMode(true)
|
||||||
d.bus.Write([]byte{command})
|
d.bus.Write([]byte{command})
|
||||||
|
|
||||||
for d.isBusy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
|
for d.busy(command == DISPLAY_CLEAR || command == CURSOR_HOME) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +219,7 @@ func (d *Device) sendData(data byte) {
|
|||||||
d.bus.SetCommandMode(false)
|
d.bus.SetCommandMode(false)
|
||||||
d.bus.Write([]byte{data})
|
d.bus.Write([]byte{data})
|
||||||
|
|
||||||
for d.isBusy(false) {
|
for d.busy(false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,9 +231,9 @@ func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isBusy returns true when hd447890 is isBusy
|
// busy returns true when hd447890 is busy
|
||||||
// or after the timeout specified
|
// or after the timeout specified
|
||||||
func (d *Device) isBusy(longDelay bool) bool {
|
func (d *Device) busy(longDelay bool) bool {
|
||||||
if d.bus.WriteOnly() {
|
if d.bus.WriteOnly() {
|
||||||
// Can't read busy flag if write only, so sleep a bit then return
|
// Can't read busy flag if write only, so sleep a bit then return
|
||||||
if longDelay {
|
if longDelay {
|
||||||
@@ -261,7 +261,7 @@ func (d *Device) isBusy(longDelay bool) bool {
|
|||||||
|
|
||||||
// Busy returns true when hd447890 is busy
|
// Busy returns true when hd447890 is busy
|
||||||
func (d *Device) Busy() bool {
|
func (d *Device) Busy() bool {
|
||||||
return d.isBusy(false)
|
return d.busy(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the current size of the display.
|
// Size returns the current size of the display.
|
||||||
|
|||||||
-191
@@ -1,191 +0,0 @@
|
|||||||
package honeyhsc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errSensorMissing = errors.New("hsc: not connected")
|
|
||||||
errDiagnostic = errors.New("hsc: diagnostic error")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
measuremask = drivers.Pressure | drivers.Temperature
|
|
||||||
statusMask = 0b1100_0000
|
|
||||||
statusOffset = 6
|
|
||||||
)
|
|
||||||
|
|
||||||
// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric
|
|
||||||
// analog or digital output for reading pressure over the specified full scale pressure span and temperature range.
|
|
||||||
type DevI2C struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
dev
|
|
||||||
addr uint8
|
|
||||||
buf [6]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDevI2C creates and returns a new DevI2C that communicates with an HSC device over the provided I2C bus.
|
|
||||||
// Parameters:
|
|
||||||
// - bus: the I2C bus to use.
|
|
||||||
// - addr: the 7-bit I2C address of the sensor.
|
|
||||||
// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model.
|
|
||||||
// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model.
|
|
||||||
//
|
|
||||||
// The returned DevI2C will use these calibration parameters to convert raw bridge counts to pressure.
|
|
||||||
func NewDevI2C(bus drivers.I2C, addr, outMin, outMax uint16, pMin, pMax int32) *DevI2C {
|
|
||||||
h := &DevI2C{
|
|
||||||
bus: bus,
|
|
||||||
addr: uint8(addr),
|
|
||||||
dev: dev{
|
|
||||||
cmin: outMin,
|
|
||||||
cmax: outMax,
|
|
||||||
pmin: pMin,
|
|
||||||
pmax: pMax,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the I2C-attached HSC device.
|
|
||||||
// It performs an Update internally to get the latest temperature value.
|
|
||||||
func (h *DevI2C) ReadTemperature() (int32, error) {
|
|
||||||
err := h.Update(drivers.Temperature)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return h.Temperature(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update reads both temperature and pressure data from the I2C-attached HSC device when
|
|
||||||
// the requested measurement mask includes pressure or temperature.
|
|
||||||
// If neither pressure nor temperature is requested, Update is a no-op.
|
|
||||||
func (d *DevI2C) Update(which drivers.Measurement) error {
|
|
||||||
// Update performs an I2C transaction to read 4 bytes, parses the status bits, 14-bit bridge data and
|
|
||||||
// temperature bits, and forwards them to the internal update routine. Any I2C transport error is returned,
|
|
||||||
// as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic).
|
|
||||||
if which&measuremask == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rbuf := d.buf[:4]
|
|
||||||
wbuf := d.buf[4:6]
|
|
||||||
const reg = 0
|
|
||||||
value := (d.addr << 1) | 1
|
|
||||||
wbuf[0] = reg
|
|
||||||
wbuf[1] = value
|
|
||||||
err := d.bus.Tx(uint16(d.addr), wbuf, rbuf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
status := (rbuf[0] & statusMask) >> statusOffset
|
|
||||||
bridgeData := (uint16(rbuf[0]&^statusMask) << 8) | uint16(rbuf[1])
|
|
||||||
tempData := uint16(rbuf[2])<<8 | uint16(rbuf[3]&0xe0)>>5
|
|
||||||
return d.dev.update(status, bridgeData, tempData)
|
|
||||||
}
|
|
||||||
|
|
||||||
type pinout func(level bool)
|
|
||||||
|
|
||||||
// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric
|
|
||||||
// analog or digital output for reading pressure over the specified full scale pressure span and temperature range.
|
|
||||||
type DevSPI struct {
|
|
||||||
spi drivers.SPI
|
|
||||||
cs pinout
|
|
||||||
dev
|
|
||||||
buf [4]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDevSPI creates and returns a new DevSPI that communicates with an HSC device over SPI.
|
|
||||||
// Parameters:
|
|
||||||
// - conn: the SPI connection to use.
|
|
||||||
// - cs: a chip-select function that drives the device select line low/high.
|
|
||||||
// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model.
|
|
||||||
// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model.
|
|
||||||
//
|
|
||||||
// The function returns the constructed DevSPI and an error value (currently always nil).
|
|
||||||
func NewDevSPI(conn drivers.SPI, cs pinout, outMin, outMax uint16, pMin, pMax int32) (*DevSPI, error) {
|
|
||||||
h := &DevSPI{
|
|
||||||
spi: conn,
|
|
||||||
cs: cs,
|
|
||||||
dev: dev{
|
|
||||||
cmin: outMin,
|
|
||||||
cmax: outMax,
|
|
||||||
pmin: pMin,
|
|
||||||
pmax: pMax,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the SPI-attached HSC device.
|
|
||||||
// It performs an Update internally to get the latest temperature value.
|
|
||||||
func (h *DevSPI) ReadTemperature() (int32, error) {
|
|
||||||
err := h.Update(drivers.Temperature)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return h.Temperature(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update reads pressure and temperature data from the SPI-attached HSC device when the requested measurement mask includes
|
|
||||||
// pressure or temperature. If neither pressure nor temperature is requested, Update is a no-op.
|
|
||||||
func (h *DevSPI) Update(which drivers.Measurement) error {
|
|
||||||
// It toggles the provided chip-select, performs an SPI transfer to read 4 bytes, parses the status bits,
|
|
||||||
// 14-bit bridge data and temperature bits, and forwards them to the internal update routine. Any SPI
|
|
||||||
// transport error is returned, as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic).
|
|
||||||
if which&measuremask == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
buf := &h.buf
|
|
||||||
h.cs(false)
|
|
||||||
err := h.spi.Tx(nil, buf[:4])
|
|
||||||
h.cs(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// First two bits are status bits.
|
|
||||||
status := (buf[0] & statusMask) >> statusOffset
|
|
||||||
bridgeData := (uint16(buf[0]&^statusMask) << 8) | uint16(buf[1])
|
|
||||||
|
|
||||||
tempData := uint16(buf[2])<<8 | uint16(buf[3]&0xe0)>>5
|
|
||||||
return h.dev.update(status, bridgeData, tempData)
|
|
||||||
}
|
|
||||||
|
|
||||||
type dev struct {
|
|
||||||
pressure int32
|
|
||||||
temp int32
|
|
||||||
cmin, cmax uint16
|
|
||||||
pmin, pmax int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pressure returns the most recently computed pressure value in millipascals (mPa).
|
|
||||||
// The value is taken from the last successful Update.
|
|
||||||
func (d *dev) Pressure() int32 {
|
|
||||||
return d.pressure
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temperature returns the most recently read temperature value in milliKelvin (mC).
|
|
||||||
// The value is taken from the last successful Update.
|
|
||||||
func (d *dev) Temperature() int32 {
|
|
||||||
return d.temp + 273_150
|
|
||||||
}
|
|
||||||
|
|
||||||
// update interprets raw sensor fields (status, bridgeData, tempData) and updates the dev's stored
|
|
||||||
// pressure and temperature. It returns errSensorMissing when the temperature raw value indicates no sensor
|
|
||||||
// (tempData == math.MaxUint16), errDiagnostic when the status indicates a device diagnostic condition
|
|
||||||
// (status == 3), or nil on success. Pressure is computed with integer arithmetic using the configured
|
|
||||||
// cmin/cmax -> pmin/pmax linear mapping in order to avoid overflows.
|
|
||||||
func (d *dev) update(status uint8, bridgeData, tempData uint16) error {
|
|
||||||
if tempData == math.MaxUint16 {
|
|
||||||
return errSensorMissing
|
|
||||||
} else if status == 3 {
|
|
||||||
return errDiagnostic
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take care not to overflow here.
|
|
||||||
p := (int32(bridgeData)-int32(d.cmin))*(d.pmax-d.pmin)/int32(d.cmax-d.cmin) + d.pmin
|
|
||||||
d.temp = int32(tempData)
|
|
||||||
d.pressure = p
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package legacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The pingconfig group of files serve to abstract away
|
|
||||||
// pin configuration calls on the machine.Pin type.
|
|
||||||
// It was observed this way of developing drivers was
|
|
||||||
// non-portable and unusable on "big" Go projects so
|
|
||||||
// future projects should NOT configure pins in driver code.
|
|
||||||
// Users must configure pins before passing them as arguments
|
|
||||||
// to drivers.
|
|
||||||
|
|
||||||
// ConfigurePinOut is a legacy function used to configure pins as outputs.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not configure pins in drivers.
|
|
||||||
// This is a legacy feature and should only be used by drivers that
|
|
||||||
// previously configured pins in initialization to avoid breaking users.
|
|
||||||
func ConfigurePinOut(po pin.Output) {
|
|
||||||
configurePinOut(po)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigurePinInput is a legacy function used to configure pins as inputs.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not configure pins in drivers.
|
|
||||||
// This is a legacy feature and should only be used by drivers that
|
|
||||||
// previously configured pins in initialization to avoid breaking users.
|
|
||||||
func ConfigurePinInputPulldown(pi pin.Input) {
|
|
||||||
configurePinInputPulldown(pi)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigurePinInput is a legacy function used to configure pins as inputs.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not configure pins in drivers.
|
|
||||||
// This is a legacy feature and should only be used by drivers that
|
|
||||||
// previously configured pins in initialization to avoid breaking users.
|
|
||||||
func ConfigurePinInput(pi pin.Input) {
|
|
||||||
configurePinInput(pi)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigurePinInput is a legacy function used to configure pins as inputs.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not configure pins in drivers.
|
|
||||||
// This is a legacy feature and should only be used by drivers that
|
|
||||||
// previously configured pins in initialization to avoid breaking users.
|
|
||||||
func ConfigurePinInputPullup(pi pin.Input) {
|
|
||||||
configurePinInputPullup(pi)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PinIsNoPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
|
|
||||||
//
|
|
||||||
// Deprecated: Drivers do not require pin knowledge from now on.
|
|
||||||
func PinIsNoPin(pin any) bool {
|
|
||||||
return pinIsNoPin(pin)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
|
|
||||||
)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//go:build !tinygo
|
|
||||||
|
|
||||||
package legacy
|
|
||||||
|
|
||||||
import "tinygo.org/x/drivers/internal/pin"
|
|
||||||
|
|
||||||
// This file compiles for non-tinygo builds
|
|
||||||
// for use with "big" or "upstream" Go where
|
|
||||||
// there is no machine package.
|
|
||||||
|
|
||||||
func configurePinOut(p pin.Output) {}
|
|
||||||
func configurePinInput(p pin.Input) {}
|
|
||||||
func configurePinInputPulldown(p pin.Input) {}
|
|
||||||
func configurePinInputPullup(p pin.Input) {}
|
|
||||||
func pinIsNoPin(a any) bool { return false }
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
//go:build baremetal && fe310
|
|
||||||
|
|
||||||
package legacy
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
const (
|
|
||||||
pulldown = machine.PinInput
|
|
||||||
pullup = machine.PinInput
|
|
||||||
)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//go:build baremetal && !fe310
|
|
||||||
|
|
||||||
package legacy
|
|
||||||
|
|
||||||
import "machine"
|
|
||||||
|
|
||||||
// If you are getting a build error here you then we missed adding
|
|
||||||
// your CPU build tag to the list of CPUs that do not have pulldown/pullups.
|
|
||||||
// Add it above and in pinhal_nopulls! You should also add a smoketest for it :)
|
|
||||||
const (
|
|
||||||
pulldown = machine.PinInputPulldown
|
|
||||||
pullup = machine.PinInputPullup
|
|
||||||
)
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
//go:build baremetal
|
|
||||||
|
|
||||||
package legacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func configurePinOut(po pin.Output) {
|
|
||||||
configurePin(po, machine.PinOutput)
|
|
||||||
}
|
|
||||||
|
|
||||||
func configurePinInputPulldown(pi pin.Input) {
|
|
||||||
configurePin(pi, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput.
|
|
||||||
}
|
|
||||||
|
|
||||||
func configurePinInput(pi pin.Input) {
|
|
||||||
configurePin(pi, machine.PinInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
func configurePinInputPullup(pi pin.Input) {
|
|
||||||
configurePin(pi, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput.
|
|
||||||
}
|
|
||||||
|
|
||||||
func pinIsNoPin(a any) bool {
|
|
||||||
p, ok := a.(machine.Pin)
|
|
||||||
return ok && p == machine.NoPin
|
|
||||||
}
|
|
||||||
|
|
||||||
func configurePin(p any, mode machine.PinMode) {
|
|
||||||
machinePin, ok := p.(machine.Pin)
|
|
||||||
if ok {
|
|
||||||
machinePin.Configure(machine.PinConfig{Mode: mode})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
// package pin implements a TinyGo Pin HAL.
|
|
||||||
// It serves to eliminate machine.Pin from driver constructors
|
|
||||||
// so that drivers can be used in "big" Go projects where
|
|
||||||
// there is no machine package.
|
|
||||||
// This file contains both function and interface-style Pin HAL definitions.
|
|
||||||
package pin
|
|
||||||
|
|
||||||
// OutputFunc is hardware abstraction for a pin which outputs a
|
|
||||||
// digital signal (high or low level).
|
|
||||||
//
|
|
||||||
// // Code conversion demo: from machine.Pin to pin.OutputFunc
|
|
||||||
// led := machine.LED
|
|
||||||
// led.Configure(machine.PinConfig{Mode: machine.Output})
|
|
||||||
// var pin pin.OutputFunc = led.Set // Going from a machine.Pin to a pin.OutputFunc
|
|
||||||
//
|
|
||||||
// This is an alternative to [Output] which is an interface type.
|
|
||||||
type OutputFunc func(level bool)
|
|
||||||
|
|
||||||
// High sets the underlying pin's level to high. This is equivalent to calling PinOutput(true).
|
|
||||||
func (setPin OutputFunc) High() {
|
|
||||||
setPin(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Low sets the underlying pin's level to low. This is equivalent to calling PinOutput(false).
|
|
||||||
func (setPin OutputFunc) Low() {
|
|
||||||
setPin(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputFunc is hardware abstraction for a pin which receives a
|
|
||||||
// digital signal and reads it (high or low level).
|
|
||||||
//
|
|
||||||
// // Code conversion demo: from machine.Pin to pin.InputFunc
|
|
||||||
// input := machine.LED
|
|
||||||
// input.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) // or use machine.PinInputPullup or machine.Input
|
|
||||||
// var pin pin.InputFunc = input.Get // Going from a machine.Pin to a pin.InputFunc
|
|
||||||
//
|
|
||||||
// This is an alternative to [Input] which is an interface type.
|
|
||||||
type InputFunc func() (level bool)
|
|
||||||
|
|
||||||
// // Below is an example on how to define a input/output pin HAL for a
|
|
||||||
// // pin that must switch between input and output mode:
|
|
||||||
//
|
|
||||||
// var pinIsOutput bool
|
|
||||||
// var po PinOutputFunc = func(b bool) {
|
|
||||||
// if !pinIsOutput {
|
|
||||||
// pin.Configure(outputMode)
|
|
||||||
// pinIsOutput = true
|
|
||||||
// }
|
|
||||||
// pin.Set(b)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var pi PinInputFunc = func() bool {
|
|
||||||
// if pinIsOutput {
|
|
||||||
// pin.Configure(inputMode)
|
|
||||||
// pinIsOutput = false
|
|
||||||
// }
|
|
||||||
// return pin.Get()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Output interface represents a pin hardware abstraction layer for a pin that can output a digital signal.
|
|
||||||
//
|
|
||||||
// This is an alternative to [OutputFunc] abstraction which is a function type.
|
|
||||||
type Output interface {
|
|
||||||
Set(level bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Input interface represents a pin hardware abstraction layer for a pin that can read a digital signal.
|
|
||||||
//
|
|
||||||
// This is an alternative to [InputFunc] abstraction which is a function type.
|
|
||||||
type Input interface {
|
|
||||||
Get() (level bool)
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package regmap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device8 implements common logic to most 8-bit peripherals with an I2C or SPI bus.
|
|
||||||
// All methods expect the target to support conventional register read and write operations
|
|
||||||
// where the first byte sent is the register address being accessed.
|
|
||||||
//
|
|
||||||
// All methods use an internal buffer and perform no dynamic memory allocation.
|
|
||||||
type Device8 struct {
|
|
||||||
buf [10]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear zeroes Device8's buffers.
|
|
||||||
func (d *Device8) clear() {
|
|
||||||
d.buf = [10]byte{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C methods.
|
|
||||||
|
|
||||||
// Read8I2C reads a single byte from register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
func (d *Device8) Read8I2C(bus drivers.I2C, i2cAddr uint16, addr uint8) (byte, error) {
|
|
||||||
d.buf[0] = addr
|
|
||||||
err := bus.Tx(i2cAddr, d.buf[0:1], d.buf[1:2])
|
|
||||||
return d.buf[1], err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read16I2C reads a 16-bit value from register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
// The byte order is specified by order.
|
|
||||||
func (d *Device8) Read16I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, order binary.ByteOrder) (uint16, error) {
|
|
||||||
d.buf[0] = addr
|
|
||||||
err := bus.Tx(i2cAddr, d.buf[0:1], d.buf[1:3])
|
|
||||||
return order.Uint16(d.buf[1:3]), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read32I2C reads a 32-bit value from register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
// The byte order is specified by order.
|
|
||||||
func (d *Device8) Read32I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, order binary.ByteOrder) (uint32, error) {
|
|
||||||
d.buf[0] = addr
|
|
||||||
err := bus.Tx(i2cAddr, d.buf[0:1], d.buf[1:5])
|
|
||||||
return order.Uint32(d.buf[1:5]), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDataI2C reads dataLength bytes from register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
// The data is stored in dataDestination.
|
|
||||||
func (d *Device8) ReadDataI2C(bus drivers.I2C, i2cAddr uint16, addr uint8, dataDestination []byte) error {
|
|
||||||
d.buf[0] = addr
|
|
||||||
return bus.Tx(i2cAddr, d.buf[:1], dataDestination)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write8I2C writes a single byte value to register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
func (d *Device8) Write8I2C(bus drivers.I2C, i2cAddr uint16, addr, value uint8) error {
|
|
||||||
d.buf[0] = addr
|
|
||||||
d.buf[1] = value
|
|
||||||
return bus.Tx(i2cAddr, d.buf[:2], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write16I2C writes a 16-bit value to register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
// The byte order is specified by order.
|
|
||||||
func (d *Device8) Write16I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, value uint16, order binary.ByteOrder) error {
|
|
||||||
d.buf[0] = addr
|
|
||||||
order.PutUint16(d.buf[1:3], value)
|
|
||||||
return bus.Tx(i2cAddr, d.buf[0:3], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write32I2C writes a 32-bit value to register addr of the device at i2cAddr using the provided I2C bus.
|
|
||||||
// The byte order is specified by order.
|
|
||||||
func (d *Device8) Write32I2C(bus drivers.I2C, i2cAddr uint16, addr uint8, value uint32, order binary.ByteOrder) error {
|
|
||||||
d.buf[0] = addr
|
|
||||||
order.PutUint32(d.buf[1:5], value)
|
|
||||||
return bus.Tx(i2cAddr, d.buf[0:5], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI methods.
|
|
||||||
|
|
||||||
// Read8SPI reads a single byte from register addr using the provided SPI bus.
|
|
||||||
func (d *Device8) Read8SPI(bus drivers.SPI, addr uint8) (byte, error) {
|
|
||||||
d.clear()
|
|
||||||
d.buf[0] = addr
|
|
||||||
err := bus.Tx(d.buf[0:1], d.buf[1:2]) // We suppose data is returned after first byte in SPI.
|
|
||||||
return d.buf[1], err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read16SPI reads a 16-bit value from register addr using the provided SPI bus. The byte order is specified by order.
|
|
||||||
func (d *Device8) Read16SPI(bus drivers.SPI, addr uint8, order binary.ByteOrder) (uint16, error) {
|
|
||||||
d.clear()
|
|
||||||
d.buf[0] = addr
|
|
||||||
err := bus.Tx(d.buf[0:3], d.buf[3:6]) // We suppose data is returned after first byte in SPI.
|
|
||||||
return order.Uint16(d.buf[4:6]), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read32SPI reads a 32-bit value from register addr using the provided SPI bus. The byte order is specified by order.
|
|
||||||
func (d *Device8) Read32SPI(bus drivers.SPI, addr uint8, order binary.ByteOrder) (uint32, error) {
|
|
||||||
d.clear()
|
|
||||||
d.buf[0] = addr
|
|
||||||
err := bus.Tx(d.buf[0:5], d.buf[5:10]) // We suppose data is returned after first byte in SPI.
|
|
||||||
return order.Uint32(d.buf[6:10]), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDataSPI reads data from a 8bit device address. It assumes data at register address is sent back
|
|
||||||
// from device after first byte is written as address.
|
|
||||||
// It needs the auxiliary buffer length to be large enough to contain both the write and read portions of buffer,
|
|
||||||
// so 2*(dataLength+1) < len(auxiliaryBuf) must hold.
|
|
||||||
func (d *Device8) ReadDataSPI(bus drivers.SPI, addr uint8, dataLength int, auxiliaryBuf []byte) ([]byte, error) {
|
|
||||||
split := len(auxiliaryBuf) / 2
|
|
||||||
if split < dataLength+1 {
|
|
||||||
return nil, io.ErrShortBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
wbuf, rbuf := auxiliaryBuf[:split], auxiliaryBuf[split:]
|
|
||||||
wbuf[0] = addr
|
|
||||||
err := bus.Tx(wbuf, rbuf)
|
|
||||||
return rbuf[1:], err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write8SPI writes a single byte value to register addr using the provided SPI bus.
|
|
||||||
func (d *Device8) Write8SPI(bus drivers.SPI, addr, value uint8) error {
|
|
||||||
d.clear()
|
|
||||||
d.buf[0] = addr
|
|
||||||
d.buf[1] = value
|
|
||||||
return bus.Tx(d.buf[:2], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write16SPI writes a 16-bit value to register addr using the provided SPI bus. The byte order is specified by order.
|
|
||||||
func (d *Device8) Write16SPI(bus drivers.SPI, addr uint8, value uint16, order binary.ByteOrder) error {
|
|
||||||
d.clear()
|
|
||||||
d.buf[0] = addr
|
|
||||||
order.PutUint16(d.buf[1:3], value)
|
|
||||||
return bus.Tx(d.buf[:3], nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write32SPI writes a 32-bit value to register addr using the provided SPI bus. The byte order is specified by order.
|
|
||||||
func (d *Device8) Write32SPI(bus drivers.SPI, addr uint8, value uint32, order binary.ByteOrder) error {
|
|
||||||
d.clear()
|
|
||||||
d.buf[0] = addr
|
|
||||||
order.PutUint32(d.buf[1:5], value)
|
|
||||||
return bus.Tx(d.buf[:5], nil)
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
package regmap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device8SPI implements common logic to most 8-bit peripherals with an SPI bus.
|
|
||||||
// All methods expect the target to support conventional register read and write operations
|
|
||||||
// where the first byte sent is the register address being accessed.
|
|
||||||
//
|
|
||||||
// All methods use an internal buffer and perform no dynamic memory allocation.
|
|
||||||
type Device8SPI struct {
|
|
||||||
bus drivers.SPI
|
|
||||||
order binary.ByteOrder
|
|
||||||
d Device8
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBus sets the SPI bus and byte order for the Device8SPI.
|
|
||||||
//
|
|
||||||
// As a hint, most SPI devices use big-endian (MSB) byte order.
|
|
||||||
// - Big endian: A value of 0x1234 is transmitted as 0x12 followed by 0x34.
|
|
||||||
// - Little endian: A value of 0x1234 is transmitted as 0x34 followed by 0x12.
|
|
||||||
func (d *Device8SPI) SetBus(bus drivers.SPI, order binary.ByteOrder) {
|
|
||||||
d.bus = bus
|
|
||||||
d.order = order
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read8 reads a single byte from register addr.
|
|
||||||
func (d *Device8SPI) Read8(addr uint8) (byte, error) {
|
|
||||||
return d.d.Read8SPI(d.bus, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read16 reads a 16-bit value from register addr.
|
|
||||||
func (d *Device8SPI) Read16(addr uint8) (uint16, error) {
|
|
||||||
return d.d.Read16SPI(d.bus, addr, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read32 reads a 32-bit value from register addr.
|
|
||||||
func (d *Device8SPI) Read32(addr uint8) (uint32, error) {
|
|
||||||
return d.d.Read32SPI(d.bus, addr, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadData reads dataLength bytes from register addr. Due to the internal functioning of
|
|
||||||
// SPI, an auxiliary buffer must be provided to perform the operation and avoid memory allocation.
|
|
||||||
// The returned slice is a subslice of auxBuffer containing the read data.
|
|
||||||
func (d *Device8SPI) ReadData(addr uint8, datalength int, auxBuffer []byte) ([]byte, error) {
|
|
||||||
return d.d.ReadDataSPI(d.bus, addr, datalength, auxBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write8 writes a single byte value to register addr.
|
|
||||||
func (d *Device8SPI) Write8(addr, value uint8) error {
|
|
||||||
return d.d.Write8SPI(d.bus, addr, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write16 writes a 16-bit value to register addr.
|
|
||||||
func (d *Device8SPI) Write16(addr uint8, value uint16) error {
|
|
||||||
return d.d.Write16SPI(d.bus, addr, value, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write32 writes a 32-bit value to register addr.
|
|
||||||
func (d *Device8SPI) Write32(addr uint8, value uint32) error {
|
|
||||||
return d.d.Write32SPI(d.bus, addr, value, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device8I2C implements common logic to most 8-bit peripherals with an I2C bus.
|
|
||||||
// All methods expect the target to support conventional register read and write operations
|
|
||||||
// where the first byte sent is the register address being accessed.
|
|
||||||
//
|
|
||||||
// All methods use an internal buffer and perform no dynamic memory allocation.
|
|
||||||
type Device8I2C struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
i2cAddr uint16
|
|
||||||
order binary.ByteOrder
|
|
||||||
d Device8
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBus sets the I2C bus, device address, and byte order for the Device8I2C.
|
|
||||||
//
|
|
||||||
// As a hint, most I2C devices use big-endian (MSB) byte order.
|
|
||||||
// - Big endian: A value of 0x1234 is transmitted as 0x12 followed by 0x34.
|
|
||||||
// - Little endian: A value of 0x1234 is transmitted as 0x34 followed by 0x12.
|
|
||||||
func (d *Device8I2C) SetBus(bus drivers.I2C, i2cAddr uint16, order binary.ByteOrder) {
|
|
||||||
d.bus = bus
|
|
||||||
d.i2cAddr = i2cAddr
|
|
||||||
d.order = order
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read8 reads a single byte from register addr.
|
|
||||||
func (d *Device8I2C) Read8(addr uint8) (byte, error) {
|
|
||||||
return d.d.Read8I2C(d.bus, d.i2cAddr, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read16 reads a 16-bit value from register addr.
|
|
||||||
func (d *Device8I2C) Read16(addr uint8) (uint16, error) {
|
|
||||||
return d.d.Read16I2C(d.bus, d.i2cAddr, addr, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read32 reads a 32-bit value from register addr.
|
|
||||||
func (d *Device8I2C) Read32(addr uint8) (uint32, error) {
|
|
||||||
return d.d.Read32I2C(d.bus, d.i2cAddr, addr, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadData reads dataLength bytes from register addr.
|
|
||||||
func (d *Device8I2C) ReadData(addr uint8, dataDestination []byte) error {
|
|
||||||
return d.d.ReadDataI2C(d.bus, d.i2cAddr, addr, dataDestination)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write8 writes a single byte value to register addr.
|
|
||||||
func (d *Device8I2C) Write8(addr, value uint8) error {
|
|
||||||
return d.d.Write8I2C(d.bus, d.i2cAddr, addr, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write16 writes a 16-bit value to register addr.
|
|
||||||
func (d *Device8I2C) Write16(addr uint8, value uint16) error {
|
|
||||||
return d.d.Write16I2C(d.bus, d.i2cAddr, addr, value, d.order)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write32 writes a 32-bit value to register addr.
|
|
||||||
func (d *Device8I2C) Write32(addr uint8, value uint32) error {
|
|
||||||
return d.d.Write32I2C(d.bus, d.i2cAddr, addr, value, d.order)
|
|
||||||
}
|
|
||||||
+36
-115
@@ -11,57 +11,37 @@ import (
|
|||||||
// Device wraps an I2C connection to a LIS3DH device.
|
// Device wraps an I2C connection to a LIS3DH device.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.I2C
|
bus drivers.I2C
|
||||||
address uint16
|
|
||||||
r Range
|
|
||||||
accel [6]byte // stored acceleration data (from the Update call)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Driver configuration, used for the Configure call. All fields are optional.
|
|
||||||
type Config struct {
|
|
||||||
Address uint16
|
Address uint16
|
||||||
|
r Range
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new LIS3DH connection. The I2C bus must already be configured.
|
// New creates a new LIS3DH connection. The I2C bus must already be configured.
|
||||||
//
|
//
|
||||||
// This function only creates the Device object, it does not touch the device.
|
// This function only creates the Device object, it does not touch the device.
|
||||||
func New(bus drivers.I2C) Device {
|
func New(bus drivers.I2C) Device {
|
||||||
return Device{bus: bus, address: Address0}
|
return Device{bus: bus, Address: Address0}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the device for communication
|
// Configure sets up the device for communication
|
||||||
func (d *Device) Configure(config Config) error {
|
func (d *Device) Configure() {
|
||||||
if config.Address != 0 {
|
|
||||||
d.address = config.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable all axes, normal mode
|
// enable all axes, normal mode
|
||||||
err := legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL1, []byte{0x07})
|
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL1, []byte{0x07})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 400Hz rate
|
// 400Hz rate
|
||||||
err = d.SetDataRate(DATARATE_400_HZ)
|
d.SetDataRate(DATARATE_400_HZ)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// High res & BDU enabled
|
// High res & BDU enabled
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL4, []byte{0x88})
|
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL4, []byte{0x88})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current range
|
// get current range
|
||||||
d.r, err = d.ReadRange()
|
d.r = d.ReadRange()
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connected returns whether a LIS3DH has been found.
|
// Connected returns whether a LIS3DH has been found.
|
||||||
// It does a "who am I" request and checks the response.
|
// It does a "who am I" request and checks the response.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data := []byte{0}
|
data := []byte{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.address), WHO_AM_I, data)
|
err := legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -69,51 +49,46 @@ func (d *Device) Connected() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetDataRate sets the speed of data collected by the LIS3DH.
|
// SetDataRate sets the speed of data collected by the LIS3DH.
|
||||||
func (d *Device) SetDataRate(rate DataRate) error {
|
func (d *Device) SetDataRate(rate DataRate) {
|
||||||
ctl1 := []byte{0}
|
ctl1 := []byte{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.address), REG_CTRL1, ctl1)
|
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL1, ctl1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
println(err.Error())
|
||||||
}
|
}
|
||||||
// mask off bits
|
// mask off bits
|
||||||
ctl1[0] &^= 0xf0
|
ctl1[0] &^= 0xf0
|
||||||
ctl1[0] |= (byte(rate) << 4)
|
ctl1[0] |= (byte(rate) << 4)
|
||||||
return legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL1, ctl1)
|
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL1, ctl1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRange sets the G range for LIS3DH.
|
// SetRange sets the G range for LIS3DH.
|
||||||
func (d *Device) SetRange(r Range) error {
|
func (d *Device) SetRange(r Range) {
|
||||||
ctl := []byte{0}
|
ctl := []byte{0}
|
||||||
err := legacy.ReadRegister(d.bus, uint8(d.address), REG_CTRL4, ctl)
|
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
println(err.Error())
|
||||||
}
|
}
|
||||||
// mask off bits
|
// mask off bits
|
||||||
ctl[0] &^= 0x30
|
ctl[0] &^= 0x30
|
||||||
ctl[0] |= (byte(r) << 4)
|
ctl[0] |= (byte(r) << 4)
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.address), REG_CTRL4, ctl)
|
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the new range
|
// store the new range
|
||||||
d.r = r
|
d.r = r
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadRange returns the current G range for LIS3DH.
|
// ReadRange returns the current G range for LIS3DH.
|
||||||
func (d *Device) ReadRange() (r Range, err error) {
|
func (d *Device) ReadRange() (r Range) {
|
||||||
ctl := []byte{0}
|
ctl := []byte{0}
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.address), REG_CTRL4, ctl)
|
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CTRL4, ctl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
println(err.Error())
|
||||||
}
|
}
|
||||||
// mask off bits
|
// mask off bits
|
||||||
r = Range(ctl[0] >> 4)
|
r = Range(ctl[0] >> 4)
|
||||||
r &= 0x03
|
r &= 0x03
|
||||||
|
|
||||||
return r, nil
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAcceleration reads the current acceleration from the device and returns
|
// ReadAcceleration reads the current acceleration from the device and returns
|
||||||
@@ -121,17 +96,28 @@ func (d *Device) ReadRange() (r Range, err error) {
|
|||||||
// and the sensor is not moving the returned value will be around 1000000 or
|
// and the sensor is not moving the returned value will be around 1000000 or
|
||||||
// -1000000.
|
// -1000000.
|
||||||
func (d *Device) ReadAcceleration() (int32, int32, int32, error) {
|
func (d *Device) ReadAcceleration() (int32, int32, int32, error) {
|
||||||
rawX, rawY, rawZ := d.ReadRawAcceleration()
|
x, y, z := d.ReadRawAcceleration()
|
||||||
x, y, z := normalizeRange(rawX, rawY, rawZ, d.r)
|
divider := float32(1)
|
||||||
return x, y, z, nil
|
switch d.r {
|
||||||
|
case RANGE_16_G:
|
||||||
|
divider = 1365
|
||||||
|
case RANGE_8_G:
|
||||||
|
divider = 4096
|
||||||
|
case RANGE_4_G:
|
||||||
|
divider = 8190
|
||||||
|
case RANGE_2_G:
|
||||||
|
divider = 16380
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(float32(x) / divider * 1000000), int32(float32(y) / divider * 1000000), int32(float32(z) / divider * 1000000), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadRawAcceleration returns the raw x, y and z axis from the LIS3DH
|
// ReadRawAcceleration returns the raw x, y and z axis from the LIS3DH
|
||||||
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
||||||
legacy.WriteRegister(d.bus, uint8(d.address), REG_OUT_X_L|0x80, nil)
|
legacy.WriteRegister(d.bus, uint8(d.Address), REG_OUT_X_L|0x80, nil)
|
||||||
|
|
||||||
data := []byte{0, 0, 0, 0, 0, 0}
|
data := []byte{0, 0, 0, 0, 0, 0}
|
||||||
d.bus.Tx(d.address, nil, data)
|
d.bus.Tx(d.Address, nil, data)
|
||||||
|
|
||||||
x = int16((uint16(data[1]) << 8) | uint16(data[0]))
|
x = int16((uint16(data[1]) << 8) | uint16(data[0]))
|
||||||
y = int16((uint16(data[3]) << 8) | uint16(data[2]))
|
y = int16((uint16(data[3]) << 8) | uint16(data[2]))
|
||||||
@@ -139,68 +125,3 @@ func (d *Device) ReadRawAcceleration() (x int16, y int16, z int16) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the sensor values of the 'which' parameter. Only acceleration is
|
|
||||||
// supported at the moment.
|
|
||||||
func (d *Device) Update(which drivers.Measurement) error {
|
|
||||||
if which&drivers.Acceleration != 0 {
|
|
||||||
// Read raw acceleration values and store them in the driver.
|
|
||||||
err := legacy.WriteRegister(d.bus, uint8(d.address), REG_OUT_X_L|0x80, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = d.bus.Tx(d.address, nil, d.accel[:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acceleration returns the last read acceleration in µg (micro-gravity).
|
|
||||||
// When one of the axes is pointing straight to Earth and the sensor is not
|
|
||||||
// moving the returned value will be around 1000000 or -1000000.
|
|
||||||
func (d *Device) Acceleration() (x, y, z int32) {
|
|
||||||
// Extract the raw 16-bit values.
|
|
||||||
rawX := int16((uint16(d.accel[1]) << 8) | uint16(d.accel[0]))
|
|
||||||
rawY := int16((uint16(d.accel[3]) << 8) | uint16(d.accel[2]))
|
|
||||||
rawZ := int16((uint16(d.accel[5]) << 8) | uint16(d.accel[4]))
|
|
||||||
|
|
||||||
// Normalize these values, to be in µg (micro-gravity).
|
|
||||||
return normalizeRange(rawX, rawY, rawZ, d.r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert raw 16-bit values to normalized 32-bit values while avoiding floats
|
|
||||||
// and divisions.
|
|
||||||
func normalizeRange(rawX, rawY, rawZ int16, r Range) (x, y, z int32) {
|
|
||||||
// We're going to convert the 16-bit raw values to values in the range
|
|
||||||
// -1000_000..1000_000. For now we're going to assume a range of 16G, we'll
|
|
||||||
// adjust that range later.
|
|
||||||
// The formula is derived as follows, and carefully selected to avoid
|
|
||||||
// overflow and integer divisions (the division will be optimized to a
|
|
||||||
// bitshift):
|
|
||||||
// x = x * 1000_000 / 2048
|
|
||||||
// x = x * (1000_000/64) / (2048/64)
|
|
||||||
// x = x * 15625 / 32
|
|
||||||
x = int32(rawX) * 15625 / 32
|
|
||||||
y = int32(rawY) * 15625 / 32
|
|
||||||
z = int32(rawZ) * 15625 / 32
|
|
||||||
|
|
||||||
// Now we need to normalize the three values, since we assumed 16G before.
|
|
||||||
shift := uint32(0)
|
|
||||||
switch r {
|
|
||||||
case RANGE_16_G:
|
|
||||||
shift = 0
|
|
||||||
case RANGE_8_G:
|
|
||||||
shift = 1
|
|
||||||
case RANGE_4_G:
|
|
||||||
shift = 2
|
|
||||||
case RANGE_2_G:
|
|
||||||
shift = 3
|
|
||||||
}
|
|
||||||
x >>= shift
|
|
||||||
y >>= shift
|
|
||||||
z >>= shift
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ type Configuration struct {
|
|||||||
MagDataRate uint8
|
MagDataRate uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
var errNotConnected = errors.New("lsm303agr: failed to communicate with either accel or magnet sensor")
|
var errNotConnected = errors.New("lsm303agr: failed to communicate with either acel or magnet sensor")
|
||||||
|
|
||||||
// New creates a new LSM303AGR connection. The I2C bus must already be configured.
|
// New creates a new LSM303AGR connection. The I2C bus must already be configured.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
// Package lsm303dlhc implements a driver for the LSM303dlhc,
|
|
||||||
// a 3 axis accelerometer/magnetic sensor typically available on breakout boards.
|
|
||||||
//
|
|
||||||
// Datasheet: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf
|
|
||||||
|
|
||||||
package lsm303dlhc // import "tinygo.org/x/drivers/lsm303dlhc"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device wraps an I2C connection to a LSM303dlhc device.
|
|
||||||
type Device struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
AccelAddress uint8
|
|
||||||
MagAddress uint8
|
|
||||||
AccelPowerMode uint8
|
|
||||||
AccelRange uint8
|
|
||||||
AccelDataRate uint8
|
|
||||||
MagPowerMode uint8
|
|
||||||
MagSystemMode uint8
|
|
||||||
MagDataRate uint8
|
|
||||||
buf [6]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configuration for LSM303dlhc device.
|
|
||||||
type Configuration struct {
|
|
||||||
AccelPowerMode uint8
|
|
||||||
AccelRange uint8
|
|
||||||
AccelDataRate uint8
|
|
||||||
MagPowerMode uint8
|
|
||||||
MagSystemMode uint8
|
|
||||||
MagDataRate uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new LSM303DLHC connection. The I2C bus must already be configured.
|
|
||||||
// This function only creates the Device object, it does not touch the device.
|
|
||||||
func New(bus drivers.I2C) *Device {
|
|
||||||
return &Device{
|
|
||||||
bus: bus,
|
|
||||||
AccelAddress: ACCEL_ADDRESS,
|
|
||||||
MagAddress: MAG_ADDRESS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure sets up the LSM303dlhc device for communication.
|
|
||||||
func (d *Device) Configure(cfg Configuration) (err error) {
|
|
||||||
|
|
||||||
if cfg.AccelDataRate != 0 {
|
|
||||||
d.AccelDataRate = cfg.AccelDataRate
|
|
||||||
} else {
|
|
||||||
d.AccelDataRate = ACCEL_DATARATE_100HZ
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.AccelPowerMode != 0 {
|
|
||||||
d.AccelPowerMode = cfg.AccelPowerMode
|
|
||||||
} else {
|
|
||||||
d.AccelPowerMode = ACCEL_POWER_NORMAL
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.AccelRange != 0 {
|
|
||||||
d.AccelRange = cfg.AccelRange
|
|
||||||
} else {
|
|
||||||
d.AccelRange = ACCEL_RANGE_2G
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.MagPowerMode != 0 {
|
|
||||||
d.MagPowerMode = cfg.MagPowerMode
|
|
||||||
} else {
|
|
||||||
d.MagPowerMode = MAG_POWER_NORMAL
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.MagDataRate != 0 {
|
|
||||||
d.MagDataRate = cfg.MagDataRate
|
|
||||||
} else {
|
|
||||||
d.MagDataRate = MAG_DATARATE_10HZ
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.MagSystemMode != 0 {
|
|
||||||
d.MagSystemMode = cfg.MagSystemMode
|
|
||||||
} else {
|
|
||||||
d.MagSystemMode = MAG_SYSTEM_CONTINUOUS
|
|
||||||
}
|
|
||||||
|
|
||||||
data := d.buf[:1]
|
|
||||||
|
|
||||||
data[0] = byte(d.AccelDataRate<<4 | d.AccelPowerMode | 0x07)
|
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG1_A, data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data[0] = byte(0x80 | d.AccelRange<<4)
|
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), ACCEL_CTRL_REG4_A, data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data[0] = byte(0xC0)
|
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.AccelAddress), CRA_REG_M, data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temperature compensation is on for magnetic sensor
|
|
||||||
data[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
|
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAcceleration reads the current acceleration from the device and returns
|
|
||||||
// it in µg (micro-gravity). When one of the axes is pointing straight to Earth
|
|
||||||
// and the sensor is not moving the returned value will be around 1000000 or
|
|
||||||
// -1000000.
|
|
||||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
|
||||||
data := d.buf[:6]
|
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), ACCEL_OUT_AUTO_INC, data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rangeFactor := int16(0)
|
|
||||||
switch d.AccelRange {
|
|
||||||
case ACCEL_RANGE_2G:
|
|
||||||
rangeFactor = 1
|
|
||||||
case ACCEL_RANGE_4G:
|
|
||||||
rangeFactor = 2
|
|
||||||
case ACCEL_RANGE_8G:
|
|
||||||
rangeFactor = 4
|
|
||||||
case ACCEL_RANGE_16G:
|
|
||||||
rangeFactor = 12 // the readings in 16G are a bit lower
|
|
||||||
}
|
|
||||||
|
|
||||||
x = int32(int32(int16((uint16(data[1])<<8|uint16(data[0])))>>4*rangeFactor) * 1000000 / 1024)
|
|
||||||
y = int32(int32(int16((uint16(data[3])<<8|uint16(data[2])))>>4*rangeFactor) * 1000000 / 1024)
|
|
||||||
z = int32(int32(int16((uint16(data[5])<<8|uint16(data[4])))>>4*rangeFactor) * 1000000 / 1024)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPitchRoll reads the current pitch and roll angles from the device and
|
|
||||||
// returns it in micro-degrees. When the z axis is pointing straight to Earth
|
|
||||||
// the returned values of pitch and roll would be zero.
|
|
||||||
func (d *Device) ReadPitchRoll() (pitch, roll int32, err error) {
|
|
||||||
|
|
||||||
x, y, z, err := d.ReadAcceleration()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
xf, yf, zf := float64(x), float64(y), float64(z)
|
|
||||||
pitch = int32((math.Round(math.Atan2(yf, math.Sqrt(math.Pow(xf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
|
|
||||||
roll = int32((math.Round(math.Atan2(xf, math.Sqrt(math.Pow(yf, 2)+math.Pow(zf, 2)))*(180/math.Pi)*100) / 100) * 1000000)
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMagneticField reads the current magnetic field from the device and returns
|
|
||||||
// it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
|
|
||||||
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
|
||||||
|
|
||||||
if d.MagSystemMode == MAG_SYSTEM_SINGLE {
|
|
||||||
cmd := d.buf[:1]
|
|
||||||
cmd[0] = byte(0x80 | d.MagPowerMode<<4 | d.MagDataRate<<2 | d.MagSystemMode)
|
|
||||||
err = legacy.WriteRegister(d.bus, uint8(d.MagAddress), MAG_MR_REG_M, cmd)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data := d.buf[0:6]
|
|
||||||
legacy.ReadRegister(d.bus, uint8(d.MagAddress), MAG_OUT_AUTO_INC, data)
|
|
||||||
|
|
||||||
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
|
|
||||||
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
|
|
||||||
z = int32(int16((uint16(data[5])<<8 | uint16(data[4]))))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadCompass reads the current compass heading from the device and returns
|
|
||||||
// it in micro-degrees. When the z axis is pointing straight to Earth and
|
|
||||||
// the y axis is pointing to North, the heading would be zero.
|
|
||||||
//
|
|
||||||
// However, the heading may be off due to electronic compasses would be effected
|
|
||||||
// by strong magnetic fields and require constant calibration.
|
|
||||||
func (d *Device) ReadCompass() (h int32, err error) {
|
|
||||||
|
|
||||||
x, y, _, err := d.ReadMagneticField()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
xf, yf := float64(x), float64(y)
|
|
||||||
h = int32(float32((180/math.Pi)*math.Atan2(yf, xf)) * 1000000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
|
|
||||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
|
||||||
|
|
||||||
data := d.buf[:2]
|
|
||||||
err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), TEMP_OUT_AUTO_INC, data)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r := int16((uint16(data[1])<<8 | uint16(data[0]))) >> 4 // temperature offset from 25 °C
|
|
||||||
t = 25000 + int32((float32(r)/8)*1000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package lsm303dlhc
|
|
||||||
|
|
||||||
const (
|
|
||||||
|
|
||||||
// Constants/addresses used for I2C.
|
|
||||||
ACCEL_ADDRESS = 0x19
|
|
||||||
MAG_ADDRESS = 0x1E
|
|
||||||
|
|
||||||
// i2C 8-bit subaddress (SUB): the 7 LSb represent the actual register address
|
|
||||||
// while the MSB enables address auto increment.
|
|
||||||
// If the MSb of the SUB field is 1, the SUB (register address) is
|
|
||||||
// automatically increased to allow multiple data read/writes.
|
|
||||||
ADDR_AUTO_INC_MASK = 0x80
|
|
||||||
|
|
||||||
// accelerometer registers.
|
|
||||||
ACCEL_CTRL_REG1_A = 0x20
|
|
||||||
ACCEL_CTRL_REG4_A = 0x23
|
|
||||||
ACCEL_OUT_X_L_A = 0x28
|
|
||||||
ACCEL_OUT_X_H_A = 0x29
|
|
||||||
ACCEL_OUT_Y_L_A = 0x2A
|
|
||||||
ACCEL_OUT_Y_H_A = 0x2B
|
|
||||||
ACCEL_OUT_Z_L_A = 0x2C
|
|
||||||
ACCEL_OUT_Z_H_A = 0x2D
|
|
||||||
ACCEL_OUT_AUTO_INC = ACCEL_OUT_X_L_A | ADDR_AUTO_INC_MASK
|
|
||||||
|
|
||||||
// magnetic sensor registers.
|
|
||||||
MAG_MR_REG_M = 0x02
|
|
||||||
MAG_OUT_X_L_M = 0x68
|
|
||||||
MAG_OUT_X_H_M = 0x69
|
|
||||||
MAG_OUT_Y_L_M = 0x6A
|
|
||||||
MAG_OUT_Y_H_M = 0x6B
|
|
||||||
MAG_OUT_Z_L_M = 0x6C
|
|
||||||
MAG_OUT_Z_H_M = 0x6D
|
|
||||||
MAG_OUT_AUTO_INC = MAG_OUT_X_L_M | ADDR_AUTO_INC_MASK
|
|
||||||
|
|
||||||
// temperature sensor registers.
|
|
||||||
CRA_REG_M = 0x80
|
|
||||||
TEMP_OUT_L_M = 0x32
|
|
||||||
TEMP_OUT_H_M = 0x31
|
|
||||||
TEMP_OUT_AUTO_INC = TEMP_OUT_L_M | ADDR_AUTO_INC_MASK
|
|
||||||
|
|
||||||
// accelerometer power mode.
|
|
||||||
ACCEL_POWER_NORMAL = 0x00 // default
|
|
||||||
ACCEL_POWER_LOW = 0x08
|
|
||||||
|
|
||||||
// accelerometer range.
|
|
||||||
ACCEL_RANGE_2G = 0x00 // default
|
|
||||||
ACCEL_RANGE_4G = 0x01
|
|
||||||
ACCEL_RANGE_8G = 0x02
|
|
||||||
ACCEL_RANGE_16G = 0x03
|
|
||||||
|
|
||||||
// accelerometer data rate.
|
|
||||||
ACCEL_DATARATE_1HZ = 0x01
|
|
||||||
ACCEL_DATARATE_10HZ = 0x02
|
|
||||||
ACCEL_DATARATE_25HZ = 0x03
|
|
||||||
ACCEL_DATARATE_50HZ = 0x04
|
|
||||||
ACCEL_DATARATE_100HZ = 0x05 // default
|
|
||||||
ACCEL_DATARATE_200HZ = 0x06
|
|
||||||
ACCEL_DATARATE_400HZ = 0x07
|
|
||||||
ACCEL_DATARATE_1344HZ = 0x09 // 5376Hz in low-power mode
|
|
||||||
|
|
||||||
// magnetic sensor power mode.
|
|
||||||
MAG_POWER_NORMAL = 0x00 // default
|
|
||||||
MAG_POWER_LOW = 0x01
|
|
||||||
|
|
||||||
// magnetic sensor operate mode.
|
|
||||||
MAG_SYSTEM_CONTINUOUS = 0x00 // default
|
|
||||||
MAG_SYSTEM_SINGLE = 0x01
|
|
||||||
|
|
||||||
// magnetic sensor data rate
|
|
||||||
MAG_DATARATE_10HZ = 0x00 // default
|
|
||||||
MAG_DATARATE_20HZ = 0x01
|
|
||||||
MAG_DATARATE_50HZ = 0x02
|
|
||||||
MAG_DATARATE_100HZ = 0x03
|
|
||||||
)
|
|
||||||
+28
-35
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccelRange uint8
|
type AccelRange uint8
|
||||||
@@ -27,7 +28,7 @@ type Device struct {
|
|||||||
accelMultiplier int32
|
accelMultiplier int32
|
||||||
gyroMultiplier int32
|
gyroMultiplier int32
|
||||||
magMultiplier int32
|
magMultiplier int32
|
||||||
buf [7]uint8 // up to 6 bytes for read + 1 byte for the register address
|
buf [6]uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration for LSM9DS1 device.
|
// Configuration for LSM9DS1 device.
|
||||||
@@ -60,15 +61,10 @@ func New(bus drivers.I2C) *Device {
|
|||||||
// Case of boolean false and error nil means I2C is up,
|
// Case of boolean false and error nil means I2C is up,
|
||||||
// but "who am I" responses have unexpected values.
|
// but "who am I" responses have unexpected values.
|
||||||
func (d *Device) Connected() bool {
|
func (d *Device) Connected() bool {
|
||||||
data, err := d.readBytes(d.AccelAddress, WHO_AM_I, 1)
|
data1, data2 := d.buf[:1], d.buf[1:2]
|
||||||
if err != nil || data[0] != 0x68 {
|
legacy.ReadRegister(d.bus, d.AccelAddress, WHO_AM_I, data1)
|
||||||
return false
|
legacy.ReadRegister(d.bus, d.MagAddress, WHO_AM_I_M, data2)
|
||||||
}
|
return data1[0] == 0x68 && data2[0] == 0x3D
|
||||||
data, err = d.readBytes(d.MagAddress, WHO_AM_I_M, 1)
|
|
||||||
if err != nil || data[0] != 0x3D {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAcceleration reads the current acceleration from the device and returns
|
// ReadAcceleration reads the current acceleration from the device and returns
|
||||||
@@ -76,7 +72,8 @@ func (d *Device) Connected() bool {
|
|||||||
// and the sensor is not moving the returned value will be around 1000000 or
|
// and the sensor is not moving the returned value will be around 1000000 or
|
||||||
// -1000000.
|
// -1000000.
|
||||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||||
data, err := d.readBytes(d.AccelAddress, OUT_X_L_XL, 6)
|
data := d.buf[:6]
|
||||||
|
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_XL, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -91,7 +88,8 @@ func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
|||||||
// rotation along one axis and while doing so integrate all values over time,
|
// rotation along one axis and while doing so integrate all values over time,
|
||||||
// you would get a value close to 360000000.
|
// you would get a value close to 360000000.
|
||||||
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
||||||
data, err := d.readBytes(d.AccelAddress, OUT_X_L_G, 6)
|
data := d.buf[:6]
|
||||||
|
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_G, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -104,7 +102,8 @@ func (d *Device) ReadRotation() (x, y, z int32, err error) {
|
|||||||
// ReadMagneticField reads the current magnetic field from the device and returns
|
// ReadMagneticField reads the current magnetic field from the device and returns
|
||||||
// it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
|
// it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
|
||||||
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
||||||
data, err := d.readBytes(d.MagAddress, OUT_X_L_M, 6)
|
data := d.buf[:6]
|
||||||
|
err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), OUT_X_L_M, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -116,7 +115,8 @@ func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
|||||||
|
|
||||||
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
|
// ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
|
||||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||||
data, err := d.readBytes(d.AccelAddress, OUT_TEMP_L, 2)
|
data := d.buf[:2]
|
||||||
|
err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_TEMP_L, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -167,16 +167,20 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
|||||||
d.magMultiplier = 58
|
d.magMultiplier = 58
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data := d.buf[:1]
|
||||||
|
|
||||||
// Configure accelerometer
|
// Configure accelerometer
|
||||||
// Sample rate & measurement range
|
// Sample rate & measurement range
|
||||||
err = d.writeByte(d.AccelAddress, CTRL_REG6_XL, uint8(cfg.AccelSampleRate)<<5|uint8(cfg.AccelRange)<<3)
|
data[0] = uint8(cfg.AccelSampleRate)<<5 | uint8(cfg.AccelRange)<<3
|
||||||
|
err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG6_XL, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure gyroscope
|
// Configure gyroscope
|
||||||
// Sample rate & measurement range
|
// Sample rate & measurement range
|
||||||
err = d.writeByte(d.AccelAddress, CTRL_REG1_G, uint8(cfg.GyroSampleRate)<<5|uint8(cfg.GyroRange)<<3)
|
data[0] = uint8(cfg.GyroSampleRate)<<5 | uint8(cfg.GyroRange)<<3
|
||||||
|
err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG1_G, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -186,44 +190,33 @@ func (d *Device) doConfigure(cfg Configuration) (err error) {
|
|||||||
// Temperature compensation enabled
|
// Temperature compensation enabled
|
||||||
// High-performance mode XY axis
|
// High-performance mode XY axis
|
||||||
// Sample rate
|
// Sample rate
|
||||||
err = d.writeByte(d.MagAddress, CTRL_REG1_M, 0b10000000|0b01000000|uint8(cfg.MagSampleRate)<<2)
|
data[0] = 0b10000000 | 0b01000000 | uint8(cfg.MagSampleRate)<<2
|
||||||
|
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG1_M, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Measurement range
|
// Measurement range
|
||||||
err = d.writeByte(d.MagAddress, CTRL_REG2_M, uint8(cfg.MagRange)<<5)
|
data[0] = uint8(cfg.MagRange) << 5
|
||||||
|
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG2_M, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continuous-conversion mode
|
// Continuous-conversion mode
|
||||||
// https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode
|
// https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode
|
||||||
err = d.writeByte(d.MagAddress, CTRL_REG3_M, 0b00000000)
|
data[0] = 0b00000000
|
||||||
|
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG3_M, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// High-performance mode Z axis
|
// High-performance mode Z axis
|
||||||
err = d.writeByte(d.MagAddress, CTRL_REG4_M, 0b00001000)
|
data[0] = 0b00001000
|
||||||
|
err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG4_M, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) readBytes(addr, reg, size uint8) ([]byte, error) {
|
|
||||||
d.buf[0] = reg
|
|
||||||
err := d.bus.Tx(uint16(addr), d.buf[0:1], d.buf[1:size+1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return d.buf[1 : size+1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) writeByte(addr, reg, value uint8) error {
|
|
||||||
d.buf[0] = reg
|
|
||||||
d.buf[1] = value
|
|
||||||
return d.bus.Tx(uint16(addr), d.buf[0:2], nil)
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-4
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrThermocoupleOpen is returned when the thermocouple input is open.
|
// ErrThermocoupleOpen is returned when the thermocouple input is open.
|
||||||
@@ -14,16 +13,16 @@ var ErrThermocoupleOpen = errors.New("thermocouple input open")
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Device to read from a MAX6675 thermocouple.
|
// Create a new Device to read from a MAX6675 thermocouple.
|
||||||
// Pins must be configured before use. Frequency for SPI
|
// Pins must be configured before use. Frequency for SPI
|
||||||
// should be 4.3MHz maximum.
|
// should be 4.3MHz maximum.
|
||||||
func NewDevice(bus drivers.SPI, cs pin.Output) *Device {
|
func NewDevice(bus drivers.SPI, cs drivers.Pin) *Device {
|
||||||
return &Device{
|
return &Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: cs.Set,
|
cs: cs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-15
@@ -4,35 +4,25 @@ package max72xx
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
configurePins func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDriver creates a new max7219 connection. The SPI wire must already be configured
|
// NewDriver creates a new max7219 connection. The SPI wire must already be configured
|
||||||
// The SPI frequency must not be higher than 10MHz.
|
// The SPI frequency must not be higher than 10MHz.
|
||||||
// parameter cs: the datasheet also refers to this pin as "load" pin.
|
// parameter cs: the datasheet also refers to this pin as "load" pin.
|
||||||
func NewDevice(bus drivers.SPI, cs pin.Output) *Device {
|
func NewDevice(bus drivers.SPI, cs drivers.Pin) *Device {
|
||||||
return &Device{
|
return &Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: cs.Set,
|
cs: cs,
|
||||||
configurePins: func() {
|
|
||||||
legacy.ConfigurePinOut(cs)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure setups the pins.
|
// Configure expects that the pin of the Device has already been configured by the user as output.
|
||||||
func (driver *Device) Configure() {
|
func (driver *Device) Configure() {
|
||||||
if driver.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
driver.configurePins()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScanLimit sets the scan limit. Maximum is 8.
|
// SetScanLimit sets the scan limit. Maximum is 8.
|
||||||
|
|||||||
+8
-17
@@ -11,17 +11,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps MCP2515 SPI CAN Module.
|
// Device wraps MCP2515 SPI CAN Module.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
spi SPI
|
spi SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
msg *CANMsg
|
msg *CANMsg
|
||||||
mcpMode byte
|
mcpMode byte
|
||||||
configurePins func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CANMsg stores CAN message fields.
|
// CANMsg stores CAN message fields.
|
||||||
@@ -38,29 +35,23 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
|
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
|
||||||
func New(b drivers.SPI, csPin pin.Output) *Device {
|
func New(b drivers.SPI, csPin drivers.Pin) *Device {
|
||||||
d := &Device{
|
d := &Device{
|
||||||
spi: SPI{
|
spi: SPI{
|
||||||
bus: b,
|
bus: b,
|
||||||
tx: make([]byte, 0, bufferSize),
|
tx: make([]byte, 0, bufferSize),
|
||||||
rx: make([]byte, 0, bufferSize),
|
rx: make([]byte, 0, bufferSize),
|
||||||
},
|
},
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
msg: &CANMsg{},
|
msg: &CANMsg{},
|
||||||
configurePins: func() {
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the device for communication.
|
// Configure sets up the device for communication. It expects the SPI interface to be already
|
||||||
|
// configured, and the CS configured as output.
|
||||||
func (d *Device) Configure() {
|
func (d *Device) Configure() {
|
||||||
if d.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
d.configurePins()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const beginTimeoutValue int = 10
|
const beginTimeoutValue int = 10
|
||||||
|
|||||||
+16
-24
@@ -5,9 +5,8 @@ package onewire // import "tinygo.org/x/drivers/onewire"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"machine"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OneWire ROM commands
|
// OneWire ROM commands
|
||||||
@@ -20,8 +19,7 @@ const (
|
|||||||
|
|
||||||
// Device wraps a connection to an 1-Wire devices.
|
// Device wraps a connection to an 1-Wire devices.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
set pin.OutputFunc
|
p machine.Pin
|
||||||
get pin.InputFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config wraps a configuration to an 1-Wire devices.
|
// Config wraps a configuration to an 1-Wire devices.
|
||||||
@@ -34,30 +32,24 @@ var (
|
|||||||
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.")
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewFromFuncs expects pin setter and getter for DQ line. Ideally this driver should receive
|
// New creates a new GPIO 1-Wire connection.
|
||||||
// a one-wire bus HAL abstraction, but I was asked to show how this could be done using pin HAL so here goes.
|
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
|
||||||
func NewFromFuncs(getPinLevel pin.InputFunc, setPinLevel pin.OutputFunc) *Device {
|
func New(p machine.Pin) Device {
|
||||||
return &Device{
|
return Device{
|
||||||
set: setPinLevel,
|
p: p,
|
||||||
get: getPinLevel,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure initializes the protocol.
|
// Configure initializes the protocol.
|
||||||
func (d *Device) Configure(config Config) {}
|
func (d *Device) Configure(config Config) {}
|
||||||
|
|
||||||
// By setting the pin value one should configure as output and also expect a more
|
|
||||||
// consistent behaviour across all tinygo hosts by pulling DQ line low consistently. Win-win.
|
|
||||||
func (d *Device) cfgOut() { d.set.Low() }
|
|
||||||
func (d *Device) cfgIn() { d.get() }
|
|
||||||
|
|
||||||
// Reset pull DQ line low, then up.
|
// Reset pull DQ line low, then up.
|
||||||
func (d Device) Reset() error {
|
func (d Device) Reset() error {
|
||||||
d.cfgOut()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
time.Sleep(480 * time.Microsecond)
|
time.Sleep(480 * time.Microsecond)
|
||||||
d.cfgIn()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
time.Sleep(70 * time.Microsecond)
|
time.Sleep(70 * time.Microsecond)
|
||||||
precence := d.get()
|
precence := d.p.Get()
|
||||||
time.Sleep(410 * time.Microsecond)
|
time.Sleep(410 * time.Microsecond)
|
||||||
if precence {
|
if precence {
|
||||||
return errNoPresence
|
return errNoPresence
|
||||||
@@ -67,14 +59,14 @@ func (d Device) Reset() error {
|
|||||||
|
|
||||||
// WriteBit transmits a bit to 1-Wire bus.
|
// WriteBit transmits a bit to 1-Wire bus.
|
||||||
func (d Device) WriteBit(data uint8) {
|
func (d Device) WriteBit(data uint8) {
|
||||||
d.cfgOut()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
if data&1 == 1 { // Send '1'
|
if data&1 == 1 { // Send '1'
|
||||||
time.Sleep(5 * time.Microsecond)
|
time.Sleep(5 * time.Microsecond)
|
||||||
d.cfgIn()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
time.Sleep(60 * time.Microsecond)
|
time.Sleep(60 * time.Microsecond)
|
||||||
} else { // Send '0'
|
} else { // Send '0'
|
||||||
time.Sleep(60 * time.Microsecond)
|
time.Sleep(60 * time.Microsecond)
|
||||||
d.cfgIn()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
time.Sleep(5 * time.Microsecond)
|
time.Sleep(5 * time.Microsecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,11 +81,11 @@ func (d Device) Write(data uint8) {
|
|||||||
|
|
||||||
// ReadBit receives a bit from 1-Wire bus.
|
// ReadBit receives a bit from 1-Wire bus.
|
||||||
func (d Device) ReadBit() (data uint8) {
|
func (d Device) ReadBit() (data uint8) {
|
||||||
d.cfgOut()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
time.Sleep(3 * time.Microsecond)
|
time.Sleep(3 * time.Microsecond)
|
||||||
d.cfgIn()
|
d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
time.Sleep(8 * time.Microsecond)
|
time.Sleep(8 * time.Microsecond)
|
||||||
if d.get() {
|
if d.p.Get() {
|
||||||
data = 1
|
data = 1
|
||||||
}
|
}
|
||||||
time.Sleep(60 * time.Microsecond)
|
time.Sleep(60 * time.Microsecond)
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
//go:build tinygo
|
|
||||||
|
|
||||||
package onewire
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New creates a new GPIO 1-Wire connection.
|
|
||||||
// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k).
|
|
||||||
func New(p machine.Pin) Device {
|
|
||||||
isOut := false
|
|
||||||
return Device{
|
|
||||||
set: func(level bool) {
|
|
||||||
if !isOut {
|
|
||||||
legacy.ConfigurePinOut(p)
|
|
||||||
isOut = true
|
|
||||||
}
|
|
||||||
p.Set(level)
|
|
||||||
},
|
|
||||||
get: func() (level bool) {
|
|
||||||
if isOut {
|
|
||||||
legacy.ConfigurePinInputPullup(p)
|
|
||||||
isOut = false
|
|
||||||
}
|
|
||||||
return p.Get()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+8
-9
@@ -9,15 +9,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps an SPI connection.
|
// Device wraps an SPI connection.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
dcPin pin.OutputFunc
|
dcPin drivers.Pin
|
||||||
rstPin pin.OutputFunc
|
rstPin drivers.Pin
|
||||||
scePin pin.OutputFunc
|
scePin drivers.Pin
|
||||||
buffer []byte
|
buffer []byte
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
@@ -29,13 +28,13 @@ type Config struct {
|
|||||||
Height int16
|
Height int16
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new PCD8544 connection. The SPI bus must already be configured.
|
// New creates a new PCD8544 connection. The SPI bus and pins must already be configured.
|
||||||
func New(bus drivers.SPI, dcPin, rstPin, scePin pin.Output) *Device {
|
func New(bus drivers.SPI, dcPin, rstPin, scePin drivers.Pin) *Device {
|
||||||
return &Device{
|
return &Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
dcPin: dcPin.Set,
|
dcPin: dcPin,
|
||||||
rstPin: rstPin.Set,
|
rstPin: rstPin,
|
||||||
scePin: scePin.Set,
|
scePin: scePin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package drivers
|
||||||
|
|
||||||
|
// Pin is a digital pin interface. It is notably implemented by the
|
||||||
|
// machine.Pin type.
|
||||||
|
type Pin interface {
|
||||||
|
Get() bool
|
||||||
|
High()
|
||||||
|
Low()
|
||||||
|
Set(high bool)
|
||||||
|
}
|
||||||
+3
-45
@@ -9,30 +9,9 @@ import (
|
|||||||
"tinygo.org/x/drivers/pixel"
|
"tinygo.org/x/drivers/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestImageRGB888(t *testing.T) {
|
|
||||||
image := pixel.NewImage[pixel.RGB888](5, 3)
|
|
||||||
if width, height := image.Size(); width != 5 || height != 3 {
|
|
||||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
|
||||||
}
|
|
||||||
for _, c := range []color.RGBA{
|
|
||||||
{R: 0xff, A: 0xff},
|
|
||||||
{G: 0xff, A: 0xff},
|
|
||||||
{B: 0xff, A: 0xff},
|
|
||||||
{R: 0x10, A: 0xff},
|
|
||||||
{G: 0x10, A: 0xff},
|
|
||||||
{B: 0x10, A: 0xff},
|
|
||||||
} {
|
|
||||||
image.Set(4, 2, pixel.NewColor[pixel.RGB888](c.R, c.G, c.B))
|
|
||||||
c2 := image.Get(4, 2).RGBA()
|
|
||||||
if c2 != c {
|
|
||||||
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImageRGB565BE(t *testing.T) {
|
func TestImageRGB565BE(t *testing.T) {
|
||||||
image := pixel.NewImage[pixel.RGB565BE](5, 3)
|
image := pixel.NewImage[pixel.RGB565BE](5, 3)
|
||||||
if width, height := image.Size(); width != 5 || height != 3 {
|
if width, height := image.Size(); width != 5 && height != 3 {
|
||||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
||||||
}
|
}
|
||||||
for _, c := range []color.RGBA{
|
for _, c := range []color.RGBA{
|
||||||
@@ -51,30 +30,9 @@ func TestImageRGB565BE(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImageRGB555(t *testing.T) {
|
|
||||||
image := pixel.NewImage[pixel.RGB555](5, 3)
|
|
||||||
if width, height := image.Size(); width != 5 || height != 3 {
|
|
||||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
|
||||||
}
|
|
||||||
for _, c := range []color.RGBA{
|
|
||||||
{R: 0xff, A: 0xff},
|
|
||||||
{G: 0xff, A: 0xff},
|
|
||||||
{B: 0xff, A: 0xff},
|
|
||||||
{R: 0x10, A: 0xff},
|
|
||||||
{G: 0x10, A: 0xff},
|
|
||||||
{B: 0x10, A: 0xff},
|
|
||||||
} {
|
|
||||||
image.Set(4, 2, pixel.NewColor[pixel.RGB555](c.R, c.G, c.B))
|
|
||||||
c2 := image.Get(4, 2).RGBA()
|
|
||||||
if c2 != c {
|
|
||||||
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestImageRGB444BE(t *testing.T) {
|
func TestImageRGB444BE(t *testing.T) {
|
||||||
image := pixel.NewImage[pixel.RGB444BE](5, 3)
|
image := pixel.NewImage[pixel.RGB444BE](5, 3)
|
||||||
if width, height := image.Size(); width != 5 || height != 3 {
|
if width, height := image.Size(); width != 5 && height != 3 {
|
||||||
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
|
||||||
}
|
}
|
||||||
for _, c := range []color.RGBA{
|
for _, c := range []color.RGBA{
|
||||||
@@ -109,7 +67,7 @@ func TestImageRGB444BE(t *testing.T) {
|
|||||||
|
|
||||||
func TestImageMonochrome(t *testing.T) {
|
func TestImageMonochrome(t *testing.T) {
|
||||||
image := pixel.NewImage[pixel.Monochrome](128, 64)
|
image := pixel.NewImage[pixel.Monochrome](128, 64)
|
||||||
if width, height := image.Size(); width != 128 || height != 64 {
|
if width, height := image.Size(); width != 128 && height != 64 {
|
||||||
t.Errorf("image.Size(): expected 128, 64 but got %d, %d", width, height)
|
t.Errorf("image.Size(): expected 128, 64 but got %d, %d", width, height)
|
||||||
}
|
}
|
||||||
for _, expected := range []color.RGBA{
|
for _, expected := range []color.RGBA{
|
||||||
|
|||||||
+3
-3
@@ -161,9 +161,9 @@ func (c RGB555) BitsPerPixel() int {
|
|||||||
|
|
||||||
func (c RGB555) RGBA() color.RGBA {
|
func (c RGB555) RGBA() color.RGBA {
|
||||||
color := color.RGBA{
|
color := color.RGBA{
|
||||||
R: (uint8(c) & 0x1F) << 3,
|
R: uint8(c>>10) << 3,
|
||||||
G: (uint8(c>>5) & 0x1F) << 3,
|
G: uint8(c>>5) << 3,
|
||||||
B: (uint8(c>>10) & 0x1F) << 3,
|
B: uint8(c) << 3,
|
||||||
A: 255,
|
A: 255,
|
||||||
}
|
}
|
||||||
// Correct color rounding, so that 0xff roundtrips back to 0xff.
|
// Correct color rounding, so that 0xff roundtrips back to 0xff.
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package seesaw
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errInvalidEncoderNumber = errors.New("invalid encoder choice, 0-15 are supported")
|
|
||||||
|
|
||||||
// GetEncoderPosition returns the absolute position (or delta since the previous call) of the specified rotary encoder.
|
|
||||||
func (d *Device) GetEncoderPosition(encoder uint, asDelta bool) (int32, error) {
|
|
||||||
if encoder >= 16 {
|
|
||||||
return 0, errInvalidEncoderNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// The function address' upper nibble is the function, the lower nibble selects which encoder to communicate with
|
|
||||||
fnAddr := FunctionAddress(encoder)
|
|
||||||
if asDelta {
|
|
||||||
fnAddr |= FunctionEncoderDelta
|
|
||||||
} else {
|
|
||||||
fnAddr |= FunctionEncoderPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf [4]byte
|
|
||||||
err := d.Read(ModuleEncoderBase, fnAddr, buf[:])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return int32(buf[0])<<24 | int32(buf[1])<<16 | int32(buf[2])<<8 | int32(buf[3]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEncoderPosition calibrate's the encoder's current absolute position to be whatever the provided position is.
|
|
||||||
func (d *Device) SetEncoderPosition(encoder uint, position int32) error {
|
|
||||||
if encoder >= 16 {
|
|
||||||
return errInvalidEncoderNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// The function address' upper nibble is the function, the lower nibble selects which encoder to communicate with
|
|
||||||
fnAddr := FunctionEncoderPosition | FunctionAddress(encoder)
|
|
||||||
|
|
||||||
buf := [4]byte{
|
|
||||||
byte(position >> 24),
|
|
||||||
byte(position >> 16),
|
|
||||||
byte(position >> 8),
|
|
||||||
byte(position),
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.Write(ModuleEncoderBase, fnAddr, buf[:])
|
|
||||||
}
|
|
||||||
@@ -98,13 +98,3 @@ const (
|
|||||||
FunctionKeypadCount FunctionAddress = 0x04
|
FunctionKeypadCount FunctionAddress = 0x04
|
||||||
FunctionKeypadFifo FunctionAddress = 0x10
|
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
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
package shiftregister
|
package shiftregister
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type NumberBit int8
|
type NumberBit int8
|
||||||
@@ -17,39 +16,31 @@ const (
|
|||||||
|
|
||||||
// Device holds pin number
|
// Device holds pin number
|
||||||
type Device struct {
|
type Device struct {
|
||||||
latch, clock, out pin.OutputFunc // IC wiring
|
latch, clock, out drivers.Pin // IC wiring
|
||||||
config func()
|
bits NumberBit // Pin number
|
||||||
bits NumberBit // Pin number
|
mask uint32 // keep all pins state
|
||||||
mask uint32 // keep all pins state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShiftPin is the implementation of the ShiftPin interface.
|
// ShiftPin is the implementation of the ShiftPin interface.
|
||||||
// ShiftPin provide an interface like regular machine.Pin
|
// ShiftPin provide an interface like regular drivers.Pin
|
||||||
type ShiftPin struct {
|
type ShiftPin struct {
|
||||||
mask uint32 // Bit representing the pin
|
mask uint32 // Bit representing the pin
|
||||||
d *Device // Reference to the register
|
d *Device // Reference to the register
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new shift output register device
|
// New returns a new shift output register device
|
||||||
func New(Bits NumberBit, Latch, Clock, Out pin.Output) *Device {
|
func New(Bits NumberBit, Latch, Clock, Out drivers.Pin) *Device {
|
||||||
return &Device{
|
return &Device{
|
||||||
latch: Latch.Set,
|
latch: Latch,
|
||||||
clock: Clock.Set,
|
clock: Clock,
|
||||||
out: Out.Set,
|
out: Out,
|
||||||
bits: Bits,
|
bits: Bits,
|
||||||
config: func() {
|
|
||||||
legacy.ConfigurePinOut(Latch)
|
|
||||||
legacy.ConfigurePinOut(Clock)
|
|
||||||
legacy.ConfigurePinOut(Out)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure set hardware configuration
|
// Configure set hardware configuration. Make sure that the pins are already configured
|
||||||
|
// as output.
|
||||||
func (d *Device) Configure() {
|
func (d *Device) Configure() {
|
||||||
if d.config == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
d.latch.High()
|
d.latch.High()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +51,7 @@ func (d *Device) WriteMask(mask uint32) {
|
|||||||
d.latch.Low()
|
d.latch.Low()
|
||||||
for i := 0; i < int(d.bits); i++ {
|
for i := 0; i < int(d.bits); i++ {
|
||||||
d.clock.Low()
|
d.clock.Low()
|
||||||
d.out(mask&1 != 0)
|
d.out.Set(mask&1 != 0)
|
||||||
mask = mask >> 1
|
mask = mask >> 1
|
||||||
d.clock.High()
|
d.clock.High()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
package si5351
|
|
||||||
|
|
||||||
// The I2C address which this device listens to.
|
|
||||||
const AddressDefault = 0x60 // Assumes ADDR pin is low
|
|
||||||
const AddressAlternative = 0x61 // Assumes ADDR pin is high
|
|
||||||
|
|
||||||
const (
|
|
||||||
OUTPUT_ENABLE_CONTROL = 3
|
|
||||||
|
|
||||||
CLK0_CONTROL = 16
|
|
||||||
CLK1_CONTROL = 17
|
|
||||||
CLK2_CONTROL = 18
|
|
||||||
CLK3_CONTROL = 19
|
|
||||||
CLK4_CONTROL = 20
|
|
||||||
CLK5_CONTROL = 21
|
|
||||||
CLK6_CONTROL = 22
|
|
||||||
CLK7_CONTROL = 23
|
|
||||||
|
|
||||||
MULTISYNTH0_PARAMETERS_1 = 42
|
|
||||||
MULTISYNTH0_PARAMETERS_3 = 44
|
|
||||||
MULTISYNTH1_PARAMETERS_1 = 50
|
|
||||||
MULTISYNTH1_PARAMETERS_3 = 52
|
|
||||||
MULTISYNTH2_PARAMETERS_1 = 58
|
|
||||||
MULTISYNTH2_PARAMETERS_3 = 60
|
|
||||||
|
|
||||||
SPREAD_SPECTRUM_PARAMETERS = 149
|
|
||||||
|
|
||||||
PLL_RESET = 177
|
|
||||||
|
|
||||||
CRYSTAL_INTERNAL_LOAD_CAPACITANCE = 183
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
CRYSTAL_LOAD_6PF = (1 << 6)
|
|
||||||
CRYSTAL_LOAD_8PF = (2 << 6)
|
|
||||||
CRYSTAL_LOAD_10PF = (3 << 6)
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
CRYSTAL_FREQ_25MHZ = 25000000
|
|
||||||
CRYSTAL_FREQ_27MHZ = 27000000
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PLL_A = iota
|
|
||||||
PLL_B
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
R_DIV_1 = iota
|
|
||||||
R_DIV_2
|
|
||||||
R_DIV_4
|
|
||||||
R_DIV_8
|
|
||||||
R_DIV_16
|
|
||||||
R_DIV_32
|
|
||||||
R_DIV_64
|
|
||||||
R_DIV_128
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
MULTISYNTH_DIV_4 = 4
|
|
||||||
MULTISYNTH_DIV_6 = 6
|
|
||||||
MULTISYNTH_DIV_8 = 8
|
|
||||||
)
|
|
||||||
@@ -1,448 +0,0 @@
|
|||||||
package si5351
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
|
||||||
"tinygo.org/x/drivers/internal/regmap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device wraps an I2C connection to a SI5351 device.
|
|
||||||
type Device struct {
|
|
||||||
bus drivers.I2C
|
|
||||||
Address uint8
|
|
||||||
|
|
||||||
rw regmap.Device8I2C
|
|
||||||
initialised bool
|
|
||||||
crystalFreq uint32
|
|
||||||
crystalLoad uint8
|
|
||||||
pllaConfigured bool
|
|
||||||
pllaFreq uint32
|
|
||||||
pllbConfigured bool
|
|
||||||
pllbFreq uint32
|
|
||||||
lastRdivValue [3]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrNotInitialised = errors.New("Si5351 not initialised")
|
|
||||||
var ErrInvalidParameter = errors.New("Si5351 invalid parameter")
|
|
||||||
|
|
||||||
// New creates a new SI5351 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 {
|
|
||||||
rw := regmap.Device8I2C{}
|
|
||||||
rw.SetBus(bus, AddressDefault, binary.BigEndian)
|
|
||||||
|
|
||||||
return Device{
|
|
||||||
bus: bus,
|
|
||||||
rw: rw,
|
|
||||||
Address: AddressDefault,
|
|
||||||
crystalFreq: CRYSTAL_FREQ_25MHZ,
|
|
||||||
crystalLoad: CRYSTAL_LOAD_10PF,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure sets up the device for communication
|
|
||||||
// TODO error handling
|
|
||||||
func (d *Device) Configure() error {
|
|
||||||
// // Disable all outputs setting CLKx_DIS high
|
|
||||||
d.rw.Write8(OUTPUT_ENABLE_CONTROL, 0xFF)
|
|
||||||
|
|
||||||
// Set the load capacitance for the XTAL
|
|
||||||
d.rw.Write8(CRYSTAL_INTERNAL_LOAD_CAPACITANCE, d.crystalLoad)
|
|
||||||
|
|
||||||
// Power down all output drivers
|
|
||||||
buf := []byte{CLK0_CONTROL, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}
|
|
||||||
d.bus.Tx(uint16(d.Address), buf, nil)
|
|
||||||
|
|
||||||
// Disable spread spectrum output.
|
|
||||||
if err := d.DisableSpreadSpectrum(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.initialised = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connected returns whether a device at SI5351 address has been found.
|
|
||||||
func (d *Device) Connected() (bool, error) {
|
|
||||||
if err := d.bus.Tx(uint16(d.Address), []byte{}, []byte{0}); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableSpreadSpectrum enables spread spectrum modulation to reduce EMI.
|
|
||||||
func (d *Device) EnableSpreadSpectrum() error {
|
|
||||||
data, err := d.rw.Read8(SPREAD_SPECTRUM_PARAMETERS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data |= 0x80
|
|
||||||
return d.rw.Write8(SPREAD_SPECTRUM_PARAMETERS, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) DisableSpreadSpectrum() error {
|
|
||||||
data, err := d.rw.Read8(SPREAD_SPECTRUM_PARAMETERS)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data &^= 0x80
|
|
||||||
return d.rw.Write8(SPREAD_SPECTRUM_PARAMETERS, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) OutputEnable(output uint8, enable bool) error {
|
|
||||||
if !d.initialised {
|
|
||||||
return ErrNotInitialised
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the current value of the OUTPUT_ENABLE_CONTROL register
|
|
||||||
regVal, err := d.rw.Read8(OUTPUT_ENABLE_CONTROL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify regVal based on clk and enable
|
|
||||||
if enable {
|
|
||||||
regVal &= ^(1 << output)
|
|
||||||
} else {
|
|
||||||
regVal |= (1 << output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the modified value back to the OUTPUT_ENABLE_CONTROL register
|
|
||||||
return d.rw.Write8(OUTPUT_ENABLE_CONTROL, regVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) EnableOutputs() error {
|
|
||||||
if !d.initialised {
|
|
||||||
return ErrNotInitialised
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.rw.Write8(OUTPUT_ENABLE_CONTROL, 0x00)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) DisableOutputs() error {
|
|
||||||
if !d.initialised {
|
|
||||||
return ErrNotInitialised
|
|
||||||
}
|
|
||||||
return d.rw.Write8(OUTPUT_ENABLE_CONTROL, 0xFF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigurePLL sets the multiplier for the specified PLL
|
|
||||||
// pll The PLL to configure, which must be one of the following:
|
|
||||||
// - PLL_A
|
|
||||||
// - PLL_B
|
|
||||||
//
|
|
||||||
// mult The PLL integer multiplier (must be between 15 and 90)
|
|
||||||
//
|
|
||||||
// num The 20-bit numerator for fractional output (0..1,048,575).
|
|
||||||
// Set this to '0' for integer output.
|
|
||||||
//
|
|
||||||
// denom The 20-bit denominator for fractional output (1..1,048,575).
|
|
||||||
// Set this to '1' or higher to avoid divider by zero errors.
|
|
||||||
//
|
|
||||||
// PLL Configuration
|
|
||||||
// fVCO is the PLL output, and must be between 600..900MHz, where:
|
|
||||||
//
|
|
||||||
// fVCO = fXTAL * (a+(b/c))
|
|
||||||
//
|
|
||||||
// fXTAL = the crystal input frequency
|
|
||||||
// a = an integer between 15 and 90
|
|
||||||
// b = the fractional numerator (0..1,048,575)
|
|
||||||
// c = the fractional denominator (1..1,048,575)
|
|
||||||
//
|
|
||||||
// NOTE: Try to use integers whenever possible to avoid clock jitter
|
|
||||||
// (only use the a part, setting b to '0' and c to '1').
|
|
||||||
//
|
|
||||||
// See: http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf
|
|
||||||
func (d *Device) ConfigurePLL(pll uint8, mult uint8, num uint32, denom uint32) error {
|
|
||||||
// Basic validation
|
|
||||||
if !d.initialised {
|
|
||||||
return ErrNotInitialised
|
|
||||||
}
|
|
||||||
// mult = 15..90
|
|
||||||
if !((mult > 14) && (mult < 91)) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
// Avoid divide by zero
|
|
||||||
if !(denom > 0) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
// 20-bit limit
|
|
||||||
if !(num <= 0xFFFFF) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
// 20-bit limit
|
|
||||||
if !(denom <= 0xFFFFF) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
|
|
||||||
// PLL Multiplier Equations
|
|
||||||
//
|
|
||||||
// P1 register is an 18-bit value using following formula:
|
|
||||||
//
|
|
||||||
// P1[17:0] = 128 * mult + floor(128*(num/denom)) - 512
|
|
||||||
//
|
|
||||||
// P2 register is a 20-bit value using the following formula:
|
|
||||||
//
|
|
||||||
// P2[19:0] = 128 * num - denom * floor(128*(num/denom))
|
|
||||||
//
|
|
||||||
// P3 register is a 20-bit value using the following formula:
|
|
||||||
//
|
|
||||||
// P3[19:0] = denom
|
|
||||||
//
|
|
||||||
|
|
||||||
// Set PLL config registers
|
|
||||||
var p1, p2, p3 uint32
|
|
||||||
if num == 0 {
|
|
||||||
// Integer mode
|
|
||||||
p1 = 128*uint32(mult) - 512
|
|
||||||
p2 = num
|
|
||||||
p3 = denom
|
|
||||||
} else {
|
|
||||||
// Fractional mode
|
|
||||||
p1 = uint32(128*float64(mult) + math.Floor(128*(float64(num)/float64(denom))) - 512)
|
|
||||||
p2 = uint32(128*float64(num) - float64(denom)*math.Floor(128*(float64(num)/float64(denom))))
|
|
||||||
p3 = denom
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the appropriate starting point for the PLL registers
|
|
||||||
baseaddr := uint8(26)
|
|
||||||
if pll == PLL_B {
|
|
||||||
baseaddr = 34
|
|
||||||
}
|
|
||||||
|
|
||||||
// The datasheet is a nightmare of typos and inconsistencies here!
|
|
||||||
data := [8]byte{}
|
|
||||||
data[0] = uint8((p3 & 0x0000FF00) >> 8)
|
|
||||||
data[1] = uint8(p3 & 0x000000FF)
|
|
||||||
data[2] = uint8((p1 & 0x00030000) >> 16)
|
|
||||||
data[3] = uint8((p1 & 0x0000FF00) >> 8)
|
|
||||||
data[4] = uint8(p1 & 0x000000FF)
|
|
||||||
data[5] = uint8(((p3 & 0x000F0000) >> 12) | ((p2 & 0x000F0000) >> 16))
|
|
||||||
data[6] = uint8((p2 & 0x0000FF00) >> 8)
|
|
||||||
data[7] = uint8(p2 & 0x000000FF)
|
|
||||||
if err := d.bus.Tx(uint16(baseaddr), data[:], nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset both PLLs
|
|
||||||
if err := d.rw.Write8(PLL_RESET, (1<<7)|(1<<5)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the frequency settings for use with the Multisynth helper
|
|
||||||
fvco := float64(d.crystalFreq) * (float64(mult) + (float64(num) / float64(denom)))
|
|
||||||
if pll == PLL_A {
|
|
||||||
d.pllaConfigured = true
|
|
||||||
d.pllaFreq = uint32(math.Floor(fvco))
|
|
||||||
} else {
|
|
||||||
d.pllbConfigured = true
|
|
||||||
d.pllbFreq = uint32(math.Floor(fvco))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigureMultisynth divider, which determines the
|
|
||||||
// output clock frequency based on the specified PLL input.
|
|
||||||
//
|
|
||||||
// output The output channel to use (0..2)
|
|
||||||
//
|
|
||||||
// pll The PLL input source to use, which must be one of:
|
|
||||||
// - PLL_A
|
|
||||||
// - PLL_B
|
|
||||||
//
|
|
||||||
// div The integer divider for the Multisynth output.
|
|
||||||
//
|
|
||||||
// If pure integer values are used, this value must be one of:
|
|
||||||
// - MULTISYNTH_DIV_4
|
|
||||||
// - MULTISYNTH_DIV_6
|
|
||||||
// - MULTISYNTH_DIV_8
|
|
||||||
// If fractional output is used, this value must be between 8 and 900.
|
|
||||||
//
|
|
||||||
// num The 20-bit numerator for fractional output (0..1,048,575).
|
|
||||||
//
|
|
||||||
// Set this to '0' for integer output.
|
|
||||||
//
|
|
||||||
// denom The 20-bit denominator for fractional output (1..1,048,575).
|
|
||||||
//
|
|
||||||
// Set this to '1' or higher to avoid divide by zero errors.
|
|
||||||
//
|
|
||||||
// # Output Clock Configuration
|
|
||||||
//
|
|
||||||
// The multisynth dividers are applied to the specified PLL output,
|
|
||||||
// and are used to reduce the PLL output to a valid range (500kHz
|
|
||||||
// to 160MHz). The relationship can be seen in this formula, where
|
|
||||||
// fVCO is the PLL output frequency and MSx is the multisynth divider:
|
|
||||||
//
|
|
||||||
// fOUT = fVCO / MSx
|
|
||||||
//
|
|
||||||
// Valid multisynth dividers are 4, 6, or 8 when using integers,
|
|
||||||
// or any fractional values between 8 + 1/1,048,575 and 900 + 0/1
|
|
||||||
// The following formula is used for the fractional mode divider:
|
|
||||||
//
|
|
||||||
// a + b / c
|
|
||||||
//
|
|
||||||
// a = The integer value, which must be 4, 6 or 8 in integer mode (MSx_INT=1) or 8..900 in fractional mode (MSx_INT=0).
|
|
||||||
// b = The fractional numerator (0..1,048,575)
|
|
||||||
// c = The fractional denominator (1..1,048,575)
|
|
||||||
//
|
|
||||||
// NOTE: Try to use integers whenever possible to avoid clock jitter
|
|
||||||
// NOTE: For output frequencies > 150MHz, you must set the divider
|
|
||||||
//
|
|
||||||
// to 4 and adjust to PLL to generate the frequency (for example
|
|
||||||
// a PLL of 640 to generate a 160MHz output clock). This is not
|
|
||||||
// yet supported in the driver, which limits frequencies to 500kHz .. 150MHz.
|
|
||||||
//
|
|
||||||
// NOTE: For frequencies below 500kHz (down to 8kHz) Rx_DIV must be
|
|
||||||
//
|
|
||||||
// used, but this isn't currently implemented in the driver.
|
|
||||||
func (d *Device) ConfigureMultisynth(output uint8, pll uint8, div uint32, num uint32, denom uint32) error {
|
|
||||||
// Basic validation
|
|
||||||
if !d.initialised {
|
|
||||||
return ErrNotInitialised
|
|
||||||
}
|
|
||||||
// Channel range
|
|
||||||
if !(output < 3) {
|
|
||||||
return fmt.Errorf("output channel must be between 0 and 2")
|
|
||||||
}
|
|
||||||
// Divider integer value
|
|
||||||
if !((div > 3) && (div < 2049)) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
// Avoid divide by zero
|
|
||||||
if !(denom > 0) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
// 20-bit limit
|
|
||||||
if !(num <= 0xFFFFF) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
// 20-bit limit
|
|
||||||
if !(denom <= 0xFFFFF) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the requested PLL has been initialised
|
|
||||||
if pll == PLL_A && !d.pllaConfigured {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
if pll == PLL_B && !d.pllbConfigured {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output Multisynth Divider Equations
|
|
||||||
//
|
|
||||||
// where: a = div, b = num and c = denom
|
|
||||||
//
|
|
||||||
// P1 register is an 18-bit value using following formula:
|
|
||||||
//
|
|
||||||
// P1[17:0] = 128 * a + floor(128*(b/c)) - 512
|
|
||||||
//
|
|
||||||
// P2 register is a 20-bit value using the following formula:
|
|
||||||
//
|
|
||||||
// P2[19:0] = 128 * b - c * floor(128*(b/c))
|
|
||||||
//
|
|
||||||
// P3 register is a 20-bit value using the following formula:
|
|
||||||
//
|
|
||||||
// P3[19:0] = c
|
|
||||||
//
|
|
||||||
|
|
||||||
// Set PLL config registers
|
|
||||||
var p1, p2, p3 uint32
|
|
||||||
if num == 0 {
|
|
||||||
// Integer mode
|
|
||||||
p1 = 128*div - 512
|
|
||||||
p2 = 0
|
|
||||||
p3 = denom
|
|
||||||
} else if denom == 1 {
|
|
||||||
// Fractional mode, simplified calculations
|
|
||||||
p1 = 128*div + 128*num - 512
|
|
||||||
p2 = 128*num - 128
|
|
||||||
p3 = 1
|
|
||||||
} else {
|
|
||||||
// Fractional mode
|
|
||||||
p1 = uint32(128*float64(div) + math.Floor(128*(float64(num)/float64(denom))) - 512)
|
|
||||||
p2 = uint32(128*float64(num) - float64(denom)*math.Floor(128*(float64(num)/float64(denom))))
|
|
||||||
p3 = denom
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the appropriate starting point for the PLL registers
|
|
||||||
baseaddr := uint8(0)
|
|
||||||
switch output {
|
|
||||||
case 0:
|
|
||||||
baseaddr = MULTISYNTH0_PARAMETERS_1
|
|
||||||
case 1:
|
|
||||||
baseaddr = MULTISYNTH1_PARAMETERS_1
|
|
||||||
case 2:
|
|
||||||
baseaddr = MULTISYNTH2_PARAMETERS_1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the MSx config registers
|
|
||||||
data := [8]byte{}
|
|
||||||
data[0] = uint8((p3 & 0xFF00) >> 8)
|
|
||||||
data[1] = uint8(p3 & 0xFF)
|
|
||||||
data[2] = uint8(((p1 & 0x30000) >> 16)) | d.lastRdivValue[output]
|
|
||||||
data[3] = uint8((p1 & 0xFF00) >> 8)
|
|
||||||
data[4] = uint8(p1 & 0xFF)
|
|
||||||
data[5] = uint8(((p3 & 0xF0000) >> 12) | ((p2 & 0xF0000) >> 16))
|
|
||||||
data[6] = uint8((p2 & 0xFF00) >> 8)
|
|
||||||
data[7] = uint8(p2 & 0xFF)
|
|
||||||
if err := d.bus.Tx(uint16(baseaddr), data[:], nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the clk control and enable the output
|
|
||||||
// TODO: Check if the clk control byte needs to be updated.
|
|
||||||
clkControlReg := uint8(0x0F) // 8mA drive strength, MS0 as CLK0 source, Clock not inverted, powered up
|
|
||||||
if pll == PLL_B {
|
|
||||||
clkControlReg |= (1 << 5) // Uses PLLB
|
|
||||||
}
|
|
||||||
if num == 0 {
|
|
||||||
clkControlReg |= (1 << 6) // Integer mode
|
|
||||||
}
|
|
||||||
|
|
||||||
var register uint8
|
|
||||||
switch output {
|
|
||||||
case 0:
|
|
||||||
register = CLK0_CONTROL
|
|
||||||
case 1:
|
|
||||||
register = CLK1_CONTROL
|
|
||||||
case 2:
|
|
||||||
register = CLK2_CONTROL
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.rw.Write8(register, clkControlReg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Device) ConfigureRdiv(output uint8, div uint8) error {
|
|
||||||
// Channel range
|
|
||||||
if !(output < 3) {
|
|
||||||
return ErrInvalidParameter
|
|
||||||
}
|
|
||||||
|
|
||||||
var register uint8
|
|
||||||
switch output {
|
|
||||||
case 0:
|
|
||||||
register = MULTISYNTH0_PARAMETERS_3
|
|
||||||
case 1:
|
|
||||||
register = MULTISYNTH1_PARAMETERS_3
|
|
||||||
case 2:
|
|
||||||
register = MULTISYNTH2_PARAMETERS_3
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := d.rw.Read8(register)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.lastRdivValue[output] = (div & 0x07) << 4
|
|
||||||
data = (data & 0x0F) | d.lastRdivValue[output]
|
|
||||||
return d.rw.Write8(register, data)
|
|
||||||
}
|
|
||||||
+3
-8
@@ -44,7 +44,6 @@ tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341
|
|||||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
|
tinygo build -size short -o ./build/test.hex -target=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=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=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=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/mag3110/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
|
||||||
@@ -59,17 +58,15 @@ tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/ma
|
|||||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
|
tinygo build -size short -o ./build/test.hex -target=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/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=microbit ./examples/pcd8544/setpixel/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw/soil-sensor
|
tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/seesaw
|
||||||
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=arduino ./examples/servo
|
||||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/sgp30
|
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=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/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/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/shtc3/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=xiao-ble ./examples/ssd1306/
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=xiao-rp2040 ./examples/ssd1306/
|
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=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/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/st7735/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
|
||||||
@@ -143,8 +140,6 @@ 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.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.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=feather-nrf52840 ./examples/max6675/main.go
|
||||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/ens160/main.go
|
|
||||||
tinygo build -size short -o ./build/test.hex -target=pico ./examples/si5351/main.go
|
|
||||||
# network examples (espat)
|
# network examples (espat)
|
||||||
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
|
tinygo build -size short -o ./build/test.hex -target=challenger-rp2040 ./examples/net/ntpclient/
|
||||||
# network examples (wifinina)
|
# network examples (wifinina)
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build tinygo
|
|
||||||
|
|
||||||
package ssd1289
|
package ssd1289
|
||||||
|
|
||||||
import "machine"
|
import "machine"
|
||||||
|
|||||||
+13
-19
@@ -5,11 +5,9 @@ package ssd1289
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
"machine"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bus interface {
|
type Bus interface {
|
||||||
@@ -17,32 +15,28 @@ type Bus interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
rs pin.OutputFunc
|
rs drivers.Pin
|
||||||
wr pin.OutputFunc
|
wr drivers.Pin
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
bus Bus
|
bus Bus
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = int16(240)
|
const width = int16(240)
|
||||||
const height = int16(320)
|
const height = int16(320)
|
||||||
|
|
||||||
func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bus) Device {
|
func New(rs drivers.Pin, wr drivers.Pin, cs drivers.Pin, rst drivers.Pin, bus Bus) Device {
|
||||||
d := Device{
|
d := Device{
|
||||||
rs: rs.Set,
|
rs: rs,
|
||||||
wr: wr.Set,
|
wr: wr,
|
||||||
cs: cs.Set,
|
cs: cs,
|
||||||
rst: rst.Set,
|
rst: rst,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
}
|
}
|
||||||
legacy.ConfigurePinOut(rs)
|
|
||||||
legacy.ConfigurePinOut(wr)
|
|
||||||
legacy.ConfigurePinOut(cs)
|
|
||||||
legacy.ConfigurePinOut(rst)
|
|
||||||
|
|
||||||
cs.Set(true)
|
cs.High()
|
||||||
rst.Set(true)
|
rst.High()
|
||||||
wr.Set(true)
|
wr.High()
|
||||||
|
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|||||||
+133
-29
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
"tinygo.org/x/drivers/pixel"
|
"tinygo.org/x/drivers/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,15 +22,16 @@ type ResetValue [2]byte
|
|||||||
|
|
||||||
// Device wraps I2C or SPI connection.
|
// Device wraps I2C or SPI connection.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus Buser
|
bus Buser
|
||||||
buffer []byte
|
buffer []byte
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
vccState VccMode
|
bufferSize int16
|
||||||
canReset bool
|
vccState VccMode
|
||||||
resetCol ResetValue
|
canReset bool
|
||||||
resetPage ResetValue
|
resetCol ResetValue
|
||||||
rotation drivers.Rotation
|
resetPage ResetValue
|
||||||
|
rotation drivers.Rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config is the configuration for the display
|
// Config is the configuration for the display
|
||||||
@@ -48,15 +50,48 @@ type Config struct {
|
|||||||
Rotation drivers.Rotation
|
Rotation drivers.Rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type I2CBus struct {
|
||||||
|
wire drivers.I2C
|
||||||
|
Address uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type SPIBus struct {
|
||||||
|
wire drivers.SPI
|
||||||
|
dcPin drivers.Pin
|
||||||
|
resetPin drivers.Pin
|
||||||
|
csPin drivers.Pin
|
||||||
|
}
|
||||||
|
|
||||||
type Buser interface {
|
type Buser interface {
|
||||||
configure(address uint16, size int16) []byte // configure the bus and return the image buffer to use
|
configure() error
|
||||||
command(cmd uint8) error // send a command to the display
|
tx(data []byte, isCommand bool) error
|
||||||
flush() error // send the image to the display, faster than "tx()" in i2c case since avoids slice copy
|
setAddress(address uint16) error
|
||||||
tx(data []byte, isCommand bool) error // generic transmit function
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type VccMode uint8
|
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 and pins must already be configured.
|
||||||
|
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin drivers.Pin) Device {
|
||||||
|
return Device{
|
||||||
|
bus: &SPIBus{
|
||||||
|
wire: bus,
|
||||||
|
dcPin: dcPin,
|
||||||
|
resetPin: resetPin,
|
||||||
|
csPin: csPin,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure initializes the display with default configuration
|
// Configure initializes the display with default configuration
|
||||||
func (d *Device) Configure(cfg Config) {
|
func (d *Device) Configure(cfg Config) {
|
||||||
var zeroReset ResetValue
|
var zeroReset ResetValue
|
||||||
@@ -70,6 +105,9 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
} else {
|
} else {
|
||||||
d.height = 64
|
d.height = 64
|
||||||
}
|
}
|
||||||
|
if cfg.Address != 0 {
|
||||||
|
d.bus.setAddress(cfg.Address)
|
||||||
|
}
|
||||||
if cfg.VccState != 0 {
|
if cfg.VccState != 0 {
|
||||||
d.vccState = cfg.VccState
|
d.vccState = cfg.VccState
|
||||||
} else {
|
} else {
|
||||||
@@ -85,9 +123,11 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
} else {
|
} else {
|
||||||
d.resetPage = ResetValue{0, uint8(d.height/8) - 1}
|
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.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
|
||||||
|
|
||||||
d.buffer = d.bus.configure(cfg.Address, d.width*d.height/8)
|
d.bus.configure()
|
||||||
|
|
||||||
time.Sleep(100 * time.Nanosecond)
|
time.Sleep(100 * time.Nanosecond)
|
||||||
d.Command(DISPLAYOFF)
|
d.Command(DISPLAYOFF)
|
||||||
@@ -149,22 +189,11 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.Command(NORMALDISPLAY)
|
d.Command(NORMALDISPLAY)
|
||||||
d.Command(DEACTIVATE_SCROLL)
|
d.Command(DEACTIVATE_SCROLL)
|
||||||
d.Command(DISPLAYON)
|
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
|
// ClearBuffer clears the image buffer
|
||||||
func (d *Device) ClearBuffer() {
|
func (d *Device) ClearBuffer() {
|
||||||
for i := 0; i < len(d.buffer); i++ {
|
for i := int16(0); i < d.bufferSize; i++ {
|
||||||
d.buffer[i] = 0
|
d.buffer[i] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +219,7 @@ func (d *Device) Display() error {
|
|||||||
d.Command(d.resetPage[1])
|
d.Command(d.resetPage[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.bus.flush()
|
return d.Tx(d.buffer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPixel enables or disables a pixel in the buffer
|
// SetPixel enables or disables a pixel in the buffer
|
||||||
@@ -219,10 +248,12 @@ func (d *Device) GetPixel(x int16, y int16) bool {
|
|||||||
|
|
||||||
// SetBuffer changes the whole buffer at once
|
// SetBuffer changes the whole buffer at once
|
||||||
func (d *Device) SetBuffer(buffer []byte) error {
|
func (d *Device) SetBuffer(buffer []byte) error {
|
||||||
if len(buffer) != len(d.buffer) {
|
if int16(len(buffer)) != d.bufferSize {
|
||||||
return errBufferSize
|
return errBufferSize
|
||||||
}
|
}
|
||||||
copy(d.buffer, buffer)
|
for i := int16(0); i < d.bufferSize; i++ {
|
||||||
|
d.buffer[i] = buffer[i]
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +262,79 @@ func (d *Device) GetBuffer() []byte {
|
|||||||
return d.buffer
|
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()
|
||||||
|
b.dcPin.Low()
|
||||||
|
b.csPin.Low()
|
||||||
|
|
||||||
|
err = b.wire.Tx(data, nil)
|
||||||
|
b.csPin.High()
|
||||||
|
} else {
|
||||||
|
b.csPin.High()
|
||||||
|
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.
|
// Size returns the current size of the display.
|
||||||
func (d *Device) Size() (w, h int16) {
|
func (d *Device) Size() (w, h int16) {
|
||||||
return d.width, d.height
|
return d.width, d.height
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
+9
-14
@@ -10,8 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model uint8
|
type Model uint8
|
||||||
@@ -20,9 +18,9 @@ type Rotation uint8
|
|||||||
// Device wraps an SPI connection.
|
// Device wraps an SPI connection.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
dcPin pin.OutputFunc
|
dcPin drivers.Pin
|
||||||
resetPin pin.OutputFunc
|
resetPin drivers.Pin
|
||||||
csPin pin.OutputFunc
|
csPin drivers.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
batchLength int16
|
batchLength int16
|
||||||
@@ -36,16 +34,13 @@ type Config struct {
|
|||||||
Height int16
|
Height int16
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new SSD1331 connection. The SPI wire must already be configured.
|
// New creates a new SSD1331 connection. The SPI wire and pins must already be configured.
|
||||||
func New(bus drivers.SPI, resetPin, dcPin, csPin pin.Output) Device {
|
func New(bus drivers.SPI, resetPin, dcPin, csPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(resetPin)
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
dcPin: dcPin.Set,
|
dcPin: dcPin,
|
||||||
resetPin: resetPin.Set,
|
resetPin: resetPin,
|
||||||
csPin: csPin.Set,
|
csPin: csPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +247,7 @@ func (d *Device) Data(data uint8) {
|
|||||||
|
|
||||||
// Tx sends data to the display
|
// Tx sends data to the display
|
||||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||||
d.dcPin(!isCommand)
|
d.dcPin.Set(!isCommand)
|
||||||
d.bus.Tx(data, nil)
|
d.bus.Tx(data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-34
@@ -9,8 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -20,18 +18,17 @@ var (
|
|||||||
|
|
||||||
// Device wraps an SPI connection.
|
// Device wraps an SPI connection.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
dcPin pin.OutputFunc
|
dcPin drivers.Pin
|
||||||
resetPin pin.OutputFunc
|
resetPin drivers.Pin
|
||||||
csPin pin.OutputFunc
|
csPin drivers.Pin
|
||||||
enPin pin.OutputFunc
|
enPin drivers.Pin
|
||||||
rwPin pin.OutputFunc
|
rwPin drivers.Pin
|
||||||
configurePins func()
|
width int16
|
||||||
width int16
|
height int16
|
||||||
height int16
|
rowOffset int16
|
||||||
rowOffset int16
|
columnOffset int16
|
||||||
columnOffset int16
|
bufferLength int16
|
||||||
bufferLength int16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config is the configuration for the display
|
// Config is the configuration for the display
|
||||||
@@ -43,29 +40,19 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new SSD1351 connection. The SPI wire must already be configured.
|
// New creates a new SSD1351 connection. The SPI wire must already be configured.
|
||||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin pin.Output) Device {
|
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin drivers.Pin) Device {
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
dcPin: dcPin.Set,
|
dcPin: dcPin,
|
||||||
resetPin: resetPin.Set,
|
resetPin: resetPin,
|
||||||
csPin: csPin.Set,
|
csPin: csPin,
|
||||||
enPin: enPin.Set,
|
enPin: enPin,
|
||||||
rwPin: rwPin.Set,
|
rwPin: rwPin,
|
||||||
configurePins: func() {
|
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(resetPin)
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
legacy.ConfigurePinOut(enPin)
|
|
||||||
legacy.ConfigurePinOut(rwPin)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure initializes the display with default configuration
|
// Configure initializes the display with default configuration
|
||||||
func (d *Device) Configure(cfg Config) {
|
func (d *Device) Configure(cfg Config) {
|
||||||
if d.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
if cfg.Width == 0 {
|
if cfg.Width == 0 {
|
||||||
cfg.Width = 128
|
cfg.Width = 128
|
||||||
}
|
}
|
||||||
@@ -84,9 +71,6 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.bufferLength = d.height
|
d.bufferLength = d.height
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure GPIO pins
|
|
||||||
d.configurePins()
|
|
||||||
|
|
||||||
// reset the device
|
// reset the device
|
||||||
d.resetPin.High()
|
d.resetPin.High()
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@@ -286,7 +270,7 @@ func (d *Device) Data(data uint8) {
|
|||||||
|
|
||||||
// Tx sends data to the display
|
// Tx sends data to the display
|
||||||
func (d *Device) Tx(data []byte, isCommand bool) {
|
func (d *Device) Tx(data []byte, isCommand bool) {
|
||||||
d.dcPin(!isCommand)
|
d.dcPin.Set(!isCommand)
|
||||||
d.csPin.Low()
|
d.csPin.Low()
|
||||||
d.bus.Tx(data, nil)
|
d.bus.Tx(data, nil)
|
||||||
d.csPin.High()
|
d.csPin.High()
|
||||||
|
|||||||
+12
-18
@@ -10,8 +10,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
"tinygo.org/x/drivers/pixel"
|
"tinygo.org/x/drivers/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,10 +38,10 @@ type Device = DeviceOf[pixel.RGB565BE]
|
|||||||
// formats.
|
// formats.
|
||||||
type DeviceOf[T Color] struct {
|
type DeviceOf[T Color] struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
dcPin pin.OutputFunc
|
dcPin drivers.Pin
|
||||||
resetPin pin.OutputFunc
|
resetPin drivers.Pin
|
||||||
csPin pin.OutputFunc
|
csPin drivers.Pin
|
||||||
blPin pin.OutputFunc
|
blPin drivers.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
columnOffset int16
|
columnOffset int16
|
||||||
@@ -66,23 +64,19 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ST7735 connection. The SPI wire must already be configured.
|
// New creates a new ST7735 connection. The SPI wire must already be configured.
|
||||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
|
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) Device {
|
||||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI
|
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI
|
||||||
// wire must already be configured.
|
// wire and pins must already be configured.
|
||||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) DeviceOf[T] {
|
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) DeviceOf[T] {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(resetPin)
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
legacy.ConfigurePinOut(blPin)
|
|
||||||
return DeviceOf[T]{
|
return DeviceOf[T]{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
dcPin: dcPin.Set,
|
dcPin: dcPin,
|
||||||
resetPin: resetPin.Set,
|
resetPin: resetPin,
|
||||||
csPin: csPin.Set,
|
csPin: csPin,
|
||||||
blPin: blPin.Set,
|
blPin: blPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,7 +418,7 @@ func (d *DeviceOf[T]) Data(data uint8) {
|
|||||||
|
|
||||||
// Tx sends data to the display
|
// Tx sends data to the display
|
||||||
func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) {
|
func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) {
|
||||||
d.dcPin(!isCommand)
|
d.dcPin.Set(!isCommand)
|
||||||
d.bus.Tx(data, nil)
|
d.bus.Tx(data, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-23
@@ -7,14 +7,13 @@ package st7789 // import "tinygo.org/x/drivers/st7789"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"machine"
|
||||||
"math"
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
"tinygo.org/x/drivers/pixel"
|
"tinygo.org/x/drivers/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,10 +46,10 @@ type Device = DeviceOf[pixel.RGB565BE]
|
|||||||
// formats.
|
// formats.
|
||||||
type DeviceOf[T Color] struct {
|
type DeviceOf[T Color] struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
dcPin pin.OutputFunc
|
dcPin drivers.Pin
|
||||||
resetPin pin.OutputFunc
|
resetPin drivers.Pin
|
||||||
csPin pin.OutputFunc
|
csPin drivers.Pin
|
||||||
blPin pin.OutputFunc
|
blPin drivers.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
columnOffsetCfg int16
|
columnOffsetCfg int16
|
||||||
@@ -84,27 +83,19 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
// New creates a new ST7789 connection. The SPI wire must already be configured.
|
||||||
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device {
|
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) Device {
|
||||||
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
|
// NewOf creates a new ST7789 connection with a particular pixel format. The SPI
|
||||||
// wire must already be configured.
|
// wire and pins must already be configured.
|
||||||
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) DeviceOf[T] {
|
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.Pin) DeviceOf[T] {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(resetPin)
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
legacy.ConfigurePinOut(blPin)
|
|
||||||
var cs pin.OutputFunc
|
|
||||||
if !legacy.PinIsNoPin(csPin) {
|
|
||||||
cs = csPin.Set
|
|
||||||
}
|
|
||||||
return DeviceOf[T]{
|
return DeviceOf[T]{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
dcPin: dcPin.Set,
|
dcPin: dcPin,
|
||||||
resetPin: resetPin.Set,
|
resetPin: resetPin,
|
||||||
csPin: cs,
|
csPin: csPin,
|
||||||
blPin: blPin.Set,
|
blPin: blPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +225,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
|
// startWrite must be called at the beginning of all exported methods to set the
|
||||||
// chip select pin low.
|
// chip select pin low.
|
||||||
func (d *DeviceOf[T]) startWrite() {
|
func (d *DeviceOf[T]) startWrite() {
|
||||||
if d.csPin != nil {
|
if d.csPin != machine.NoPin {
|
||||||
d.csPin.Low()
|
d.csPin.Low()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +233,7 @@ func (d *DeviceOf[T]) startWrite() {
|
|||||||
// endWrite must be called at the end of all exported methods to set the chip
|
// endWrite must be called at the end of all exported methods to set the chip
|
||||||
// select pin high.
|
// select pin high.
|
||||||
func (d *DeviceOf[T]) endWrite() {
|
func (d *DeviceOf[T]) endWrite() {
|
||||||
if d.csPin != nil {
|
if d.csPin != machine.NoPin {
|
||||||
d.csPin.High()
|
d.csPin.High()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build tinygo
|
|
||||||
|
|
||||||
package sx127x
|
package sx127x
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+3
-4
@@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
"tinygo.org/x/drivers/lora"
|
"tinygo.org/x/drivers/lora"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ const (
|
|||||||
// Device wraps an SPI connection to a SX127x device.
|
// Device wraps an SPI connection to a SX127x device.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
spi drivers.SPI // SPI bus for module communication
|
spi drivers.SPI // SPI bus for module communication
|
||||||
rstPin pin.OutputFunc // GPIO for reset
|
rstPin drivers.Pin // GPIO for reset
|
||||||
radioEventChan chan lora.RadioEvent // Channel for Receiving events
|
radioEventChan chan lora.RadioEvent // Channel for Receiving events
|
||||||
loraConf lora.Config // Current Lora configuration
|
loraConf lora.Config // Current Lora configuration
|
||||||
controller RadioController // to manage interactions with the radio
|
controller RadioController // to manage interactions with the radio
|
||||||
@@ -43,10 +42,10 @@ func (d *Device) GetRadioEventChan() chan lora.RadioEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new SX127x connection. The SPI bus must already be configured.
|
// New creates a new SX127x connection. The SPI bus must already be configured.
|
||||||
func New(spi drivers.SPI, rstPin pin.Output) *Device {
|
func New(spi drivers.SPI, rstPin drivers.Pin) *Device {
|
||||||
k := Device{
|
k := Device{
|
||||||
spi: spi,
|
spi: spi,
|
||||||
rstPin: rstPin.Set,
|
rstPin: rstPin,
|
||||||
radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
|
radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE),
|
||||||
spiTxBuf: make([]byte, SPI_BUFFER_SIZE),
|
spiTxBuf: make([]byte, SPI_BUFFER_SIZE),
|
||||||
spiRxBuf: make([]byte, SPI_BUFFER_SIZE),
|
spiRxBuf: make([]byte, SPI_BUFFER_SIZE),
|
||||||
|
|||||||
+14
-19
@@ -11,8 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
"tinygo.org/x/drivers/pixel"
|
"tinygo.org/x/drivers/pixel"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,10 +30,10 @@ type Config struct {
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
buffer []uint8
|
buffer []uint8
|
||||||
@@ -49,18 +47,15 @@ type Device struct {
|
|||||||
|
|
||||||
type Speed uint8
|
type Speed uint8
|
||||||
|
|
||||||
// New returns a new uc8151 driver. Pass in a fully configured SPI bus.
|
// New returns a new uc8151 driver. Pass in a fully configured SPI bus and pins.
|
||||||
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
// busyPin should be set and input, the others as outputs.
|
||||||
legacy.ConfigurePinOut(csPin)
|
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(rstPin)
|
|
||||||
legacy.ConfigurePinInput(busyPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
dc: dcPin.Set,
|
dc: dcPin,
|
||||||
rst: rstPin.Set,
|
rst: rstPin,
|
||||||
isBusy: busyPin.Get,
|
busy: busyPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,14 +309,14 @@ func (d *Device) ClearDisplay() {
|
|||||||
|
|
||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for !d.isBusy() {
|
for !d.busy.Get() {
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBusy returns the busy status of the display
|
// IsBusy returns the busy status of the display
|
||||||
func (d *Device) IsBusy() bool {
|
func (d *Device) IsBusy() bool {
|
||||||
return d.isBusy()
|
return d.busy.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBuffer sets the buffer to 0xFF (white)
|
// ClearBuffer sets the buffer to 0xFF (white)
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@ package drivers
|
|||||||
|
|
||||||
// Version returns a user-readable string showing the version of the drivers package for support purposes.
|
// 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.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.33.0"
|
const Version = "0.32.0"
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import (
|
|||||||
"machine"
|
"machine"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -25,14 +24,14 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus *machine.SPI
|
bus *machine.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
configurePins func()
|
|
||||||
buffer []uint8
|
buffer []uint8
|
||||||
rotation Rotation
|
rotation Rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rotation uint8
|
type Rotation uint8
|
||||||
@@ -82,29 +81,18 @@ var partialRefresh = [159]uint8{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new epd1in54 driver. Pass in a fully configured SPI bus.
|
// New returns a new epd1in54 driver. Pass in a fully configured SPI bus.
|
||||||
func New(bus *machine.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
func New(bus *machine.SPI, csPin, dcPin, rstPin, busyPin drivers.Pin) Device {
|
||||||
return Device{
|
return Device{
|
||||||
buffer: make([]uint8, (uint32(Width)*uint32(Height))/8),
|
buffer: make([]uint8, (uint32(Width)*uint32(Height))/8),
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
dc: dcPin.Set,
|
dc: dcPin,
|
||||||
rst: rstPin.Set,
|
rst: rstPin,
|
||||||
isBusy: busyPin.Get,
|
busy: busyPin,
|
||||||
configurePins: func() {
|
|
||||||
legacy.ConfigurePinOut(csPin)
|
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(rstPin)
|
|
||||||
legacy.ConfigurePinInput(busyPin)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) LDirInit(cfg Config) {
|
func (d *Device) LDirInit(cfg Config) {
|
||||||
if d.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
d.configurePins()
|
|
||||||
|
|
||||||
d.bus.Configure(machine.SPIConfig{
|
d.bus.Configure(machine.SPIConfig{
|
||||||
Frequency: 2000000,
|
Frequency: 2000000,
|
||||||
Mode: 0,
|
Mode: 0,
|
||||||
@@ -159,11 +147,6 @@ func (d *Device) LDirInit(cfg Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) HDirInit(cfg Config) {
|
func (d *Device) HDirInit(cfg Config) {
|
||||||
if d.configurePins == nil {
|
|
||||||
panic(legacy.ErrConfigBeforeInstantiated)
|
|
||||||
}
|
|
||||||
d.configurePins()
|
|
||||||
|
|
||||||
d.bus.Configure(machine.SPIConfig{
|
d.bus.Configure(machine.SPIConfig{
|
||||||
Frequency: 2000000,
|
Frequency: 2000000,
|
||||||
Mode: 0,
|
Mode: 0,
|
||||||
@@ -378,7 +361,7 @@ func (d *Device) Clear() {
|
|||||||
|
|
||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for d.isBusy() {
|
for d.busy.Get() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
@@ -386,7 +369,7 @@ func (d *Device) WaitUntilIdle() {
|
|||||||
|
|
||||||
// IsBusy returns the busy status of the display
|
// IsBusy returns the busy status of the display
|
||||||
func (d *Device) IsBusy() bool {
|
func (d *Device) IsBusy() bool {
|
||||||
return d.isBusy()
|
return d.busy.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBuffer sets the buffer to 0xFF (white)
|
// ClearBuffer sets the buffer to 0xFF (white)
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -22,10 +20,10 @@ type Config struct {
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
logicalWidth int16
|
logicalWidth int16
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
@@ -53,18 +51,15 @@ var lutPartialUpdate = [30]uint8{
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
|
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus and pins.
|
||||||
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
// Busy pin should input, the rest should be output.
|
||||||
legacy.ConfigurePinOut(csPin)
|
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(rstPin)
|
|
||||||
legacy.ConfigurePinInput(busyPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
dc: dcPin.Set,
|
dc: dcPin,
|
||||||
rst: rstPin.Set,
|
rst: rstPin,
|
||||||
isBusy: busyPin.Get,
|
busy: busyPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,14 +294,14 @@ func (d *Device) setMemoryPointer(x int16, y int16) {
|
|||||||
|
|
||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for d.isBusy() {
|
for d.busy.Get() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBusy returns the busy status of the display
|
// IsBusy returns the busy status of the display
|
||||||
func (d *Device) IsBusy() bool {
|
func (d *Device) IsBusy() bool {
|
||||||
return d.isBusy()
|
return d.busy.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBuffer sets the buffer to 0xFF (white)
|
// ClearBuffer sets the buffer to 0xFF (white)
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -21,10 +19,10 @@ type Config struct {
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
buffer [][]uint8
|
buffer [][]uint8
|
||||||
@@ -33,18 +31,15 @@ type Device struct {
|
|||||||
|
|
||||||
type Color uint8
|
type Color uint8
|
||||||
|
|
||||||
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
|
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus and pins.
|
||||||
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
// Busy pin should input, the rest should be output.
|
||||||
legacy.ConfigurePinOut(csPin)
|
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(rstPin)
|
|
||||||
legacy.ConfigurePinInput(busyPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
dc: dcPin.Set,
|
dc: dcPin,
|
||||||
rst: rstPin.Set,
|
rst: rstPin,
|
||||||
isBusy: busyPin.Get,
|
busy: busyPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,14 +273,14 @@ func (d *Device) ClearDisplay() {
|
|||||||
|
|
||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for !d.isBusy() {
|
for !d.busy.Get() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBusy returns the busy status of the display
|
// IsBusy returns the busy status of the display
|
||||||
func (d *Device) IsBusy() bool {
|
func (d *Device) IsBusy() bool {
|
||||||
return d.isBusy()
|
return d.busy.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBuffer sets the buffer to 0xFF (white)
|
// ClearBuffer sets the buffer to 0xFF (white)
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -19,12 +17,20 @@ const (
|
|||||||
|
|
||||||
const Baudrate = 4_000_000 // 4 MHz
|
const Baudrate = 4_000_000 // 4 MHz
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ResetPin drivers.Pin
|
||||||
|
DataPin drivers.Pin
|
||||||
|
ChipSelectPin drivers.Pin
|
||||||
|
BusyPin drivers.Pin
|
||||||
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
|
|
||||||
blackBuffer []byte
|
blackBuffer []byte
|
||||||
redBuffer []byte
|
redBuffer []byte
|
||||||
}
|
}
|
||||||
@@ -43,23 +49,14 @@ func New(bus drivers.SPI) Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
// Configure configures the device. Note that pins should already
|
||||||
ResetPin pin.Output
|
// be configured. Busy pin should be input, the rest should be output.
|
||||||
DataPin pin.Output
|
|
||||||
ChipSelectPin pin.Output
|
|
||||||
BusyPin pin.Input
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure configures the device and its pins.
|
|
||||||
func (d *Device) Configure(c Config) error {
|
func (d *Device) Configure(c Config) error {
|
||||||
d.cs = c.ChipSelectPin.Set
|
d.cs = c.ChipSelectPin
|
||||||
d.dc = c.DataPin.Set
|
d.dc = c.DataPin
|
||||||
d.rst = c.ResetPin.Set
|
d.rst = c.ResetPin
|
||||||
d.isBusy = c.BusyPin.Get
|
d.busy = c.BusyPin
|
||||||
legacy.ConfigurePinOut(c.ChipSelectPin)
|
|
||||||
legacy.ConfigurePinOut(c.DataPin)
|
|
||||||
legacy.ConfigurePinOut(c.ResetPin)
|
|
||||||
legacy.ConfigurePinInput(c.BusyPin)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +224,7 @@ func (d *Device) WaitUntilIdle() {
|
|||||||
// give it some time to get busy
|
// give it some time to get busy
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
for d.isBusy() { // high = busy
|
for d.busy.Get() { // high = busy
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -29,10 +27,10 @@ type Config struct {
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
logicalWidth int16
|
logicalWidth int16
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
@@ -61,18 +59,15 @@ var lutPartialUpdate = [30]uint8{
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new epd2in9 driver. Pass in a fully configured SPI bus.
|
// New returns a new epd2in9 driver. Pass in a fully configured SPI bus and pins.
|
||||||
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
// Busy pin should input, the rest should be output.
|
||||||
legacy.ConfigurePinOut(csPin)
|
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(rstPin)
|
|
||||||
legacy.ConfigurePinInput(busyPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
dc: dcPin.Set,
|
dc: dcPin,
|
||||||
rst: rstPin.Set,
|
rst: rstPin,
|
||||||
isBusy: busyPin.Get,
|
busy: busyPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,14 +241,14 @@ func (d *Device) setMemoryPointer(x int16, y int16) {
|
|||||||
|
|
||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for d.isBusy() {
|
for d.busy.Get() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBusy returns the busy status of the display
|
// IsBusy returns the busy status of the display
|
||||||
func (d *Device) IsBusy() bool {
|
func (d *Device) IsBusy() bool {
|
||||||
return d.isBusy()
|
return d.busy.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBuffer sets the buffer to 0xFF (white)
|
// ClearBuffer sets the buffer to 0xFF (white)
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
"tinygo.org/x/drivers/internal/legacy"
|
|
||||||
"tinygo.org/x/drivers/internal/pin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -26,10 +24,10 @@ type Config struct {
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs pin.OutputFunc
|
cs drivers.Pin
|
||||||
dc pin.OutputFunc
|
dc drivers.Pin
|
||||||
rst pin.OutputFunc
|
rst drivers.Pin
|
||||||
isBusy pin.InputFunc
|
busy drivers.Pin
|
||||||
logicalWidth int16
|
logicalWidth int16
|
||||||
width int16
|
width int16
|
||||||
height int16
|
height int16
|
||||||
@@ -40,18 +38,15 @@ type Device struct {
|
|||||||
|
|
||||||
type Rotation uint8
|
type Rotation uint8
|
||||||
|
|
||||||
// New returns a new epd4in2 driver. Pass in a fully configured SPI bus.
|
// New returns a new epd4in2 driver. Pass in a fully configured SPI bus and pins.
|
||||||
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
|
// Busy pin should input, the rest should be output.
|
||||||
legacy.ConfigurePinOut(csPin)
|
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin drivers.Pin) Device {
|
||||||
legacy.ConfigurePinOut(dcPin)
|
|
||||||
legacy.ConfigurePinOut(rstPin)
|
|
||||||
legacy.ConfigurePinInput(busyPin)
|
|
||||||
return Device{
|
return Device{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
cs: csPin.Set,
|
cs: csPin,
|
||||||
dc: dcPin.Set,
|
dc: dcPin,
|
||||||
rst: rstPin.Set,
|
rst: rstPin,
|
||||||
isBusy: busyPin.Get,
|
busy: busyPin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,14 +307,14 @@ func (d *Device) ClearDisplay() {
|
|||||||
|
|
||||||
// WaitUntilIdle waits until the display is ready
|
// WaitUntilIdle waits until the display is ready
|
||||||
func (d *Device) WaitUntilIdle() {
|
func (d *Device) WaitUntilIdle() {
|
||||||
for d.isBusy() {
|
for d.busy.Get() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBusy returns the busy status of the display
|
// IsBusy returns the busy status of the display
|
||||||
func (d *Device) IsBusy() bool {
|
func (d *Device) IsBusy() bool {
|
||||||
return d.isBusy()
|
return d.busy.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearBuffer sets the buffer to 0xFF (white)
|
// ClearBuffer sets the buffer to 0xFF (white)
|
||||||
|
|||||||
@@ -931,356 +931,6 @@ void ws2812_writeByte125(char c, uint32_t *portSet, uint32_t *portClear, uint32_
|
|||||||
[portClear]"m"(*portClear));
|
[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))
|
__attribute__((always_inline))
|
||||||
void ws2812_writeByte168(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
|
void ws2812_writeByte168(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
|
||||||
// Timings:
|
// Timings:
|
||||||
@@ -2182,16 +1832,6 @@ func (d Device) writeByte125(c byte) {
|
|||||||
interrupt.Restore(mask)
|
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) {
|
func (d Device) writeByte168(c byte) {
|
||||||
portSet, maskSet := d.Pin.PortMaskSet()
|
portSet, maskSet := d.Pin.PortMaskSet()
|
||||||
portClear, maskClear := d.Pin.PortMaskClear()
|
portClear, maskClear := d.Pin.PortMaskClear()
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips.
|
// Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips.
|
||||||
package ws2812 // import "tinygo.org/x/drivers/ws2812"
|
package ws2812 // import "tinygo.org/x/drivers/ws2812"
|
||||||
|
|
||||||
//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=cortexm 16 48 64 120 125 168 200
|
||||||
//go:generate go run gen-ws2812.go -arch=tinygoriscv 160 320
|
//go:generate go run gen-ws2812.go -arch=tinygoriscv 160 320
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ func (d Device) WriteByte(c byte) error {
|
|||||||
case 125_000_000: // 125 MHz e.g. rp2040 originally
|
case 125_000_000: // 125 MHz e.g. rp2040 originally
|
||||||
d.writeByte125(c)
|
d.writeByte125(c)
|
||||||
return nil
|
return nil
|
||||||
case 150_000_000: // 150MHz, e.g. rp2350
|
|
||||||
d.writeByte150(c)
|
|
||||||
return nil
|
|
||||||
case 168_000_000: // 168MHz, e.g. stm32f405
|
case 168_000_000: // 168MHz, e.g. stm32f405
|
||||||
d.writeByte168(c)
|
d.writeByte168(c)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user