forked from google/periph
/
unicornhd.go
116 lines (101 loc) · 2.87 KB
/
unicornhd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package unicornhd implements interfacing code to Pimoroni's Unicorn HD hat.
package unicornhd
import (
"fmt"
"image"
"image/color"
"image/draw"
"periph.io/x/periph/conn/display"
"periph.io/x/periph/conn/physic"
"periph.io/x/periph/conn/spi"
)
const (
height = 16
width = 16
speed = 9 * physic.MegaHertz
bits = 8
prefix = 0x72
)
// Dev represents a Unicorn HAT HD (https://shop.pimoroni.com/products/unicorn-hat-hd)
// connected over a SPI port.
type Dev struct {
// Communication
connector spi.Conn
pixels *image.NRGBA
txBuffer []byte
}
// New returns a unicornHD driver that communicates over SPI.
//
// The SPI port speed must be 9MHz and the SPI mode, 0, as in the
// python example library.
func New(port spi.Port) (*Dev, error) {
connector, err := port.Connect(speed, spi.Mode0, bits)
if err != nil {
return nil, err
}
return &Dev{
connector: connector,
pixels: image.NewNRGBA(image.Rect(0, 0, width, height)),
txBuffer: make([]byte, width*height*3+1),
}, nil
}
// String implements display.Drawer
//
// Returns a string with the driver name and the width and height of the
// display.
func (device *Dev) String() string {
return fmt.Sprintf("UnicornHD{%d, %d}", width, height)
}
// Halt sets all the pixels to black. Error is always nil.
func (device *Dev) Halt() error {
black := color.RGBA{0, 0, 0, 0}
return device.Draw(device.Bounds(), &image.Uniform{black}, image.ZP)
}
// ColorModel implements devices.Display. There's no surprise, it is
// color.RGBAModel.
func (device *Dev) ColorModel() color.Model {
return color.NRGBAModel
}
// Bounds implements devices.Display.
//
// Min is guaranteed to be {0, 0}.
func (device *Dev) Bounds() image.Rectangle {
return device.pixels.Bounds()
}
// Draw implements devices.Display.
//
// Using something else than image.NRGBA is 10x slower. When using image.NRGBA,
// the alpha channel is ignored.
func (device *Dev) Draw(dstRect image.Rectangle, src image.Image, srcPts image.Point) error {
// Use stdlib image copying functionality.
draw.Draw(device.pixels, dstRect, src, srcPts, draw.Src)
// And then copy the image into the transmission buffer, where it is sent via SPI.
return device.flush()
}
func (device *Dev) flush() error {
device.txBuffer[0] = prefix
x := 0
y := 0
for i := 0; i < width*height; i++ {
color := device.pixels.NRGBAAt(x, y)
x++
if x >= width {
x = 0
y++
}
red := color.R
green := color.G
blue := color.B
k := 3*i + 1
device.txBuffer[k] = red
device.txBuffer[k+1] = green
device.txBuffer[k+2] = blue
}
return device.connector.Tx(device.txBuffer, nil)
}
// Test that driver implements display.Drawer interface. This is
// enforced at compile time.
var _ display.Drawer = (*Dev)(nil)