]> sigrok.org Git - libsigrokdecode.git/blame - decoders/nrf905/pd.py
nrf905: Simplify a few code snippets.
[libsigrokdecode.git] / decoders / nrf905 / pd.py
CommitLineData
3801453c
JS
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2020 Jorge Solla Rubiales <jorgesolla@gmail.com>
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
24import sigrokdecode as srd
23e55dd5 25from common.srdhelper import SrdIntEnum
3801453c
JS
26
27CFG_REGS = {
28 0: [{'name': 'CH_NO', 'stbit': 7, 'nbits': 8}],
29 1: [
30 {'name': 'AUTO_RETRAN', 'stbit': 5, 'nbits': 1,
31 'opts': {0: 'No retransmission', 1: 'Retransmission of data packet'}},
32 {'name': 'RX_RED_PWR', 'stbit': 4, 'nbits': 1,
33 'opts': {0: 'Normal operation', 1: 'Reduced power'}},
34 {'name': 'PA_PWR', 'stbit': 3, 'nbits': 2,
35 'opts': {0: '-10 dBm', 1: '-2 dBm', 2: '+6 dBm', 3: '+10 dBm'}},
36 {'name': 'HFREQ_PLL', 'stbit': 1, 'nbits': 1,
37 'opts': {0: '433 MHz', 1: '868 / 915 MHz'}},
38 {'name': 'CH_NO_8', 'stbit': 0, 'nbits': 1},
39 ],
40 2: [
41 {'name': 'TX_AFW (TX addr width)', 'stbit': 6, 'nbits': 3},
42 {'name': 'RX_AFW (RX addr width)', 'stbit': 2, 'nbits': 3},
43 ],
44 3: [{'name': 'RW_PW (RX payload width)', 'stbit': 5, 'nbits': 6}],
45 4: [{'name': 'TX_PW (TX payload width)', 'stbit': 5, 'nbits': 6}],
46 5: [{'name': 'RX_ADDR_0', 'stbit': 7, 'nbits': 8}],
47 6: [{'name': 'RX_ADDR_1', 'stbit': 7, 'nbits': 8}],
48 7: [{'name': 'RX_ADDR_2', 'stbit': 7, 'nbits': 8}],
49 8: [{'name': 'RX_ADDR_3', 'stbit': 7, 'nbits': 8}],
50 9: [
51 {'name': 'CRC_MODE', 'stbit': 7, 'nbits': 1,
52 'opts': {0: '8 CRC check bit', 1: '16 CRC check bit'}},
53 {'name': 'CRC_EN', 'stbit': 6, 'nbits': 1,
54 'opts': {0: 'Disabled', 1: 'Enabled'}},
55 {'name': 'XOR', 'stbit': 5, 'nbits': 3,
56 'opts': {0: '4 MHz', 1: '8 MHz', 2: '12 MHz',
57 3: '16 MHz', 4: '20 MHz'}},
58 {'name': 'UP_CLK_EN', 'stbit': 2, 'nbits': 1,
59 'opts': {0: 'No external clock signal avail.',
60 1: 'External clock signal enabled'}},
61 {'name': 'UP_CLK_FREQ', 'stbit': 1, 'nbits': 2,
62 'opts': {0: '4 MHz', 1: '2 MHz', 2: '1 MHz', 3: '500 kHz'}},
63 ],
64}
65
66CHN_CFG = [
67 {'name': 'PA_PWR', 'stbit': 3, 'nbits': 2,
68 'opts': {0: '-10 dBm', 1: '-2 dBm', 2: '+6 dBm', 3: '+10 dBm'}},
69 {'name': 'HFREQ_PLL', 'stbit': 1, 'nbits': 1,
70 'opts': {0: '433 MHz', 1: '868 / 915 MHz'}},
71]
72
73STAT_REG = [
74 {'name': 'AM', 'stbit': 7, 'nbits': 1},
75 {'name': 'DR', 'stbit': 5, 'nbits': 1},
76]
77
23e55dd5
UH
78Ann = SrdIntEnum.from_str('Ann', 'CMD REG_WR REG_RD TX RX RESP WARN')
79
3801453c
JS
80class Decoder(srd.Decoder):
81 api_version = 3
82 id = 'nrf905'
83 name = 'nRF905'
84 longname = 'Nordic Semiconductor nRF905'
85 desc = '433/868/933MHz transceiver chip.'
86 license = 'mit'
87 inputs = ['spi']
88 outputs = ['nrf905']
82865866 89 tags = ['IC', 'Wireless/RF']
3801453c
JS
90 annotations = (
91 ('cmd', 'Command sent to the device'),
92 ('reg-write', 'Config register written to the device'),
93 ('reg-read', 'Config register read from the device'),
94 ('tx-data', 'Payload sent to the device'),
95 ('rx-data', 'Payload read from the device'),
96 ('resp', 'Response to commands received from the device'),
23e55dd5 97 ('warning', 'Warning'),
3801453c 98 )
3801453c 99 annotation_rows = (
23e55dd5
UH
100 ('commands', 'Commands', (Ann.CMD,)),
101 ('responses', 'Responses', (Ann.RESP,)),
102 ('registers', 'Registers', (Ann.REG_WR, Ann.REG_RD)),
103 ('tx', 'Transmitted data', (Ann.TX,)),
104 ('rx', 'Received data', (Ann.RX,)),
105 ('warnings', 'Warnings', (Ann.WARN,)),
3801453c
JS
106 )
107
3801453c
JS
108 def __init__(self):
109 self.ss_cmd, self.es_cmd = 0, 0
110 self.cs_asserted = False
826981ac
UH
111 self.reset()
112
113 def reset(self):
114 self.mosi_bytes, self.miso_bytes = [], []
115 self.cmd_samples = {'ss': 0, 'es': 0}
3801453c
JS
116
117 def start(self):
118 self.out_ann = self.register(srd.OUTPUT_ANN)
119
120 def extract_bits(self, byte, start_bit, num_bits):
121 begin = 7 - start_bit
122 end = begin + num_bits
3801453c
JS
123 if begin < 0 or end > 8:
124 return 0
3801453c
JS
125 binary = format(byte, '08b')[begin:end]
126 return int(binary, 2)
127
128 def extract_vars(self, reg_vars, reg_value):
129 # Iterate all vars on current register.
130 data = ''
131 for var in reg_vars:
104719c1 132 var_value = self.extract_bits(reg_value, var['stbit'], var['nbits'])
3801453c
JS
133 data += var['name'] + ' = ' + str(var_value)
134 opt = ''
135
136 # If var has options, just add the option meaning.
137 if 'opts' in var:
104719c1 138 opt = var['opts'].get(var_value, 'unknown')
3801453c
JS
139 data += ' (' + opt + ')'
140
141 # Add var separator.
142 if reg_vars.index(var) != len(reg_vars) - 1:
104719c1 143 data += ' | '
3801453c
JS
144 return data
145
146 def parse_config_register(self, addr, value, is_write):
147 reg_value = value[0]
148 data = 'CFG_REG[' + hex(addr) + '] -> '
149
150 # Get register vars for this register.
151 if addr in CFG_REGS:
152 reg_vars = CFG_REGS[addr]
153 else:
154 # Invalid register address.
155 self.put(value[1], value[2],
23e55dd5 156 self.out_ann, [Ann.WARN, ['Invalid reg. addr']])
3801453c
JS
157 return
158
159 data += self.extract_vars(reg_vars, reg_value)
160
23e55dd5 161 ann = Ann.REG_WR if is_write else Ann.REG_RD
3801453c
JS
162 self.put(value[1], value[2], self.out_ann, [ann, [data]])
163
164 def parse_config_registers(self, addr, registers, is_write):
165 i = 0
166 while i < len(registers):
167 reg_addr = i + addr
168 if reg_addr <= 9:
169 self.parse_config_register(reg_addr, registers[i], is_write)
170 else:
171 print('INVALID REGISTER ADDR ' + hex(reg_addr))
172 i += 1
173
174 def dump_cmd_bytes(self, prefix, cmd_bytes, ann):
104719c1 175 ss, es = cmd_bytes[1][1], 0
3801453c
JS
176 data = ''
177 for byte in cmd_bytes[1:]:
178 data += '0x' + format(byte[0], '02x') + ' '
179 es = byte[2]
3801453c
JS
180 self.put(ss, es, self.out_ann, [ann, [prefix + data]])
181
182 def handle_WC(self):
183 start_addr = self.mosi_bytes[0][0] & 0x0F
3801453c
JS
184 if start_addr > 9:
185 print('ERROR: WRONG OFFSET')
186 return
3801453c
JS
187 self.parse_config_registers(start_addr, self.mosi_bytes[1:], True)
188
189 def handle_RC(self):
190 start_addr = self.mosi_bytes[0][0] & 0x0F
3801453c
JS
191 if start_addr > 9:
192 print('ERROR: WRONG OFFSET')
193 return
194 self.parse_config_registers(start_addr, self.miso_bytes[1:], False)
195
196 def handle_WTP(self):
23e55dd5 197 self.dump_cmd_bytes('Write TX payload.: ', self.mosi_bytes, Ann.TX)
3801453c
JS
198
199 def handle_RTP(self):
23e55dd5 200 self.dump_cmd_bytes('Read TX payload: ', self.miso_bytes, Ann.RESP)
3801453c
JS
201
202 def handle_WTA(self):
23e55dd5 203 self.dump_cmd_bytes('Write TX addr: ', self.mosi_bytes, Ann.REG_WR)
3801453c
JS
204
205 def handle_RTA(self):
23e55dd5 206 self.dump_cmd_bytes('Read TX addr: ', self.miso_bytes, Ann.RESP)
3801453c
JS
207
208 def handle_RRP(self):
23e55dd5 209 self.dump_cmd_bytes('Read RX payload: ', self.miso_bytes, Ann.RX)
3801453c
JS
210
211 def handle_CC(self):
104719c1
UH
212 cmd, dta = self.mosi_bytes[0], self.mosi_bytes[1]
213 channel = ((cmd[0] & 0x01) << 8) + dta
3801453c 214 data = self.extract_vars(CHN_CFG, cmd[0])
104719c1 215 data += '| CHN = ' + str(channel)
3801453c 216 self.put(self.mosi_bytes[0][1], self.mosi_bytes[1][2],
23e55dd5 217 self.out_ann, [Ann.REG_WR, [data]])
3801453c
JS
218
219 def handle_STAT(self):
220 status = 'STAT = ' + self.extract_vars(STAT_REG, self.miso_bytes[0][0])
221 self.put(self.miso_bytes[0][1], self.miso_bytes[0][2],
23e55dd5 222 self.out_ann, [Ann.REG_RD, [status]])
3801453c
JS
223
224 def process_cmd(self):
104719c1 225 cmd, cmd_name, cmd_hnd = '', '', None
3801453c
JS
226
227 for byte in self.mosi_bytes:
228 cmd += hex(byte[0]) + ' '
229
230 cmd = self.mosi_bytes[0][0]
231
232 if (cmd & 0xF0) == 0x00:
104719c1 233 cmd_name, cmd_hnd = 'CMD: W_CONFIG (WC)', self.handle_WC
3801453c 234 elif (cmd & 0xF0) == 0x10:
104719c1 235 cmd_name, cmd_hnd = 'CMD: R_CONFIG (RC)', self.handle_RC
3801453c 236 elif cmd == 0x20:
104719c1 237 cmd_name, cmd_hnd = 'CMD: W_TX_PAYLOAD (WTP)', self.handle_WTP
3801453c 238 elif cmd == 0x21:
104719c1 239 cmd_name, cmd_hnd = 'CMD: R_TX_PAYLOAD (RTP)', self.handle_RTP
3801453c 240 elif cmd == 0x22:
104719c1 241 cmd_name, cmd_hnd = 'CMD: W_TX_ADDRESS (WTA)', self.handle_WTA
3801453c 242 elif cmd == 0x23:
104719c1 243 cmd_name, cmd_hnd = 'CMD: R_TX_ADDRESS (RTA)', self.handle_RTA
3801453c 244 elif cmd == 0x24:
104719c1 245 cmd_name, cmd_hnd = 'CMD: R_RX_PAYLOAD (RRP)', self.handle_RRP
3801453c 246 elif (cmd & 0xF0 == 0x80):
104719c1 247 cmd_name, cmd_hnd = 'CMD: CHANNEL_CONFIG (CC)', self.handle_CC
3801453c
JS
248
249 # Report command name.
250 self.put(self.cmd_samples['ss'], self.cmd_samples['es'],
23e55dd5 251 self.out_ann, [Ann.CMD, [cmd_name]])
3801453c
JS
252
253 # Handle status byte.
254 self.handle_STAT()
255
256 # Handle command.
257 if cmd_hnd is not None:
258 cmd_hnd()
259
260 def set_cs_status(self, sample, asserted):
261 if self.cs_asserted == asserted:
262 return
263
264 if asserted:
265 self.cmd_samples['ss'] = sample
266 self.cmd_samples['es'] = -1
267 else:
268 self.cmd_samples['es'] = sample
269
270 self.cs_asserted = asserted
271
272 def decode(self, ss, es, data):
273 ptype, data1, data2 = data
274
275 if ptype == 'CS-CHANGE':
276 if data1 is None and data2 is None:
277 self.requirements_met = False
278 raise ChannelError('CS# pin required.')
279
280 if data1 is None and data2 == 0:
281 self.set_cs_status(ss, True)
282
283 elif data1 is None and data2 == 1:
284 self.set_cs_status(ss, False)
285
286 elif data1 == 1 and data2 == 0:
287 self.set_cs_status(ss, True)
288
289 elif data1 == 0 and data2 == 1:
290 self.set_cs_status(ss, False)
291 if len(self.mosi_bytes):
292 self.process_cmd()
826981ac 293 self.reset()
3801453c
JS
294
295 elif ptype == 'DATA':
296 # Ignore traffic if CS is not asserted.
297 if self.cs_asserted is False:
298 return
299
300 mosi, miso = data1, data2
301 if miso is None or mosi is None:
302 raise ChannelError('Both MISO and MOSI pins required.')
303
304 self.mosi_bytes.append((mosi, ss, es))
305 self.miso_bytes.append((miso, ss, es))