]> sigrok.org Git - libsigrokdecode.git/blob - decoders/rfm12/pd.py
Use consistent __init__() format across all PDs.
[libsigrokdecode.git] / decoders / rfm12 / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2014 SÅ‚awek Piotrowski <sentinel@atteo.org>
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
17 ## along with this program; if not, write to the Free Software
18 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 ##
20
21 import sigrokdecode as srd
22
23 class Decoder(srd.Decoder):
24     api_version = 2
25     id = 'rfm12'
26     name = 'RFM12'
27     longname = 'RFM12 control protocol'
28     desc = 'HopeRF RFM12 wireless transceiver control protocol.'
29     license = 'gplv2+'
30     inputs = ['spi']
31     outputs = ['rfm12']
32     annotations = (
33         ('cmd', 'Command'),
34         ('params', 'Command parameters'),
35         ('disabled', 'Disabled bits'),
36         ('return', 'Returned values'),
37         ('disabled_return', 'Disabled returned values'),
38         ('interpretation', 'Interpretation'),
39     )
40     annotation_rows = (
41         ('commands', 'Commands', (0, 1, 2)),
42         ('return', 'Return', (3, 4)),
43         ('interpretation', 'Interpretation', (5,)),
44     )
45
46     def __init__(self):
47         self.mosi_bytes, self.miso_bytes = [], []
48         self.mosi_bits, self.miso_bits = [], []
49         self.row_pos = [0, 0, 0]
50
51         self.ann_to_row = [0, 0, 0, 1, 1, 2]
52
53         # Initialize with Power-On-Reset values.
54         self.last_status = [0x00, 0x00]
55         self.last_config = 0x08
56         self.last_power = 0x08
57         self.last_freq = 0x680
58         self.last_data_rate = 0x23
59         self.last_fifo_and_reset = 0x80
60         self.last_afc = 0xF7
61         self.last_transceiver = 0x00
62         self.last_pll = 0x77
63
64     def advance_ann(self, ann, length):
65         row = self.ann_to_row[ann]
66         self.row_pos[row] += length
67
68     def putx(self, ann, length, description):
69         if not isinstance(description, list):
70             description = [description]
71         row = self.ann_to_row[ann]
72         bit = self.row_pos[row]
73         self.put(self.mosi_bits[bit][1], self.mosi_bits[bit + length - 1][2],
74                  self.out_ann, [ann, description])
75         bit += length
76         self.row_pos[row] = bit
77
78     def describe_bits(self, data, names):
79         i = 0x01 << len(names) - 1
80         bit = 0
81         while i != 0:
82             if names[bit] != '':
83                 self.putx(1 if (data & i) else 2, 1, names[bit])
84             i >>= 1
85             bit += 1
86
87     def describe_return_bits(self, data, names):
88         i = 0x01 << len(names) - 1
89         bit = 0
90         while i != 0:
91             if names[bit] != '':
92                 self.putx(3 if (data & i) else 4, 1, names[bit])
93             else:
94                 self.advance_ann(3, 1)
95             i >>= 1
96             bit += 1
97
98     def describe_changed_bits(self, data, old_data, names):
99         changes = data ^ old_data
100         i = 0x01 << (len(names) - 1)
101         bit = 0
102         while i != 0:
103             if names[bit] != '' and changes & i:
104                 s = ['+', 'Turning on'] if (data & i) else ['-', 'Turning off']
105                 self.putx(5, 1, s)
106             else:
107                 self.advance_ann(5, 1)
108             i >>= 1
109             bit += 1
110
111     def start(self):
112         self.out_ann = self.register(srd.OUTPUT_ANN)
113
114     def handle_configuration_cmd(self, cmd, ret):
115         self.putx(0, 8, ['Configuration command', 'Configuration'])
116         NAMES = [['Internal data register', 'el'], ['FIFO mode', 'ef']]
117
118         bits = (cmd[1] & 0xC0) >> 6
119         old_bits = (self.last_config & 0xC0) >> 6
120         self.describe_bits(bits, NAMES)
121         self.describe_changed_bits(bits, old_bits, NAMES)
122
123         FREQUENCIES = ['315', '433', '868', '915']
124         f = FREQUENCIES[(cmd[1] & 0x30) >> 4] + 'MHz'
125         self.putx(1, 2, ['Frequency: ' + f, f])
126         if cmd[1] & 0x30 != self.last_config & 0x30:
127             self.putx(5, 2, ['Changed', '~'])
128
129         c = '%.1fpF' % (8.5 + (cmd[1] & 0xF) * 0.5)
130         self.putx(1, 4, ['Capacitance: ' + c, c])
131         if cmd[1] & 0xF != self.last_config & 0xF:
132             self.putx(5, 4, ['Changed', '~'])
133
134         self.last_config = cmd[1]
135
136     def handle_power_management_cmd(self, cmd, ret):
137         self.putx(0, 8, ['Power management', 'Power'])
138         NAMES = [['Receiver chain', 'er'], ['Baseband circuit', 'ebb'],
139                  ['Transmission', 'et'], ['Synthesizer', 'es'],
140                  ['Crystal oscillator', 'ex'], ['Low battery detector', 'eb'],
141                  ['Wake-up timer', 'ew'], ['Clock output off switch', 'dc']]
142
143         self.describe_bits(cmd[1], NAMES)
144
145         power = cmd[1]
146
147         # Some bits imply other, even if they are set to 0.
148         if power & 0x80:
149             power |= 0x58
150         if power & 0x20:
151             power |= 0x18
152         self.describe_changed_bits(power, self.last_power, NAMES)
153
154         self.last_power = power
155
156     def handle_frequency_setting_cmd(self, cmd, ret):
157         self.putx(0, 4, ['Frequency setting', 'Frequency'])
158         f = ((cmd[1] & 0xF) << 8) + cmd[2]
159         self.putx(0, 12, ['F = %3.4f' % f])
160         self.row_pos[2] -= 4
161         if self.last_freq != f:
162             self.putx(5, 12, ['Changing', '~'])
163         self.last_freq = f
164
165     def handle_data_rate_cmd(self, cmd, ret):
166         self.putx(0, 8, ['Data rate command', 'Data rate'])
167         r = cmd[1] & 0x7F
168         cs = (cmd[1] & 0x80) >> 7
169         rate = 10000 / 29.0 / (r + 1) / (1 + 7 * cs)
170         self.putx(0, 8, ['%3.1fkbps' % rate])
171         if self.last_data_rate != cmd[1]:
172             self.putx(5, 8, ['Changing', '~'])
173         self.last_data_rate = cmd[1]
174
175     def handle_receiver_control_cmd(self, cmd, ret):
176         self.putx(0, 5, ['Receiver control command'])
177         s = 'interrupt input' if (cmd[0] & 0x04) else 'VDI output'
178         self.putx(0, 1, ['pin16 = ' + s])
179         VDI_NAMES = ['Fast', 'Medium', 'Slow', 'Always on']
180         vdi_speed = VDI_NAMES[cmd[0] & 0x3]
181         self.putx(0, 2, ['VDI: %s' % vdi_speed])
182         BANDWIDTH_NAMES = ['Reserved', '400kHz', '340kHz', '270kHz', '200kHz',
183                            '134kHz', '67kHz', 'Reserved']
184         bandwidth = BANDWIDTH_NAMES[(cmd[1] & 0xE0) >> 5]
185         self.putx(0, 3, ['Bandwidth: %s' % bandwidth])
186         LNA_GAIN_NAMES = [0, -6, -14, -20]
187         lna_gain = LNA_GAIN_NAMES[(cmd[1] & 0x18) >> 3]
188         self.putx(0, 2, ['LNA gain: %ddB' % lna_gain])
189         RSSI_THRESHOLD_NAMES = ['-103', '-97', '-91', '-85', '-79', '-73',
190                                 'Reserved', 'Reserved']
191         rssi_threshold = RSSI_THRESHOLD_NAMES[cmd[1] & 0x7]
192         self.putx(0, 3, ['RSSI threshold: %s' % rssi_threshold])
193
194     def handle_data_filter_cmd(self, cmd, ret):
195         self.putx(0, 8, ['Data filter command'])
196         if cmd[1] & 0x80:
197             clock_recovery = 'auto'
198         elif cmd[1] & 0x40:
199             clock_recovery = 'fast'
200         else:
201             clock_recovery = 'slow'
202         self.putx(0, 2, ['Clock recovery: %s mode' % clock_recovery])
203         self.advance_ann(0, 1) # Should always be 1.
204         s = 'analog' if (cmd[1] & 0x10) else 'digital'
205         self.putx(0, 1, ['Data filter: ' + s])
206         self.advance_ann(0, 1) # Should always be 1.
207         self.putx(0, 3, ['DQD threshold: %d' % (cmd[1] & 0x7)])
208
209     def handle_fifo_and_reset_cmd(self, cmd, ret):
210         self.putx(0, 8, ['FIFO and reset command'])
211         fifo_level = (cmd[1] & 0xF0) >> 4
212         self.putx(0, 4, ['FIFO trigger level: %d' % fifo_level])
213         last_fifo_level = (self.last_fifo_and_reset & 0xF0) >> 4
214         if fifo_level != last_fifo_level:
215             self.putx(5, 4, ['Changing', '~'])
216         else:
217             self.advance_ann(5, 4)
218         s = 'one byte' if (cmd[1] & 0x08) else 'two bytes'
219         self.putx(0, 1, ['Synchron length: ' + s])
220         if (cmd[1] & 0x08) != (self.last_fifo_and_reset & 0x08):
221             self.putx(5, 1, ['Changing', '~'])
222         else:
223             self.advance_ann(5, 1)
224
225         if cmd[1] & 0x04:
226             fifo_fill = 'Always'
227         elif cmd[1] & 0x02:
228             fifo_fill = 'After synchron pattern'
229         else:
230             fifo_fill = 'Never'
231         self.putx(0, 2, ['FIFO fill: %s' % fifo_fill])
232         if (cmd[1] & 0x06) != (self.last_fifo_and_reset & 0x06):
233             self.putx(5, 2, ['Changing', '~'])
234         else:
235             self.advance_ann(5, 2)
236
237         s = 'non-sensitive' if (cmd[1] & 0x01) else 'sensitive'
238         self.putx(0, 1, ['Reset mode: ' + s])
239         if (cmd[1] & 0x01) != (self.last_fifo_and_reset & 0x01):
240             self.putx(5, 1, ['Changing', '~'])
241         else:
242             self.advance_ann(5, 1)
243
244         self.last_fifo_and_reset = cmd[1]
245
246     def handle_synchron_pattern_cmd(self, cmd, ret):
247         self.putx(0, 8, ['Synchron pattern command'])
248         if self.last_fifo_and_reset & 0x08:
249             self.putx(0, 8, ['Pattern: 0x2D%02X' % pattern])
250         else:
251             self.putx(0, 8, ['Pattern: %02X' % pattern])
252
253     def handle_fifo_read_cmd(self, cmd, ret):
254         self.putx(0, 8, ['FIFO read command', 'FIFO read'])
255         self.putx(3, 8, ['Data: %02X' % ret[1]])
256
257     def handle_afc_cmd(self, cmd, ret):
258         self.putx(0, 8, ['AFC command'])
259         MODES = ['Off', 'Once', 'During receiving', 'Always']
260         mode = (cmd[1] & 0xC0) >> 6
261         self.putx(0, 2, ['Mode: %s' % MODES[mode]])
262         if (cmd[1] & 0xC0) != (self.last_afc & 0xC0):
263             self.putx(5, 2, ['Changing', '~'])
264         else:
265             self.advance_ann(5, 2)
266
267         range_limit = (cmd[1] & 0x30) >> 4
268         FREQ_TABLE = [0.0, 2.5, 5.0, 7.5]
269         freq_delta = FREQ_TABLE[(self.last_config & 0x30) >> 4]
270
271         if range_limit == 0:
272             self.putx(0, 2, ['Range: No limit'])
273         elif range_limit == 1:
274             self.putx(0, 2, ['Range: +/-%dkHz' % (15 * freq_delta)])
275         elif range_limit == 2:
276             self.putx(0, 2, ['Range: +/-%dkHz' % (7 * freq_delta)])
277         elif range_limit == 3:
278             self.putx(0, 2, ['Range: +/-%dkHz' % (3 * freq_delta)])
279
280         if (cmd[1] & 0x30) != (self.last_afc & 0x30):
281             self.putx(5, 2, ['Changing', '~'])
282         else:
283             self.advance_ann(5, 2)
284
285         NAMES = ['Strobe edge', 'High accuracy mode', 'Enable offset register',
286                  'Enable offset calculation']
287         self.describe_bits(cmd[1] & 0xF, NAMES)
288         self.describe_changed_bits(cmd[1] & 0xF, self.last_afc & 0xF, NAMES)
289
290         self.last_afc = cmd[1]
291
292     def handle_transceiver_control_cmd(self, cmd, ret):
293         self.putx(0, 8, ['Transceiver control command'])
294         self.putx(0, 4, ['FSK frequency delta: %dkHz' % (15 * ((cmd[1] & 0xF0) >> 4))])
295         if cmd[1] & 0xF0 != self.last_transceiver & 0xF0:
296             self.putx(5, 4, ['Changing', '~'])
297         else:
298             self.advance_ann(5, 4)
299
300         POWERS = [0, -2.5, -5, -7.5, -10, -12.5, -15, -17.5]
301         self.advance_ann(0, 1)
302         self.advance_ann(5, 1)
303         self.putx(0,3, ['Relative power: %dB' % (cmd[1] & 0x07)])
304         if (cmd[1] & 0x07) != (self.last_transceiver & 0x07):
305             self.putx(5, 3, ['Changing', '~'])
306         else:
307             self.advance_ann(5, 3)
308         self.last_transceiver = cmd[1]
309
310     def handle_pll_setting_cmd(self, cmd, ret):
311         self.putx(0, 8, ['PLL setting command'])
312         self.advance_ann(0, 1)
313         self.putx(0, 2, ['Clock buffer rise and fall time'])
314         self.advance_ann(0, 1)
315         self.advance_ann(5, 4)
316         NAMES = [['Delay in phase detector', 'dly'], ['Disable dithering', 'ddit']]
317         self.describe_bits((cmd[1] & 0xC) >> 2, NAMES)
318         self.describe_changed_bits((cmd[1] & 0xC) >> 2, (self.last_pll & 0xC) >> 2, NAMES)
319         s = '256kbps, high' if (cmd[1] & 0x01) else '86.2kbps, low'
320         self.putx(0, 1, ['Max bit rate: %s noise' % s])
321
322         self.advance_ann(5, 1)
323         if (cmd[1] & 0x01) != (self.last_pll & 0x01):
324             self.putx(5, 1, ['Changing', '~'])
325
326         self.last_pll = cmd[1]
327
328     def handle_transmitter_register_cmd(self, cmd, ret):
329         self.putx(0, 8, ['Transmitter register command', 'Transmit'])
330         self.putx(0, 8, ['Data: %s' % cmd[1], '%s' % cmd[1]])
331
332     def handle_software_reset_cmd(self, cmd, ret):
333         self.putx(0, 16, ['Software reset command'])
334
335     def handle_wake_up_timer_cmd(self, cmd, ret):
336         self.putx(0, 3, ['Wake-up timer command', 'Timer'])
337         r = cmd[0] & 0x1F
338         m = cmd[1]
339         time = 1.03 * m * pow(2, r) + 0.5
340         self.putx(0, 13, ['Time: %7.2f' % time])
341
342     def handle_low_duty_cycle_cmd(self, cmd, ret):
343         self.putx(0, 16, ['Low duty cycle command'])
344
345     def handle_low_battery_detector_cmd(self, cmd, ret):
346         self.putx(0, 8, ['Low battery detector command'])
347         NAMES = ['1', '1.25', '1.66', '2', '2.5', '3.33', '5', '10']
348         clock = NAMES[(cmd[1] & 0xE0) >> 5]
349         self.putx(0, 3, ['Clock output: %sMHz' % clock, '%sMHz' % clock])
350         self.advance_ann(0, 1)
351         v = 2.25 + (cmd[1] & 0x0F) * 0.1
352         self.putx(0, 4, ['Low battery voltage: %1.2fV' % v, '%1.2fV' % v])
353
354     def handle_status_read_cmd(self, cmd, ret):
355         self.putx(0, 8, ['Status read command', 'Status'])
356         NAMES = ['RGIT/FFIT', 'POR', 'RGUR/FFOV', 'WKUP', 'EXT', 'LBD',
357                  'FFEM', 'RSSI/ATS', 'DQD', 'CRL', 'ATGL']
358         status = (ret[0] << 3) + (ret[1] >> 5)
359         self.row_pos[1] -= 8
360         self.row_pos[2] -= 8
361         self.describe_return_bits(status, NAMES)
362         receiver_enabled = (self.last_power & 0x80) >> 7
363
364         if ret[0] & 0x80:
365             if receiver_enabled:
366                 s = 'Received data in FIFO'
367             else:
368                 s = 'Transmit register ready'
369             self.putx(5, 1, s)
370         else:
371             self.advance_ann(5, 1)
372         if ret[0] & 0x40:
373             self.putx(5, 1, 'Power on Reset')
374         else:
375             self.advance_ann(5, 1)
376         if ret[0] & 0x20:
377             if receiver_enabled:
378                 s = 'RX FIFO overflow'
379             else:
380                 s = 'Transmit register under run'
381             self.putx(5, 1, s)
382         else:
383             self.advance_ann(5, 1)
384         if ret[0] & 0x10:
385             self.putx(5, 1, 'Wake-up timer')
386         else:
387             self.advance_ann(5, 1)
388         if ret[0] & 0x08:
389             self.putx(5, 1, 'External interrupt')
390         else:
391             self.advance_ann(5, 1)
392         if ret[0] & 0x04:
393             self.putx(5, 1, 'Low battery')
394         else:
395             self.advance_ann(5, 1)
396         if ret[0] & 0x02:
397             self.putx(5, 1, 'FIFO is empty')
398         else:
399             self.advance_ann(5, 1)
400         if ret[0] & 0x01:
401             if receiver_enabled:
402                 s = 'Incoming signal above limit'
403             else:
404                 s = 'Antenna detected RF signal'
405             self.putx(5, 1, s)
406         else:
407             self.advance_ann(5, 1)
408         if ret[1] & 0x80:
409             self.putx(5, 1, 'Data quality detector')
410         else:
411             self.advance_ann(5, 1)
412         if ret[1] & 0x40:
413             self.putx(5, 1, 'Clock recovery locked')
414         else:
415             self.advance_ann(5, 1)
416         self.advance_ann(5, 1)
417
418         self.putx(3, 5, ['AFC offset'])
419         if (self.last_status[1] & 0x1F) != (ret[1] & 0x1F):
420             self.putx(5, 5, ['Changed', '~'])
421         self.last_status = ret
422
423     def handle_cmd(self, cmd, ret):
424         if cmd[0] == 0x80:
425             self.handle_configuration_cmd(cmd, ret)
426         elif cmd[0] == 0x82:
427             self.handle_power_management_cmd(cmd, ret)
428         elif cmd[0] & 0xF0 == 0xA0:
429             self.handle_frequency_setting_cmd(cmd, ret)
430         elif cmd[0] == 0xC6:
431             self.handle_data_rate_cmd(cmd, ret)
432         elif cmd[0] & 0xF8 == 0x90:
433             self.handle_receiver_control_cmd(cmd, ret)
434         elif cmd[0] == 0xC2:
435             self.handle_data_filter_cmd(cmd, ret)
436         elif cmd[0] == 0xCA:
437             self.handle_fifo_and_reset_cmd(cmd, ret)
438         elif cmd[0] == 0xCE:
439             self.handle_synchron_pattern_cmd(cmd, ret)
440         elif cmd[0] == 0xB0:
441             self.handle_fifo_read_cmd(cmd, ret)
442         elif cmd[0] == 0xC4:
443             self.handle_afc_cmd(cmd, ret)
444         elif cmd[0] & 0xFE == 0x98:
445             self.handle_transceiver_control_cmd(cmd, ret)
446         elif cmd[0] == 0xCC:
447             self.handle_pll_setting_cmd(cmd, ret)
448         elif cmd[0] == 0xB8:
449             self.handle_transmitter_register_cmd(cmd, ret)
450         elif cmd[0] == 0xFE:
451             self.handle_software_reset_cmd(cmd, ret)
452         elif cmd[0] & 0xE0 == 0xE0:
453             self.handle_wake_up_timer_cmd(cmd, ret)
454         elif cmd[0] == 0xC8:
455             self.handle_low_duty_cycle_cmd(cmd, ret)
456         elif cmd[0] == 0xC0:
457             self.handle_low_battery_detector_cmd(cmd, ret)
458         elif cmd[0] == 0x00:
459             self.handle_status_read_cmd(cmd, ret)
460         else:
461             c = '%02x %02x' % tuple(cmd)
462             r = '%02x %02x' % tuple(ret)
463             self.putx(0, 16, ['Unknown command: %s (reply: %s)!' % (c, r)])
464
465     def decode(self, ss, es, data):
466         ptype, mosi, miso = data
467
468         # For now, only use DATA and BITS packets.
469         if ptype not in ('DATA', 'BITS'):
470             return
471
472         # Store the individual bit values and ss/es numbers. The next packet
473         # is guaranteed to be a 'DATA' packet belonging to this 'BITS' one.
474         if ptype == 'BITS':
475             if mosi is not None:
476                 self.mosi_bits.extend(reversed(mosi))
477             if miso is not None:
478                 self.miso_bits.extend(reversed(miso))
479             return
480
481         # Append new bytes.
482         self.mosi_bytes.append(mosi)
483         self.miso_bytes.append(miso)
484
485         # All commands consist of 2 bytes.
486         if len(self.mosi_bytes) < 2:
487             return
488
489         self.row_pos = [0, 8, 8]
490
491         self.handle_cmd(self.mosi_bytes, self.miso_bytes)
492
493         self.mosi_bytes, self.miso_bytes = [], []
494         self.mosi_bits, self.miso_bits = [], []