tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/microbitmatrix/microbitmatrix.go (about) 1 package microbitmatrix // import "tinygo.org/x/drivers/microbitmatrix" 2 3 import ( 4 "image/color" 5 "time" 6 ) 7 8 type Config struct { 9 // Rotation of the LED matrix. 10 // 11 // Valid values: 12 // 13 // 0: regular orientation, (0 degree rotation) 14 // 1: 90 degree rotation clock wise 15 // 2: 180 degree rotation clock wise 16 // 3: 270 degree rotation clock wise 17 Rotation uint8 18 } 19 20 const ( 21 RotationNormal = 0 22 Rotation90 = 1 23 Rotation180 = 2 24 Rotation270 = 3 25 ) 26 27 // New returns a new microbitmatrix driver. 28 func New() Device { 29 return Device{} 30 } 31 32 // Configure sets up the device. 33 func (d *Device) Configure(cfg Config) { 34 d.SetRotation(cfg.Rotation) 35 36 d.assignPins() 37 38 d.ClearDisplay() 39 d.DisableAll() 40 } 41 42 // SetRotation changes the rotation of the LED matrix. 43 // 44 // Valid values for rotation: 45 // 46 // 0: regular orientation, (0 degree rotation) 47 // 1: 90 degree rotation clock wise 48 // 2: 180 degree rotation clock wise 49 // 3: 270 degree rotation clock wise 50 func (d *Device) SetRotation(rotation uint8) { 51 d.rotation = rotation % 4 52 } 53 54 // Source: 55 // https://github.com/bbcmicrobit/micropython/blob/1252f887ddc790676bf9314a136bd17650b9c36c/source/microbit/microbitdisplay.cpp#L282 56 var renderTimings = []time.Duration{ 57 0, // Bright, Ticks Duration, Relative power 58 2, // 1, 2, 32µs, inf 59 2, // 2, 4, 64µs, 200% 60 4, // 3, 8, 128µs, 200% 61 7, // 4, 15, 240µs, 187% 62 13, // 5, 28, 448µs, 187% 63 25, // 6, 53, 848µs, 189% 64 49, // 7, 102, 1632µs, 192% 65 97, // 8, 199, 3184µs, 195% 66 } 67 68 // Source: 69 // https://github.com/bbcmicrobit/micropython/blob/1252f887ddc790676bf9314a136bd17650b9c36c/source/microbit/microbitdisplay.cpp#L368 70 const tickDuration = 16 * time.Microsecond 71 72 const ( 73 rowIdx = 0 74 colIdx = 1 75 ) 76 77 // SetPixel modifies the internal buffer in a single pixel. 78 // 79 // The alpha channel of the RGBA is used to control the brightness of the LED 80 // in 9 different levels. 81 // 82 // alpha channel, brightness level 83 // 0 - 27, 9 (no transparency = highest brightness) 84 // 28 - 55, 8 85 // 56 - 83, 7 86 // 84 - 111, 6 87 // 112 - 139, 5 88 // 140 - 167, 4 89 // 168 - 195, 3 90 // 196 - 223, 2 91 // 224 - 251, 1 (very high transparency = lowest brightness) 92 // 252 - 255, 0 (full transparency = off) 93 func (d *Device) SetPixel(x int16, y int16, c color.RGBA) { 94 if x < 0 || x >= 5 || y < 0 || y >= 5 { 95 return 96 } 97 col := x 98 row := y 99 if c.R != 0 || c.G != 0 || c.B != 0 { 100 d.buffer[matrixRotations[d.rotation][row][col][rowIdx]][matrixRotations[d.rotation][row][col][colIdx]] = brightness(c.A) 101 } else { 102 d.buffer[matrixRotations[d.rotation][row][col][rowIdx]][matrixRotations[d.rotation][row][col][colIdx]] = 0 103 } 104 } 105 106 const ( 107 brightnessLevels = 9 108 brightnessDivider = int8(255 / brightnessLevels) 109 ) 110 111 var ( 112 Brightness0 = color.RGBA{R: 0, G: 0, B: 0, A: 0} 113 Brightness1 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*1} 114 Brightness2 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*2} 115 Brightness3 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*3} 116 Brightness4 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*4} 117 Brightness5 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*5} 118 Brightness6 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*6} 119 Brightness7 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*7} 120 Brightness8 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*8} 121 Brightness9 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*9} 122 123 BrightnessOff = Brightness0 124 BrightnessFull = Brightness9 125 ) 126 127 func brightness(alpha uint8) int8 { 128 return brightnessLevels - int8(alpha/uint8(brightnessDivider)) 129 } 130 131 // GetPixel returns if the specific pixels is enabled. 132 func (d *Device) GetPixel(x int16, y int16) bool { 133 if x < 0 || x >= 5 || y < 0 || y >= 5 { 134 return false 135 } 136 col := x 137 row := y 138 return d.buffer[matrixRotations[d.rotation][row][col][rowIdx]][matrixRotations[d.rotation][row][col][colIdx]] > 0 139 } 140 141 const displayRefreshDelay = 8 * time.Millisecond 142 143 // Display sends the buffer (if any) to the screen. 144 func (d *Device) Display() error { 145 var displayBuffer [ledRows][ledCols]int8 146 for row := 0; row < ledRows; row++ { 147 for col := 0; col < ledCols; col++ { 148 displayBuffer[row][col] = d.buffer[row][col] 149 } 150 } 151 152 for row := 0; row < ledRows; row++ { 153 d.DisableAll() 154 d.pin[ledCols+row].High() 155 156 for col := 0; col < ledCols; col++ { 157 if displayBuffer[row][col] > 0 { 158 d.pin[col].Low() 159 } 160 } 161 162 then := time.Now() 163 var offset time.Duration = 0 164 for _, ticks := range renderTimings { 165 for time.Since(then).Nanoseconds() < int64(ticks*tickDuration+offset) { 166 time.Sleep(offset / 10) 167 } 168 offset += ticks + tickDuration 169 for col := 0; col < ledCols; col++ { 170 displayBuffer[row][col]-- 171 if displayBuffer[row][col] <= 0 { 172 d.pin[col].High() 173 } 174 } 175 } 176 } 177 time.Sleep(displayRefreshDelay) 178 return nil 179 } 180 181 // ClearDisplay erases the internal buffer. 182 func (d *Device) ClearDisplay() { 183 for row := 0; row < ledRows; row++ { 184 for col := 0; col < ledCols; col++ { 185 d.buffer[row][col] = 0 186 } 187 } 188 } 189 190 // DisableAll disables all the LEDs without modifying the buffer. 191 func (d *Device) DisableAll() { 192 for i := 0; i < ledCols; i++ { 193 d.pin[i].High() 194 } 195 for i := 0; i < ledRows; i++ { 196 d.pin[ledCols+i].Low() 197 } 198 } 199 200 // EnableAll enables all the LEDs without modifying the buffer. 201 func (d *Device) EnableAll() { 202 for i := 0; i < ledCols; i++ { 203 d.pin[i].Low() 204 } 205 for i := 0; i < ledRows; i++ { 206 d.pin[ledCols+i].High() 207 } 208 } 209 210 // Size returns the current size of the display. 211 func (d *Device) Size() (w, h int16) { 212 return 5, 5 213 }