X-Git-Url: https://sigrok.org/gitweb/?p=libsigrokdecode.git;a=blobdiff_plain;f=scripts%2Fi2c.py;h=f0e168849cca00d68b13356ab3de6b4e3cf38c45;hp=f7022f5c3fce29397e66ed8acf98442a2523f3c5;hb=15167916e6e9d88b2b421c3ce2a80f7b29f25ce0;hpb=0588ed702725edbb5ed37ceb63fced93a5e68251 diff --git a/scripts/i2c.py b/scripts/i2c.py index f7022f5..f0e1688 100644 --- a/scripts/i2c.py +++ b/scripts/i2c.py @@ -41,13 +41,13 @@ # Repeated START condition (Sr): same as S # STOP condition (P): SDA = rising, SCL = high # -# All data bytes on SDA are extactly 8 bits long (transmitted MSB-first). +# All data bytes on SDA are exactly 8 bits long (transmitted MSB-first). # Each byte has to be followed by a 9th ACK/NACK bit. If that bit is low, # that indicates an ACK, if it's high that indicates a NACK. # # After the first START condition, a master sends the device address of the # slave it wants to talk to. Slave addresses are 7 bits long (MSB-first). -# After those 7 bits a data direction bit is sent. If the bit is low that +# After those 7 bits, a data direction bit is sent. If the bit is low that # indicates a WRITE operation, if it's high that indicates a READ operation. # # Later an optional 10bit slave addressing scheme was added. @@ -65,44 +65,108 @@ # TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0). # TODO: Implement support for detecting various bus errors. -# TODO: Return two buffers, one with structured data for the GUI to parse -# and display, and one with human-readable ASCII output. +# +# I2C output format: +# +# The output consists of a (Python) list of I2C "packets", each of which +# has an (implicit) index number (its index in the list). +# Each packet consists of a Python dict with certain key/value pairs. +# +# TODO: Make this a list later instead of a dict? +# +# 'type': (string) +# - 'S' (START condition) +# - 'Sr' (Repeated START) +# - 'AR' (Address, read) +# - 'AW' (Address, write) +# - 'DR' (Data, read) +# - 'DW' (Data, write) +# - 'P' (STOP condition) +# 'range': (tuple of 2 integers, the min/max samplenumber of this range) +# - (min, max) +# - min/max can also be identical. +# 'data': (actual data as integer ???) TODO: This can be very variable... +# 'ann': (string; additional annotations / comments) +# +# Example output: +# [{'type': 'S', 'range': (150, 160), 'data': None, 'ann': 'Foobar'}, +# {'type': 'AW', 'range': (200, 300), 'data': 0x50, 'ann': 'Slave 4'}, +# {'type': 'DW', 'range': (310, 370), 'data': 0x00, 'ann': 'Init cmd'}, +# {'type': 'AR', 'range': (500, 560), 'data': 0x50, 'ann': 'Get stat'}, +# {'type': 'DR', 'range': (580, 640), 'data': 0xfe, 'ann': 'OK'}, +# {'type': 'P', 'range': (650, 660), 'data': None, 'ann': None}] +# +# Possible other events: +# - Error event in case protocol looks broken: +# [{'type': 'ERROR', 'range': (min, max), +# 'data': TODO, 'ann': 'This is not a Microchip 24XX64 EEPROM'}, +# [{'type': 'ERROR', 'range': (min, max), +# 'data': TODO, 'ann': 'TODO'}, +# - TODO: Make list of possible errors accessible as metadata? +# +# TODO: I2C address of slaves. +# TODO: Handle multiple different I2C devices on same bus +# -> we need to decode multiple protocols at the same time. +# TODO: range: Always contiguous? Splitted ranges? Multiple per event? +# + +# +# I2C input format: +# +# signals: +# [[id, channel, description], ...] # TODO +# +# Example: +# {'id': 'SCL', 'ch': 5, 'desc': 'Serial clock line'} +# {'id': 'SDA', 'ch': 7, 'desc': 'Serial data line'} +# ... +# +# {'inbuf': [...], +# 'signals': [{'SCL': }]} +# -def sigrokdecode_i2c(inbuf): +def decode(inbuf): """I2C protocol decoder""" + # FIXME: Get the data in the correct format in the first place. + inbuf = [ord(x) for x in inbuf] + # FIXME: This should be passed in as metadata, not hardcoded here. - signals = (2, 5) - channels = 8 + metadata = { + 'numchannels': 8, + 'signals': { + 'scl': {'ch': 5, 'name': 'SCL', 'desc': 'Serial clock line'}, + 'sda': {'ch': 7, 'name': 'SDA', 'desc': 'Serial data line'}, + }, + } o = wr = ack = d = '' bitcount = data = 0 - state = 'IDLE' + IDLE, START, ADDRESS, DATA = range(4) + state = IDLE - # Get the bit number (and thus probe index) of the SCL/SDA signals. - scl_bit, sda_bit = signals + # Get the channel/probe number of the SCL/SDA signals. + scl_bit = metadata['signals']['scl']['ch'] + sda_bit = metadata['signals']['sda']['ch'] # Get SCL/SDA bit values (0/1 for low/high) of the first sample. - s = ord(inbuf[0]) - oldscl = (s & (1 << scl_bit)) != 0 - oldsda = (s & (1 << sda_bit)) != 0 + s = inbuf[0] + oldscl = (s & (1 << scl_bit)) >> scl_bit + oldsda = (s & (1 << sda_bit)) >> sda_bit # Loop over all samples. # TODO: Handle LAs with more/less than 8 channels. for samplenum, s in enumerate(inbuf[1:]): # We skip the first byte... - - s = ord(s) # FIXME - # Get SCL/SDA bit values (0/1 for low/high). - scl = (s & (1 << scl_bit)) != 0 - sda = (s & (1 << sda_bit)) != 0 + scl = (s & (1 << scl_bit)) >> scl_bit + sda = (s & (1 << sda_bit)) >> sda_bit # TODO: Wait until the bus is idle (SDA = SCL = 1) first? # START condition (S): SDA = falling, SCL = high if (oldsda == 1 and sda == 0) and scl == 1: o += "%d\t\tSTART\n" % samplenum - state = 'ADDRESS' + state = ADDRESS bitcount = data = 0 # Data latching by transmitter: SCL = low @@ -125,20 +189,21 @@ def sigrokdecode_i2c(inbuf): # We received 8 address/data bits and the ACK/NACK bit. data >>= 1 # Shift out unwanted ACK/NACK bit here. - o += "%d\t\t%s: " % (samplenum, state) + # o += "%d\t\t%s: " % (samplenum, state) + o += "%d\t\tTODO:STATE: " % samplenum ack = (sda == 1) and 'NACK' or 'ACK' - d = (state == 'ADDRESS') and (data & 0xfe) or data + d = (state == ADDRESS) and (data & 0xfe) or data wr = '' - if state == 'ADDRESS': + if state == ADDRESS: wr = (data & 1) and ' (W)' or ' (R)' - state = 'DATA' + state = DATA o += "0x%02x%s (%s)\n" % (d, wr, ack) bitcount = data = 0 # STOP condition (P): SDA = rising, SCL = high elif (oldsda == 0 and sda == 1) and scl == 1: o += "%d\t\tSTOP\n" % samplenum - state = 'IDLE' + state = IDLE # Save current SDA/SCL values for the next round. oldscl = scl @@ -146,19 +211,23 @@ def sigrokdecode_i2c(inbuf): return o -# This is just a draft. -def sigrokdecode_register_i2c(): - metadata = { +def register(): + return { 'id': 'i2c', 'name': 'I2C', - 'description': 'Inter-Integrated Circuit (I2C) bus', - 'function': 'sigrokdecode_i2c', + 'desc': 'Inter-Integrated Circuit (I2C) bus', 'inputformats': ['raw'], 'signalnames': { 'SCL': 'Serial clock line', 'SDA': 'Serial data line', }, - 'ouputformats': ['i2c', 'ascii'], + 'outputformats': ['i2c'], } - return metadata + +# Use psyco (if available) as it results in huge performance improvements. +try: + import psyco + psyco.bind(decode) +except ImportError: + pass