From ab9b04f4f0fe09ab71a1f5053d16288595bdd5d2 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Sun, 9 Aug 2020 01:04:40 -0300 Subject: [PATCH 1/3] Add pcf8574 / hd44780 sigrok decoders --- .gitignore | 1 + Hardware/sigrok-decoders/hd44780/__init__.py | 24 ++ Hardware/sigrok-decoders/hd44780/pd.py | 233 +++++++++++++++++++ Hardware/sigrok-decoders/pcf8574/__init__.py | 24 ++ Hardware/sigrok-decoders/pcf8574/pd.py | 125 ++++++++++ 5 files changed, 407 insertions(+) create mode 100644 .gitignore create mode 100644 Hardware/sigrok-decoders/hd44780/__init__.py create mode 100644 Hardware/sigrok-decoders/hd44780/pd.py create mode 100644 Hardware/sigrok-decoders/pcf8574/__init__.py create mode 100644 Hardware/sigrok-decoders/pcf8574/pd.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/Hardware/sigrok-decoders/hd44780/__init__.py b/Hardware/sigrok-decoders/hd44780/__init__.py new file mode 100644 index 0000000..3c17561 --- /dev/null +++ b/Hardware/sigrok-decoders/hd44780/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Lucas Teske +## +## 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 . +## + +''' +HD44780 is a LCD Controller +''' + +from .pd import Decoder diff --git a/Hardware/sigrok-decoders/hd44780/pd.py b/Hardware/sigrok-decoders/hd44780/pd.py new file mode 100644 index 0000000..23b9bd8 --- /dev/null +++ b/Hardware/sigrok-decoders/hd44780/pd.py @@ -0,0 +1,233 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Lucas Teske +## +## 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 sigrokdecode as srd + + +CMD_CLEAR_DISPLAY = 0x01 +CMD_RETURN_HOME = 0x02 +CMD_ENTRY_MODE_SET = 0x04 +CMD_DISPLAY_CONTROL = 0x08 +CMD_CURSOR_SHIFT = 0x10 +CMD_FUNCTION_SET = 0x20 +CMD_SET_CGRAM_ADDR = 0x40 +CMD_SET_DDRAM_ADDR = 0x80 + +commands_desc = { + CMD_CLEAR_DISPLAY: "Clear Display", + CMD_RETURN_HOME: "Return Home", + CMD_ENTRY_MODE_SET: "Entry Mode Set", + CMD_DISPLAY_CONTROL: "Display Control", + CMD_CURSOR_SHIFT: "Cursor Shift", + CMD_FUNCTION_SET: "Function Set", + CMD_SET_CGRAM_ADDR: "Set cgram address", + CMD_SET_DDRAM_ADDR: "Set ddram address", +} + +command_list = [ + # From higher to lower + CMD_SET_DDRAM_ADDR, + CMD_SET_CGRAM_ADDR, + CMD_FUNCTION_SET, + CMD_CURSOR_SHIFT, + CMD_DISPLAY_CONTROL, + CMD_ENTRY_MODE_SET, + CMD_RETURN_HOME, + CMD_CLEAR_DISPLAY, +] + +def get_cmd(opcode): + got_cmd = 0x00 + for idx in range(len(command_list)): + cmd = command_list[idx] + if opcode & cmd == cmd: + got_cmd = cmd + break + return got_cmd + +def get_cmd_description(opcode): + got_cmd = get_cmd(opcode) + if got_cmd == 0: + return "CMD 0x%02x" % opcode + + cmd_str = commands_desc[got_cmd] + if got_cmd == CMD_SET_DDRAM_ADDR: + addr = opcode & ~(got_cmd) + cmd_str += " 0x%02x" % addr + elif got_cmd == CMD_SET_CGRAM_ADDR: + addr = opcode & ~(got_cmd) + cmd_str += " 0x%02x" % addr + return cmd_str + +class Decoder(srd.Decoder): + api_version = 3 + id = 'hd44780' + name = 'HD44780' + longname = 'HD44780 LCD' + desc = 'A HD44780 LCD Decoder' + license = 'gplv2+' + inputs = ['logic'] + outputs = [] + tags = ['LCD'] + options = () + annotations = ( + ('text', 'Text'), # 0 + ('warning', 'Warning'), # 1 + ('data-write-nibble', 'Data nibble write'), # 2 + ('cmd-write-nibble', 'Command nibble write'), # 3 + ('data-write', 'Data write'), # 4 + ('cmd-write', 'Command write'), # 5 + ('lcd_rs', 'LCD Command / Data'), # 6 + ('lcd_en', 'LCD Enable'), # 7 + ('lcd_rw', 'LCD Read / Write'), # 8 + ) + annotation_rows = ( + ('lcd-rs', 'RS', (6,)), + ('lcd-en', 'Enable', (7,)), + ('lcd-rw', 'R/W', (8,)), + ('data-write-nibble', 'Write Nibble', (2, 3)), + ('write', 'Write', (4,5)), + ) + binary = ( + ('data-read', 'Data read'), + ('data-write', 'Data write'), + ) + options = ( + {'id': 'lcd_mode', 'desc': 'LCD Mode', + 'default': '4bit', 'values': ('4bit', '8bit')}, + ) + + def __init__(self): + self.reset() + + def reset(self): + self.state = 'IDLE' + self.nibble_start = 0 + self.nibbles = [0x0, 0x0] + self.current_nibble = 0 + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.out_binary = self.register(srd.OUTPUT_BINARY) + + def putp(self, data): + self.put(self.ss, self.es, self.out_python, data) + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def putb(self, data): + self.put(self.ss, self.es, self.out_binary, data) + + def report_nibble(self, ss, es, is_cmd, nibble, data): + self.put(ss, es, self.out_ann, [2 if not is_cmd else 3, ["NIBBLE %d = 0x%x" % (nibble, data)]]) + + def report_rs(self, ss, es, lcd_rs): + self.put(ss, es, self.out_ann, [6, ["CMD" if lcd_rs == 0 else "DATA"]]) + + def report_en(self, ss, es, lcd_en): + self.put(ss, es, self.out_ann, [7, ["%d" % (lcd_en)]]) + + def report_rw(self, ss, es, lcd_rw): + self.put(ss, es, self.out_ann, [8, ["READ" if lcd_rw == 1 else "WRITE"]]) + + def update_data(self): + self.data = (self.nibbles[0] << 4) + self.nibbles[1] + + def to_byte(self, data): + databyte = 0 + num_bits = len(data) + for i in range(num_bits): + bit = data[i][0] + databyte += bit << (num_bits-1-i) + return databyte + + def process_data(self): + self.ss = self.data_start + self.es = self.data_end + if (self.data >= 0x20 and self.data <= 0x7E): + # Printable characters + self.putx([4, ["'%s'" % chr(self.data)]]) + elif (self.data == ord('\n')): + # Line Break + self.putx([4, ["\\n" % chr(self.data)]]) + else: + self.putx([4, ["0x%02x" % self.data]]) + #print("HD44780: DATA 0x%02x" % self.data) + + def process_cmd(self): + self.ss = self.data_start + self.es = self.data_end + self.putx([5, [get_cmd_description(self.data)]]) + #print("HD44780: CMD 0x%02x" % self.data) + + def process_enable_low(self, ss, es, lcd_rs, lcd_rw, lcd_e, lcd_data): + self.nibbles[self.current_nibble] = lcd_data + self.report_nibble(self.nibble_start, es, lcd_rs == 0, self.current_nibble, lcd_data) + self.state = 'IDLE' + self.current_nibble += 1 + if self.current_nibble == 2: # Last nibble, set the end and process + self.current_nibble = 0 + self.data_end = es + self.update_data() # Update data + if lcd_rs == 0: # Command + self.process_cmd() + else: # Data + self.process_data() + + def process4bit(self, ss, es, data): + #print("HD44780(4B): ", data) + # LCD: | RS | RW | E | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | + # IN: | D7 | D6 | D5 | NC | NC | NC | NC | D0 | D1 | D2 | D3 | + if len(data) < 8: + print("ERROR") + return + + # Received bits are MSB-First. So inverted related to DX notation + lcd_rs = data[0][0] + lcd_rw = data[1][0] + lcd_e = data[2][0] + lcd_data = self.to_byte([data[7], data[6], data[5], data[4]]) + + self.report_rs(ss, es, lcd_rs) + self.report_rw(ss, es, lcd_rw) + self.report_en(ss, es, lcd_e) + + if lcd_e == 0: # The LCD Latches the data on lower edge + if self.state == 'NIBBLE': + self.process_enable_low(ss, es, lcd_rs, lcd_rw, lcd_e, lcd_data) + else: + self.nibble_start = ss + if self.state == 'IDLE': + if self.current_nibble == 0: + self.data_start = ss + self.state = 'NIBBLE' + + def process8bit(self, ss, es, data): + # TODO + print("HD44780(8B): ", data) + pass + + def decode(self, ss, es, data): + if self.options['lcd_mode'] == '4bit': + self.process4bit(ss, es, data) + else: + self.process8bit(ss, es, data) diff --git a/Hardware/sigrok-decoders/pcf8574/__init__.py b/Hardware/sigrok-decoders/pcf8574/__init__.py new file mode 100644 index 0000000..280fa3b --- /dev/null +++ b/Hardware/sigrok-decoders/pcf8574/__init__.py @@ -0,0 +1,24 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Lucas Teske +## +## 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 . +## + +''' +PCF8574 is a I²C 8 bit I/O Expander +''' + +from .pd import Decoder diff --git a/Hardware/sigrok-decoders/pcf8574/pd.py b/Hardware/sigrok-decoders/pcf8574/pd.py new file mode 100644 index 0000000..f969a97 --- /dev/null +++ b/Hardware/sigrok-decoders/pcf8574/pd.py @@ -0,0 +1,125 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2020 Lucas Teske +## +## 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 sigrokdecode as srd + +pcfaddr = 0x27 + +class Decoder(srd.Decoder): + api_version = 3 + id = 'pcf8574' + name = 'PCF8574' + longname = 'PCB8574 I2C I/O Extender' + desc = 'I2C 8 Bit I/O extender.' + license = 'gplv2+' + inputs = ['i2c'] + outputs = ['logic'] + tags = ['IO'] + options = () + annotations = ( + ('text', 'Text'), + ('warning', 'Warning'), + ('data-read', 'Data read'), + ('data-write', 'Data write'), + ) + binary = ( + ('data-read', 'Data read'), + ('data-write', 'Data write'), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.state = 'IDLE' + self.nextstate = 'IDLE' + self.databytes = [] + self.addr = 0x00 + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.out_binary = self.register(srd.OUTPUT_BINARY) + + def putp(self, data): + self.put(self.ss, self.es, self.out_python, data) + + def putx(self, data): + self.put(self.ss, self.es, self.out_ann, data) + + def putb(self, data): + self.put(self.ss, self.es, self.out_binary, data) + + def process_datawrite(self, ss, es, data): + cmd, series = data + self.state = 'IDLE' + self.state = 'WAITACK' + self.nextstate = 'IDLE' + databyte = self.bits_to_byte(data) + #print("Wrote data", hex(databyte)) + self.putx([3, ["Write 0x%02x" % databyte]]) + self.putb(databyte) + self.putp(series) + + def process_dataread(self, ss, es, data): + self.state = 'WAITACK' + self.nextstate = 'IDLE' + databyte = self.bits_to_byte(data) + self.putx([3, ["Read 0x%02x" % databyte]]) + self.putb(databyte) + #print("Read data", hex(databyte)) + + def process_ack(self, ss, es, data): + #print("ACK Received. Next state: %s" %self.nextstate) + self.state = self.nextstate + + def bits_to_byte(self, data): + cmd, series = data + databyte = 0 + for i in range(len(series)): + bit = series[i][0] + databyte += bit << (7-i) + return databyte + + def decode(self, ss, es, data): + cmd, databyte = data + + # Store the start/end samples of this I²C packet. + self.ss, self.es = ss, es + + #print("DEBUG(%s): %s" %(cmd, databyte)) + if self.state == 'IDLE': + # print("CMD", cmd, ", ", databyte, "") + if cmd == "ADDRESS WRITE" and databyte == pcfaddr: + #print("ADDR WROTE ", hex(databyte)) + self.addr = databyte + self.nextstate = 'DATAWRITE' + self.state = 'WAITACK' + if cmd == "ADDRESS READ" and databyte == pcfaddr: + #print("ADDR READ ", hex(databyte)) + self.addr = databyte + self.nextstate = 'DATAREAD' + self.state = 'WAITACK' + elif self.state == 'WAITACK' and cmd == 'ACK': + self.process_ack(ss, es, data) + elif self.state == 'DATAREAD': + self.process_dataread(ss, es, data) + elif self.state == 'DATAWRITE': + self.process_datawrite(ss, es, data) From ca0973722296c4485153339ca3197410be72a575 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Sun, 9 Aug 2020 01:12:09 -0300 Subject: [PATCH 2/3] Add READMEs --- Hardware/sigrok-decoders/README.md | 17 ++++++++++++++ Hardware/sigrok-decoders/hd44780/README.md | 7 ++++++ Hardware/sigrok-decoders/hd44780/pd.py | 2 +- Hardware/sigrok-decoders/pcf8574/README.md | 27 ++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 Hardware/sigrok-decoders/README.md create mode 100644 Hardware/sigrok-decoders/hd44780/README.md create mode 100644 Hardware/sigrok-decoders/pcf8574/README.md diff --git a/Hardware/sigrok-decoders/README.md b/Hardware/sigrok-decoders/README.md new file mode 100644 index 0000000..2cc5e4d --- /dev/null +++ b/Hardware/sigrok-decoders/README.md @@ -0,0 +1,17 @@ +# Sigrok Decoders + +Some [sigrok decorders](https://sigrok.org/wiki/Protocol_decoders) + +* PCF8574 +* HD44780 (Only 4 bit mode, expecting PCF8574 decoder output) + + +## How to install / use + + +Copy this folder content to: + +* On Linux: `~/.local/share/libsigrokdecode/decoders` +* On Windows: `%ProgramData%\libsigrokdecode\decoders` + +And then restart pulseview / sigrok \ No newline at end of file diff --git a/Hardware/sigrok-decoders/hd44780/README.md b/Hardware/sigrok-decoders/hd44780/README.md new file mode 100644 index 0000000..da0b9cb --- /dev/null +++ b/Hardware/sigrok-decoders/hd44780/README.md @@ -0,0 +1,7 @@ +# HD44780 LCD Decoder + +The HD44780 is a LCD Controller + +* Only works in 4 bit mode +* Expects a PCF8574 compatible output + diff --git a/Hardware/sigrok-decoders/hd44780/pd.py b/Hardware/sigrok-decoders/hd44780/pd.py index 23b9bd8..692676b 100644 --- a/Hardware/sigrok-decoders/hd44780/pd.py +++ b/Hardware/sigrok-decoders/hd44780/pd.py @@ -111,7 +111,7 @@ class Decoder(srd.Decoder): ) options = ( {'id': 'lcd_mode', 'desc': 'LCD Mode', - 'default': '4bit', 'values': ('4bit', '8bit')}, + 'default': '4bit', 'values': ('4bit')}, ) def __init__(self): diff --git a/Hardware/sigrok-decoders/pcf8574/README.md b/Hardware/sigrok-decoders/pcf8574/README.md new file mode 100644 index 0000000..299d9ca --- /dev/null +++ b/Hardware/sigrok-decoders/pcf8574/README.md @@ -0,0 +1,27 @@ +# PCF8574 Decoder + +The PCF8574 is a 8 bit I2C I/O Expander + +It receives the input from sigrok i2c decoder and then outputs the raw data that has been written + + +It sends the data to next stack decoder like this: + + +```python +[ + [ bitvalue, startsample, endsample ], # Repeats 8 times, one for each bit +] + +# For example: +[ + [1, 53636, 53656], # Bit 7 + [0, 53616, 53636], # Bit 6 + [0, 53596, 53616], # Bit 5 + [1, 53576, 53596], # Bit 4 + [0, 53556, 53576], # Bit 3 + [0, 53536, 53556], # Bit 2 + [0, 53516, 53536], # Bit 1 + [1, 53496, 53516] # Bit 0 +] +``` \ No newline at end of file From 2e321ffe91ef5c0b4bb103529a0223288d383d22 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Sun, 9 Aug 2020 01:13:35 -0300 Subject: [PATCH 3/3] Update README --- Hardware/sigrok-decoders/README.md | 7 ++++--- README.md | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Hardware/sigrok-decoders/README.md b/Hardware/sigrok-decoders/README.md index 2cc5e4d..2e2b1a4 100644 --- a/Hardware/sigrok-decoders/README.md +++ b/Hardware/sigrok-decoders/README.md @@ -1,10 +1,11 @@ # Sigrok Decoders -Some [sigrok decorders](https://sigrok.org/wiki/Protocol_decoders) +Some [sigrok decorders](https://sigrok.org/wiki/Protocol_decoders) compatible with [PulseView](https://sigrok.org/wiki/PulseView) -* PCF8574 -* HD44780 (Only 4 bit mode, expecting PCF8574 decoder output) +## Decoders +* [**PCF8574**](pcf8574/README.md) +* [**HD44780**](hd44780/README.md) (Only 4 bit mode, expecting PCF8574 decoder output) ## How to install / use diff --git a/README.md b/README.md index 8454b56..1911104 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,5 @@ **[✧] Hardware:**         » [*Liquid Crystal PCF8574 Data extractor*](/Hardware/pcf8574_data_extractor.py) + +        » [*Sigrok Decoders*](/Hardware/sigrok-decoders/README.md)