class Decoder(srd.Decoder): """Protocol decoder for 7-segments digital tubes control ``TM1638``.""" api_version = 3 id = "tm1638" name = "TM1638" longname = "Special circuit for LED driver control" desc = "Titan Micro Electronics LED drive control special circuit for \ driving displays with 7-segments digital tubes, two-color LEDs, \ and keyboard scanning." license = "gplv2+" inputs = ["tmc"] outputs = ["tm1638"] annotations = hlp.create_annots({ "bit": bits, "info": info, }) annotation_rows = ( ("bits", "Bits", tuple(range(AnnBits.RESERVED, AnnBits.ON + 1))), ("display", "Display", (AnnInfo.DISPLAY, )), ("leds", "LEDchain", (AnnInfo.LEDS, )), ("keys", "Keyboard", (AnnInfo.KEYS, )), ("warnings", "Warnings", (AnnInfo.WARN, )), ) def __init__(self): """Initialize decoder.""" self.reset() def reset(self): """Reset decoder and initialize instance variables.""" # Common parameters self.ss = 0 # Start sample self.es = 0 # End sample self.ssb = 0 # Start sample of an annotation transmission self.bits = [] # List of recent processed byte bits self.write = None # Flag about recent R/W command self.state = "IDLE" # Specific parameters for a device self.auto = None # Flag about current addressing self.position = 0 # Processed address position self.clear_data() def clear_data(self): """Clear data cache.""" self.display = [] # Buffer for displayed chars self.leds = [] # Buffer for displayed LEDs self.keys = [] # Buffer for pressed keys def start(self): """Actions before the beginning of the decoding.""" self.out_ann = self.register(srd.OUTPUT_ANN) def putd(self, ss, es, data): """Span data output across bit range. - Output is an annotation block from the start sample of the first bit to the end sample of the last bit. """ self.put(self.bits[ss][1], self.bits[es][2], self.out_ann, data) def putr(self, start, end=None): """Span reserved bit annotation across bit range bit by bit. - Parameters should be considered as a range, so that the end bit number is not annotated. """ annots = hlp.compose_annot(bits[AnnBits.RESERVED]) for bit in range(start, end or (start + 1)): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [AnnBits.RESERVED, annots]) def handle_command(self, data): """Detect command and call its handler.""" mask = 0 for i in range(CommandBits.MIN, CommandBits.MAX + 1): mask |= 1 << i cmd = data & mask for attr, value in vars(Command).items(): if not attr.startswith("__") and value == cmd: # Bits row - Command bits ann = cmd_annot[cmd] annots = hlp.compose_annot(bits[ann]) self.putd(CommandBits.MIN, CommandBits.MAX, [ann, annots]) # Handler fn = getattr(self, "handle_command_{}".format(attr.lower())) fn(data & ~mask) def handle_command_data(self, data): """Process data command.""" # Bits row - Reserved self.putr(DataBits.MODE + 1, CommandBits.MIN) # Bits row - Mode bit ann = (AnnBits.NORMAL, AnnBits.TEST)[data >> DataBits.MODE & 1] annots = hlp.compose_annot(bits[ann]) self.putd(DataBits.MODE, DataBits.MODE, [ann, annots]) # Bits row - Addressing bit ann = (AnnBits.AUTO, AnnBits.FIXED)[data >> DataBits.ADDR & 1] self.auto = (ann == AnnBits.AUTO) annots = hlp.compose_annot(bits[ann]) self.putd(DataBits.ADDR, DataBits.ADDR, [ann, annots]) # Bits row - Read/Write bit self.write = (data >> DataBits.RW & 1 == 0) ann = (AnnBits.READ, AnnBits.WRITE)[self.write] annots = hlp.compose_annot(bits[ann]) self.putd(DataBits.RW, DataBits.RW, [ann, annots]) # Bits row - Prohibited bit self.putr(0, DataBits.RW) def handle_command_display(self, data): """Process display command.""" # Bits row - Reserved self.putr(DisplayBits.SWITCH + 1, CommandBits.MIN) # Bits row - Switch bit ann = (AnnBits.OFF, AnnBits.ON)[data >> DisplayBits.SWITCH & 1] annots = hlp.compose_annot(bits[ann]) self.putd(DisplayBits.SWITCH, DisplayBits.SWITCH, [ann, annots]) # Bits row - PWM bits mask = 0 for i in range(DisplayBits.MIN, DisplayBits.MAX + 1): mask |= 1 << i pwm = contrasts[data & mask] ann = AnnBits.CONTRAST annots = hlp.compose_annot(bits[ann], ann_value=pwm) self.putd(DisplayBits.MIN, DisplayBits.MAX, [ann, annots]) def handle_command_address(self, data): """Process address command.""" # Bits row - Reserved self.putr(AddressBits.MAX + 1, CommandBits.MIN) # Bits row - Digit bits mask = 0 for i in range(AddressBits.MIN, AddressBits.MAX + 1): mask |= 1 << i adr = (data & mask) + 1 self.position = adr # Start address ann = AnnBits.POSITION annots = hlp.compose_annot(bits[ann], ann_value=adr) self.putd(AddressBits.MIN, AddressBits.MAX, [ann, annots]) def handle_data(self, data): """Detect display unit and call its handler.""" if self.write: sfx = ("led", "digit")[self.position % 2] fn = getattr(self, "handle_data_{}".format(sfx.lower())) else: sfx = "keyboard" fn = getattr(self, "handle_data_{}".format(sfx.lower())) fn(data) if self.auto: self.position += 1 # Automatic address adding def handle_data_digit(self, data): """Process digital tube data.""" # Bits row - Active segments bits for i in range(8): if data >> i & 1: annots = [segments[i]] self.putd(i, i, [AnnBits.DIGIT, annots]) # Register digit mask = data & ~(1 << 7) char = Params.UNKNOWN_CHAR dp = "" if mask in fonts: char = fonts[mask] if (data >> 7) & 1: dp = Params.DP self.display.append(char + dp) def handle_data_led(self, data): """Process LED data.""" # Bits row - Active LED led = Params.LEDOFF for i in range(8): if data >> i & 1: annots = hlp.compose_annot(bits[led_annot[i]]) self.putd(i, i, [AnnBits.LED, annots]) led = leds[i] # Register LED self.leds.append(led) def handle_data_keyboard(self, data): """Process key scanning data.""" # Bits row - Pressed keys for bit in range(8): if data >> bit & 1: key = keybits[bit % 4] seg = "KS{}".format(2 * self.position + (bit // 4) + 1) keytag = "{}-{}".format(key, seg) annots = [keytag] self.putd(bit, bit, [AnnBits.KEY, annots]) self.keys.append(keytag) def handle_info(self): """Process info rows.""" # Display row if self.display: ann = AnnInfo.DISPLAY val = "{}".format("".join(self.display)) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) # LEDchain row if self.leds: ann = AnnInfo.LEDS val = "{}".format("".join(self.leds)) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) # Keyboard row if self.keys: ann = AnnInfo.KEYS val = "{}".format(",".join( map(lambda key: switches[key], self.keys))) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) self.clear_data() def decode(self, ss, es, data): """Decode samples provided by parent decoder.""" cmd, databyte = data self.ss, self.es = ss, es if cmd == "BITS": """Collect packet of bits that belongs to the following command. - Packet is in the form of list of bit lists: ["BITS", bitlist] - Bit list is a list of 3 items list [[bitvalue, startsample, endsample], ...] - Samples are counted for aquisition sampling frequency. - Parent decoder ``tmc``stores individual bits in the list from the least significant bit (LSB) to the most significant bit (MSB) as it is at representing numbers in computers. """ self.bits = databyte return # State machine if self.state == "IDLE": """Wait for new transmission.""" if cmd != "START": return self.ssb = self.ss self.state = "REGISTER COMMAND" elif self.state == "REGISTER COMMAND": """Process command register.""" if cmd == "COMMAND": self.handle_command(databyte) self.state = "REGISTER DATA" elif cmd == "STOP": self.state = "IDLE" elif self.state == "REGISTER DATA": """Process data register.""" if cmd == "DATA": self.handle_data(databyte) elif cmd == "STOP": self.handle_info() self.state = "IDLE"
class Decoder(srd.Decoder): """Protocol decoder for digital ambient light sensor ``BH1750``.""" api_version = 3 id = "bh1750" name = "BH1750" longname = "Digital ambient light sensor BH1750" desc = "Digital 16bit Serial Output Type Ambient Light Sensor IC." license = "gplv2+" inputs = ["i2c"] outputs = ["bh1750"] options = ( { "id": "radix", "desc": "Number format", "default": "Hex", "values": ("Hex", "Dec", "Oct", "Bin") }, { "id": "params", "desc": "Datasheet parameter used", "default": "Typical", "values": ("Typical", "Maximal", "Minimal") }, ) annotations = hlp.create_annots({ "addr": addresses, "reg": registers, "bit": bits, "info": info, }) annotation_rows = ( ("bits", "Bits", (AnnBits.RESERVED, AnnBits.DATA)), ("regs", "Registers", tuple(range(AnnAddrs.GND, AnnRegs.DATA + 1))), ("info", "Info", tuple(range(AnnInfo.CHECK, AnnInfo.MTIME + 1))), ("warnings", "Warnings", (AnnInfo.WARN, AnnInfo.BADADD)), ) def __init__(self): """Initialize decoder.""" self.reset() def reset(self): """Reset decoder and initialize instance variables.""" # Common parameters for I2C sampling self.ss = 0 # Start sample self.es = 0 # End sample self.ssb = 0 # Start sample of an annotation transmission block self.write = None # Flag about recent write action self.state = "IDLE" # Specific parameters for a device self.addr = Address.GND # Slave address self.reg = Register.PWRDOWN # Processed register self.mode = Register.MCHIGH # Measurement mode self.mtreg = Params.MTREG_TYP # MTreg default value self.clear_data() def clear_data(self): """Clear data cache.""" self.ssd = 0 # Start sample of an annotation data block self.bytes = [] # List of recent processed bytes self.bits = [] # List of recent processed byte bits def start(self): """Actions before the beginning of the decoding.""" self.out_ann = self.register(srd.OUTPUT_ANN) def putd(self, sb, eb, data): """Span data output across bit range. - Because bits are order with MSB first, the output is an annotation block from the last sample of the start bit (sb) to the first sample of the end bit (eb). - The higher bit the lower sample number. """ self.put(self.bits[eb][1], self.bits[sb][2], self.out_ann, data) def putb(self, sb, eb=None, ann=AnnBits.RESERVED): """Span special bit annotation across bit range bit by bit. Arguments --------- sb : integer Number of the annotated start bit counting from 0. eb : integer Number of the end bit right after the last annotated bit counting from 0. If none value is provided, the method uses start value increased by 1, so that just the first bit will be annotated. ann : integer Index of the special bit's annotation in the annotations list `bits`. Default value is for reserved bit. """ annots = hlp.compose_annot(bits[ann]) for bit in range(sb, eb or (sb + 1)): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [ann, annots]) def check_addr(self, addr_slave): """Check correct slave address.""" if addr_slave in (Address.GND, Address.VCC): return True ann = AnnInfo.BADADD val = hlp.format_data(self.addr, self.options["radix"]) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ss, self.es, self.out_ann, [ann, annots]) return False def calculate_sensitivity(self): """Calculate measurement light sensitivity in lux per count.""" accuracy = prm_map_accuracy[self.options["params"]] sensitivity = 1 / accuracy * Params.MTREG_TYP / self.mtreg # lux/count if self.mode in [Register.MCHIGH2, Register.MOHIGH2]: sensitivity /= 2 return sensitivity def calculate_light(self, rawdata): """Calculate ambient light. Arguments --------- rawdata : int Content of the illuminance data register. Returns ------- float Ambient light in lux. """ light = rawdata * self.calculate_sensitivity() return light def collect_data(self, databyte): """Collect data byte to a data cache.""" if self.bytes: self.bytes.insert(0, databyte) else: self.ssd = self.ss self.bytes.append(databyte) def handle_addr(self): """Process slave address.""" if not self.bytes: return # Registers row self.addr = self.bytes[0] ann = addr_annots[self.addr] annots = hlp.compose_annot(addresses[ann]) self.put(self.ss, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_reg(self): """Process slave register and call its handler.""" if not (self.bytes and self.write): return self.reg = self.bytes[0] # Handle measurement time registers mask_mthigh = ~((1 << (MTregHighBits.MAX + 1)) - 1) if (self.reg & mask_mthigh) == Register.MTHIGH: self.handle_mtreg_high() return mask_mtlow = ~((1 << (MTregLowBits.MAX + 1)) - 1) if (self.reg & mask_mtlow) == Register.MTLOW: self.handle_mtreg_low() return # Detect measurement mode registers if self.reg in range(Register.MCHIGH, Register.MOLOW + 1): self.mode = self.reg # Registers row ann = reg_annots[self.reg] annots = hlp.compose_annot(registers[ann]) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_mtreg_high(self): """Process measurement time register with high bits.""" mask = (1 << (MTregLowBits.MAX + 1)) - 1 self.mtreg &= mask # Clear high bits mtreg = (self.reg << (MTregLowBits.MAX + 1)) & 0xff self.mtreg |= mtreg self.reg = Register.MTHIGH # Bits row - high bits bit_min = MTregHighBits.MIN bit_max = MTregHighBits.MAX + 1 self.putb(bit_min, bit_max, AnnBits.DATA) # Registers row ann = AnnRegs.MTHIGH annots = hlp.compose_annot(registers[ann]) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_mtreg_low(self): """Process measurement time register with low bits.""" mask = (1 << (MTregLowBits.MAX + 1)) - 1 self.mtreg &= ~mask # Clear low bits mtreg = self.reg & mask self.mtreg |= mtreg self.reg = Register.MTLOW # Bits row - low bits bit_min = MTregLowBits.MIN bit_max = MTregLowBits.MAX + 1 self.putb(bit_min, bit_max, AnnBits.DATA) # Registers row ann = AnnRegs.MTLOW annots = hlp.compose_annot(registers[ann]) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_nodata(self): """Process transmission without any data.""" # Info row ann = AnnInfo.CHECK annots = hlp.compose_annot(info[ann]) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_data(self): """Process read data.""" if self.write: # Info row if self.reg in [Register.MTHIGH, Register.MTLOW]: ann = AnnInfo.MTREG val = hlp.format_data(self.mtreg, self.options["radix"]) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) if self.reg in range(Register.MCHIGH, Register.MOLOW + 1): ann = AnnInfo.SENSE val = "{:.2f}".format(self.calculate_sensitivity()) unit = " {}/cnt".format(Params.UNIT_LIGHT) annots = hlp.compose_annot(info[ann], ann_value=val, ann_unit=unit) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) else: regword = (self.bytes[1] << 8) + self.bytes[0] # Registers row ann = AnnRegs.DATA annots = hlp.compose_annot(registers[ann]) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) # # Info row ann = AnnInfo.LIGHT val = "{:.2f}".format(self.calculate_light(regword)) unit = " {}".format(Params.UNIT_LIGHT) annots = hlp.compose_annot(info[ann], ann_value=val, ann_unit=unit) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) self.clear_data() def decode(self, ss, es, data): """Decode samples provided by parent decoder.""" cmd, databyte = data self.ss, self.es = ss, es if cmd == "BITS": """Collect packet of bits that belongs to the following command. - Packet is in the form of list of bit lists: ["BITS", bitlist] - Bit list is a list of 3 items list [[bitvalue, startsample, endsample], ...] - Samples are counted for aquisition sampling frequency. - Parent decoder ``i2c``stores individual bits in the list from the least significant bit (LSB) to the most significant bit (MSB) as it is at representing numbers in computers, although I2C bus transmits data in oposite order with MSB first. """ self.bits = databyte + self.bits return # State machine if self.state == "IDLE": """Wait for an I2C transmission.""" if cmd != "START": return self.ssb = self.ss self.state = "ADDRESS SLAVE" elif self.state == "ADDRESS SLAVE": """Wait for a slave address.""" if cmd in ["ADDRESS WRITE", "ADDRESS READ"]: if self.check_addr(databyte): self.collect_data(databyte) self.handle_addr() if cmd == "ADDRESS READ": self.write = False elif cmd == "ADDRESS WRITE": self.write = True self.state = "REGISTER ADDRESS" else: self.state = "IDLE" elif self.state == "REGISTER ADDRESS": """Process slave register.""" if cmd in ["DATA WRITE", "DATA READ"]: self.collect_data(databyte) self.handle_reg() self.state = "REGISTER DATA" elif cmd in ["STOP", "START REPEAT"]: """End of transmission without any register and data.""" self.handle_nodata() self.state = "IDLE" elif self.state == "REGISTER DATA": """Process data of a slave register. - Individual command or data can end either with repeated start condition or with stop condition. """ if cmd in ["DATA WRITE", "DATA READ"]: self.collect_data(databyte) elif cmd == "START REPEAT": """Output read data and continue in transmission.""" self.handle_data() self.ssb = self.ss self.state = "ADDRESS SLAVE" elif cmd == "STOP": """Output formatted string with register data. - This is end of an I2C transmission. Start waiting for another one. """ self.handle_data() self.state = "IDLE"
class Decoder(srd.Decoder): """Protocol decoder for real time clock chip ``DS1307``.""" api_version = 3 id = "ds1307" name = "DS1307" longname = "Dallas DS1307 RTC chip" desc = "Real time clock chip protocol decoder, v 1.0.0." license = "gplv2+" inputs = ["i2c"] outputs = ["ds1307"] options = ({ "id": "radix", "desc": "Number format", "default": "Hex", "values": ("Hex", "Dec", "Oct", "Bin") }, { "id": "start_weekday", "desc": "The first day of the week", "default": "Monday", "values": weekdays }, { "id": "date_format", "desc": "Date format", "default": "European", "values": ("European", "American", "ANSI") }) annotations = hlp.create_annots({ "addr": addresses, "reg": registers, "bit": bits, "info": info, }) annotation_rows = ( ("bits", "Bits", tuple(range(AnnBits.RESERVED, AnnBits.NVRAM + 1))), ("regs", "Registers", tuple(range(AnnAddrs.SLAVE, AnnRegs.NVRAM + 1))), ("datetime", "Datetime", (AnnInfo.DATETIME, AnnInfo.NVRAM)), ("warnings", "Warnings", (AnnInfo.WARN, AnnInfo.BADADD)), ) def __init__(self): """Initialize decoder.""" self.reset() def reset(self): """Reset decoder and initialize instance variables.""" # Common parameters for I2C sampling self.ss = 0 # Start sample self.es = 0 # End sample self.ssb = 0 # Start sample of an annotation transmission block self.write = True # Flag about recent write action (default write) self.state = "IDLE" # Specific parameters for a device self.addr = Address.SLAVE self.reg = -1 self.second = -1 self.minute = -1 self.hour = -1 self.weekday = -1 self.day = -1 self.month = -1 self.year = -1 self.clear_data() def clear_data(self): """Clear data cache.""" self.ssd = 0 self.bits = [] self.bytes = [] def start(self): """Actions before the beginning of the decoding.""" self.out_ann = self.register(srd.OUTPUT_ANN) def putd(self, sb, eb, data): """Span data output across bit range. - Because bits are order with MSB first, the output is an annotation block from the last sample of the start bit (sb) to the first sample of the end bit (eb). - The higher bit the lower sample number. """ self.put(self.bits[eb][1], self.bits[sb][2], self.out_ann, data) # self.put(self.bits[ss][1], self.bits[es][2], self.out_ann, data) def putb(self, sb, eb=None, ann=AnnBits.RESERVED): """Span special bit annotation across bit range bit by bit. Arguments --------- sb : integer Number of the annotated start bit counting from 0. eb : integer Number of the end bit right after the last annotated bit counting from 0. If none value is provided, the method uses start value increased by 1, so that just the first bit will be annotated. ann : integer Index of the special bit's annotation in the annotations list `bits`. Default value is for reserved bit. """ annots = hlp.compose_annot(bits[ann]) for bit in range(sb, eb or (sb + 1)): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [ann, annots]) def check_addr(self, addr_slave): """Check correct slave address.""" if addr_slave == Address.SLAVE: return True ann = AnnInfo.BADADD val = hlp.format_data(self.addr, self.options["radix"]) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ss, self.es, self.out_ann, [ann, annots]) return False def collect_data(self, databyte): """Collect data byte to a data cache.""" if self.bytes: self.bytes.insert(0, databyte) else: self.ssd = self.ss self.bytes.append(databyte) def format_rw(self): """Format read/write action.""" act = (AnnInfo.READ, AnnInfo.WRITE)[self.write] return info[act] def output_datetime(self): """Format datetime string and prefix it by recent r/w operation. - Applied decoder options for the starting weekday and date format. - Datetime parts are numbered in the format string equally to numbering of time keeping registers. """ if self.options["date_format"] == "European": format_datetime =\ "{3:s} {4:02d}.{5:02d}.{6:04d} {2:02d}:{1:02d}:{0:02d}" elif self.options["date_format"] == "American": format_datetime =\ "{3:s}, {5:02d}/{4:02d}/{6:04d} {2:02d}:{1:02d}:{0:02d}" elif self.options["date_format"] == "ANSI": format_datetime =\ "{6:04d}-{4:02d}-{5:02d}T{2:02d}:{1:02d}:{0:02d}" else: format_datetime = "Unknown format" dt_str = format_datetime.format( self.second, self.minute, self.hour, weekdays[self.weekday], self.day, self.month, self.year, ) # Info row ann = AnnInfo.DATETIME val = dt_str act = self.format_rw() annots = hlp.compose_annot(info[ann], ann_value=val, ann_action=act) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_address(self): """Process slave address.""" if not self.bytes: return # Registers row ann = AnnAddrs.SLAVE annots = hlp.compose_annot(addresses[ann]) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_pointer(self): """Process register pointer.""" # Registers row ann = AnnRegs.POINTER val = hlp.format_data(self.reg, self.options["radix"]) act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_value=val, ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_nodata(self): """Process transmission without any data.""" # Info row ann = AnnInfo.CHECK annots = hlp.compose_annot(info[ann]) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_reg(self): """Create name and call corresponding slave registers handler. - Honor auto increment of the register at reading. - When the address reaches maximal nvram position, it will wrap around to address 0. """ reg = self.reg if self.reg < NvRAM.MIN else NvRAM.MAX fn = getattr(self, "handle_reg_{:#04x}".format(reg)) fn(self.bytes[0]) self.reg += 1 # Address auto increment if self.reg > NvRAM.MAX: # Address rollover self.reg = 0 self.clear_data() def handle_reg_0x00(self, databyte): """Process seconds (0-59) and Clock halt bit.""" # Bits row - Clock Halt bit ch = databyte >> TimeBits.CH & 1 ch_l = ("Run", "Halt")[ch] ch_s = ch_l[0].upper() ann = AnnBits.CH val = [ch, ch_l, ch_s] annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(TimeBits.CH, TimeBits.CH, [ann, annots]) # Bits row - Second bits self.second = hlp.bcd2int(databyte & ~(1 << TimeBits.CH)) ann = AnnBits.SECOND val = self.second annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, TimeBits.CH - 1, [ann, annots]) # Registers row ann = AnnRegs.SECOND act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x01(self, databyte): """Process minutes (0-59).""" # Bits row self.putb(7) self.minute = hlp.bcd2int(databyte & 0x7f) ann = AnnBits.MINUTE val = self.minute annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, 6, [ann, annots]) # Registers row ann = AnnRegs.MINUTE act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x02(self, databyte): """Process hours (1-12+AM/PM or 0-23) and 12/24 hours mode. - In case of 12 hours mode convert hours to 24 hours mode to instance variable for formatting. """ # Bits row self.putb(7) mode12h = databyte >> TimeBits.MODE & 1 if mode12h: # Bits row - 12h mode ann = AnnBits.MODE val = "12h" annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(TimeBits.MODE, TimeBits.MODE, [ann, annots]) # Bits row - AM/PM mode pm = databyte >> TimeBits.AMPM & 1 pm_l = ("AM", "PM")[pm] pm_s = pm_l[0].upper() ann = AnnBits.AMPM val = [pm, pm_l, pm_s] annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(TimeBits.AMPM, TimeBits.AMPM, [ann, annots]) # Bits row - hours self.hour = hlp.bcd2int(databyte & 0x1f) # Convert to 24h expression self.hour %= 12 if pm: self.hour += 12 ann = AnnBits.HOUR val = self.hour annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, TimeBits.AMPM - 1, [ann, annots]) else: # Bits row - 24h mode ann = AnnBits.MODE val = "24h" annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(TimeBits.MODE, TimeBits.MODE, [ann, annots]) # Bits row - hours self.hour = hlp.bcd2int(databyte & 0x3f) ann = AnnBits.HOUR val = self.hour annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, TimeBits.MODE - 1, [ann, annots]) # Registers row ann = AnnRegs.HOUR act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x03(self, databyte): """Process weekday (1-7). - Recalculate weekday in respect to starting weekday option to instance variable for formatting. """ # Bits row - reserved self.putb(3, 8) # Bits row - calculate weekday self.weekday = hlp.bcd2int(databyte & 0x07) start_weekday_index = 0 for i, weekday in enumerate(weekdays): if weekday == self.options["start_weekday"]: start_weekday_index = i break start_weekday_index += self.weekday - 1 start_weekday_index %= 7 self.weekday = start_weekday_index weekday = weekdays[self.weekday] # Bits row - weekday ann = AnnBits.WEEKDAY val = weekday annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, 2, [ann, annots]) # Registers row ann = AnnRegs.WEEKDAY act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x04(self, databyte): """Process day (1-31).""" # Bits row self.putb(6, 8) self.day = hlp.bcd2int(databyte & 0x3f) ann = AnnBits.DAY val = self.day annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, 5, [ann, annots]) # Registers row ann = AnnRegs.DAY act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x05(self, databyte): """Process month (1-12).""" # Bits row self.putb(5, 8) self.month = hlp.bcd2int(databyte & 0x1f) ann = AnnBits.MONTH val = months[self.month] annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, 4, [ann, annots]) # Registers row ann = AnnRegs.MONTH act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x06(self, databyte): """Process year (0-99). - Add 2000 to double digit year number (expect 21st century) to instance variable for formatting. """ # Bits row self.year = hlp.bcd2int(databyte & 0xff) + 2000 ann = AnnBits.YEAR val = self.year annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, 7, [ann, annots]) # Registers row ann = AnnRegs.YEAR act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x07(self, databyte): """Process control register.""" # Bits row - Reserved bits self.putb(2, 4) self.putb(5, 7) # Bits row - OUT bit out = databyte >> ControlBits.OUT & 1 ann = AnnBits.OUT annots = hlp.compose_annot(bits[ann], ann_value=out) self.putd(ControlBits.OUT, ControlBits.OUT, [ann, annots]) # Bits row - SQWE bit sqwe = databyte >> ControlBits.SQWE & 1 sqwe_l = ("dis", "en")[sqwe] + "abled" sqwe_s = sqwe_l[0].upper() ann = AnnBits.SQWE val = [sqwe, sqwe_l, sqwe_s] annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(ControlBits.SQWE, ControlBits.SQWE, [ann, annots]) # Bits row - RS bits rate = rates[databyte & 0x03] ann = AnnBits.RS0 val = rate unit = Params.UNIT_HZ annots = hlp.compose_annot(bits[ann], ann_value=val, ann_unit=unit) val //= 1000 unit = Params.UNIT_KHZ annots_add = hlp.compose_annot(bits[ann], ann_value=val, ann_unit=unit) annots.extend(annots_add) self.putd(ControlBits.RS0, ControlBits.RS1, [ann, annots]) # Registers row ann = AnnRegs.CONTROL act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def handle_reg_0x3f(self, databyte): """Process NVRAM.""" # Bits row ann = AnnBits.NVRAM val = hlp.format_data(databyte, self.options["radix"]) annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(0, 7, [ann, annots]) # Registers row ann = AnnRegs.NVRAM act = self.format_rw() annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) def decode(self, ss, es, data): """Decode samples provided by parent decoder.""" cmd, databyte = data self.ss, self.es = ss, es if cmd == "BITS": """Collect packet of bits that belongs to the following command. - Packet is in the form of list of bit lists: ["BITS", [[bit, startsample, endsample], ...] - Samples are counted for aquisition sampling frequency. - Parent decoder ``i2c``stores individual bits in the list from the least significant bit (LSB) to the most significant bit (MSB) as it is at representing numbers in computers, although I2C bus transmits data in oposite order with MSB first. """ self.bits = databyte + self.bits return # State machine if self.state == "IDLE": """Wait for an I2C transmission.""" if cmd != "START": return self.ssb = self.ss self.state = "ADDRESS SLAVE" elif self.state == "ADDRESS SLAVE": """Wait for a slave address.""" if cmd in ["ADDRESS WRITE", "ADDRESS READ"]: if self.check_addr(databyte): self.collect_data(databyte) self.handle_address() if cmd == "ADDRESS READ": self.write = False self.state = "REGISTER DATA" elif cmd == "ADDRESS WRITE": self.write = True self.state = "REGISTER ADDRESS" else: self.state = "IDLE" elif self.state == "REGISTER ADDRESS": """Initial slave register""" if cmd == "DATA WRITE": self.reg = databyte self.collect_data(databyte) self.handle_pointer() self.state = "REGISTER DATA" elif cmd == "STOP": """Output end of transmission without any register and data.""" self.handle_nodata() self.state = "IDLE" elif self.state == "REGISTER DATA": """Process slave register""" if cmd in ["DATA WRITE", "DATA READ"]: self.collect_data(databyte) self.handle_reg() self.state = "REGISTER DATA" elif cmd == "START REPEAT": self.state = "ADDRESS SLAVE" elif cmd == "STOP": """Wait for next transmission.""" self.output_datetime() self.state = "IDLE"
class Decoder(srd.Decoder): """Protocol decoder for digital temperature sensor ``TMP102``.""" api_version = 3 id = "tmp102" name = "TMP102" longname = "Digital temperature sensor TMP102" desc = "Low power digital temperature sensor." license = "gplv2+" inputs = ["i2c"] outputs = ["tmp102"] options = ( { "id": "radix", "desc": "Number format", "default": "Hex", "values": ("Hex", "Dec", "Oct", "Bin") }, { "id": "units", "desc": "Temperature unit", "default": "Celsius", "values": ("Celsius", "Fahrenheit", "Kelvin") }, ) annotations = hlp.create_annots({ "addr": addresses, "reg": registers, "bit": bits, "info": info, }) annotation_rows = ( ("bits", "Bits", tuple(range(AnnBits.RESERVED, AnnBits.OS + 1))), ("regs", "Registers", tuple(range(AnnAddrs.GC, AnnRegs.THIGH + 1))), ("info", "Info", tuple(range(AnnInfo.GRST, AnnInfo.THIGH + 1))), ("warnings", "Warnings", (AnnInfo.WARN, AnnInfo.BADADD)), ) def __init__(self): """Initialize decoder.""" self.reset() def reset(self): """Reset decoder and initialize instance variables.""" # Common parameters for I2C sampling self.ss = 0 # Start sample self.es = 0 # End sample self.ssb = 0 # Start sample of an annotation transmission block self.write = True # Flag about recent write action (default write) self.state = "IDLE" # Specific parameters for a device self.addr = Address.GND # Slave address (default ADD0 grounded) self.reg = Register.TEMP # Processed slave register (default temp) self.em = False # Flag about extended mode (default Normal) self.clear_data() def clear_data(self): """Clear data cache.""" self.ssd = 0 # Start sample of an annotation data block self.bytes = [] # List of recent processed bytes self.bits = [] # List of recent processed byte bits def start(self): """Actions before the beginning of the decoding.""" self.out_ann = self.register(srd.OUTPUT_ANN) def putd(self, sb, eb, data): """Span data output across bit range. - Because bits are order with MSB first, the output is an annotation block from the last sample of the start bit (sb) to the first sample of the end bit (eb). - The higher bit the lower sample number. """ self.put(self.bits[eb][1], self.bits[sb][2], self.out_ann, data) def putb(self, sb, eb=None, ann=AnnBits.RESERVED): """Span special bit annotation across bit range bit by bit. Arguments --------- sb : integer Number of the annotated start bit counting from 0. eb : integer Number of the end bit right after the last annotated bit counting from 0. If none value is provided, the method uses start value increased by 1, so that just the first bit will be annotated. ann : integer Index of the special bit's annotation in the annotations list `bits`. Default value is for reserved bit. """ annots = hlp.compose_annot(bits[ann]) for bit in range(sb, eb or (sb + 1)): self.put(self.bits[bit][1], self.bits[bit][2], self.out_ann, [ann, annots]) def check_addr(self, addr_slave, check_gencall=False): """Check correct slave address or general call.""" if addr_slave in ( Address.GND, Address.VCC, Address.SDA, Address.SCL, ) or not check_gencall or addr_slave == GeneralCall.ADDRESS: return True ann = AnnInfo.BADADD val = hlp.format_data(self.addr, self.options["radix"]) annots = hlp.compose_annot(info[ann], ann_value=val) self.put(self.ss, self.es, self.out_ann, [ann, annots]) return False def calculate_temperature(self, rawdata): """Calculate and convert temperature. Arguments --------- rawdata : int Content of the temperature, TLOW, or THIGH register. Returns ------- tuple: float, string Temperature and unit in a scale determined by corresponding decoder option. """ if rawdata & (1 << TempBits.EM): self.em = True # Extended mode (13-bit resolution) if self.em: rawdata >>= 3 if rawdata > 0x0fff: rawdata |= 0xe000 # 2s complement # Normal mode (12-bit resolution) else: rawdata >>= 4 if rawdata > 0x07ff: rawdata |= 0xf000 # 2s complement temperature = rawdata / 16 # Celsius if self.options["units"] == "Fahrenheit": temperature *= 9 / 5 temperature += 32 elif self.options["units"] == "Kelvin": temperature += 273.15 # Measurement unit unit = " {}".format(temp_units[self.options["units"]]) return temperature, unit def collect_data(self, databyte): """Collect data byte to a data cache.""" if self.bytes: self.bytes.insert(0, databyte) else: self.ssd = self.ss self.bytes.append(databyte) def format_rw(self): """Format read/write action.""" act = (AnnInfo.READ, AnnInfo.WRITE)[self.write] return info[act] def handle_addr(self): """Process slave address.""" if not self.bytes: return # Registers row self.addr = self.bytes[0] ann = addr_annots[self.addr] annots = hlp.compose_annot(addresses[ann]) self.put(self.ss, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_reg(self): """Process slave register.""" if not self.bytes: return self.reg = self.bytes[0] if self.addr == GeneralCall.ADDRESS: ann = reg_annots_gc[self.reg] act = None else: ann = reg_annots[self.reg] act = info[AnnInfo.SELECT] annots = hlp.compose_annot(registers[ann], ann_action=act) self.put(self.ss, self.es, self.out_ann, [ann, annots]) self.clear_data() def handle_nodata(self): """Process transmission without any data.""" # Info row ann = AnnInfo.CHECK annots = hlp.compose_annot(info[ann]) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_data(self): """Create name and call corresponding data register handler.""" fn = getattr(self, "handle_datareg_{:#04x}".format(self.reg)) dataword = ((self.bytes[1] << 8) + self.bytes[0]) if (self.bytes) \ else None fn(dataword) self.clear_data() def handle_datareg_0x06(self, dataword): """Process general reset register.""" # Info row ann = AnnInfo.GRST annots = hlp.compose_annot(info[ann]) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_datareg_0x01(self, dataword): """Process configuration register.""" # Bits row - OS bit - one-shot measurement os = dataword >> ConfigBits.OS & 1 os_l = ("dis", "en")[os] + "abled" os_s = os_l[0].upper() ann = AnnBits.OS annots = hlp.compose_annot(bits[ann], [os, os_l, os_s]) self.putd(ConfigBits.OS, ConfigBits.OS, [ann, annots]) # Bits row - R0/R1 bits - converter resolution res = resolutions[dataword >> ConfigBits.R0 & 0b11] ann = AnnBits.R0 val = "{}".format(res) annots = hlp.compose_annot(bits[ann], ann_value=val, ann_unit="bit") self.putd(ConfigBits.R0, ConfigBits.R1, [ann, annots]) # Bits row - F0/F1 bits - fault queue flt = faults[dataword >> ConfigBits.F0 & 0b11] ann = AnnBits.F0 val = "{}".format(flt) annots = hlp.compose_annot(bits[ann], ann_value=val) self.putd(ConfigBits.F0, ConfigBits.F1, [ann, annots]) # Bits row - POL bit - polarity, alert active pol = dataword >> ConfigBits.POL & 1 pol_l = ("low", "high")[pol] pol_s = pol_l[0].upper() ann = AnnBits.POL annots = hlp.compose_annot(bits[ann], ann_value=[pol, pol_l, pol_s]) self.putd(ConfigBits.POL, ConfigBits.POL, [ann, annots]) # Bits row - TM bit - thermostat mode tm = dataword >> ConfigBits.TM & 1 tm_l = ("comparator", "interrupt")[tm] tm_s = tm_l[0].upper() ann = AnnBits.TM annots = hlp.compose_annot(bits[ann], ann_value=[tm, tm_l, tm_s]) self.putd(ConfigBits.TM, ConfigBits.TM, [ann, annots]) # Bits row - SD bit - shutdown mode sd = dataword >> ConfigBits.SD & 1 sd_l = ("dis", "en")[sd] + "abled" sd_s = sd_l[0].upper() ann = AnnBits.SD annots = hlp.compose_annot(bits[ann], ann_value=[sd, sd_l, sd_s]) self.putd(ConfigBits.SD, ConfigBits.SD, [ann, annots]) # Bits row - CR0/CR1 bits - conversion rate rate = rates[dataword >> ConfigBits.CR0 & 0b11] ann = AnnBits.CR0 annots = hlp.compose_annot(bits[ann], ann_value=rate, ann_unit="Hz") self.putd(ConfigBits.CR0, ConfigBits.CR1, [ann, annots]) # Bits row - AL bit - alert al = dataword >> ConfigBits.AL & 1 al_l = ("", "in")[al ^ pol] + "active" al_s = al_l[0].upper() ann = AnnBits.AL annots = hlp.compose_annot(bits[ann], ann_value=[al, al_l, al_s]) self.putd(ConfigBits.AL, ConfigBits.AL, [ann, annots]) # Bits row - EM bit - extended mode em = dataword >> ConfigBits.EM & 1 self.em = bool(em) em_l = ("dis", "en")[em] + "abled" em_s = em_l[0].upper() ann = AnnBits.EM annots = hlp.compose_annot(bits[ann], ann_value=[em, em_l, em_s]) self.putd(ConfigBits.EM, ConfigBits.EM, [ann, annots]) # Bits row - reserved bits for i in range(ConfigBits.EM - 1, -1, -1): self.putb(i) # Registers row ann = AnnRegs.CONF val = hlp.format_data(dataword, self.options["radix"]) annots = hlp.compose_annot(registers[ann], ann_value=val) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) # Info row ann = AnnInfo.CONF val = info[prm_annots[(Params.CUSTOM, dataword)[dataword == Params.POWERUP]]] act = self.format_rw() annots = hlp.compose_annot(info[ann], ann_value=val, ann_action=act) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_datareg_0x00(self, dataword): """Process temperature register.""" temp, unit = self.calculate_temperature(dataword) # Bits row - EM bit - extended mode em = int(self.em) em_l = ("dis", "en")[self.em] + "abled" em_s = em_l[0].upper() ann = AnnBits.EM annots = hlp.compose_annot(bits[ann], [em, em_l, em_s]) self.putd(TempBits.EM, TempBits.EM, [ann, annots]) # Bits row - reserved bits res_bits = (3, 2)[self.em] bit_min = TempBits.RESERVED bit_max = bit_min + res_bits self.putb(bit_min, bit_max) # Bits row - data bits data_bits = 8 * len(self.bytes) - 1 - res_bits bit_min = bit_max bit_max = bit_min + data_bits self.putb(bit_min, bit_max, AnnBits.DATA) # Registers row ann = AnnRegs.TEMP val = hlp.format_data(dataword, self.options["radix"]) annots = hlp.compose_annot(registers[ann], ann_value=val) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) # Info row ann = AnnInfo.TEMP annots = hlp.compose_annot(info[ann], ann_value=temp, ann_unit=unit) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_datareg_0x02(self, dataword): """Process TLOW register.""" temp, unit = self.calculate_temperature(dataword) # Registers row ann = AnnRegs.TLOW val = hlp.format_data(dataword, self.options["radix"]) annots = hlp.compose_annot(registers[ann], ann_value=val) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) # Info row ann = AnnInfo.TLOW act = self.format_rw() annots = hlp.compose_annot(info[ann], ann_value=temp, ann_unit=unit, ann_action=act) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def handle_datareg_0x03(self, dataword): """Process THIGH register.""" temp, unit = self.calculate_temperature(dataword) # Registers row ann = AnnRegs.THIGH val = hlp.format_data(dataword, self.options["radix"]) annots = hlp.compose_annot(registers[ann], ann_value=val) self.put(self.ssd, self.es, self.out_ann, [ann, annots]) # Info row ann = AnnInfo.THIGH act = self.format_rw() annots = hlp.compose_annot(info[ann], ann_value=temp, ann_unit=unit, ann_action=act) self.put(self.ssb, self.es, self.out_ann, [ann, annots]) def decode(self, ss, es, data): """Decode samples provided by parent decoder.""" cmd, databyte = data self.ss, self.es = ss, es if cmd == "BITS": """Collect packet of bits that belongs to the following command. - Packet is in the form of list of bit lists: ["BITS", bitlist] - Bit list is a list of 3 items list [[bitvalue, startsample, endsample], ...] - Samples are counted for aquisition sampling frequency. - Parent decoder ``i2c``stores individual bits in the list from the least significant bit (LSB) to the most significant bit (MSB) as it is at representing numbers in computers, although I2C bus transmits data in oposite order with MSB first. """ self.bits = databyte + self.bits return # State machine if self.state == "IDLE": """Wait for an I2C transmission.""" if cmd != "START": return self.ssb = self.ss self.state = "ADDRESS SLAVE" elif self.state == "ADDRESS SLAVE": """Wait for a slave address.""" if cmd in ["ADDRESS WRITE", "ADDRESS READ"]: if self.check_addr(databyte, check_gencall=True): self.collect_data(databyte) self.handle_addr() if cmd == "ADDRESS READ": self.write = False self.state = "REGISTER DATA" elif cmd == "ADDRESS WRITE": self.write = True self.state = "REGISTER ADDRESS" else: self.state = "IDLE" elif self.state == "REGISTER ADDRESS": """Process slave register""" if cmd in ["DATA WRITE", "DATA READ"]: self.collect_data(databyte) self.handle_reg() self.state = "REGISTER DATA" elif cmd in ["STOP", "START REPEAT"]: """Output end of transmission without any register and data.""" self.handle_nodata() self.state = "IDLE" elif self.state == "REGISTER DATA": """Process data of a slave register. - Individual command or data can end either with repeated start condition or with stop condition. """ if cmd in ["DATA WRITE", "DATA READ"]: self.collect_data(databyte) elif cmd == "START REPEAT": self.state = "ADDRESS SLAVE" elif cmd == "STOP": """Output formatted string with register data. - This is end of an I2C transmission. Start waiting for another one. """ self.handle_data() self.state = "IDLE"