]> sigrok.org Git - libsigrokdecode.git/commitdiff
xy2-100: Rewrite PD for XY2-100E compatibility and features
authorSoeren Apel <redacted>
Fri, 10 Jul 2020 18:47:17 +0000 (20:47 +0200)
committerSoeren Apel <redacted>
Fri, 10 Jul 2020 18:47:17 +0000 (20:47 +0200)
decoders/xy2-100/pd.py

index 83f9422fd6cfd104d01a82838b299b1af805da27..4df70852ed4252b0e81b0f49136211ce65dc29cf 100644 (file)
@@ -2,6 +2,7 @@
 ## This file is part of the libsigrokdecode project.
 ##
 ## Copyright (C) 2019 Uli Huber
+## Copyright (C) 2020 Soeren Apel
 ##
 ## 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
 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
 ##
 
-import re
 import sigrokdecode as srd
 
-ann_hdrbit, ann_databit, ann_paritybit, ann_bitlegende, ann_pos, ann_warning = range(6)
+ann_bit, ann_type, ann_command, ann_parameter, ann_parity, ann_pos, ann_warning = range(7)
+frame_type_none, frame_type_command, frame_type_16bit_pos, frame_type_18bit_pos = range(4)
 
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'xy2-100'
     name = 'XY2-100'
-    longname = 'XY2-100 Galvo Protocol'
-    desc = 'Serial protocol for Galvo positioning'
+    longname = 'XY2-100(E) and XY-200(E) galvanometer protocol'
+    desc = 'Serial protocol for galvanometer positioning in laser systems'
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
+
     tags = ['Embedded/industrial']
+
     channels = (
-        {'id': 'clk', 'name': 'D0', 'desc': 'Clock','default': 0},
-        {'id': 'sync', 'name': 'D1', 'desc': 'Sync','default': 1},
-        {'id': 'PosX', 'name': 'D2', 'desc': 'X/Y/Z','default': 2},
+        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
+        {'id': 'sync', 'name': 'SYNC', 'desc': 'Sync'},
+        {'id': 'data', 'name': 'DATA', 'desc': 'X, Y or Z axis data'},
     )
 
     annotations = (
-        ('hdrbit', 'Header bit'),
-        ('databit', 'Data bit'),
-        ('paritybit', 'Parity bit'),
-        ('bitlegende', 'Bit Legende'),
-        ('position', 'Position Data'),
+        ('bit', 'Bit'),
+        ('type', 'Frame Type'),
+        ('command', 'Command'),
+        ('parameter', 'Parameter'),
+        ('parity', 'Parity'),
+        ('position', 'Position'),
         ('warning', 'Human-readable warnings'),
     )
     annotation_rows = (
-        ('bits', 'Bits', (ann_hdrbit, ann_databit, ann_paritybit)),
-        ('legende', 'Legende', (ann_bitlegende,)),
-        ('positions', 'Position', (ann_pos,)),
+        ('bits', 'Bits', (ann_bit,)),
+        ('data', 'Data', (ann_type, ann_command, ann_parameter, ann_parity)),
+        ('positions', 'Positions', (ann_pos,)),
         ('warnings', 'Warnings', (ann_warning,)),
     )
 
@@ -58,9 +62,7 @@ class Decoder(srd.Decoder):
         self.reset()
 
     def reset(self):
-        self.hdrbits = []
-        self.databits = []
-        self.paritybits = []
+        self.bits = []
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -69,67 +71,133 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
-    def putbit(self, ss, es, typ, value):
-        self.put(ss, es, self.out_ann, [typ, ['%s' % (value)]])
+    def put_ann(self, ss, es, ann_class, value):
+        self.put(ss, es, self.out_ann, [ann_class, value])
+
+    def process_bit(self, sync, bit_ss, bit_es, bit_value):
+        self.put_ann(bit_ss, bit_es, ann_bit, ['%d' % bit_value])
+        self.bits.append((bit_ss, bit_es, bit_value))
+
+        if sync == 0:
+            if len(self.bits) < 20:
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Not enough data bits'])
+                self.reset()
+                return
+
+            # Bit structure:
+            # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+            # T --------------- 18-bit pos ----------------- PARITY    or
+            # -TYPE-- ------------ 16-bit pos -------------- PARITY    or
+            # -TYPE-- -8-bit command -8-bit parameter value- PARITY
+
+            # Calculate parity, excluding the parity bit itself
+            parity = 0
+            for ss, es, value in self.bits[:-1]:
+                parity ^= value
+
+            par_ss, par_es, par_value = self.bits[19]
+            parity_even = 0
+            parity_odd = 0
+            if (par_value == parity):
+                parity_even = 1
+            else:
+                parity_odd = 1
+
+            type_1_value = self.bits[0][2]
+            type_3_value = (self.bits[0][2] << 2) | (self.bits[1][2] << 1) | self.bits[2][2]
+
+            # Determine frame type
+            type = frame_type_none
+            parity_status = ['X', 'Unknown']
+            type_ss = self.bits[0][0]
+            type_es = self.bits[2][1]
+
+            ### 18-bit position
+            if (type_1_value == 1) and (parity_odd == 1):
+                type = frame_type_18bit_pos
+                type_es = self.bits[0][1]
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
+            ### 16-bit position
+            elif (type_3_value == 1):
+                type = frame_type_16bit_pos
+                if (parity_even == 1):
+                    parity_status = ['OK']
+                else:
+                    parity_status = ['NOK']
+                    self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Parity error', 'PE'])
+            ### Command
+            elif (type_3_value == 7) and (parity_even == 1):
+                type = frame_type_command
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
+            ### Other
+            else:
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Error', 'Unknown command or parity error'])
+                self.reset()
+                return
+
+            # Output command and parity annotations
+            if (type == frame_type_16bit_pos):
+                self.put_ann(type_ss, type_es, ann_type, ['16 bit Position Frame', '16 bit Pos', 'Pos', 'P'])
+            if (type == frame_type_18bit_pos):
+                self.put_ann(type_ss, type_es, ann_type, ['18 bit Position Frame', '18 bit Pos', 'Pos', 'P'])
+            if (type == frame_type_command):
+                self.put_ann(type_ss, type_es, ann_type, ['Command Frame', 'Command', 'C'])
+
+            self.put_ann(par_ss, par_es, ann_parity, parity_status)
+
+            # Output value
+            if (type == frame_type_16bit_pos) or (type == frame_type_18bit_pos):
+               pos = 0
+
+               if (type == frame_type_16bit_pos):
+                   count = 15
+                   for ss, es, value in self.bits[3:19]:
+                       pos |= value << count
+                       count -= 1
+                   pos = pos if pos < 32768 else pos - 65536
+               else:
+                   count = 17
+                   for ss, es, value in self.bits[3:19]:
+                       pos |= value << count
+                       count -= 1
+                   pos = pos if pos < 131072 else pos - 262144
+
+               self.put_ann(type_es, par_ss, ann_pos, ['%d' % pos])
+
+            if (type == frame_type_command):
+               count = 7
+               cmd = 0
+               cmd_es = 0
+               for ss, es, value in self.bits[3:11]:
+                   cmd |= value << count
+                   count -= 1
+                   cmd_es = es
+               self.put_ann(type_es, cmd_es, ann_command, ['Command 0x%X' % cmd, 'Cmd 0x%X' % cmd, '0x%X' % cmd])
+
+               count = 7
+               param = 0
+               for ss, es, value in self.bits[11:19]:
+                   param |= value << count
+                   count -= 1
+               self.put_ann(cmd_es, par_ss, ann_parameter, ['Parameter 0x%X / %d' % (param, param), '0x%X / %d' % (param, param),'0x%X' % param])
+
+            self.reset()
 
     def decode(self):
-        headerstart = 0
-        datastart = 0
-        dataend = 0
-        lastsample = 0
+        bit_ss = None
+        bit_es = None
+        bit_value = 0
+        sync_value = 0
+
         while True:
-            # Wait for any edge CLK  or SYNC
-            clk, sync, PosX = self.wait({0: 'r'})
-            bitstart = self.samplenum
-            bitend = bitstart+(bitstart-lastsample)
-
-            # start data collection
-            if sync == 1:
-                # wait for falling edge clk
-                clk, sync, PosX = self.wait({0: 'f'})
-                if len(self.hdrbits) < 3:
-                    if len(self.hdrbits) == 0:
-                        headerstart = bitstart
-                        self.hdrbits = [(PosX, bitstart, self.samplenum)] + self.hdrbits
-                        self.putbit(bitstart, bitend, ann_hdrbit, PosX)
-                else:
-                    if len(self.databits) == 0:
-                        datastart = bitstart
-                    self.databits = [(PosX, bitstart, self.samplenum)] + self.databits
-                    #self.putbit(bitstart, self.samplenum+1, ann_databit, PosX)
-                    self.putbit(bitstart,bitend, ann_databit, PosX)
-                    dataend = bitend
-
-            # get parity bit, calculate position
-            elif sync == 0:
-                clk, sync, PosX = self.wait({0: 'f'})
-                self.paritybits = [PosX]
-                self.putbit(dataend, bitend, ann_paritybit, PosX)
-                self.put(dataend,bitend, self.out_ann, [ann_bitlegende, ['Parity' ]])
-                self.put(headerstart,datastart, self.out_ann, [ann_bitlegende, ['Header' ]])
-                self.put(datastart, dataend, self.out_ann, [ann_bitlegende, ['Position' ]])
-
-                par=0
-                for x in self.hdrbits:
-                    par ^= x[0]&1
-                positionX = 0
-                stelle = 0
-                for x in self.databits:
-                    par ^= x[0]&1
-                    if x[0]  == 1:
-                        positionX = positionX + (1 << stelle)
-                    stelle += 1
-
-                self.put(datastart, dataend, self.out_ann,  [ann_pos, ['%02d' % (positionX)]])
-                check = 'NOK'
-                if PosX == par:
-                    check = 'OK'
-                self.put(dataend, bitend, self.out_ann,  [ann_pos, ['%02s' % (check)]])
-
-                #self.put(datastart, self.samplenum, self.out_ann,
-                    #[ann_warning, ['%s: %02X' % ('WARNUNG: ', 4711)]])
-
-                self.databits = []
-                self.hdrbits = []
-
-            lastsample = bitstart
+            # Wait for any edge on clk
+            clk, sync, data = self.wait({0: 'e'})
+
+            if clk == 1:
+                bit_es = self.samplenum
+                if bit_ss:
+                    self.process_bit(sync_value, bit_ss, bit_es, bit_value)
+                bit_ss = self.samplenum
+            else:
+                bit_value = data
+                sync_value = sync