| 1 | ## |
| 2 | ## This file is part of the libsigrokdecode project. |
| 3 | ## |
| 4 | ## Copyright (C) 2020 Tomas Mudrunka <harvie@github> |
| 5 | ## |
| 6 | ## Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | ## of this software and associated documentation files (the "Software"), to deal |
| 8 | ## in the Software without restriction, including without limitation the rights |
| 9 | ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | ## copies of the Software, and to permit persons to whom the Software is |
| 11 | ## furnished to do so, subject to the following conditions: |
| 12 | ## |
| 13 | ## The above copyright notice and this permission notice shall be included in all |
| 14 | ## copies or substantial portions of the Software. |
| 15 | ## |
| 16 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 19 | ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 22 | ## SOFTWARE. |
| 23 | |
| 24 | import sigrokdecode as srd |
| 25 | |
| 26 | class Decoder(srd.Decoder): |
| 27 | api_version = 3 |
| 28 | id = 'caliper' |
| 29 | name = 'Caliper' |
| 30 | longname = 'Digital calipers' |
| 31 | desc = 'Protocol of cheap generic digital calipers' |
| 32 | license = 'mit' |
| 33 | inputs = ['logic'] |
| 34 | outputs = [] |
| 35 | channels = ( |
| 36 | {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'}, |
| 37 | {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'}, |
| 38 | ) |
| 39 | options = ( |
| 40 | {'id': 'timeout_ms', 'desc': 'Timeout packet after X ms, 0 to disable', 'default': 10}, |
| 41 | {'id': 'unit', 'desc': 'Convert units', 'default': 'keep', 'values': ('keep', 'mm', 'inch')}, |
| 42 | {'id': 'changes', 'desc': 'Changes only', 'default': 'no', 'values': ('no', 'yes')}, |
| 43 | ) |
| 44 | tags = ['Analog/digital', 'IC', 'Sensor'] |
| 45 | annotations = ( |
| 46 | ('measurements', 'Measurements'), |
| 47 | ('warning', 'Warnings'), |
| 48 | ) |
| 49 | annotation_rows = ( |
| 50 | ('measurements', 'Measurements', (0,)), |
| 51 | ('warnings', 'Warnings', (1,)), |
| 52 | ) |
| 53 | |
| 54 | def reset_data(self): |
| 55 | self.bits = 0 |
| 56 | self.number = 0 |
| 57 | self.flags = 0 |
| 58 | |
| 59 | def metadata(self, key, value): |
| 60 | if key == srd.SRD_CONF_SAMPLERATE: |
| 61 | self.samplerate = value |
| 62 | |
| 63 | def __init__(self): |
| 64 | self.reset() |
| 65 | |
| 66 | def reset(self): |
| 67 | self.ss_cmd, self.es_cmd = 0, 0 |
| 68 | self.reset_data() |
| 69 | |
| 70 | def start(self): |
| 71 | self.out_ann = self.register(srd.OUTPUT_ANN) |
| 72 | |
| 73 | #Switch bit order of variable x, which is l bit long |
| 74 | def bitr(self,x,l): |
| 75 | return int(bin(x)[2:].zfill(l)[::-1], 2) |
| 76 | |
| 77 | def decode(self): |
| 78 | self.last_measurement = None |
| 79 | while True: |
| 80 | clk, data = self.wait([{0: 'r'},{'skip': round(self.samplerate/1000)}]) |
| 81 | #print([clk,data]) |
| 82 | |
| 83 | #Timeout after inactivity |
| 84 | if(self.options['timeout_ms'] > 0): |
| 85 | if self.samplenum > self.es_cmd + (self.samplerate/(1000/self.options['timeout_ms'])): |
| 86 | if self.bits > 0: |
| 87 | self.put(self.ss_cmd, self.samplenum, self.out_ann, [1, ['timeout with %s bits in buffer'%(self.bits),'timeout']]) |
| 88 | self.reset() |
| 89 | |
| 90 | #Do nothing if there was timeout without rising clock edge |
| 91 | if self.matched == (False, True): |
| 92 | continue |
| 93 | |
| 94 | #Store position of last activity |
| 95 | self.es_cmd = self.samplenum |
| 96 | |
| 97 | #Store position of first bit |
| 98 | if self.ss_cmd == 0: |
| 99 | self.ss_cmd = self.samplenum |
| 100 | |
| 101 | #Shift in measured number |
| 102 | if self.bits < 16: |
| 103 | self.number = (self.number << 1) | (data & 0b1) |
| 104 | self.bits+=1 |
| 105 | continue |
| 106 | |
| 107 | #Shift in flag bits |
| 108 | if self.bits < 24: |
| 109 | self.flags = (self.flags << 1) | (data & 0b1) |
| 110 | self.bits+=1 |
| 111 | if self.bits < 24: |
| 112 | continue |
| 113 | #Hooray! We got last bit of data |
| 114 | self.es_cmd = self.samplenum |
| 115 | |
| 116 | #Do actual decoding |
| 117 | |
| 118 | #print(format(self.flags, '08b')); |
| 119 | |
| 120 | negative = ((self.flags & 0b00001000) >> 3) |
| 121 | inch = (self.flags & 0b00000001) |
| 122 | |
| 123 | number = self.bitr(self.number, 16) |
| 124 | |
| 125 | #print(format(number, '016b')) |
| 126 | |
| 127 | if negative > 0: |
| 128 | number = -number |
| 129 | |
| 130 | inchmm = 25.4 #how many mms in inch |
| 131 | |
| 132 | if inch: |
| 133 | number = number/2000 |
| 134 | if self.options['unit'] == 'mm': |
| 135 | number *= inchmm |
| 136 | inch = 0 |
| 137 | else: |
| 138 | number = number/100 |
| 139 | if self.options['unit'] == 'inch': |
| 140 | number = round(number/inchmm,4) |
| 141 | inch = 1 |
| 142 | |
| 143 | units = "in" if inch else "mm" |
| 144 | |
| 145 | measurement = (str(number)+units) |
| 146 | #print(measurement) |
| 147 | |
| 148 | if ((self.options['changes'] == 'no') or (self.last_measurement != measurement)): |
| 149 | self.put(self.ss_cmd, self.es_cmd, self.out_ann, [0, [measurement, str(number)]]) |
| 150 | self.last_measurement = measurement |
| 151 | |
| 152 | #Prepare for next packet |
| 153 | self.reset() |