Last active
October 11, 2022 15:14
-
-
Save lordmatt/8ebf9bff0bc2fdd5f04f86ae78156d33 to your computer and use it in GitHub Desktop.
This is the class that I wrote to control a MAX7219 8 Digit Seven Segment Display Module using undocumented or poorly documented chip features.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is the class that I wrote to control a MAX7219 8 Digit Seven Segment Display Module which | |
# you can find on Amazon here: https://amzn.to/3g0ls8P | |
# | |
# This class makes use of a feature that I do not think was documented. To do this, I devided | |
# the list of serial instructions into columns one for each display module in use which I use | |
# to replace no-op codes and addrress other chips where the no-op padding should be. | |
# | |
# The datasheet I worked from is here: https://cdn-shop.adafruit.com/datasheets/MAX7219.pdf | |
# The blog post telling how I made it is here: https://lordmatt.co.uk/max7219-story | |
# | |
# The class assumes the following pin connections | |
# | |
# Pin-15 #CS/LOAD | |
# Pin-14 #DIN | |
# Pin-18 #CLK | |
# | |
# I'm releasing this under the GNU GPL 3 even though I am certain it is far from the best code | |
# you will see. It works for me with two display modules. | |
from machine import Pin | |
from time import sleep | |
from picozero import pico_led | |
# If you are reading this code, please forgive me for how poor it is. | |
# This is my first python script. | |
# Everything runs right to left | |
# Digit-0 is rightmost | |
# Digit 7 is leftmost | |
# Col/Board 0 is the last in the chain | |
# Col/Board 1 (if you have two) is first | |
junk = [0,1,1,0] | |
def dp(hexbit): | |
hexbit[0]=1 | |
return hexbit | |
register = dict() | |
register['no-op']=junk+[0,0,0,0] | |
register['no-op2']=register['no-op'] + register['no-op'] | |
register['digit0']=junk+[0,0,0,1] | |
register['digit1']=junk+[0,0,1,0] | |
register['digit2']=junk+[0,0,1,1] | |
register['digit3']=junk+[0,1,0,0] | |
register['digit4']=junk+[0,1,0,1] | |
register['digit5']=junk+[0,1,1,0] | |
register['digit6']=junk+[0,1,1,1] | |
register['digit7']=junk+[1,0,0,0] | |
register['decode mode']=junk+[1,0,0,1] | |
register['intensity']=junk+[1,0,1,0] | |
register['scan limit']=junk+[1,0,1,1] | |
register['shutdown']=junk+[1,1,0,0] | |
register['test']=junk+[1,1,1,1] | |
command = dict() | |
command['shutdown'] = register['shutdown'] + junk + [0,0,0,0] # shutdown mode | |
command['normal'] = register['shutdown'] + junk + [0,0,0,1] # Normal Opperation | |
command['reset'] = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0] | |
# Mode B for digits | |
command['none'] = register['decode mode'] + [0,0,0,0,0,0,0,0] | |
# others in here if I can be bothered later | |
command['all'] = register['decode mode'] + [1,1,1,1,1,1,1,1] | |
codeBFont = dict() | |
codeBFont['0'] = junk+[0,0,0,0] | |
codeBFont['1'] = junk+[0,0,0,1] | |
codeBFont['2'] = junk+[0,0,1,0] | |
codeBFont['3'] = junk+[0,0,1,1] | |
codeBFont['4'] = junk+[0,1,0,0] | |
codeBFont['5'] = junk+[0,1,0,1] | |
codeBFont['6'] = junk+[0,1,1,0] | |
codeBFont['7'] = junk+[0,1,1,1] | |
codeBFont['8'] = junk+[1,0,0,0] | |
codeBFont['9'] = junk+[1,0,0,1] | |
codeBFont['-'] = junk+[1,0,1,0] | |
codeBFont['E'] = junk+[1,0,1,1] | |
codeBFont['H'] = junk+[1,1,0,0] | |
codeBFont['L'] = junk+[1,1,0,1] | |
codeBFont['P'] = junk+[1,1,1,0] | |
codeBFont[' '] = junk+[1,1,1,1] | |
codeBFont['blank'] = codeBFont[' '] | |
intensity = dict() | |
intensity[0] = junk+[0,0,0,0] | |
intensity[1] = junk+[0,0,0,1] | |
intensity[2] = junk+[0,0,1,0] | |
intensity[3] = junk+[0,0,1,1] | |
intensity[4] = junk+[0,1,0,0] | |
intensity[5] = junk+[0,1,0,1] | |
intensity[6] = junk+[0,1,1,0] | |
intensity[7] = junk+[0,1,1,1] | |
intensity[8] = junk+[1,0,0,0] | |
intensity[9] = junk+[1,0,0,1] | |
intensity[10] = junk+[1,0,1,0] | |
intensity[11] = junk+[1,0,1,1] | |
intensity[12] = junk+[1,1,0,0] | |
intensity[13] = junk+[1,1,0,1] | |
intensity[14] = junk+[1,1,1,0] | |
intensity[15] = junk+[1,1,1,1] | |
scanlimit = dict() | |
scanlimit[0] = junk+[0,0,0,0] # 0 only | |
scanlimit[1] = junk+[0,0,0,1] # 0 - 1 | |
scanlimit[2] = junk+[0,0,1,0] # 0 - 2 | |
scanlimit[3] = junk+[0,0,1,1] # 0 - 3 | |
scanlimit[4] = junk+[0,1,0,0] # 0 - 4 | |
scanlimit[5] = junk+[0,1,0,1] # 0 - 5 | |
scanlimit[6] = junk+[0,1,1,0] # 0 - 6 | |
scanlimit[7] = junk+[0,1,1,1] # 0 - 7 | |
testmode = dict() | |
testmode['normal'] = junk+[0,0,0,0] | |
testmode['display'] = junk+[0,0,0,1] | |
class Seg7Display: | |
colSize = 2 # number of command columns (chips/boards in use) | |
cols = dict() # command columns (for correct no-op padding) | |
baudr = 5*(10^-8) # you might get away with making this faster but YMMV | |
def __init__(self,colSize=2,prime=False): | |
self.colSizeDef(colSize) | |
if prime: | |
self.shutDown() | |
self.reset() | |
self.allUseFont() | |
#start of chainable methods | |
def colSizeDef(self,colSize): | |
self.colSize=colSize | |
for col in range(0, (colSize)): | |
self.cols[col]=dict() | |
return self | |
def changeDigit(self,digit,ShowAs,col=0): | |
self.useCommand(col,register['digit'+str(digit)] + codeBFont[str(ShowAs)]) | |
return self | |
# this method assumes you send a valid command array | |
def useCommand(self,col,command): | |
#print(f"{self.cols}") | |
nexti = len(self.cols[col]) | |
self.cols[col][nexti] = command | |
return self | |
def normalDisplay(self,col): | |
self.useCommand(col,command['normal']) | |
return self | |
def shutDown(self): | |
for col in range(0, self.colSize): | |
self.useCommand(col,command['shutdown']) | |
return self | |
def reset(self): | |
for col in range(0, self.colSize): | |
self.useCommand(col,command['reset']) | |
return self | |
def scanLimit(self,col,limit): | |
if limit<0: | |
limit = 0 | |
if limit > 7: | |
limit = 7 | |
self.useCommand(col,register['scan limit'] +scanlimit[limit]) | |
return self | |
def intensity(self,col,level): | |
if level < 0: | |
level = 0 | |
if level > 15: | |
level = 15 | |
self.useCommand(col,register['intensity']+intensity[level]) | |
return self | |
def allUseFont(self): | |
for col in range(0, self.colSize): | |
self.useCommand(col,command['all']) | |
return self | |
def insertNormalCommand(self): | |
for col in range(0, self.colSize): | |
self.normalDisplay(col) | |
return self | |
def setDigitInCol(self,col,digit,value): | |
self.useCommand(col,register[f'digit{digit}'] + codeBFont[f'{value}']) | |
return self | |
# end of chainable mthods | |
def getOrNoOp(self,col,row): | |
try: | |
self.cols[col][row] | |
#print(f"C-{col}/R-{row}:GOOD::[{self.cols[col][row]}]") | |
except KeyError: | |
#print(f"C-{col}/R-{row}::NO-OP") | |
return register['no-op2'] | |
return self.cols[col][row] | |
def doIt(self,insertNormal=True,waitAtEnd=True): | |
if insertNormal: | |
self.insertNormalCommand() | |
size=0 | |
for col in range(0, self.colSize): | |
sizeOf = len(self.cols[col]) | |
if sizeOf>size : | |
size=sizeOf | |
instructions = [] | |
for row in range(0,size-1): | |
for col in range(0, self.colSize): | |
instructions = instructions + self.getOrNoOp(col,row) | |
bitsPerCycle = self.colSize*16 | |
self.SendBits(instructions,bitsPerCycle,waitAtEnd) | |
#self.colSize.clear() | |
self.colSizeDef(self.colSize) # reset the buffer | |
def SendBits(self,bits,highon=16,waitAtEnd=True): | |
baudr = self.baudr # side effect of refactoring | |
p_cs = Pin(15,Pin.OUT) #CS/LOAD | |
p_din = Pin(14,Pin.OUT) #DIN | |
p_clk = Pin(18,Pin.OUT) #CLK | |
p_clk.value(0) | |
sleep(baudr) | |
counter = 0 | |
for bit in bits: | |
counter += 1 | |
p_clk.value(0) #clock low | |
pico_led.off() | |
p_cs.value(0) # CS low | |
sleep(baudr) #Regulate clock | |
p_din.value(bit) # send bit | |
p_clk.value(1) #clock pulse | |
pico_led.on() | |
if(counter>(highon-1)): # CS high on 16th rising edge | |
p_cs.value(1) # load | |
counter = 0 | |
sleep(baudr) #Regulate clock | |
p_cs.value(0) # reset | |
p_din.value(0) # reset | |
if waitAtEnd: | |
sleep(baudr*highon*2) # wait for two entire command cycles | |
pico_led.off() | |
# Note: this method does not use multichannel methods | |
def SetADigit(digit,value): | |
if(digit>7): | |
digit=digit-8 | |
action = [] | |
action = action + register[f'digit{digit}'] | |
action = action + codeBFont[f'{value}'] | |
Cnormal = command['normal'] | |
# now we set the right number of no-ops | |
ii=0 | |
byteboi = 16 | |
mybyteboi = byteboi | |
while(ii<chaind): | |
action = action + register['no-op2'] | |
Cnormal + register['no-op2'] | |
ii=ii+1 | |
mybyteboi = mybyteboi + byteboi | |
# now we set the digit | |
sendBits(action+Cnormal,mybyteboi) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment