-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add sharpmem #724
base: dev
Are you sure you want to change the base?
Add sharpmem #724
Conversation
Marking as draft as I would like to perform some additional manual testing. |
In case it helps someone else, here is an example where the display, if wired correctly, will do some back-and-forth between white and black, and then scan right and down. (nice!nano + nice!view) Note: a refined version of the script is further down in the comments. package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/sharpmem"
)
var display *sharpmem.Device
func initDisplay() error {
machine.P0_06.Configure(machine.PinConfig{Mode: machine.PinOutput})
err := machine.SPI0.Configure(machine.SPIConfig{
Frequency: 2000000,
SCK: machine.P0_20,
SDO: machine.P0_17,
SDI: machine.P0_25,
Mode: 0,
LSBFirst: true,
})
if err != nil {
println("spi configure", err)
return err
}
d := sharpmem.NewSPI(machine.SPI0, machine.P0_06)
d.Configure(sharpmem.Config{
Width: 160,
Height: 68,
})
display = &d
return nil
}
func main() {
err := initDisplay()
if err != nil {
println(err.Error())
return
}
it := 0
for {
time.Sleep(500 * time.Millisecond)
switch it {
case 0, 2, 4:
for x := range int16(160) {
for y := range int16(68) {
display.SetPixel(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
}
}
err = display.Display()
if err != nil {
println(err.Error())
}
println("all pixels black")
case 1:
err := display.Clear()
if err != nil {
println(err.Error())
}
println("all pixels white (display.Clear)")
case 3:
display.ClearBuffer()
err = display.Display()
if err != nil {
println(err.Error())
}
println("all pixels white (display.ClearBuffer + display.Display)")
case 5:
for x := range int16(160) {
for y := range int16(68) {
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
}
err = display.Display()
if err != nil {
println(err.Error())
}
println("all pixels white (display.SetPixel + display.Display)")
case 6:
println("scan right")
for x := range int16(160) {
for y := range int16(68) {
display.SetPixel(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
}
err = display.Display()
if err != nil {
println(err.Error())
}
}
err := display.Clear()
if err != nil {
println(err.Error())
}
println("all pixels white (display.Clear)")
case 7:
println("scan down")
for y := range int16(68) {
for x := range int16(160) {
display.SetPixel(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
}
err = display.Display()
if err != nil {
println(err.Error())
}
// scan down is really fast because of the line-diffing
time.Sleep(16 * time.Millisecond)
}
err := display.Clear()
if err != nil {
println(err.Error())
}
println("all pixels white (display.Clear)")
}
it = (it + 1) % 8
}
} 24eff85fef15fcb9.mp4 |
I'd say the PR is ready for review. Let me know if anything needs to be revised. |
Found an out of bounds exception, something to do with the line calculations during |
I did some refactoring and studied all the datasheets, and I refactored the driver to allow for all of the supported devices to (theoretically) work. Some observations I made that led to the refactor:
Here, I have derived a chart with the (theoretically) supported SKUs, their resolutions, the clocks required to transfer a line of pixels (the bits of the pixels + padding), the padding itself explicitly, and lastly the bits required for the address:
TL;DR: Lots of complexity, but the implementation tries to transfer the bytes in a way that should be compatible with all SKUs. With the only exception being the shifting of the high bits of the address based on max address size. For completeness, I am unable to test in any other device except for LS011B7DH03. I can test the other implementations for out of bounds exceptions and panics, but ofc the display renders gibberish if I try to render with a different resolution. |
Thanks for all the work on this @rdnt
I think testing for out of bounds exceptions and panics on displays you do not have sounds like a good idea.
I think leaving them in while documenting that only the |
I added a (speculative) bug fix regarding the address padding (and the assumed intricacies of it), and some tests for it, and did some cleanup. @deadprogram I added the doc comment you mentioned on the constructor. Marking as ready-for-review again. 🙏🏻 Oh, and here is a refined script for others to test the implementation with: package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/sharpmem"
)
func initSPI() error {
machine.P0_06.Configure(machine.PinConfig{Mode: machine.PinOutput})
err := machine.SPI0.Configure(machine.SPIConfig{
Frequency: 2000000,
SCK: machine.P0_20,
SDO: machine.P0_17,
SDI: machine.P0_25,
Mode: 0,
LSBFirst: true,
})
if err != nil {
println("spi configure", err)
return err
}
return nil
}
func main() {
time.Sleep(time.Second)
configs := []sharpmem.Config{
//{Width: 128, Height: 128}, // LS010B7DH04, LS013B7DH03
{Width: 160, Height: 68}, // LS011B7DH03
//{Width: 184, Height: 38}, // LS012B7DD01
//{Width: 144, Height: 168}, // LS013B7DH05
//{Width: 230, Height: 303}, // LS018B7DH02
//{Width: 400, Height: 240}, // LS027B7DH01, LS027B7DH01A
//{Width: 336, Height: 536}, //LS032B7DD02
//{Width: 320, Height: 240}, //LS044Q7DH01
}
// test with optimizations being disabled
cfgLen := len(configs)
for i := 0; i < cfgLen; i++ {
configs = append(configs, sharpmem.Config{
Width: configs[i].Width,
Height: configs[i].Height,
DisableOptimizations: true,
})
}
err := initSPI()
if err != nil {
println(err.Error())
return
}
display := sharpmem.New(machine.SPI0, machine.P0_06)
println("initialized")
for {
for _, cfg := range configs {
print("=== Testing resolution: ", cfg.Width, "x", cfg.Height, " (optimizations: ", !cfg.DisableOptimizations, ")", "\n")
display.Configure(cfg)
for it := range 10 {
time.Sleep(500 * time.Millisecond)
switch it {
case 0, 2, 4:
for x := range cfg.Width {
for y := range cfg.Height {
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
}
err = display.Display()
if err != nil {
println(err.Error())
}
println("all pixels black")
case 1:
err := display.Clear()
if err != nil {
println(err.Error())
}
println("all pixels white (display.Clear)")
case 3:
display.ClearBuffer()
err = display.Display()
if err != nil {
println(err.Error())
}
println("all pixels white (display.ClearBuffer + display.Display)")
case 5:
for x := range cfg.Width {
for y := range cfg.Height {
display.SetPixel(x, y, color.RGBA{R: 0, G: 0, B: 0, A: 255})
}
}
err = display.Display()
if err != nil {
println(err.Error())
}
println("all pixels white (display.SetPixel + display.Display)")
case 6:
println("scan right")
for x := range cfg.Width {
for y := range cfg.Height {
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
err = display.Display()
if err != nil {
println(err.Error())
}
}
err = display.Clear()
if err != nil {
println(err.Error())
}
println("all pixels white (display.Clear)")
case 7:
println("scan down")
for y := range cfg.Height {
for x := range cfg.Width {
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
err = display.Display()
if err != nil {
println(err.Error())
}
// scan down is really fast because of the line-diffing (when enabled)
//time.Sleep(16 * time.Millisecond)
}
err = display.Clear()
if err != nil {
println(err.Error())
}
println("all pixels white (display.Clear)")
case 8:
display.ClearBuffer()
for x := range cfg.Width / 2 {
for y := range cfg.Height {
display.SetPixel(x, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
}
for range 10 {
err = display.Display()
if err != nil {
println(err.Error())
}
time.Sleep(100 * time.Millisecond)
}
display.ClearBuffer()
for x := range cfg.Width / 2 {
for y := range cfg.Height {
display.SetPixel(x+cfg.Width/2, y, color.RGBA{R: 255, G: 255, B: 255, A: 255})
}
}
for range 10 {
err = display.Display()
if err != nil {
println(err.Error())
}
time.Sleep(100 * time.Millisecond)
}
}
}
}
}
} |
@rdnt looking at the sample code, makes me think some predefined config values would be useful.Something like this? ConfigLS010B7DH04 = Config{Width: 128, Height: 128}
ConfigLS013B7DH03 = ConfigLS010B7DH04
ConfigLS011B7DH03 = Config{Width: 160, Height: 68}
... |
@deadprogram excellent suggestion! Added. |
@rdnt I think the olny thing missing now is to add an example, and then add building that example to the smoketests here https://github.com/tinygo-org/drivers/blob/dev/smoketest.sh Thanks! |
@deadprogram Added!
|
I noticed on the repo's README that some of the pins for the examples are specified using global variables for easy tweaking by new users. I'll update the example to do the same and maybe add a wiring diagram for the pins. |
…entation/comments
Done! |
This PR adds the Sharp Memody Display SPI driver.
The implementation has been tested with nice!view (Sharp LS011B7DH03) on a nice!nano v2
Inspired by github.com/funkycode/tinygo-corne