forked from aykevl/board
-
Notifications
You must be signed in to change notification settings - Fork 0
/
common.go
327 lines (268 loc) · 8.61 KB
/
common.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
package board
import (
"time"
"unsafe"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/pixel"
)
var (
AddressableLEDs LEDArray = dummyAddressableLEDs{}
)
// Settings for the simulator. These can be modified at any time, but it is
// recommended to modify them before configuring any of the board peripherals.
//
// These can be modified to match whatever board your main target is. For
// example, if your board has a display that's only 160 by 128 pixels, you can
// modify the window size here to get a realistic simulation.
var Simulator = struct {
WindowTitle string
// Width and height in virtual pixels (matching Size()). The window will
// take up more physical pixels on high-DPI screens.
WindowWidth int
WindowHeight int
// Pixels per inch. The default is 120, which matches many commonly used
// high-DPI screens (for example, Apple screens).
WindowPPI int
// How much time it takes (in nanoseconds) to draw a single pixel.
// For example, for 8MHz and 16 bits per color:
// time.Second * 16 / 8e6
WindowDrawSpeed time.Duration
// Number of addressable LEDs used by default.
AddressableLEDs int
}{
WindowTitle: "Simulator",
WindowWidth: 240,
WindowHeight: 240,
WindowPPI: 120, // common on many modern displays (for example Retina is 254 / 2 = 127)
// This matches common event badges like the PyBadge and the MCH2022 badge
// (but not the SHA2017 badge which uses 6 RGBW LEDs).
AddressableLEDs: 5,
}
// ChargeState is the charging status of a battery.
type ChargeState uint8
const (
// A battery might be attached, this is unknown (no way to know by reading a
// pin).
UnknownBattery ChargeState = iota
// This board doesn't have batteries.
NoBattery
// No battery is attached to the board, but there could be one.
BatteryUnavailable
// There is a battery attached and it's charging (usually from USB)
Charging
// Power is present, but the battery is not charging (usually when it is
// fully charged).
NotCharging
// There is a battery attached and it's not charging (no power connected).
Discharging
)
// Return a string representation of the charge status, mainly for debugging.
func (c ChargeState) String() string {
switch c {
default:
return "unknown"
case NoBattery:
return "none"
case BatteryUnavailable:
return "not connected"
case Charging:
return "charging"
case NotCharging:
return "not charging"
case Discharging:
return "discharging"
}
}
// A LED array is a sequence of individually addressable LEDs (like WS2812).
type LEDArray interface {
// Configure the LED array. This needs to be called before any other method
// (except Len).
Configure()
// Return the length of the LED array.
Len() int
// Set a given pixel to the RGB value. The index must be in bounds,
// otherwise this method will panic. The value is not immediately visible,
// call Update() to update the pixel array.
// Note that LED arrays are usually indexed from the end, because of the way
// data is sent to them.
SetRGB(index int, r, g, b uint8)
// Update the pixel array to the values previously set in SetRGB.
Update()
}
// The display interface shared by all supported displays.
type Displayer[T pixel.Color] interface {
// The display size in pixels.
Size() (width, height int16)
// DrawBitmap copies the bitmap to the internal buffer on the screen at the
// given coordinates. It returns once the image data has been sent
// completely.
DrawBitmap(x, y int16, buf pixel.Image[T]) error
// Display the written image on screen. This call may or may not be
// necessary depending on the screen, but it's better to call it anyway.
Display() error
// Enter or exit sleep mode.
Sleep(sleepEnabled bool) error
// Return the current screen rotation.
// Note that some screens are by default configured with rotation, so by
// default you may not get drivers.Rotation0.
Rotation() drivers.Rotation
// Set a given rotation. For example, to rotate by 180° you can use:
//
// SetRotation((Rotation() + 2) % 4)
//
// Not all displays support rotation, in which case they will return an
// error.
SetRotation(drivers.Rotation) error
}
// TouchInput reads the touch screen (resistive/capacitive) on a display and
// returns the current list of touch points.
type TouchInput interface {
ReadTouch() []TouchPoint
}
// A single touch point on the screen, from a finger, stylus, or something like
// that.
type TouchPoint struct {
// ID for this touch point. New touch events get a monotonically
// incrementing ID. Because it is a uint32 (and it's unlikely a screen will
// be touched more than 4 billion times), it can be treated as a unique ID.
ID uint32
// X and Y pixel coordinates.
X, Y int16
}
// Key is a single keyboard key (not to be confused with a single character).
type Key uint8
// List of all supported key codes.
const (
NoKey = iota
// Special keys.
KeyEscape
// Navigation keys.
KeyLeft
KeyRight
KeyUp
KeyDown
// Character keys.
KeyEnter
KeySpace
KeyA
KeyB
KeyL
KeyR
// Special keys, used on some boards.
KeySelect
KeyStart
)
// KeyEvent is a single key press or release event.
type KeyEvent uint16
const (
NoKeyEvent KeyEvent = iota // No key event was available.
keyReleased = KeyEvent(1 << 15) // The upper bit is set when this is a release event
)
// Key returns the key code for this key event.
func (k KeyEvent) Key() Key {
return Key(k) // lower 8 bits are the key code
}
// Pressed returns whether this event indicates a key press event. It returns
// true for a press, false for a release.
func (k KeyEvent) Pressed() bool {
return k&keyReleased == 0
}
// Default lithium battery charge curve.
// This data is taken from the InfiniTime project:
// https://github.com/InfiniTimeOrg/InfiniTime/pull/1444
// It is unlikely to be very accurate for other batteries, but it's a reasonable
// approximation if no specific discharge curve has been made.
var lithumBatteryApproximation = batteryApproximation{
voltages: [6]uint16{3500, 3600, 3700, 3750, 3900, 4180},
percents: [6]int8{0, 10, 25, 50, 75, 100},
}
type batteryApproximation struct {
voltages [6]uint16
percents [6]int8
}
func (approx *batteryApproximation) approximate(microvolts uint32) int8 {
if microvolts <= uint32(approx.voltages[0])*1000 {
return 0 // below the lowest value
}
for i, v := range approx.voltages {
if uint32(v)*1000 > microvolts {
voltStart := uint32(approx.voltages[i-1]) * 1000
voltEnd := uint32(v) * 1000
percentStart := approx.percents[i-1]
percentEnd := approx.percents[i]
voltOffset := microvolts - voltStart
voltDiff := voltEnd - voltStart
percentDiff := percentEnd - percentStart
percentOffset := voltOffset * uint32(percentDiff) / uint32(voltDiff)
return int8(percentOffset + uint32(percentStart))
}
}
// Outside the table, so must be 100%.
return 100
}
func (approx *batteryApproximation) approximatePPM(microvolts uint32) int32 {
if microvolts <= uint32(approx.voltages[0])*1000 {
return 0 // below the lowest value
}
for i, v := range approx.voltages {
if uint32(v)*1000 > microvolts {
voltStart := uint32(approx.voltages[i-1]) // mV
voltEnd := uint32(v) // mV
percentStart := approx.percents[i-1]
percentEnd := approx.percents[i]
voltOffset := microvolts - voltStart*1000 // µV
voltDiff := voltEnd - voltStart // mV
percentDiff := percentEnd - percentStart
percentOffset := voltOffset * uint32(percentDiff) * 10 / voltDiff
return int32(percentStart)*10000 + int32(percentOffset)
}
}
// Outside the table, so must be 100%.
return 1000_000
}
type dummyAddressableLEDs struct {
}
func (l dummyAddressableLEDs) Configure() {
// Nothing to do here.
}
func (l dummyAddressableLEDs) Len() int {
return 0 // always zero
}
func (l dummyAddressableLEDs) SetRGB(i int, r, g, b uint8) {
panic("no LEDs on this board")
}
func (l dummyAddressableLEDs) Update() {
// Nothing to do here.
}
type colorFormat interface {
colorGRB
}
type colorGRB struct{ G, R, B uint8 }
// Convert pixel data to a byte slice, for sending it to WS2812 LEDs for
// example.
func pixelsToBytes[T colorFormat](pix []T) []byte {
if len(pix) == 0 {
return nil
}
var zeroColor T
ptr := unsafe.Pointer(unsafe.SliceData(pix))
return unsafe.Slice((*byte)(ptr), len(pix)*int(unsafe.Sizeof(zeroColor)))
}
// Dummy sensor value, to be embedded in actual drivers.Sensor implementations.
type baseSensors struct {
}
func (s baseSensors) Configure(which drivers.Measurement) error {
return nil
}
func (s baseSensors) Update(which drivers.Measurement) error {
return nil
}
func (s baseSensors) Acceleration() (x, y, z int32) {
return 0, 0, 0
}
func (s baseSensors) Steps() uint32 {
return 0
}
func (s baseSensors) Temperature() int32 {
return 0
}