Skip to content

Commit 9c96ae7

Browse files
committed
unoqmatrix: LED matrix on the Arduino Uno Q
Signed-off-by: deadprogram <ron@hybridgroup.com>
1 parent b480978 commit 9c96ae7

2 files changed

Lines changed: 303 additions & 0 deletions

File tree

examples/unoqmatrix/main.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package main
2+
3+
import (
4+
"machine"
5+
6+
"image/color"
7+
"math/rand"
8+
9+
"tinygo.org/x/drivers/unoqmatrix"
10+
)
11+
12+
var on = color.RGBA{255, 255, 255, 255}
13+
14+
func main() {
15+
display := unoqmatrix.New(machine.PF0)
16+
display.ClearDisplay()
17+
18+
w, h := display.Size()
19+
x := int16(0)
20+
y := int16(0)
21+
deltaX := int16(1)
22+
deltaY := int16(1)
23+
24+
for {
25+
pixel := display.GetPixel(x, y)
26+
if pixel.R != 0 || pixel.G != 0 || pixel.B != 0 {
27+
display.ClearDisplay()
28+
x = 1 + int16(rand.Int31n(3))
29+
y = 1 + int16(rand.Int31n(3))
30+
deltaX = 1
31+
deltaY = 1
32+
if rand.Int31n(2) == 0 {
33+
deltaX = -1
34+
}
35+
if rand.Int31n(2) == 0 {
36+
deltaY = -1
37+
}
38+
}
39+
display.SetPixel(x, y, on)
40+
41+
x += deltaX
42+
y += deltaY
43+
44+
if x == 0 || x == w-1 {
45+
deltaX = -deltaX
46+
}
47+
48+
if y == 0 || y == h-1 {
49+
deltaY = -deltaY
50+
}
51+
52+
display.Display()
53+
}
54+
}

unoqmatrix/matrix.go

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// Package unoqmatrix provides a driver for the UnoQMatrix LED matrix display.
2+
//
3+
// The UnoQMatrix is an 8x13 LED matrix display that can be controlled using a single pin.
4+
// It uses a multiplexing technique to control the LEDs, which allows for a large number of LEDs to be controlled with fewer pins.
5+
//
6+
// This driver provides basic functionality to set individual pixels, clear the display, and refresh the display.
7+
//
8+
// Note: The UnoQMatrix does not support brightness control or color depth. Each pixel can only be turned on or off.
9+
// Could it suppport brightness control by using PWM on the pin? To be investigated.
10+
package unoqmatrix
11+
12+
import (
13+
"machine"
14+
15+
"image/color"
16+
"time"
17+
)
18+
19+
type Config struct {
20+
// Rotation of the LED matrix.
21+
Rotation uint8
22+
}
23+
24+
// Valid values:
25+
//
26+
// 0: regular orientation, (0 degree rotation)
27+
// 1: 90 degree rotation clockwise
28+
// 2: 180 degree rotation clockwise
29+
// 3: 270 degree rotation clockwise
30+
const (
31+
RotationNormal = 0
32+
Rotation90 = 1
33+
Rotation180 = 2
34+
Rotation270 = 3
35+
)
36+
37+
const (
38+
ledRows = 8
39+
ledCols = 13
40+
41+
pixelRefreshDelay = 10 * time.Microsecond
42+
)
43+
44+
// Device represents the UnoQMatrix LED matrix display.
45+
type Device struct {
46+
pin machine.Pin
47+
buffer [ledRows][ledCols]color.RGBA
48+
rotation uint8
49+
}
50+
51+
// New returns a new unoqmatrix driver.
52+
// The provided pin is the base pin number for the display. The driver will use this
53+
// pin and the next 10 pins to control the LEDs.
54+
func New(pin machine.Pin) Device {
55+
return Device{pin: pin}
56+
}
57+
58+
// Configure sets up the device.
59+
func (d *Device) Configure(cfg Config) {
60+
// TODO: implement rotation support
61+
//d.SetRotation(cfg.Rotation)
62+
}
63+
64+
// SetRotation changes the rotation of the LED matrix.
65+
//
66+
// Valid values for rotation:
67+
//
68+
// 0: regular orientation, (0 degree rotation)
69+
// 1: 90 degree rotation clockwise
70+
// 2: 180 degree rotation clockwise
71+
// 3: 270 degree rotation clockwise
72+
func (d *Device) SetRotation(rotation uint8) {
73+
d.rotation = rotation % 4
74+
}
75+
76+
// SetPixel sets the color of a specific pixel.
77+
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
78+
d.buffer[y][x] = c
79+
}
80+
81+
// GetPixel returns the color of a specific pixel.
82+
func (d *Device) GetPixel(x int16, y int16) color.RGBA {
83+
return d.buffer[y][x]
84+
}
85+
86+
// Display sends the buffer (if any) to the screen. Takes 103 * pixelRefreshDelay to complete.
87+
func (d *Device) Display() error {
88+
for row := 0; row < ledRows; row++ {
89+
for col := 0; col < ledCols; col++ {
90+
d.clearDisplay()
91+
92+
c := d.buffer[row][col]
93+
if c.R != 0 || c.G != 0 || c.B != 0 {
94+
d.turnLedOn(row*ledCols + col)
95+
}
96+
97+
// Sleep for a very short duration to allow the LED to light up before moving to the next one.
98+
time.Sleep(pixelRefreshDelay)
99+
}
100+
}
101+
102+
return nil
103+
}
104+
105+
// ClearDisplay turns off all the LEDs on the display.
106+
func (d *Device) ClearDisplay() {
107+
for row := 0; row < ledRows; row++ {
108+
for col := 0; col < ledCols; col++ {
109+
d.buffer[row][col] = color.RGBA{0, 0, 0, 255}
110+
}
111+
}
112+
}
113+
114+
// Size returns the current size of the display.
115+
func (d *Device) Size() (w, h int16) {
116+
return ledCols, ledRows
117+
}
118+
119+
// pinMapping defines the mapping of LED indices to pin pairs. Each entry corresponds
120+
// to an LED index (0-104) and contains the two pin numbers that need to be set to turn on that LED.
121+
// based on https://github.com/arduino/ArduinoCore-zephyr/blob/main/loader/matrix.inc#L13
122+
var pinMapping = [][2]machine.Pin{
123+
{0, 1}, // 0
124+
{1, 0},
125+
{0, 2},
126+
{2, 0},
127+
{1, 2},
128+
{2, 1},
129+
{0, 3},
130+
{3, 0},
131+
{1, 3},
132+
{3, 1},
133+
{2, 3}, // 10
134+
{3, 2},
135+
{0, 4},
136+
{4, 0},
137+
{1, 4},
138+
{4, 1},
139+
{2, 4},
140+
{4, 2},
141+
{3, 4},
142+
{4, 3},
143+
{0, 5}, // 20
144+
{5, 0},
145+
{1, 5},
146+
{5, 1},
147+
{2, 5},
148+
{5, 2},
149+
{3, 5},
150+
{5, 3},
151+
{4, 5},
152+
{5, 4},
153+
{0, 6}, // 30
154+
{6, 0},
155+
{1, 6},
156+
{6, 1},
157+
{2, 6},
158+
{6, 2},
159+
{3, 6},
160+
{6, 3},
161+
{4, 6},
162+
{6, 4},
163+
{5, 6}, // 40
164+
{6, 5},
165+
{0, 7},
166+
{7, 0},
167+
{1, 7},
168+
{7, 1},
169+
{2, 7},
170+
{7, 2},
171+
{3, 7},
172+
{7, 3},
173+
{4, 7}, // 50
174+
{7, 4},
175+
{5, 7},
176+
{7, 5},
177+
{6, 7},
178+
{7, 6},
179+
{0, 8},
180+
{8, 0},
181+
{1, 8},
182+
{8, 1},
183+
{2, 8}, // 60
184+
{8, 2},
185+
{3, 8},
186+
{8, 3},
187+
{4, 8},
188+
{8, 4},
189+
{5, 8},
190+
{8, 5},
191+
{6, 8},
192+
{8, 6},
193+
{7, 8}, // 70
194+
{8, 7},
195+
{0, 9},
196+
{9, 0},
197+
{1, 9},
198+
{9, 1},
199+
{2, 9},
200+
{9, 2},
201+
{3, 9},
202+
{9, 3},
203+
{4, 9}, // 80
204+
{9, 4},
205+
{5, 9},
206+
{9, 5},
207+
{6, 9},
208+
{9, 6},
209+
{7, 9},
210+
{9, 7},
211+
{8, 9},
212+
{9, 8},
213+
{0, 10}, // 90
214+
{10, 0},
215+
{1, 10},
216+
{10, 1},
217+
{2, 10},
218+
{10, 2},
219+
{3, 10},
220+
{10, 3},
221+
{4, 10},
222+
{10, 4},
223+
{5, 10}, // 100
224+
{10, 5},
225+
{6, 10},
226+
{10, 6},
227+
}
228+
229+
// clearDisplay turns off all the LEDs on the display by configuring all pins as input.
230+
func (d *Device) clearDisplay() {
231+
for i := machine.Pin(0); i < 11; i++ {
232+
(d.pin + i).Configure(machine.PinConfig{Mode: machine.PinInput})
233+
}
234+
}
235+
236+
// turnLedOn turns on the LED at the given index (0-104).
237+
func (d *Device) turnLedOn(idx int) {
238+
if idx < 0 || idx >= len(pinMapping) {
239+
return
240+
}
241+
242+
pin0 := d.pin + pinMapping[idx][0]
243+
pin1 := d.pin + pinMapping[idx][1]
244+
245+
pin0.Configure(machine.PinConfig{Mode: machine.PinOutput})
246+
pin1.Configure(machine.PinConfig{Mode: machine.PinOutput})
247+
pin0.High()
248+
pin1.Low()
249+
}

0 commit comments

Comments
 (0)