]>
Commit | Line | Data |
---|---|---|
92d1aba3 | 1 | ## |
50bd5d25 | 2 | ## This file is part of the libsigrokdecode project. |
92d1aba3 | 3 | ## |
ce0c47f3 | 4 | ## Copyright (C) 2012-2015 Uwe Hermann <uwe@hermann-uwe.de> |
92d1aba3 UH |
5 | ## |
6 | ## This program is free software; you can redistribute it and/or modify | |
7 | ## it under the terms of the GNU General Public License as published by | |
8 | ## the Free Software Foundation; either version 2 of the License, or | |
9 | ## (at your option) any later version. | |
10 | ## | |
11 | ## This program is distributed in the hope that it will be useful, | |
12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | ## GNU General Public License for more details. | |
15 | ## | |
16 | ## You should have received a copy of the GNU General Public License | |
4539e9ca | 17 | ## along with this program; if not, see <http://www.gnu.org/licenses/>. |
92d1aba3 UH |
18 | ## |
19 | ||
92d1aba3 | 20 | import sigrokdecode as srd |
32eae338 UH |
21 | from common.srdhelper import SrdIntEnum |
22 | ||
23 | Pin = SrdIntEnum.from_str('Pin', 'CLK DATA LOAD LDAC') | |
92d1aba3 UH |
24 | |
25 | dacs = { | |
26 | 0: 'DACA', | |
27 | 1: 'DACB', | |
28 | 2: 'DACC', | |
29 | 3: 'DACD', | |
30 | } | |
31 | ||
32 | class Decoder(srd.Decoder): | |
af495297 | 33 | api_version = 3 |
92d1aba3 UH |
34 | id = 'tlc5620' |
35 | name = 'TI TLC5620' | |
36 | longname = 'Texas Instruments TLC5620' | |
37 | desc = 'Texas Instruments TLC5620 8-bit quad DAC.' | |
38 | license = 'gplv2+' | |
39 | inputs = ['logic'] | |
6cbba91f | 40 | outputs = [] |
d6d8a8a4 | 41 | tags = ['IC', 'Analog/digital'] |
6a15597a | 42 | channels = ( |
92d1aba3 UH |
43 | {'id': 'clk', 'name': 'CLK', 'desc': 'Serial interface clock'}, |
44 | {'id': 'data', 'name': 'DATA', 'desc': 'Serial interface data'}, | |
da9bcbd9 | 45 | ) |
6a15597a | 46 | optional_channels = ( |
92d1aba3 UH |
47 | {'id': 'load', 'name': 'LOAD', 'desc': 'Serial interface load control'}, |
48 | {'id': 'ldac', 'name': 'LDAC', 'desc': 'Load DAC'}, | |
da9bcbd9 | 49 | ) |
04c310c8 UH |
50 | options = ( |
51 | {'id': 'vref_a', 'desc': 'Reference voltage DACA (V)', 'default': 3.3}, | |
52 | {'id': 'vref_b', 'desc': 'Reference voltage DACB (V)', 'default': 3.3}, | |
53 | {'id': 'vref_c', 'desc': 'Reference voltage DACC (V)', 'default': 3.3}, | |
54 | {'id': 'vref_d', 'desc': 'Reference voltage DACD (V)', 'default': 3.3}, | |
55 | ) | |
da9bcbd9 BV |
56 | annotations = ( |
57 | ('dac-select', 'DAC select'), | |
58 | ('gain', 'Gain'), | |
59 | ('value', 'DAC value'), | |
60 | ('data-latch', 'Data latch point'), | |
61 | ('ldac-fall', 'LDAC falling edge'), | |
ce0c47f3 | 62 | ('bit', 'Bit'), |
3992b1e1 UH |
63 | ('reg-write', 'Register write'), |
64 | ('voltage-update', 'Voltage update'), | |
65 | ('voltage-update-all', 'Voltage update (all DACs)'), | |
a945c536 | 66 | ('invalid-cmd', 'Invalid command'), |
ce0c47f3 UH |
67 | ) |
68 | annotation_rows = ( | |
69 | ('bits', 'Bits', (5,)), | |
70 | ('fields', 'Fields', (0, 1, 2)), | |
3992b1e1 UH |
71 | ('registers', 'Registers', (6, 7)), |
72 | ('voltage-updates', 'Voltage updates', (8,)), | |
ce0c47f3 | 73 | ('events', 'Events', (3, 4)), |
a945c536 | 74 | ('errors', 'Errors', (9,)), |
da9bcbd9 | 75 | ) |
92d1aba3 | 76 | |
92b7b49f | 77 | def __init__(self): |
10aeb8ea GS |
78 | self.reset() |
79 | ||
80 | def reset(self): | |
92d1aba3 | 81 | self.bits = [] |
3992b1e1 | 82 | self.ss_dac_first = None |
92d1aba3 UH |
83 | self.ss_dac = self.es_dac = 0 |
84 | self.ss_gain = self.es_gain = 0 | |
85 | self.ss_value = self.es_value = 0 | |
17db4008 | 86 | self.dac_select = self.gain = self.dac_value = None |
3992b1e1 | 87 | self.dacval = {'A': '?', 'B': '?', 'C': '?', 'D': '?'} |
04c310c8 | 88 | self.gains = {'A': '?', 'B': '?', 'C': '?', 'D': '?'} |
92d1aba3 | 89 | |
8915b346 | 90 | def start(self): |
be465111 | 91 | self.out_ann = self.register(srd.OUTPUT_ANN) |
92d1aba3 | 92 | |
92d1aba3 | 93 | def handle_11bits(self): |
e57813d5 UH |
94 | # Only look at the last 11 bits, the rest is ignored by the TLC5620. |
95 | if len(self.bits) > 11: | |
96 | self.bits = self.bits[-11:] | |
97 | ||
a945c536 UH |
98 | # If there are less than 11 bits, something is probably wrong. |
99 | if len(self.bits) < 11: | |
100 | ss, es = self.samplenum, self.samplenum | |
101 | if len(self.bits) >= 2: | |
102 | ss = self.bits[0][1] | |
103 | es = self.bits[-1][1] + (self.bits[1][1] - self.bits[0][1]) | |
104 | self.put(ss, es, self.out_ann, [9, ['Command too short']]) | |
105 | self.bits = [] | |
106 | return False | |
107 | ||
e57813d5 UH |
108 | self.ss_dac = self.bits[0][1] |
109 | self.es_dac = self.ss_gain = self.bits[2][1] | |
110 | self.es_gain = self.ss_value = self.bits[3][1] | |
111 | self.clock_width = self.es_gain - self.ss_gain | |
112 | self.es_value = self.bits[10][1] + self.clock_width # Guessed. | |
113 | ||
3992b1e1 UH |
114 | if self.ss_dac_first is None: |
115 | self.ss_dac_first = self.ss_dac | |
116 | ||
ce0c47f3 | 117 | s = ''.join(str(i[0]) for i in self.bits[:2]) |
7fb4935e | 118 | self.dac_select = s = dacs[int(s, 2)] |
92d1aba3 | 119 | self.put(self.ss_dac, self.es_dac, self.out_ann, |
7fb4935e UH |
120 | [0, ['DAC select: %s' % s, 'DAC sel: %s' % s, |
121 | 'DAC: %s' % s, 'D: %s' % s, s, s[3]]]) | |
92d1aba3 | 122 | |
ce0c47f3 | 123 | self.gain = g = 1 + self.bits[2][0] |
92d1aba3 | 124 | self.put(self.ss_gain, self.es_gain, self.out_ann, |
7fb4935e | 125 | [1, ['Gain: x%d' % g, 'G: x%d' % g, 'x%d' % g]]) |
92d1aba3 | 126 | |
ce0c47f3 | 127 | s = ''.join(str(i[0]) for i in self.bits[3:]) |
7fb4935e | 128 | self.dac_value = v = int(s, 2) |
92d1aba3 | 129 | self.put(self.ss_value, self.es_value, self.out_ann, |
7fb4935e UH |
130 | [2, ['DAC value: %d' % v, 'Value: %d' % v, 'Val: %d' % v, |
131 | 'V: %d' % v, '%d' % v]]) | |
17db4008 | 132 | |
ce0c47f3 UH |
133 | # Emit an annotation for each bit. |
134 | for i in range(1, 11): | |
135 | self.put(self.bits[i - 1][1], self.bits[i][1], self.out_ann, | |
136 | [5, [str(self.bits[i - 1][0])]]) | |
137 | self.put(self.bits[10][1], self.bits[10][1] + self.clock_width, | |
138 | self.out_ann, [5, [str(self.bits[10][0])]]) | |
139 | ||
e57813d5 UH |
140 | self.bits = [] |
141 | ||
a945c536 UH |
142 | return True |
143 | ||
17db4008 | 144 | def handle_falling_edge_load(self): |
a945c536 UH |
145 | if not self.handle_11bits(): |
146 | return | |
7fb4935e | 147 | s, v, g = self.dac_select, self.dac_value, self.gain |
17db4008 | 148 | self.put(self.samplenum, self.samplenum, self.out_ann, |
ce0c47f3 | 149 | [3, ['Falling edge on LOAD', 'LOAD fall', 'F']]) |
04c310c8 UH |
150 | vref = self.options['vref_%s' % self.dac_select[3].lower()] |
151 | v = '%.2fV' % (vref * (v / 256) * self.gain) | |
3992b1e1 UH |
152 | if self.ldac == 0: |
153 | # If LDAC is low, the voltage is set immediately. | |
154 | self.put(self.ss_dac, self.es_value, self.out_ann, | |
04c310c8 UH |
155 | [7, ['Setting %s voltage to %s' % (s, v), |
156 | '%s=%s' % (s, v)]]) | |
3992b1e1 UH |
157 | else: |
158 | # If LDAC is high, the voltage is not set immediately, but rather | |
159 | # stored in a register. When LDAC goes low all four DAC voltages | |
160 | # (DAC A/B/C/D) will be set at the same time. | |
161 | self.put(self.ss_dac, self.es_value, self.out_ann, | |
04c310c8 UH |
162 | [6, ['Setting %s register value to %s' % \ |
163 | (s, v), '%s=%s' % (s, v)]]) | |
3992b1e1 UH |
164 | # Save the last value the respective DAC was set to. |
165 | self.dacval[self.dac_select[-1]] = str(self.dac_value) | |
04c310c8 | 166 | self.gains[self.dac_select[-1]] = self.gain |
17db4008 UH |
167 | |
168 | def handle_falling_edge_ldac(self): | |
169 | self.put(self.samplenum, self.samplenum, self.out_ann, | |
ce0c47f3 | 170 | [4, ['Falling edge on LDAC', 'LDAC fall', 'LDAC', 'L']]) |
17db4008 | 171 | |
3992b1e1 UH |
172 | # Don't emit any annotations if we didn't see any register writes. |
173 | if self.ss_dac_first is None: | |
174 | return | |
175 | ||
04c310c8 UH |
176 | # Calculate voltages based on Vref and the per-DAC gain. |
177 | dacval = {} | |
178 | for key, val in self.dacval.items(): | |
179 | if val == '?': | |
180 | dacval[key] = '?' | |
181 | else: | |
182 | vref = self.options['vref_%s' % key.lower()] | |
183 | v = vref * (int(val) / 256) * self.gains[key] | |
184 | dacval[key] = '%.2fV' % v | |
185 | ||
186 | s = ''.join(['DAC%s=%s ' % (d, dacval[d]) for d in 'ABCD']).strip() | |
3992b1e1 UH |
187 | self.put(self.ss_dac_first, self.samplenum, self.out_ann, |
188 | [8, ['Updating voltages: %s' % s, s, s.replace('DAC', '')]]) | |
189 | self.ss_dac_first = None | |
190 | ||
af495297 UH |
191 | def handle_new_dac_bit(self, datapin): |
192 | self.bits.append([datapin, self.samplenum]) | |
92d1aba3 | 193 | |
af495297 UH |
194 | def decode(self): |
195 | while True: | |
92d1aba3 | 196 | # DATA is shifted in the DAC on the falling CLK edge (MSB-first). |
17db4008 | 197 | # A falling edge of LOAD will latch the data. |
92d1aba3 | 198 | |
af495297 UH |
199 | # Wait for one (or multiple) of the following conditions: |
200 | # a) Falling edge on CLK, and/or | |
201 | # b) Falling edge on LOAD, and/or | |
202 | # b) Falling edge on LDAC | |
32eae338 | 203 | pins = self.wait([{Pin.CLK: 'f'}, {Pin.LOAD: 'f'}, {Pin.LDAC: 'f'}]) |
af495297 UH |
204 | self.ldac = pins[3] |
205 | ||
206 | # Handle those conditions (one or more) that matched this time. | |
207 | if self.matched[0]: | |
208 | self.handle_new_dac_bit(pins[1]) | |
209 | if self.matched[1]: | |
17db4008 | 210 | self.handle_falling_edge_load() |
af495297 | 211 | if self.matched[2]: |
17db4008 | 212 | self.handle_falling_edge_ldac() |