# # This file is part of the libsigrokdecode project. # # Copyright (C) 2020 Thomas Hoffmann # Copyright (C) 2023 ALIENTEK(正点原子) <39035605@qq.com> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # import re import sigrokdecode as srd # FIXME: reduce annotation types anb_DA, anb_RES, anb_SL, anb_CB, anb_DC, anb_PG, \ anb_CO, anb_mux, anb_par, anb_LAST = range(10) bits = ( 'Display Addressing', 'Reserved', 'Start Line', 'Continuation', 'Data / Command', 'Page', 'Column', 'mux', 'Parameter', 'Last' ) (ann_LC, ann_HC, ann_DM, ann_SCA, ann_SPA, ann_SFB, ann_RHS, ann_LHS, ann_VRHS, ann_VLHS, ann_SS, ann_AS, ann_DSL, ann_SCC, ann_SCPU, ann_MC0TS0, ann_MCFFTS0, ann_SVSA, ann_DOR, ann_DOI, ann_ND, ann_ID, ann_SMR, ann_DOFF, ann_DON, ann_PSA, ann_CSU, ann_CSD, ann_SVO, ann_DCR, ann_ZI, ann_SPP, ann_SCPI, ann_SVD, ann_NOP, ann_GR, ann_DA, ann_CB, ann_LAST) = range(anb_LAST+1, anb_LAST+40) cmds2 = { # ann name annID cmd txt param? 0x00: ('LowerColStart', ann_LC, ['Set Lower Column Start Address', 'Set L Col Start', 'LC'], 0), 0x10: ('HigherColStart', ann_HC, ['Set Higher Column Start Address', 'Set H Col Start', 'HC'], 0), 0x20: ('DisplayMode', ann_DM, ['Set Display Mode', 'Set Dsp Md', 'DM'], 1), 0x21: ('SetColAddress', ann_SCA, ['Set Column Address', 'Set Col Adr', 'CA'], 1), 0x22: ('SetPageAddress', ann_SPA, ['Set Page Address', 'Set Pg Adr', 'PA'], 1), 0x23: ('SetFadeoutBlinking', ann_SFB, ['Set Fade-out and Blinking', 'Set FO Blnk', 'FB'], 1), 0x26: ('RightHorScroll', ann_RHS, ['Right horizontal scroll', 'Right hor scr', 'RHS'], 1), 0x27: ('LeftHorScroll', ann_LHS, ['Left horizontal scroll', 'Left hor scr', 'LHS'], 1), 0x29: ('VertRightHorScroll', ann_VRHS, ['Vertical and right horizontal scroll', 'Vert right hor scr', 'VRHS'], 1), 0x2A: ('VertLeftHorScroll', ann_VLHS, ['Vertical and left horizontal scroll', 'Vert left hor scr', 'VLHS'], 1), 0x2E: ('StopScrolling', ann_SS, ['Stop scrolling', 'Stsc'], 0), 0x2F: ('ActivateScrolling', ann_AS, ['Activate scrolling' , 'Acsc'], 0), 0x40: ('DisplayStartLine', ann_DSL, ['Display start line', 'DSL',], 0), 0x81: ('SetContrast', ann_SCC, ['Set contrast control', 'Set Ctr', 'SC'], 1), 0x8D: ('SetChargePump', ann_SCPU, ['Set charge pump', 'Set Ch pmp', 'SP'], 1), 0xA0: ('MapCol0ToSeg0', ann_MC0TS0, ['Map col addr0 to seg0', 'Map C0 to S0', 'M00'], 0), 0xA1: ('MapCol127toSeg0', ann_MCFFTS0,['Map col addr7f to seg0', 'Map C7f to S0', 'M7f0'], 0), 0xA3: ('SetVertScrollArea', ann_SVSA, ['Set vertical scroll area', 'Set vert scr ar', 'SVSA'], 1), 0xA4: ('DisplayOnResume', ann_DOR, ['Display on, resume to RAM', 'Dis on, res RAM', 'D1R'], 0), 0xA5: ('DisplayOnIgnore', ann_DOI, ['Display on, ignore RAM', 'Dis on, ign RAM', 'D1I'], 0), 0xA6: ('NormalDisplay', ann_ND, ['Normal display', 'Norm disp', 'DN'], 0), 0xA7: ('InverseDisplay', ann_ID, ['Inverse display', 'Inv disp', 'DI'], 0), 0xA8: ('SetMultiplexRatio', ann_SMR, ['Set multiplex ratio', 'Set MUX rat', 'MUX'], 1), 0xAE: ('DisplayOff', ann_DOFF, ['Display OFF', 'Dis OFF', 'DO'], 0), 0xAF: ('DisplayOn', ann_DON, ['Display ON', 'Dis ON', 'D1'], 0), 0xB0: ('PgStartAddr', ann_PSA, ['Page start address', 'Pg start', 'PS'], 0), 0xC0: ('ComScanUp', ann_CSU, ['COM scan 0 to mux', 'C scan upw', 'SCU'], 0), 0xC8: ('ComScanDown', ann_CSD, ['COM scan mux to 0', 'C scan dwd', 'SCD'], 0), 0xD3: ('Set Vertical Offset', ann_SVO, ['Set vertical offset', 'Set vert ofs', 'VO'], 1), 0xD5: ('DisplayClockRatio', ann_DCR, ['Display clock ratio', 'Clock ratio', 'CR'], 1), 0xD6: ('ZoomIn', ann_ZI, ['Set zoom-in', 'Zoom in', 'ZI'], 1), 0xD9: ('PrechargePeriod', ann_SPP, ['Set precharge period', 'Pre chrg', 'PC'], 1), 0xDA: ('SetCOMPins', ann_SCPI, ['Set COM pins', 'COM pins', 'CP'], 1), 0xDB: ('SetVcomhDeselect', ann_SVD, ['Set Vcomh deselect', 'Vcomh desel', 'VD'], 1), 0xE3: ('NOP', ann_NOP, ['No operation', 'NOP'], 0), 'data': ('GDDRAM', ann_GR, ['Data write',], 0), # data write 'dev': ('DeviceAddress', ann_DA, ['Device address',], 0), # 0x3C or 0x3D 'cbyte': ('ControlByte', ann_CB, ['Control byte',], 0), # 0x80 (Command follows) or 0x40 (data) 'last': ('LastCmd', ann_LAST, ['Last',], 0) # unused: marks end of cmds } anbl_block=ann_LAST+1 anw_warn=anbl_block+1 SSD1306_I2C_ADDRESS = 0x3C SSD1306_I2C_ADDRESS_2 = 0x3D def bits_and_cmds(): l = [('bit_' + re.sub('\\/| ', '_', b).lower(), b + ' bit') for b in bits] #order dictionary entries by ann_ sl=sorted(cmds2.items(), key=lambda x: x[1][1]) l += [('cmd_' + k[1][0].lower(), k[1][0] + ' command') for k in sl] return tuple(l) class Decoder(srd.Decoder): api_version = 3 id = 'ssd1306' name = 'SSD1306' longname = 'Solomon 1306' desc = 'Solomon SSD1306 OLED controller protocol.' license = 'gplv2+' inputs = ['i2c'] outputs = [] tags = ['Display', 'IC'] annotations = bits_and_cmds() + ( ('write_block', 'Write block'), ('warning', 'Warning'), ) annotation_rows = ( ('bits', 'Bits', tuple(range(anb_LAST+1))), ('cmds', 'Commands', tuple(range(anb_LAST+1, ann_LAST+1))), ('blockdata', 'Block Data', (anbl_block, )), ('warnings', 'Warnings', (anw_warn,)), ) def __init__(self): self.reset() def reset(self): self.state = 'IDLE' self.substate = 'COMMAND' self.prevreg = -1 self.bits = [] self.sscmd = 0 self.blockstring = '' def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) def putd(self, bit1, bit2, data): self.put(self.bits[bit1][1], self.bits[bit2][2], self.out_ann, data) def putr(self, bit): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [anb_RES, ['Reserved bit NC', 'Res NC', 'NC', 'N']]) def put0(self, bit): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [anb_RES, ['fixed 0', '0',]]) def put1(self, bit): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [anb_RES, ['fixed 1', '1',]]) def handle_par_0x00(self, param): # low col start addr #bit output: for i in range(4, 8): self.put0(i) self.putd(3, 0, [cmds2[self.prevreg][1], ['Lwr col start addr= %d' % param & 0xf, 'lo col %d' % param & 0xf, 'LCS']]) #cmd output: none #block output self.blockstring += '= %d' % param & 0xf def handle_par_0x10(self, param): # high col start addr #bit output: for i in range(5, 8): self.put0(i) self.put1(4) self.putd(3, 0, [cmds2[self.prevreg][1], ['Hghr col start addr= %d' % param & 0xf, 'hi col %d' % param & 0xf, 'HCS']]) #cmd output: none #block output self.blockstring += '= %d' % param & 0xf def handle_par_0x20(self, param): # set display mode am = ['hor. addr.', 'vert. addr.', 'page addr.', 'invalid'] am2 = ['HA', 'VA', 'PA', 'IV'] #bit output: for i in range(2,8): self.putr(i) self.putd(1, 0, [anb_DA, ['mode: %s' % am2[param & 3], 'DM']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Display mode: %s' % am[param & 3], 'mode: %s' % am2[param & 3], 'DM']]) #block output: self.blockstring += ': %s' % am[param & 3] self.substate = 'COMMAND' def handle_par_0x21(self, param): # A B - set column address if self.substate == 'PARAMETER': sc = param & 0x7f res = ' (reset)' if sc == 0 else '' #bit output: self.putr(7) self.putd(6, 0, [anb_CO, ['col %d' % sc, 'CO']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Start column: %d%s' % (sc,res), 'St Col: %d' % sc, 'SC']]) #block output: self.blockstring += ' from %d%s ' % (sc, res) self.substate = 'PARAMETER2' elif self.substate == 'PARAMETER2': ec = param & 0x7f res = ' (reset)' if ec == 0x7f else '' #bit output: self.putr(7) self.putd(6, 0, [anb_CO, ['col %d' % ec, 'CO']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['End column: %d%s' % (ec,res), 'End Col: %d' % ec, 'EC']]) #block output self.blockstring += 'to %d%s' % (ec,res) self.substate = 'COMMAND' def handle_par_0x22(self, param): # A B - set page address if self.substate == 'PARAMETER': sp = param & 0x7 res = ' (reset)' if sp == 0 else '' #bit output: for i in range (3,8): self.putr(i) self.putd(2, 0, [anb_PG, ['page %d' % sp, 'PG']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Start Page: %d%s' % (sp,res), 'St Pg: %d' % sp, 'SP']]) #block output: self.blockstring += ' from %d%s ' % (sp, res) self.substate = 'PARAMETER2' elif self.substate == 'PARAMETER2': ep = param & 0x7 res = ' (reset)' if ep == 0x7 else '' #bit output: for i in range (3,8): self.putr(i) self.putd(2, 0, [anb_PG, ['page %d' % ep, 'PG']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['End Page: %d%s' % (ep,res), 'End Pg: %d' % ep, 'EP']]) #block output self.blockstring += 'to %d%s' % (ep,res) self.substate = 'COMMAND' def handle_par_0x23(self, param): # set fade-out and blinking bf = ['no FA / blnk', 'invalid', 'fade-out', 'blink'][(param >> 4) & 3] bf2 = ['no', 'IV', 'FO', 'BL'][(param >> 4) & 3] fo = ((param & 0xf) << 3) + 8 #bit output: self.putr(7) self.putr(6) self.putd(5, 4, [anb_par, [bf2, 'BF']]) self.putd(3, 0, [anb_par, ['fr: %d' % fo, 'FS']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['%s (%d frames)' % (bf, fo), '%s (%d fr)' % (bf2, fo), 'BF']]) #block output: self.blockstring += ': %s, %d frames' % (bf, fo) self.substate = 'COMMAND' def handle_par_0x26(self, param): # set right hor. scrolling if self.substate == 'PARAMETER': for i in range(8): self.put0(i) self.substate = 'PARAMETER2' elif self.substate == 'PARAMETER2': for i in range(3, 8): self.putr(i) #B[2:0] start page sp = param & 0x7 self.putd(2, 0, [anb_PG, ['page %d' % sp, 'PG']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Start Page: %d' % sp, 'St Pg: %d' % sp, 'SP']]) #block output: self.blockstring += ' from page %d, ' % sp self.substate = 'PARAMETER3' elif self.substate == 'PARAMETER3': for i in range(3, 8): self.putr(i) #C[2:0] time interval iv = (5, 64, 128, 256, 3, 4, 25, 2)[param & 0x7] self.putd(2, 0, [anb_PG, ['intvl %d' % iv, 'IV']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Scroll Interval: %d' % iv, 'Scr Iv: %d' % iv, 'IV']]) #block output self.blockstring += 'time interval %d, ' % iv self.substate = 'PARAMETER4' elif self.substate == 'PARAMETER4': for i in range(3, 8): self.putr(i) #D[2:0] end page ep = param & 0x7 self.putd(2, 0, [anb_PG, ['page %d' % ep, 'PG']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['End Page: %d' % ep, 'End Pg: %d' % ep, 'EP']]) #block output self.blockstring += 'to page %d' % ep self.substate = 'PARAMETER5' elif self.substate == 'PARAMETER5': for i in range(8): self.put0(i) self.substate = 'PARAMETER6' elif self.substate == 'PARAMETER6': for i in range(8): self.put1(i) self.substate = 'COMMAND' def handle_par_0x27(self, param): # set left hor. scrolling self.handle_par_0x26(param) def handle_par_0x29(self, param): # set vert and right hor scrolling if self.substate == 'PARAMETER': for i in range(8): self.put0(i) self.substate = 'PARAMETER2' elif self.substate == 'PARAMETER2': for i in range(3, 8): self.putr(i) #B[2:0] start page sp = param & 0x7 self.putd(2, 0, [anb_PG, ['page %d' % sp, 'PG']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Start Page: %d' % sp, 'St Pg: %d' % sp, 'SP']]) #block output: self.blockstring += ' from page %d, ' % sp self.substate = 'PARAMETER3' elif self.substate == 'PARAMETER3': for i in range(3, 8): self.putr(i) #C[2:0] time interval iv = (5, 64, 128, 256, 3, 4, 25, 2)[param & 0x7] self.putd(2, 0, [anb_PG, ['intvl %d' % iv, 'IV']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Scroll Interval: %d' % iv, 'Scr Iv: %d' % iv, 'IV']]) #block output self.blockstring += 'time interval %d, ' % iv self.substate = 'PARAMETER4' elif self.substate == 'PARAMETER4': for i in range(3, 8): self.putr(i) #D[2:0] end page ep = param & 0x7 self.putd(2, 0, [anb_PG, ['page %d' % ep, 'PG']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['End Page: %d' % ep, 'End Pg: %d' % ep, 'EP']]) #block output self.blockstring += 'to page %d' % ep self.substate = 'PARAMETER5' elif self.substate == 'PARAMETER5': for i in (6, 7): self.putr(i) #E[5:0] - vertical scrolling offset vso = param & 0x3f self.putd(2, 0, [anb_PG, ['scr ofs %d' % vso, 'VO']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Vert Scroll Ofs: %d' % vso, 'VSO: %d' % vso, 'VSO']]) #block output self.blockstring += ' (vertical offset= %d rows)' % vso self.substate = 'COMMAND' def handle_par_0x2a(self, param): # set vert and left hor scrolling self.handle_par_0x29(param) def handle_par_0x40(self, param): # display start line #bit output: self.put0(7) self.put1(6) self.putd(5, 0, [anb_SL, ['Start line= %d' % (param & 0x3f), 'st l %d' % (param & 0x3f), 'StL']]) #cmd output: none #block output self.blockstring += '= %d' % (param & 0x3f) def handle_par_0x81(self, param): # set contrast #bit output: none #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Contrast= %d' % param, 'Ctr: %d' % param, 'Ctr']]) #block output res = ' (reset)' if param == 0x7f else '' self.blockstring += ' to %d%s ' % (param, res) self.substate = 'COMMAND' def handle_par_0x8d(self, param): # set charge pump if param & 4: cp = 'on' res = '' else: cp = 'off' res = ' (reset)' #bit output: for i in (6,7): self.putr(i) for i in (0,1,3,5): self.put0(i) self.put1(4) self.putd(2, 2, [anb_par, ['cp: %s' % cp, 'cp', 'c']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Charge pump= %s' % cp, 'Ch p: %s' % cp, 'CP']]) #block output: self.blockstring += ' to %s%s' % (cp, res) self.substate = 'COMMAND' def handle_par_0xa3(self, param): # A B - set vert scroll area if self.substate == 'PARAMETER': tfr = param & 0x3f res = ' (reset)' if tfr == 0 else '' #bit output: self.putr(7) self.putr(6) self.putd(5, 0, [anb_par, ['tfr: %d' % tfr, 'tfr']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Top fixed rows: %d%s' % (tfr,res), 'Tp fx rws: %d' % tfr, 'TFR']]) #block output: self.blockstring += ', top fixed rows: %d%s, ' % (tfr, res) self.substate = 'PARAMETER2' elif self.substate == 'PARAMETER2': sr = param & 0x7f res = ' (reset)' if sr == 0x40 else '' #bit output: self.putr(7) self.putd(6, 0, [anb_par, ['sr: %d' % sr, 'sr']]) #cmd output: self.putd(7, 0, [cmds2[self.prevreg][1], ['Scroll rows: %d%s' % (sr,res), 'Scr rws: %d' % sr, 'SR']]) #block output self.blockstring += 'scroll rows: %d%s' % (sr,res) self.substate = 'COMMAND' def handle_par_0xa8(self, param): # set multiplex ratio mux = (param & 0x3f) + 1 #bit output: for i in range(6,8): self.putr(i) self.putd(5, 0, [anb_mux, ['mux: %d' % mux, 'mux']]) #cmd output: if mux < 16: self.put(self.sscmd, self.es, self.out_ann, [anw_warn, ['invalid multiplex ratio < 16 (%d)' % mux]]) self.blockstring='' else: res = ' (reset)' if mux == 64 else '' self.putd(7, 0, [cmds2[self.prevreg][1], ['%d %s' % (mux, res), '%d' % mux]]) #block output self.blockstring += ' to %d%s' % (mux, res) self.substate = 'COMMAND' def handle_par_0xb0(self, param): # page start address #bit output: for i in (4, 5, 7): self.put1(i) for i in (3, 6): self.put0(i) self.putd(2, 0, [cmds2[self.prevreg][1], ['Page start addr= %d' % param & 0x7, 'pg st %d' % param & 0x7, 'PSA']]) #cmd output: none #block output self.blockstring += '= %d' % param & 0x7 def handle_par_0xd3(self, param): # set vertical offset vo = param & 0x3f #bit output: for i in (6,7): self.putr(i) self.putd(5, 0, [anb_par, ['vo: %d' % vo, 'vo']]) #cmd output self.putd(7, 0, [cmds2[self.prevreg][1], ['Vertical offset = %d' % vo, 'V ofs: %d' % vo, 'VO']]) #block output self.blockstring += ' = %d' % vo self.substate = 'COMMAND' def handle_par_0xd5(self, param): # display clock ratio of = (param >> 4) & 0xf dr = (param & 0xf) + 1 #bit output self.putd(7, 4, [anb_par, ['of: %d' % of, 'of']]) self.putd(3, 0, [anb_par, ['dr: %d' % dr, 'dr']]) #cmd output res = '(reset)' if of == 8 else '' self.putd(7, 0, [cmds2[self.prevreg][1], ['Freq=%d, div ratio=%d %s' % (of,dr,res), 'f, r: %d, %d' % (of,dr), 'FR']]) #block output self.blockstring += ': fOSC=%d, divide ratio=%d %s' % (of,dr,res) self.substate = 'COMMAND' def handle_par_0xd6(self, param): # zoom-in if param & 1 == 0: zo = 'disable (reset)' z = 0 else: zo = 'enable' z = 1 #bit output for i in range(1,8): self.put0(i) self.putd(0, 0, [anb_par, ['zo: %s' % z, 'zo']]) #cmd output self.putd(7, 0, [cmds2[self.prevreg][1], ['Zoom-in: %s' % zo, 'Zoom: %d' % z, 'ZI']]) #block output self.blockstring += ': %s' % zo self.substate = 'COMMAND' def handle_par_0xd9(self, param): # set pre-charge period p1 = param & 0xf p2 = (param >> 4) & 0xf #bit output: self.putd(3, 0, [anb_par, ['phase1', 'p1']]) self.putd(7, 4, [anb_par, ['phase2', 'p2']]) #cmd output: if p1 == 0 or p2 == 0: self.put(self.ss_block, self.es, self.out_ann, [anw_warn, ['invalid precharge period = 0 (p1: %d, p2: %d)' % (p1, p2)]]) self.blockstring = '' else: res1 = ' (reset)' if p1 == 2 else '' res2 = ' (reset)' if p2 == 2 else '' self.putd(7, 0, [cmds2[self.prevreg][1], ['P1=%d%s, P2=%d%s' % (p1, res1, p2, res2), 'p1, p2: %d, %d' % (p1,p2), 'PC']]) #block output self.blockstring += ': P1=%d%s, P2=%d%s' % (p1, res1, p2, res2) self.substate = 'COMMAND' def handle_par_0xda(self, param): # set COM pins (seq, s) = ('sequential', 's') if param & 0x20f else ('alternative', 'a') (lrm, l) = ('no ', 'N') if param & 0x10f else ('', 'R') #bit output for i in (7,6,3,2,0): self.put0(i) self.put1(1) self.putd(5, 5, [anb_par, ['LRM', 'L']]) self.putd(4, 4, [anb_par, ['SEQ', 'S']]) #cmd output self.putd(7, 0, [cmds2[self.prevreg][1], ['COM pins: %s, %s L/R remap' % (seq, lrm), '%s %s' % (s, l), 'CP']]) #block output self.blockstring += ': %s, %s L/R remap' % (seq, lrm) self.substate = 'COMMAND' def handle_par_0xdb(self, param): # set Vcomh deselect vc = (param >> 4) & 7 #bit output for i in (7,3,2,1,0): self.put0(i) self.putd(6, 4, [anb_par, ['Vch', 'V']]) if vc not in (0, 2, 3): self.put(self.ss_block, self.es, self.out_ann, [anw_warn, ['invalid Vcomh deselect = 0x%02x' % vc]]) self.blockstring = '' else: vcomh=('0.65 Vcc', '', '0.77 Vcc (reset)', '0.83 Vcc') self.putd(7, 0, [cmds2[self.prevreg][1], ['Vcomh = %s' % vcomh[vc], '%s' % vcomh[vc], 'VD']]) self.blockstring += ': Vcomh = %s' % vcomh[vc] self.substate = 'COMMAND' def handle_command(self, b, param=-1): #normalize "range commands" ID to range start if b in range(0x0, 0x10): b1 = b b = 0x0 if b in range(0x10, 0x20): b1 = b b = 0x10 if b in range(0x40, 0x80): b1 = b b = 0x40 if b in range(0xB0, 0xB8): b1 = b b = 0xB0 if b in cmds2: if self.substate == 'COMMAND': #bit output: none #cmd output: self.putd(7, 0, [cmds2[b][1], cmds2[b][2]]) #block output: self.blockstring = cmds2[b][2][0] self.sscmd = self.ss_block self.prevreg = b #cmds w/ parameters if cmds2[b][3]: self.substate='PARAMETER' if b in (0x0, 0x10, 0x40, 0xB0): #handlers for 0x0 - 0xF, 0x10-0x1F, 0x40-0x7F, 0xB0-0xB7 fn = getattr(self, 'handle_par_0x%02x' % b) fn(b1) else: fn = getattr(self, 'handle_par_0x%02x' % b) fn(param) if self.substate == 'COMMAND': #block output self.put(self.sscmd, self.es, self.out_ann, [anbl_block, [self.blockstring]]) def handle_data(self, b): self.putd(7, 0, [ann_GR, ['GDDRAM data: 0x%02X' % b, 'RAM: %02X' % b, 'RAM']]) def is_correct_chip(self, addr): if addr in (SSD1306_I2C_ADDRESS, SSD1306_I2C_ADDRESS_2) : self.putd(7, 0, [ann_DA, ['Device address: 0x%02X' % addr, 'Dev Addr: 0x%02X' % addr, 'Dev Addr', 'DA']]) return True self.put(self.ss_block, self.es, self.out_ann, [anw_warn, ['Ignoring non-SSD1306 data (slave 0x%02X)' % addr]]) return False def handle_controlbyte(self, b): self.putd(7, 0, [ann_CB, ['Control byte = 0x%02X' % b, 'Ctrl bt = 0x%02X' % b, 'CB']]) for i in range(0,6): self.putr(i) cb=b >> 7 dc=(b >> 6) & 1 self.putd(7, 7, [anb_CB, ['Continuation bit: %d' % cb, 'cb: %d' % cb, 'CB']]) self.putd(6, 6, [anb_DC, ['Data / command bit: %d' % dc, 'dc: %d' % dc, 'DC']]) def decode(self, ss, es, data): cmd, databyte = data # Collect the 'BITS' packet, then return. The next packet is # guaranteed to belong to these bits we just stored. if cmd == 'BITS': self.bits = databyte return # Store the start/end samples of this I²C packet. self.ss, self.es = ss, es # State machine. if self.state == 'IDLE': # Wait for an I²C START condition. if cmd != 'START': return self.state = 'GET SLAVE ADDR' self.ss_block = ss elif self.state == 'GET SLAVE ADDR': # Wait for an address write operation. if cmd != 'ADDRESS WRITE': return if not self.is_correct_chip(databyte): self.state = 'IDLE' return self.state = 'WRITE CONTROL BYTE' elif self.state == 'WRITE CONTROL BYTE': # Get control byte. if cmd == 'DATA WRITE': self.handle_controlbyte(databyte) if databyte == 0x80: self.state = 'SSD COMMAND' elif databyte == 0x40: self.state = 'SSD DATA' else: self.state = 'IDLE' elif cmd == 'STOP': self.state = 'IDLE' elif self.state == 'SSD COMMAND': # Get command byte. if cmd == 'DATA WRITE': if self.substate == 'COMMAND': self.handle_command(databyte) else: #substate 'PARAMETER' (2, ...) self.handle_command(self.prevreg, databyte) self.state='WRITE CONTROL BYTE' elif self.state == 'SSD DATA': # Get data bytes until a STOP condition occurs. if cmd == 'DATA WRITE': self.handle_data(databyte) elif cmd == 'STOP': self.state = 'IDLE'