Created
April 2, 2025 12:18
-
-
Save saljam/1c0507ee85cba88aed71834f2a3046c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// program to demonstrate programming the pimoroni inky impression 7.3" eink display (ac073tc1a) from go. | |
package main | |
import ( | |
"fmt" | |
"log" | |
"os" | |
"slices" | |
"time" | |
"periph.io/x/conn/v3" | |
"periph.io/x/conn/v3/gpio" | |
"periph.io/x/conn/v3/physic" | |
"periph.io/x/conn/v3/spi" | |
"periph.io/x/conn/v3/spi/spireg" | |
"periph.io/x/host/v3" | |
"periph.io/x/host/v3/rpi" | |
) | |
const ( | |
black = 0 | |
white = 1 | |
green = 2 | |
blue = 3 | |
red = 4 | |
yellow = 5 | |
orange = 6 | |
width = 800 | |
height = 480 | |
) | |
// copied from https://github.com/pimoroni/inky/blob/main/inky/inky_ac073tc1a.py | |
const ( | |
cmdPSR = 0x00 | |
cmdPWR = 0x01 | |
cmdPOF = 0x02 | |
cmdPOFS = 0x03 | |
cmdPON = 0x04 | |
cmdBTST1 = 0x05 | |
cmdBTST2 = 0x06 | |
cmdDSLP = 0x07 | |
cmdBTST3 = 0x08 | |
cmdDTM = 0x10 | |
cmdDSP = 0x11 | |
cmdDRF = 0x12 | |
cmdIPC = 0x13 | |
cmdPLL = 0x30 | |
cmdTSC = 0x40 | |
cmdTSE = 0x41 | |
cmdTSW = 0x42 | |
cmdTSR = 0x43 | |
cmdCDI = 0x50 | |
cmdLPD = 0x51 | |
cmdTCON = 0x60 | |
cmdTRES = 0x61 | |
cmdDAM = 0x65 | |
cmdREV = 0x70 | |
cmdFLG = 0x71 | |
cmdAMV = 0x80 | |
cmdVV = 0x81 | |
cmdVDCS = 0x82 | |
cmdTVDCS = 0x84 | |
cmdAGID = 0x86 | |
cmdCMDH = 0xAA | |
cmdCCSET = 0xE0 | |
cmdPWS = 0xE3 | |
cmdTSSET = 0xE6 | |
) | |
var ( | |
pinBusy = rpi.P1_11 | |
pinReset = rpi.P1_13 | |
pinData = rpi.P1_15 | |
inkyconn spi.Conn | |
) | |
func main() { | |
port := setup() | |
defer port.Close() | |
// a demo graphic | |
img := make([]byte, width*height/2) | |
for y := range height { | |
for x := range width { | |
i, _ := slices.BinarySearch([]int{0, 80, 160, 240, 320}, x-y) | |
img[(y*width+x)/2] |= []byte{white, red, green, blue, yellow, white}[i] << (4 * (^x & 1)) | |
} | |
} | |
paint(img) | |
} | |
func setup() spi.PortCloser { | |
// init spi | |
if _, err := host.Init(); err != nil { | |
log.Fatal(err) | |
} | |
p, err := spireg.Open("") | |
if err != nil { | |
log.Fatal(err) | |
} | |
inkyconn, err = p.Connect(5*physic.MegaHertz, spi.Mode0, 8) | |
if err != nil { | |
log.Fatal(err) | |
} | |
// init gpio | |
err = pinData.Out(gpio.Low) | |
if err != nil { | |
log.Fatal(err) | |
} | |
err = pinReset.Out(gpio.High) | |
if err != nil { | |
log.Fatal(err) | |
} | |
err = pinBusy.In(gpio.PullDown, gpio.RisingEdge) | |
if err != nil { | |
log.Fatal(err) | |
} | |
// reset | |
time.Sleep(100 * time.Millisecond) | |
err = pinReset.Out(gpio.Low) | |
if err != nil { | |
log.Fatal(err) | |
} | |
time.Sleep(100 * time.Millisecond) | |
err = pinReset.Out(gpio.High) | |
if err != nil { | |
log.Fatal(err) | |
} | |
time.Sleep(1 * time.Second) | |
// mystery init sequence from inky_ac073tc1a.py | |
log.Println("init sequence") | |
for _, step := range []struct { | |
cmd byte | |
data []byte | |
}{ | |
{cmdCMDH, []byte{0x49, 0x55, 0x20, 0x08, 0x09, 0x18}}, | |
{cmdPWR, []byte{0x3F, 0x00, 0x32, 0x2A, 0x0E, 0x2A}}, | |
{cmdPSR, []byte{0x5F, 0x69}}, | |
{cmdPOFS, []byte{0x00, 0x54, 0x00, 0x44}}, | |
{cmdBTST1, []byte{0x40, 0x1F, 0x1F, 0x2C}}, | |
{cmdBTST2, []byte{0x6F, 0x1F, 0x16, 0x25}}, | |
{cmdBTST3, []byte{0x6F, 0x1F, 0x1F, 0x22}}, | |
{cmdIPC, []byte{0x00, 0x04}}, | |
{cmdPLL, []byte{0x02}}, | |
{cmdTSE, []byte{0x00}}, | |
{cmdCDI, []byte{0x3F}}, | |
{cmdTCON, []byte{0x02, 0x00}}, | |
{cmdTRES, []byte{0x03, 0x20, 0x01, 0xE0}}, | |
{cmdVDCS, []byte{0x1E}}, | |
{cmdTVDCS, []byte{0x00}}, | |
{cmdAGID, []byte{0x00}}, | |
{cmdPWS, []byte{0x2F}}, | |
{cmdCCSET, []byte{0x00}}, | |
{cmdTSSET, []byte{0x00}}, | |
} { | |
fmt.Fprintf(os.Stderr, ".") | |
err = write(step.cmd, step.data) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
fmt.Fprintf(os.Stderr, "\n") | |
return p | |
} | |
func paint(buf []byte) { | |
log.Println("painting") | |
for _, step := range []struct { | |
cmd byte | |
data []byte | |
delay time.Duration | |
}{ | |
{cmdDTM, buf, time.Second}, | |
{cmdPON, nil, time.Second}, | |
{cmdDRF, []byte{0x00}, 35 * time.Second}, | |
{cmdPOF, []byte{0x00}, time.Second}, | |
} { | |
fmt.Fprintf(os.Stderr, ".") | |
err := write(step.cmd, step.data) | |
if err != nil { | |
log.Fatal(err) | |
} | |
// there's a "busy" gpio pin to synchronise timing, but i found it unreliable. | |
time.Sleep(step.delay) | |
} | |
fmt.Fprintf(os.Stderr, "\n") | |
} | |
func write(cmd byte, data []byte) error { | |
err := inkyconn.Tx([]byte{cmd}, nil) | |
if err != nil { | |
return err | |
} | |
if data != nil { | |
pinData.Out(gpio.High) | |
defer pinData.Out(gpio.Low) | |
bufsiz := 0 | |
if limits, ok := inkyconn.(conn.Limits); ok { | |
bufsiz = limits.MaxTxSize() | |
} | |
if bufsiz == 0 { | |
bufsiz = 4096 | |
} | |
for len(data) > 0 { | |
chunk := min(len(data), bufsiz) | |
err := inkyconn.Tx(data[:chunk], nil) | |
if err != nil { | |
return err | |
} | |
data = data[chunk:] | |
} | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment