Skip to content

Instantly share code, notes, and snippets.

@pwillard
Last active April 29, 2025 11:20
Show Gist options
  • Save pwillard/88f1282a065b2bed6b5311980cd9f8fe to your computer and use it in GitHub Desktop.
Save pwillard/88f1282a065b2bed6b5311980cd9f8fe to your computer and use it in GitHub Desktop.
RP2040 Scroll Hat Mini
' === IS31FL3731 7x17 LED Matrix Scrolling Text Program ===
' Datasheet
' https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf
' Board: Pico Lipo (RP2040 Pico clone)
' Software: PicoMite MMBASIC Version 6.0 beta
' Option System I2C 20,21
' This layout is compatible with the "Breakout HAT board for PICO"
' https://52pi.com/products/52pi-breakout-hat-board-for-raspberry-pi-pico
'-------------------------------------------------------------------
' This program was designed to program the Pimoroni Scroll Hat Mini board
' Which is a board with a 40 pin Raspberry Pi interface
' So while the HAT was designed for a Raspberry Pi and Python code I can't
' hide behind supplied libraries and code examples.
'
' This program is by no means fast, or pretty, but it works
' as I just needed to crudely reverse engineer how the Pimoroni board
' worked and to translate that to some MMBASIC code.
' This is primarily a proof of concept to see if it could be done.
'
' Full 5x7 Letters numbers and symbols
' Horizontal Scrolling
' Brightness Control
' ====================================================================
' April 2025 Pete Willard
' I realize I may be the only person trying to make this work.
'=====================================================================
' --- Constants ---
CONST IS31_ADDR = &H74
' --- LED Mapping Table ---
' This table maps the LED matrix coordinates to the corresponding LED driver indices
' Pixel 0,0 is the top left corner of the display, and pixel 6,16 is the bottom right corner
' However, the LEDS are arranged in a zig-zag circular pattern staring from the bottom middle,
' so the mapping is not linear
DIM ledTable%(6,16)
' Row 0
ledTable%(0,0)=134 : ledTable%(0,1)=118 : ledTable%(0,2)=102 : ledTable%(0,3)=86 : ledTable%(0,4)=70
ledTable%(0,5)=54 : ledTable%(0,6)=38 : ledTable%(0,7)=22 : ledTable%(0,8)=6 : ledTable%(0,9)=8
ledTable%(0,10)=24 : ledTable%(0,11)=40 : ledTable%(0,12)=56 : ledTable%(0,13)=72 : ledTable%(0,14)=88
ledTable%(0,15)=104 : ledTable%(0,16)=120
' Row 1
ledTable%(1,0)=133 : ledTable%(1,1)=117 : ledTable%(1,2)=101 : ledTable%(1,3)=85 : ledTable%(1,4)=69
ledTable%(1,5)=53 : ledTable%(1,6)=37 : ledTable%(1,7)=21 : ledTable%(1,8)=5 : ledTable%(1,9)=9
ledTable%(1,10)=25 : ledTable%(1,11)=41 : ledTable%(1,12)=57 : ledTable%(1,13)=73 : ledTable%(1,14)=89
ledTable%(1,15)=105 : ledTable%(1,16)=121
' Row 2
ledTable%(2,0)=132 : ledTable%(2,1)=116 : ledTable%(2,2)=100 : ledTable%(2,3)=84 : ledTable%(2,4)=68
ledTable%(2,5)=52 : ledTable%(2,6)=36 : ledTable%(2,7)=20 : ledTable%(2,8)=4 : ledTable%(2,9)=10
ledTable%(2,10)=26 : ledTable%(2,11)=42 : ledTable%(2,12)=58 : ledTable%(2,13)=74 : ledTable%(2,14)=90
ledTable%(2,15)=106 : ledTable%(2,16)=122
' Row 3
ledTable%(3,0)=131 : ledTable%(3,1)=115 : ledTable%(3,2)=99 : ledTable%(3,3)=83 : ledTable%(3,4)=67
ledTable%(3,5)=51 : ledTable%(3,6)=35 : ledTable%(3,7)=19 : ledTable%(3,8)=3 : ledTable%(3,9)=11
ledTable%(3,10)=27 : ledTable%(3,11)=43 : ledTable%(3,12)=59 : ledTable%(3,13)=75 : ledTable%(3,14)=91
ledTable%(3,15)=107 : ledTable%(3,16)=123
' Row 4
ledTable%(4,0)=130 : ledTable%(4,1)=114 : ledTable%(4,2)=98 : ledTable%(4,3)=82 : ledTable%(4,4)=66
ledTable%(4,5)=50 : ledTable%(4,6)=34 : ledTable%(4,7)=18 : ledTable%(4,8)=2 : ledTable%(4,9)=12
ledTable%(4,10)=28 : ledTable%(4,11)=44 : ledTable%(4,12)=60 : ledTable%(4,13)=76 : ledTable%(4,14)=92
ledTable%(4,15)=108 : ledTable%(4,16)=124
' Row 5
ledTable%(5,0)=129 : ledTable%(5,1)=113 : ledTable%(5,2)=97 : ledTable%(5,3)=81 : ledTable%(5,4)=65
ledTable%(5,5)=49 : ledTable%(5,6)=33 : ledTable%(5,7)=17 : ledTable%(5,8)=1 : ledTable%(5,9)=13
ledTable%(5,10)=29 : ledTable%(5,11)=45 : ledTable%(5,12)=61 : ledTable%(5,13)=77 : ledTable%(5,14)=93
ledTable%(5,15)=109 : ledTable%(5,16)=125
' Row 6
ledTable%(6,0)=128 : ledTable%(6,1)=112 : ledTable%(6,2)=96 : ledTable%(6,3)=80 : ledTable%(6,4)=64
ledTable%(6,5)=48 : ledTable%(6,6)=32 : ledTable%(6,7)=16 : ledTable%(6,8)=-1 : ledTable%(6,9)=14
ledTable%(6,10)=30 : ledTable%(6,11)=46 : ledTable%(6,12)=62 : ledTable%(6,13)=78 : ledTable%(6,14)=94
ledTable%(6,15)=110 : ledTable%(6,16)=126
' Full Uppercase letter, numbers and symbols
' relies on a separate subroutine to map indexed entries to ASCII characters
DIM font5x7(46,6) AS INTEGER
' A
font5x7(0,0)=&B00100 : font5x7(0,1)=&B01010 : font5x7(0,2)=&B10001 : font5x7(0,3)=&B11111 : font5x7(0,4)=&B10001 : font5x7(0,5)=&B10001 : font5x7(0,6)=&B10001
' B
font5x7(1,0)=&B11110 : font5x7(1,1)=&B10001 : font5x7(1,2)=&B10001 : font5x7(1,3)=&B11110 : font5x7(1,4)=&B10001 : font5x7(1,5)=&B10001 : font5x7(1,6)=&B11110
' C
font5x7(2,0)=&B01110 : font5x7(2,1)=&B10001 : font5x7(2,2)=&B10000 : font5x7(2,3)=&B10000 : font5x7(2,4)=&B10000 : font5x7(2,5)=&B10001 : font5x7(2,6)=&B01110
' D
font5x7(3,0)=&B11100 : font5x7(3,1)=&B10010 : font5x7(3,2)=&B10001 : font5x7(3,3)=&B10001 : font5x7(3,4)=&B10001 : font5x7(3,5)=&B10010 : font5x7(3,6)=&B11100
' E
font5x7(4,0)=&B11111 : font5x7(4,1)=&B10000 : font5x7(4,2)=&B10000 : font5x7(4,3)=&B11110 : font5x7(4,4)=&B10000 : font5x7(4,5)=&B10000 : font5x7(4,6)=&B11111
' F
font5x7(5,0)=&B11111 : font5x7(5,1)=&B10000 : font5x7(5,2)=&B10000 : font5x7(5,3)=&B11110 : font5x7(5,4)=&B10000 : font5x7(5,5)=&B10000 : font5x7(5,6)=&B10000
' G
font5x7(6,0)=&B01110 : font5x7(6,1)=&B10001 : font5x7(6,2)=&B10000 : font5x7(6,3)=&B10111 : font5x7(6,4)=&B10001 : font5x7(6,5)=&B10001 : font5x7(6,6)=&B01110
' H
font5x7(7,0)=&B10001 : font5x7(7,1)=&B10001 : font5x7(7,2)=&B10001 : font5x7(7,3)=&B11111 : font5x7(7,4)=&B10001 : font5x7(7,5)=&B10001 : font5x7(7,6)=&B10001
' I
font5x7(8,0)=&B01110 : font5x7(8,1)=&B00100 : font5x7(8,2)=&B00100 : font5x7(8,3)=&B00100 : font5x7(8,4)=&B00100 : font5x7(8,5)=&B00100 : font5x7(8,6)=&B01110
' J
font5x7(9,0)=&B00001 : font5x7(9,1)=&B00001 : font5x7(9,2)=&B00001 : font5x7(9,3)=&B00001 : font5x7(9,4)=&B10001 : font5x7(9,5)=&B10001 : font5x7(9,6)=&B01110
' K
font5x7(10,0)=&B10001 : font5x7(10,1)=&B10010 : font5x7(10,2)=&B10100 : font5x7(10,3)=&B11000 : font5x7(10,4)=&B10100 : font5x7(10,5)=&B10010 : font5x7(10,6)=&B10001
' L
font5x7(11,0)=&B10000 : font5x7(11,1)=&B10000 : font5x7(11,2)=&B10000 : font5x7(11,3)=&B10000 : font5x7(11,4)=&B10000 : font5x7(11,5)=&B10000 : font5x7(11,6)=&B11111
' M
font5x7(12,0)=&B10001 : font5x7(12,1)=&B11011 : font5x7(12,2)=&B10101 : font5x7(12,3)=&B10101 : font5x7(12,4)=&B10001 : font5x7(12,5)=&B10001 : font5x7(12,6)=&B10001
' N
font5x7(13,0)=&B10001 : font5x7(13,1)=&B11001 : font5x7(13,2)=&B10101 : font5x7(13,3)=&B10011 : font5x7(13,4)=&B10001 : font5x7(13,5)=&B10001 : font5x7(13,6)=&B10001
' O
font5x7(14,0)=&B01110 : font5x7(14,1)=&B10001 : font5x7(14,2)=&B10001 : font5x7(14,3)=&B10001 : font5x7(14,4)=&B10001 : font5x7(14,5)=&B10001 : font5x7(14,6)=&B01110
' P
font5x7(15,0)=&B11110 : font5x7(15,1)=&B10001 : font5x7(15,2)=&B10001 : font5x7(15,3)=&B11110 : font5x7(15,4)=&B10000 : font5x7(15,5)=&B10000 : font5x7(15,6)=&B10000
' Q
font5x7(16,0)=&B01110 : font5x7(16,1)=&B10001 : font5x7(16,2)=&B10001 : font5x7(16,3)=&B10001 : font5x7(16,4)=&B10101 : font5x7(16,5)=&B10010 : font5x7(16,6)=&B01101
' R
font5x7(17,0)=&B11110 : font5x7(17,1)=&B10001 : font5x7(17,2)=&B10001 : font5x7(17,3)=&B11110 : font5x7(17,4)=&B10100 : font5x7(17,5)=&B10010 : font5x7(17,6)=&B10001
' S
font5x7(18,0)=&B01111 : font5x7(18,1)=&B10000 : font5x7(18,2)=&B10000 : font5x7(18,3)=&B01110 : font5x7(18,4)=&B00001 : font5x7(18,5)=&B00001 : font5x7(18,6)=&B11110
' T
font5x7(19,0)=&B11111 : font5x7(19,1)=&B00100 : font5x7(19,2)=&B00100 : font5x7(19,3)=&B00100 : font5x7(19,4)=&B00100 : font5x7(19,5)=&B00100 : font5x7(19,6)=&B00100
' U
font5x7(20,0)=&B10001 : font5x7(20,1)=&B10001 : font5x7(20,2)=&B10001 : font5x7(20,3)=&B10001 : font5x7(20,4)=&B10001 : font5x7(20,5)=&B10001 : font5x7(20,6)=&B01110
' V
font5x7(21,0)=&B10001 : font5x7(21,1)=&B10001 : font5x7(21,2)=&B10001 : font5x7(21,3)=&B10001 : font5x7(21,4)=&B10001 : font5x7(21,5)=&B01010 : font5x7(21,6)=&B00100
' W
font5x7(22,0)=&B10001 : font5x7(22,1)=&B10001 : font5x7(22,2)=&B10001 : font5x7(22,3)=&B10101 : font5x7(22,4)=&B10101 : font5x7(22,5)=&B11011 : font5x7(22,6)=&B10001
' X
font5x7(23,0)=&B10001 : font5x7(23,1)=&B10001 : font5x7(23,2)=&B01010 : font5x7(23,3)=&B00100 : font5x7(23,4)=&B01010 : font5x7(23,5)=&B10001 : font5x7(23,6)=&B10001
' Y
font5x7(24,0)=&B10001 : font5x7(24,1)=&B10001 : font5x7(24,2)=&B10001 : font5x7(24,3)=&B01010 : font5x7(24,4)=&B00100 : font5x7(24,5)=&B00100 : font5x7(24,6)=&B00100
' Z
font5x7(25,0)=&B11111 : font5x7(25,1)=&B00001 : font5x7(25,2)=&B00010 : font5x7(25,3)=&B00100 : font5x7(25,4)=&B01000 : font5x7(25,5)=&B10000 : font5x7(25,6)=&B11111
' 0
font5x7(26,0)=&B01110 : font5x7(26,1)=&B10001 : font5x7(26,2)=&B10011 : font5x7(26,3)=&B10101 : font5x7(26,4)=&B11001 : font5x7(26,5)=&B10001 : font5x7(26,6)=&B01110
' 1
font5x7(27,0)=&B00100 : font5x7(27,1)=&B01100 : font5x7(27,2)=&B00100 : font5x7(27,3)=&B00100 : font5x7(27,4)=&B00100 : font5x7(27,5)=&B00100 : font5x7(27,6)=&B01110
' 2
font5x7(28,0)=&B01110 : font5x7(28,1)=&B10001 : font5x7(28,2)=&B00001 : font5x7(28,3)=&B00110 : font5x7(28,4)=&B01000 : font5x7(28,5)=&B10000 : font5x7(28,6)=&B11111
' 3
font5x7(29,0)=&B11111 : font5x7(29,1)=&B00010 : font5x7(29,2)=&B00100 : font5x7(29,3)=&B00010 : font5x7(29,4)=&B00001 : font5x7(29,5)=&B10001 : font5x7(29,6)=&B01110
' 4
font5x7(30,0)=&B00010 : font5x7(30,1)=&B00110 : font5x7(30,2)=&B01010 : font5x7(30,3)=&B10010 : font5x7(30,4)=&B11111 : font5x7(30,5)=&B00010 : font5x7(30,6)=&B00010
' 5
font5x7(31,0)=&B11111 : font5x7(31,1)=&B10000 : font5x7(31,2)=&B11110 : font5x7(31,3)=&B00001 : font5x7(31,4)=&B00001 : font5x7(31,5)=&B10001 : font5x7(31,6)=&B01110
' 6
font5x7(32,0)=&B00110 : font5x7(32,1)=&B01000 : font5x7(32,2)=&B10000 : font5x7(32,3)=&B11110 : font5x7(32,4)=&B10001 : font5x7(32,5)=&B10001 : font5x7(32,6)=&B01110
' 7
font5x7(33,0)=&B11111 : font5x7(33,1)=&B00001 : font5x7(33,2)=&B00010 : font5x7(33,3)=&B00100 : font5x7(33,4)=&B01000 : font5x7(33,5)=&B01000 : font5x7(33,6)=&B01000
' 8
font5x7(34,0)=&B01110 : font5x7(34,1)=&B10001 : font5x7(34,2)=&B10001 : font5x7(34,3)=&B01110 : font5x7(34,4)=&B10001 : font5x7(34,5)=&B10001 : font5x7(34,6)=&B01110
'9
font5x7(35,0)=&B01110 : font5x7(35,1)=&B10001 : font5x7(35,2)=&B10001 : font5x7(35,3)=&B01111 : font5x7(35,4)=&B00001 : font5x7(35,5)=&B00010 : font5x7(35,6)=&B01100
' SPACE
font5x7(36,0)=0 : font5x7(36,1)=0 : font5x7(36,2)=0 : font5x7(36,3)=0 : font5x7(36,4)=0 : font5x7(36,5)=0 : font5x7(36,6)=0
' . (period)
font5x7(37,0)=0 : font5x7(37,1)=0 : font5x7(37,2)=0 : font5x7(37,3)=0 : font5x7(37,4)=0 : font5x7(37,5)=&B01100 : font5x7(37,6)=&B01100
' , (comma)
font5x7(38,0)=0 : font5x7(38,1)=0 : font5x7(38,2)=0 : font5x7(38,3)=0 : font5x7(38,4)=&B01100 : font5x7(38,5)=&B01100 : font5x7(38,6)=&B01000
' ! (exclamation mark)
font5x7(39,0)=&B00100 : font5x7(39,1)=&B00100 : font5x7(39,2)=&B00100 : font5x7(39,3)=&B00100 : font5x7(39,4)=0 : font5x7(39,5)=&B00100 : font5x7(39,6)=&B00100
' : (colon)
font5x7(40,0)=0 : font5x7(40,1)=&B01100 : font5x7(40,2)=&B01100 : font5x7(40,3)=0 : font5x7(40,4)=&B01100 : font5x7(40,5)=&B01100 : font5x7(40,6)=0
' - (dash)
font5x7(41,0)=0 : font5x7(41,1)=0 : font5x7(41,2)=0 : font5x7(41,3)=&B11111 : font5x7(41,4)=0 : font5x7(41,5)=0 : font5x7(41,6)=0
' ? (question mark)
font5x7(42,0)=&B01110 : font5x7(42,1)=&B10001 : font5x7(42,2)=&B00001 : font5x7(42,3)=&B00010 : font5x7(42,4)=&B00100 : font5x7(42,5)=0 : font5x7(42,6)=&B00100
' ' (apostrophe)
font5x7(43,0)=&B00100 : font5x7(43,1)=&B00100 : font5x7(43,2)=&B01000 : font5x7(43,3)=0 : font5x7(43,4)=0 : font5x7(43,5)=0 : font5x7(43,6)=0
' / (slash)
font5x7(44,0)=&B00001 : font5x7(44,1)=&B00010 : font5x7(44,2)=&B00100 : font5x7(44,3)=&B01000 : font5x7(44,4)=&B10000 : font5x7(44,5)=0 : font5x7(44,6)=0
' ( (left paren)
font5x7(45,0)=&B00010 : font5x7(45,1)=&B00100 : font5x7(45,2)=&B01000 : font5x7(45,3)=&B01000 : font5x7(45,4)=&B01000 : font5x7(45,5)=&B00100 : font5x7(45,6)=&B00010
' ) (right paren)
font5x7(46,0)=&B01000 : font5x7(46,1)=&B00100 : font5x7(46,2)=&B00010 : font5x7(46,3)=&B00010 : font5x7(46,4)=&B00010 : font5x7(46,5)=&B00100 : font5x7(46,6)=&B01000
' --- Subroutines ---
' Writes a value to a specific register in the IS31FL3731 LED driver
SUB is31_writeRegister(bank%, reg%, value%)
LOCAL s$ AS STRING
s$ = CHR$(&HFD) + CHR$(bank%)
I2C WRITE IS31_ADDR, 0, 2, s$
s$ = CHR$(reg%) + CHR$(value%)
I2C WRITE IS31_ADDR, 0, 2, s$
END SUB
' Selects the bank for writing registers
SUB is31_selectBank(bank%)
LOCAL s$ AS STRING
s$ = CHR$(&HFD) + CHR$(bank%)
I2C WRITE IS31_ADDR, 0, 2, s$
END SUB
' Initializes the display and sets up the LED mapping table
SUB is31_setup
LOCAL i%
is31_selectBank(&H0B)
is31_writeRegister(&H0B, &H0A, 1)
is31_writeRegister(&H0B, &H00, 0)
is31_selectBank(0)
FOR i% = 0 TO 17
is31_writeRegister(0, &H00 + i%, &HFF)
NEXT i%
FOR i% = 0 TO 143
is31_writeRegister(0, &H24 + i%, 0)
NEXT i%
END SUB
' Clears the display
SUB is31_clear
LOCAL i%
is31_selectBank(0)
FOR i% = 0 TO 143
is31_writeRegister(0, &H24 + i%, 0)
NEXT i%
END SUB
' Sets the brightness of a specific LED pixel on the display
'
' * row% The vertical row of the pixel (0-6)
' * col% The horizontal column of the pixel (0-16)
' * brightness% The brightness level of the pixel (0-255)
' remarks: Silently exits if row or column is out of bounds
' Uses ledTable% to map display coordinates to LED indices
SUB setPixel(row%, col%, brightness%)
LOCAL ledIndex%
IF row% > 6 OR col% > 16 THEN EXIT SUB
ledIndex% = ledTable%(row%, col%)
IF ledIndex% >= 0 THEN
is31_writeRegister(0, &H24 + ledIndex%, brightness%)
END IF
END SUB
' Converts a character to its corresponding font index for display
'
' * c$ The character to convert
' returns: The font index of the character, or -1 if not found
' remarks: Supports uppercase letters, numbers, and a limited set of punctuation marks
FUNCTION charToFontIndex(c$) AS INTEGER
LOCAL code%
code% = ASC(UCASE$(c$))
IF code% >= ASC("A") AND code% <= ASC("Z") THEN
charToFontIndex = code% - ASC("A")
ELSEIF code% >= ASC("0") AND code% <= ASC("9") THEN
charToFontIndex = 26 + (code% - ASC("0"))
ELSEIF code% = ASC(" ") THEN
charToFontIndex = 36
ELSEIF code% = ASC(".") THEN
charToFontIndex = 37
ELSEIF code% = ASC(",") THEN
charToFontIndex = 38
ELSEIF code% = ASC("!") THEN
charToFontIndex = 39
ELSEIF code% = ASC(":") THEN
charToFontIndex = 40
ELSEIF code% = ASC("-") THEN
charToFontIndex = 41
ELSEIF code% = ASC("?") THEN
charToFontIndex = 42
ELSEIF code% = ASC("'") THEN
charToFontIndex = 43
ELSEIF code% = ASC("/") THEN
charToFontIndex = 44
ELSEIF code% = ASC("(") THEN
charToFontIndex = 45
ELSEIF code% = ASC(")") THEN
charToFontIndex = 46
ELSE
charToFontIndex = -1
END IF
END FUNCTION
' Scrolls the text buffer across the display, revealing characters one column at a time
' * offset% The current horizontal offset of the text buffer
' * row% Vertical row being processed
' * col% Horizontal column being processed
' Iterates through the text buffer, rendering pixels based on the current offset
' and brightness level, creating a smooth scrolling text effect
'-----------------------------------------------------------------------------------
SUB scrollText(text$ AS STRING, speedms% AS INTEGER, brightness% AS INTEGER)
LOCAL buffer$(7) AS STRING
LOCAL i%, j%, idx%, textLen%, bufWidth%
LOCAL row%, col%, offset%
textLen% = LEN(text$)
' Build rows of text
FOR row% = 0 TO 6
buffer$(row%) = ""
FOR i% = 1 TO textLen%
idx% = charToFontIndex(MID$(text$, i%, 1))
IF idx% >= 0 THEN
buffer$(row%) = buffer$(row%) + RIGHT$("00000" + BIN$(font5x7(idx%,row%)),5) + "0" ' Add 1 column space
END IF
NEXT i%
NEXT row%
bufWidth% = LEN(buffer$(0))
FOR offset% = 0 TO bufWidth% - 1
is31_clear
FOR row% = 0 TO 6
FOR col% = 0 TO 16
IF offset% + col% < bufWidth% THEN
IF MID$(buffer$(row%), offset% + col% + 1, 1) = "1" THEN
setPixel row%, col%, brightness%
END IF
END IF
NEXT col%
NEXT row%
PAUSE speedms%
NEXT offset%
END SUB
' Main program loop that continuously displays a scrolling message
' with a 100ms scroll speed and 24 brightness level, pausing 500ms between iterations
' --- Main Program ---
is31_setup
DO
scrollText " PICOMITE RP2040! ", 100, 24
PAUSE 500
LOOP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment