tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/is31fl3731/is31fl3731.go (about) 1 // Package is31fl3731 provides a driver for the Lumissil IS31FL3731 matrix LED 2 // driver. 3 // 4 // Driver supports following layouts: 5 // - any custom LED matrix layout 6 // - Adafruit 15x7 CharliePlex LED Matrix FeatherWing (CharlieWing) 7 // https://www.adafruit.com/product/3163 8 // 9 // Datasheet: 10 // 11 // https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf 12 // 13 // This driver inspired by Adafruit Python driver: 14 // 15 // https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731 16 package is31fl3731 17 18 import ( 19 "fmt" 20 "time" 21 22 "tinygo.org/x/drivers" 23 "tinygo.org/x/drivers/internal/legacy" 24 ) 25 26 // Device implements TinyGo driver for Lumissil IS31FL3731 matrix LED driver 27 type Device struct { 28 Address uint8 29 bus drivers.I2C 30 31 // Currently selected command register (one of the frame registers or the 32 // function register) 33 selectedCommand uint8 34 } 35 36 // Configure chip for operating as a LED matrix display 37 func (d *Device) Configure() (err error) { 38 // Shutdown software 39 err = d.writeFunctionRegister(SET_SHUTDOWN, []byte{SOFTWARE_OFF}) 40 if err != nil { 41 return fmt.Errorf("failed to shutdown: %w", err) 42 } 43 44 time.Sleep(time.Millisecond * 10) 45 46 // Wake up software 47 err = d.writeFunctionRegister(SET_SHUTDOWN, []byte{SOFTWARE_ON}) 48 if err != nil { 49 return fmt.Errorf("failed to wake up: %w", err) 50 } 51 52 // Set display to a picture mode ("auto frame play mode" and "audio frame play 53 // mode" are not supported in this version of the driver) 54 err = d.writeFunctionRegister(SET_DISPLAY_MODE, []byte{DISPLAY_MODE_PICTURE}) 55 if err != nil { 56 return fmt.Errorf("failed to switch to a picture move: %w", err) 57 } 58 59 // Enable LEDs that are present (soldered) on the board. From the datasheet: 60 // LEDs which are no connected must be off by LED Control Register (Frame 61 // Registers) or it will affect other LEDs 62 err = d.enableLEDs() 63 if err != nil { 64 return fmt.Errorf("failed to enable LEDs: %w", err) 65 } 66 67 // Disable audiosync 68 err = d.writeFunctionRegister(SET_AUDIOSYNC, []byte{AUDIOSYNC_OFF}) 69 if err != nil { 70 return fmt.Errorf("failed to disable audiosync: %w", err) 71 } 72 73 // Clear all frames 74 for frame := FRAME_0; frame <= FRAME_7; frame++ { 75 err = d.Clear(frame) 76 if err != nil { 77 return fmt.Errorf("failed to clear frame %d: %w", frame, err) 78 } 79 } 80 81 // 1st frame is displayed by default 82 err = d.SetActiveFrame(FRAME_0) 83 if err != nil { 84 return fmt.Errorf("failed to set active frame: %w", err) 85 } 86 87 return nil 88 } 89 90 // selectCommand selects command register, can be: 91 // - frame registers 0-7 92 // - function register 93 func (d *Device) selectCommand(command uint8) (err error) { 94 if command != d.selectedCommand { 95 d.selectedCommand = command 96 return legacy.WriteRegister(d.bus, d.Address, COMMAND, []byte{command}) 97 } 98 99 return nil 100 } 101 102 // writeFunctionRegister selects the function register and writes data into it 103 func (d *Device) writeFunctionRegister(operation uint8, data []byte) (err error) { 104 err = d.selectCommand(FUNCTION) 105 if err != nil { 106 return err 107 } 108 109 return legacy.WriteRegister(d.bus, d.Address, operation, data) 110 } 111 112 // enableLEDs enables only LEDs that are soldered on the set board. Enabled 113 // all 16x9 LEDs by default 114 func (d *Device) enableLEDs() (err error) { 115 for frame := FRAME_0; frame <= FRAME_7; frame++ { 116 err = d.selectCommand(frame) 117 if err != nil { 118 return err 119 } 120 121 // Enable every LED (16 columns x 9 rows) 122 for i := uint8(0); i < 16; i++ { 123 err = legacy.WriteRegister(d.bus, d.Address, i, []byte{0xFF}) 124 if err != nil { 125 return err 126 } 127 } 128 } 129 130 return nil 131 } 132 133 // setPixelPWD sets individual pixel's PWM value [0-255] on the selected frame 134 func (d *Device) setPixelPWD(frame, n, value uint8) (err error) { 135 err = d.selectCommand(frame) 136 if err != nil { 137 return err 138 } 139 140 return legacy.WriteRegister(d.bus, d.Address, LED_PWM_OFFSET+n, []byte{value}) 141 } 142 143 // SetActiveFrame sets frame to display with LEDs 144 func (d *Device) SetActiveFrame(frame uint8) (err error) { 145 if frame > FRAME_7 { 146 return fmt.Errorf("frame %d is out of valid range [0-7]", frame) 147 } 148 149 return d.writeFunctionRegister(SET_ACTIVE_FRAME, []byte{frame}) 150 } 151 152 // Fill the whole frame with provided PWM value [0-255] 153 func (d *Device) Fill(frame, value uint8) (err error) { 154 if frame > FRAME_7 { 155 return fmt.Errorf("frame %d is out of valid range [0-7]", frame) 156 } 157 158 err = d.selectCommand(frame) 159 if err != nil { 160 return err 161 } 162 163 data := make([]byte, 24) 164 for i := range data { 165 data[i] = value 166 } 167 168 for i := uint8(0); i < 6; i++ { 169 err = legacy.WriteRegister(d.bus, d.Address, LED_PWM_OFFSET+i*24, data) 170 if err != nil { 171 return err 172 } 173 } 174 175 return nil 176 } 177 178 // Clear the whole frame 179 func (d *Device) Clear(frame uint8) (err error) { 180 return d.Fill(frame, 0x00) 181 } 182 183 // DrawPixelIndex draws a single pixel on the selected frame by its index with 184 // provided PWM value [0-255] 185 func (d *Device) DrawPixelIndex(frame, index, value uint8) (err error) { 186 if frame > FRAME_7 { 187 return fmt.Errorf("frame %d is out of valid range [0-7]", frame) 188 } 189 190 return d.setPixelPWD(frame, index, value) 191 } 192 193 // DrawPixelXY draws a single pixel on the selected frame by its XY coordinates 194 // with provided PWM value [0-255]. Raw LEDs layout assumed to be a 16x9 matrix, 195 // and can be used with any custom board that has IS31FL3731 driver. 196 func (d *Device) DrawPixelXY(frame, x, y, value uint8) (err error) { 197 return d.setPixelPWD(frame, 16*x+y, value) 198 } 199 200 // New creates a raw driver w/o any preset board layout. 201 // Addresses: 202 // - 0x74 (AD pin connected to GND) 203 // - 0x75 (AD pin connected to SCL) 204 // - 0x76 (AD pin connected to SDA) 205 // - 0x77 (AD pin connected to VCC) 206 func New(bus drivers.I2C, address uint8) Device { 207 return Device{ 208 Address: address, 209 bus: bus, 210 } 211 }