mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-17 13:23:26 +00:00
ssd1306: avoid unnecessary heap allocations (#767)
* ssd1306: avoid unnecessary heap allocations * ssd1306: extract i2c and spi bus implementations * ssd1306: refactor tests -- show fps and heap usage * ssd1306: bring back the lost exported methods * Adjust examples * Fix smoketests for ssd1306
This commit is contained in:
committed by
deadprogram
parent
6e1e9b75b8
commit
e61269cdc0
@@ -1,51 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"image/color"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{
|
|
||||||
Frequency: machine.TWI_FREQ_400KHZ,
|
|
||||||
})
|
|
||||||
|
|
||||||
display := ssd1306.NewI2C(machine.I2C0)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Address: ssd1306.Address_128_32,
|
|
||||||
Width: 128,
|
|
||||||
Height: 32,
|
|
||||||
})
|
|
||||||
|
|
||||||
display.ClearDisplay()
|
|
||||||
|
|
||||||
x := int16(0)
|
|
||||||
y := int16(0)
|
|
||||||
deltaX := int16(1)
|
|
||||||
deltaY := int16(1)
|
|
||||||
for {
|
|
||||||
pixel := display.GetPixel(x, y)
|
|
||||||
c := color.RGBA{255, 255, 255, 255}
|
|
||||||
if pixel {
|
|
||||||
c = color.RGBA{0, 0, 0, 255}
|
|
||||||
}
|
|
||||||
display.SetPixel(x, y, c)
|
|
||||||
display.Display()
|
|
||||||
|
|
||||||
x += deltaX
|
|
||||||
y += deltaY
|
|
||||||
|
|
||||||
if x == 0 || x == 127 {
|
|
||||||
deltaX = -deltaX
|
|
||||||
}
|
|
||||||
|
|
||||||
if y == 0 || y == 31 {
|
|
||||||
deltaY = -deltaY
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
// This example shows how to use 128x64 display over I2C
|
|
||||||
// Tested on Seeeduino XIAO Expansion Board https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
|
|
||||||
//
|
|
||||||
// According to manual, I2C address of the display is 0x78, but that's 8-bit address.
|
|
||||||
// TinyGo operates on 7-bit addresses and respective 7-bit address would be 0x3C, which we use below.
|
|
||||||
//
|
|
||||||
// To learn more about different types of I2C addresses, please see following page
|
|
||||||
// https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
|
|
||||||
"image/color"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.I2C0.Configure(machine.I2CConfig{
|
|
||||||
Frequency: machine.TWI_FREQ_400KHZ,
|
|
||||||
})
|
|
||||||
|
|
||||||
display := ssd1306.NewI2C(machine.I2C0)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Address: 0x3C,
|
|
||||||
Width: 128,
|
|
||||||
Height: 64,
|
|
||||||
})
|
|
||||||
|
|
||||||
display.ClearDisplay()
|
|
||||||
|
|
||||||
x := int16(0)
|
|
||||||
y := int16(0)
|
|
||||||
deltaX := int16(1)
|
|
||||||
deltaY := int16(1)
|
|
||||||
for {
|
|
||||||
pixel := display.GetPixel(x, y)
|
|
||||||
c := color.RGBA{255, 255, 255, 255}
|
|
||||||
if pixel {
|
|
||||||
c = color.RGBA{0, 0, 0, 255}
|
|
||||||
}
|
|
||||||
display.SetPixel(x, y, c)
|
|
||||||
display.Display()
|
|
||||||
|
|
||||||
x += deltaX
|
|
||||||
y += deltaY
|
|
||||||
|
|
||||||
if x == 0 || x == 127 {
|
|
||||||
deltaX = -deltaX
|
|
||||||
}
|
|
||||||
|
|
||||||
if y == 0 || y == 63 {
|
|
||||||
deltaY = -deltaY
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// This example shows how to use SSD1306 OLED display driver over I2C and SPI.
|
||||||
|
//
|
||||||
|
// Check the `newSSD1306Display()` functions for I2C and SPI initializations.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"image/color"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
display := newSSD1306Display()
|
||||||
|
display.ClearDisplay()
|
||||||
|
|
||||||
|
w, h := display.Size()
|
||||||
|
x := int16(0)
|
||||||
|
y := int16(0)
|
||||||
|
deltaX := int16(1)
|
||||||
|
deltaY := int16(1)
|
||||||
|
|
||||||
|
traceTime := time.Now().UnixMilli() + 1000
|
||||||
|
frames := 0
|
||||||
|
ms := runtime.MemStats{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
pixel := display.GetPixel(x, y)
|
||||||
|
c := color.RGBA{255, 255, 255, 255}
|
||||||
|
if pixel {
|
||||||
|
c = color.RGBA{0, 0, 0, 255}
|
||||||
|
}
|
||||||
|
display.SetPixel(x, y, c)
|
||||||
|
display.Display()
|
||||||
|
|
||||||
|
x += deltaX
|
||||||
|
y += deltaY
|
||||||
|
|
||||||
|
if x == 0 || x == w-1 {
|
||||||
|
deltaX = -deltaX
|
||||||
|
}
|
||||||
|
|
||||||
|
if y == 0 || y == h-1 {
|
||||||
|
deltaY = -deltaY
|
||||||
|
}
|
||||||
|
|
||||||
|
frames++
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
if now >= traceTime {
|
||||||
|
runtime.ReadMemStats(&ms)
|
||||||
|
println("TS", now, "| FPS", frames, "| HeapInuse", ms.HeapInuse)
|
||||||
|
traceTime = now + 1000
|
||||||
|
frames = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//go:build xiao_ble
|
||||||
|
|
||||||
|
// This initializes SSD1306 OLED display driver over I2C.
|
||||||
|
//
|
||||||
|
// Seeed XIAO BLE board + SSD1306 128x32 I2C OLED display.
|
||||||
|
//
|
||||||
|
// Wiring:
|
||||||
|
// - XIAO GND -> OLED GND
|
||||||
|
// - XIAO 3v3 -> OLED VCC
|
||||||
|
// - XIAO D4 (SDA) -> OLED SDA
|
||||||
|
// - XIAO D5 (SCL) -> OLED SCK
|
||||||
|
//
|
||||||
|
// For your case:
|
||||||
|
// - Connect the display to I2C pins on your board.
|
||||||
|
// - Adjust I2C address and display size as needed.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newSSD1306Display() *ssd1306.Device {
|
||||||
|
machine.I2C0.Configure(machine.I2CConfig{
|
||||||
|
Frequency: 400 * machine.KHz,
|
||||||
|
SDA: machine.SDA0_PIN,
|
||||||
|
SCL: machine.SCL0_PIN,
|
||||||
|
})
|
||||||
|
display := ssd1306.NewI2C(machine.I2C0)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Address: ssd1306.Address_128_32, // or ssd1306.Address
|
||||||
|
Width: 128,
|
||||||
|
Height: 32, // or 64
|
||||||
|
})
|
||||||
|
return display
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//go:build thumby
|
||||||
|
|
||||||
|
// This initializes SSD1306 OLED display driver over SPI.
|
||||||
|
//
|
||||||
|
// Thumby board has a tiny built-in 72x40 display.
|
||||||
|
//
|
||||||
|
// As the display is built-in, no wiring is needed.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newSSD1306Display() *ssd1306.Device {
|
||||||
|
machine.SPI0.Configure(machine.SPIConfig{})
|
||||||
|
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Width: 72,
|
||||||
|
Height: 40,
|
||||||
|
ResetCol: ssd1306.ResetValue{28, 99},
|
||||||
|
ResetPage: ssd1306.ResetValue{0, 5},
|
||||||
|
})
|
||||||
|
return display
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//go:build xiao_rp2040
|
||||||
|
|
||||||
|
// This initializes SSD1306 OLED display driver over SPI.
|
||||||
|
//
|
||||||
|
// Seeed XIAO RP2040 board + SSD1306 128x64 SPI OLED display.
|
||||||
|
//
|
||||||
|
// Wiring:
|
||||||
|
// - XIAO GND -> OLED GND
|
||||||
|
// - XIAO 3v3 -> OLED VCC
|
||||||
|
// - XIAO D8 (SCK) -> OLED D0
|
||||||
|
// - XIAO D10 (SDO) -> OLED D1
|
||||||
|
// - XIAO D4 -> OLED RES
|
||||||
|
// - XIAO D5 -> OLED DC
|
||||||
|
// - XIAO D6 -> OLED CS
|
||||||
|
//
|
||||||
|
// For your case:
|
||||||
|
// - Connect the display to SPI pins on your board.
|
||||||
|
// - Adjust RES, DC and CS pins as needed.
|
||||||
|
// - Adjust SPI frequency as needed.
|
||||||
|
// - Adjust display size as needed.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/ssd1306"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newSSD1306Display() *ssd1306.Device {
|
||||||
|
machine.SPI0.Configure(machine.SPIConfig{
|
||||||
|
Frequency: 50 * machine.MHz,
|
||||||
|
})
|
||||||
|
display := ssd1306.NewSPI(machine.SPI0, machine.D5, machine.D4, machine.D6)
|
||||||
|
display.Configure(ssd1306.Config{
|
||||||
|
Width: 128,
|
||||||
|
Height: 64,
|
||||||
|
})
|
||||||
|
return display
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.SPI0.Configure(machine.SPIConfig{
|
|
||||||
Frequency: 8000000,
|
|
||||||
})
|
|
||||||
display := ssd1306.NewSPI(machine.SPI0, machine.P8, machine.P7, machine.P9)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Width: 128,
|
|
||||||
Height: 64,
|
|
||||||
})
|
|
||||||
|
|
||||||
display.ClearDisplay()
|
|
||||||
|
|
||||||
x := int16(64)
|
|
||||||
y := int16(32)
|
|
||||||
deltaX := int16(1)
|
|
||||||
deltaY := int16(1)
|
|
||||||
for {
|
|
||||||
pixel := display.GetPixel(x, y)
|
|
||||||
c := color.RGBA{255, 255, 255, 255}
|
|
||||||
if pixel {
|
|
||||||
c = color.RGBA{0, 0, 0, 255}
|
|
||||||
}
|
|
||||||
display.SetPixel(x, y, c)
|
|
||||||
display.Display()
|
|
||||||
|
|
||||||
x += deltaX
|
|
||||||
y += deltaY
|
|
||||||
|
|
||||||
if x == 0 || x == 127 {
|
|
||||||
deltaX = -deltaX
|
|
||||||
}
|
|
||||||
|
|
||||||
if y == 0 || y == 63 {
|
|
||||||
deltaY = -deltaY
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
// This example using the SSD1306 OLED display over SPI on the Thumby board
|
|
||||||
// A very tiny 72x40 display.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers/ssd1306"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.SPI0.Configure(machine.SPIConfig{})
|
|
||||||
display := ssd1306.NewSPI(machine.SPI0, machine.THUMBY_DC_PIN, machine.THUMBY_RESET_PIN, machine.THUMBY_CS_PIN)
|
|
||||||
display.Configure(ssd1306.Config{
|
|
||||||
Width: 72,
|
|
||||||
Height: 40,
|
|
||||||
ResetCol: ssd1306.ResetValue{28, 99},
|
|
||||||
ResetPage: ssd1306.ResetValue{0, 5},
|
|
||||||
})
|
|
||||||
|
|
||||||
display.ClearDisplay()
|
|
||||||
|
|
||||||
x := int16(36)
|
|
||||||
y := int16(20)
|
|
||||||
deltaX := int16(1)
|
|
||||||
deltaY := int16(1)
|
|
||||||
for {
|
|
||||||
pixel := display.GetPixel(x, y)
|
|
||||||
c := color.RGBA{255, 255, 255, 255}
|
|
||||||
if pixel {
|
|
||||||
c = color.RGBA{0, 0, 0, 255}
|
|
||||||
}
|
|
||||||
display.SetPixel(x, y, c)
|
|
||||||
display.Display()
|
|
||||||
|
|
||||||
x += deltaX
|
|
||||||
y += deltaY
|
|
||||||
|
|
||||||
if x == 0 || x == 71 {
|
|
||||||
deltaX = -deltaX
|
|
||||||
}
|
|
||||||
|
|
||||||
if y == 0 || y == 39 {
|
|
||||||
deltaY = -deltaY
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-2
@@ -65,8 +65,9 @@ tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/
|
|||||||
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=microbit ./examples/ssd1306/i2c_128x32/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/spi_128x64/main.go
|
tinygo build -size short -o ./build/test.hex -target=xiao-rp2040 ./examples/ssd1306/
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=thumby ./examples/ssd1306/
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/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
|
||||||
|
|||||||
+29
-137
@@ -6,11 +6,9 @@ package ssd1306 // import "tinygo.org/x/drivers/ssd1306"
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"image/color"
|
"image/color"
|
||||||
"machine"
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,16 +21,15 @@ 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
|
||||||
bufferSize int16
|
vccState VccMode
|
||||||
vccState VccMode
|
canReset bool
|
||||||
canReset bool
|
resetCol ResetValue
|
||||||
resetCol ResetValue
|
resetPage ResetValue
|
||||||
resetPage ResetValue
|
rotation drivers.Rotation
|
||||||
rotation drivers.Rotation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config is the configuration for the display
|
// Config is the configuration for the display
|
||||||
@@ -51,51 +48,15 @@ type Config struct {
|
|||||||
Rotation drivers.Rotation
|
Rotation drivers.Rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
type I2CBus struct {
|
|
||||||
wire drivers.I2C
|
|
||||||
Address uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
type SPIBus struct {
|
|
||||||
wire drivers.SPI
|
|
||||||
dcPin machine.Pin
|
|
||||||
resetPin machine.Pin
|
|
||||||
csPin machine.Pin
|
|
||||||
}
|
|
||||||
|
|
||||||
type Buser interface {
|
type Buser interface {
|
||||||
configure() error
|
configure(address uint16, size int16) []byte // configure the bus and return the image buffer to use
|
||||||
tx(data []byte, isCommand bool) error
|
command(cmd uint8) error // send a command to the display
|
||||||
setAddress(address uint16) error
|
flush() error // send the image to the display, faster than "tx()" in i2c case since avoids slice copy
|
||||||
|
tx(data []byte, isCommand bool) error // generic transmit function
|
||||||
}
|
}
|
||||||
|
|
||||||
type VccMode uint8
|
type VccMode uint8
|
||||||
|
|
||||||
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
|
|
||||||
func NewI2C(bus drivers.I2C) Device {
|
|
||||||
return Device{
|
|
||||||
bus: &I2CBus{
|
|
||||||
wire: bus,
|
|
||||||
Address: Address,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
|
|
||||||
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
|
|
||||||
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
||||||
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
||||||
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
|
||||||
return Device{
|
|
||||||
bus: &SPIBus{
|
|
||||||
wire: bus,
|
|
||||||
dcPin: dcPin,
|
|
||||||
resetPin: resetPin,
|
|
||||||
csPin: csPin,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure initializes the display with default configuration
|
// 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
|
||||||
@@ -109,9 +70,6 @@ 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 {
|
||||||
@@ -127,11 +85,9 @@ 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.bus.configure()
|
d.buffer = d.bus.configure(cfg.Address, d.width*d.height/8)
|
||||||
|
|
||||||
time.Sleep(100 * time.Nanosecond)
|
time.Sleep(100 * time.Nanosecond)
|
||||||
d.Command(DISPLAYOFF)
|
d.Command(DISPLAYOFF)
|
||||||
@@ -193,11 +149,22 @@ 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 := int16(0); i < d.bufferSize; i++ {
|
for i := 0; i < len(d.buffer); i++ {
|
||||||
d.buffer[i] = 0
|
d.buffer[i] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +190,7 @@ func (d *Device) Display() error {
|
|||||||
d.Command(d.resetPage[1])
|
d.Command(d.resetPage[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.Tx(d.buffer, false)
|
return d.bus.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPixel enables or disables a pixel in the buffer
|
// SetPixel enables or disables a pixel in the buffer
|
||||||
@@ -252,12 +219,10 @@ func (d *Device) GetPixel(x int16, y int16) bool {
|
|||||||
|
|
||||||
// SetBuffer changes the whole buffer at once
|
// SetBuffer changes the whole buffer at once
|
||||||
func (d *Device) SetBuffer(buffer []byte) error {
|
func (d *Device) SetBuffer(buffer []byte) error {
|
||||||
if int16(len(buffer)) != d.bufferSize {
|
if len(buffer) != len(d.buffer) {
|
||||||
return errBufferSize
|
return errBufferSize
|
||||||
}
|
}
|
||||||
for i := int16(0); i < d.bufferSize; i++ {
|
copy(d.buffer, buffer)
|
||||||
d.buffer[i] = buffer[i]
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,79 +231,6 @@ 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
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package ssd1306
|
||||||
|
|
||||||
|
import (
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type I2CBus struct {
|
||||||
|
wire drivers.I2C
|
||||||
|
address uint16
|
||||||
|
buffer []byte // buffer to avoid heap allocations
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewI2C creates a new SSD1306 connection. The I2C wire must already be configured.
|
||||||
|
func NewI2C(bus drivers.I2C) *Device {
|
||||||
|
return &Device{
|
||||||
|
bus: &I2CBus{
|
||||||
|
wire: bus,
|
||||||
|
address: Address,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure address for the I2C bus and allocate the buffer
|
||||||
|
func (b *I2CBus) configure(address uint16, size int16) []byte {
|
||||||
|
if address != 0 {
|
||||||
|
b.address = address
|
||||||
|
}
|
||||||
|
b.buffer = make([]byte, size+2) // +1 for the mode and +1 for a command
|
||||||
|
return b.buffer[2:] // return the image buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// command sends a command to the display
|
||||||
|
func (b *I2CBus) command(cmd uint8) error {
|
||||||
|
b.buffer[0] = 0x00 // Command mode
|
||||||
|
b.buffer[1] = cmd
|
||||||
|
return b.wire.Tx(b.address, b.buffer[:2], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush sends the image to the display
|
||||||
|
func (b *I2CBus) flush() error {
|
||||||
|
b.buffer[1] = 0x40 // Data mode
|
||||||
|
return b.wire.Tx(b.address, b.buffer[1:], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx sends data to the display
|
||||||
|
func (b *I2CBus) tx(data []byte, isCommand bool) error {
|
||||||
|
if isCommand {
|
||||||
|
return b.command(data[0])
|
||||||
|
}
|
||||||
|
copy(b.buffer[2:], data)
|
||||||
|
return b.flush()
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package ssd1306
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SPIBus struct {
|
||||||
|
wire drivers.SPI
|
||||||
|
dcPin machine.Pin
|
||||||
|
resetPin machine.Pin
|
||||||
|
csPin machine.Pin
|
||||||
|
buffer []byte // buffer to avoid heap allocations
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
|
||||||
|
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) *Device {
|
||||||
|
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
return &Device{
|
||||||
|
bus: &SPIBus{
|
||||||
|
wire: bus,
|
||||||
|
dcPin: dcPin,
|
||||||
|
resetPin: resetPin,
|
||||||
|
csPin: csPin,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure pins with the SPI bus and allocate the buffer
|
||||||
|
func (b *SPIBus) configure(address uint16, size int16) []byte {
|
||||||
|
b.csPin.Low()
|
||||||
|
b.dcPin.Low()
|
||||||
|
b.resetPin.Low()
|
||||||
|
|
||||||
|
b.resetPin.High()
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
b.resetPin.Low()
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
b.resetPin.High()
|
||||||
|
|
||||||
|
b.buffer = make([]byte, size+1) // +1 for a command
|
||||||
|
return b.buffer[1:] // return the image buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// command sends a command to the display
|
||||||
|
func (b *SPIBus) command(cmd uint8) error {
|
||||||
|
b.buffer[0] = cmd
|
||||||
|
return b.tx(b.buffer[:1], true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush sends the image to the display
|
||||||
|
func (b *SPIBus) flush() error {
|
||||||
|
return b.tx(b.buffer[1:], false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx sends data to the display
|
||||||
|
func (b *SPIBus) tx(data []byte, isCommand bool) error {
|
||||||
|
b.csPin.High()
|
||||||
|
b.dcPin.Set(!isCommand)
|
||||||
|
b.csPin.Low()
|
||||||
|
err := b.wire.Tx(data, nil)
|
||||||
|
b.csPin.High()
|
||||||
|
return err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user