2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2020 Jorge Solla Rubiales <jorgesolla@gmail.com>
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:
13 ## The above copyright notice and this permission notice shall be included in all
14 ## copies or substantial portions of the Software.
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
24 import sigrokdecode
as srd
25 from common
.srdhelper
import SrdIntEnum
28 0: [{'name': 'CH_NO', 'stbit': 7, 'nbits': 8}],
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},
41 {'name': 'TX_AFW (TX addr width)', 'stbit': 6, 'nbits': 3},
42 {'name': 'RX_AFW (RX addr width)', 'stbit': 2, 'nbits': 3},
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}],
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'}},
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'}},
74 {'name': 'AM', 'stbit': 7, 'nbits': 1},
75 {'name': 'DR', 'stbit': 5, 'nbits': 1},
78 Ann
= SrdIntEnum
.from_str('Ann', 'CMD REG_WR REG_RD TX RX RESP WARN')
80 class Decoder(srd
.Decoder
):
84 longname
= 'Nordic Semiconductor nRF905'
85 desc
= '433/868/933MHz transceiver chip.'
89 tags
= ['IC', 'Wireless/RF']
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'),
97 ('warning', 'Warning'),
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
,)),
109 self
.ss_cmd
, self
.es_cmd
= 0, 0
110 self
.cs_asserted
= False
114 self
.mosi_bytes
, self
.miso_bytes
= [], []
115 self
.cmd_samples
= {'ss': 0, 'es': 0}
118 self
.out_ann
= self
.register(srd
.OUTPUT_ANN
)
120 def extract_bits(self
, byte
, start_bit
, num_bits
):
121 begin
= 7 - start_bit
122 end
= begin
+ num_bits
123 if begin
< 0 or end
> 8:
125 binary
= format(byte
, '08b')[begin
:end
]
126 return int(binary
, 2)
128 def extract_vars(self
, reg_vars
, reg_value
):
129 # Iterate all vars on current register.
132 var_value
= self
.extract_bits(reg_value
, var
['stbit'], var
['nbits'])
133 data
+= var
['name'] + ' = ' + str(var_value
)
136 # If var has options, just add the option meaning.
138 opt
= var
['opts'].get(var_value
, 'unknown')
139 data
+= ' (' + opt
+ ')'
142 if reg_vars
.index(var
) != len(reg_vars
) - 1:
146 def parse_config_register(self
, addr
, value
, is_write
):
148 data
= 'CFG_REG[' + hex(addr
) + '] -> '
150 # Get register vars for this register.
152 reg_vars
= CFG_REGS
[addr
]
154 # Invalid register address.
155 self
.put(value
[1], value
[2],
156 self
.out_ann
, [Ann
.WARN
, ['Invalid reg. addr']])
159 data
+= self
.extract_vars(reg_vars
, reg_value
)
161 ann
= Ann
.REG_WR
if is_write
else Ann
.REG_RD
162 self
.put(value
[1], value
[2], self
.out_ann
, [ann
, [data
]])
164 def parse_config_registers(self
, addr
, registers
, is_write
):
166 while i
< len(registers
):
169 self
.parse_config_register(reg_addr
, registers
[i
], is_write
)
172 def dump_cmd_bytes(self
, prefix
, cmd_bytes
, ann
):
173 ss
, es
= cmd_bytes
[1][1], 0
175 for byte
in cmd_bytes
[1:]:
176 data
+= format(byte
[0], '02X') + ' '
178 self
.put(ss
, es
, self
.out_ann
, [ann
, [prefix
+ data
]])
181 start_addr
= self
.mosi_bytes
[0][0] & 0x0F
184 self
.parse_config_registers(start_addr
, self
.mosi_bytes
[1:], True)
187 start_addr
= self
.mosi_bytes
[0][0] & 0x0F
190 self
.parse_config_registers(start_addr
, self
.miso_bytes
[1:], False)
192 def handle_WTP(self
):
193 self
.dump_cmd_bytes('Write TX payload.: ', self
.mosi_bytes
, Ann
.TX
)
195 def handle_RTP(self
):
196 self
.dump_cmd_bytes('Read TX payload: ', self
.miso_bytes
, Ann
.RESP
)
198 def handle_WTA(self
):
199 self
.dump_cmd_bytes('Write TX addr: ', self
.mosi_bytes
, Ann
.REG_WR
)
201 def handle_RTA(self
):
202 self
.dump_cmd_bytes('Read TX addr: ', self
.miso_bytes
, Ann
.RESP
)
204 def handle_RRP(self
):
205 self
.dump_cmd_bytes('Read RX payload: ', self
.miso_bytes
, Ann
.RX
)
208 cmd
, dta
= self
.mosi_bytes
[0], self
.mosi_bytes
[1]
209 channel
= ((cmd
[0] & 0x01) << 8) + dta
210 data
= self
.extract_vars(CHN_CFG
, cmd
[0])
211 data
+= '| CHN = ' + str(channel
)
212 self
.put(self
.mosi_bytes
[0][1], self
.mosi_bytes
[1][2],
213 self
.out_ann
, [Ann
.REG_WR
, [data
]])
215 def handle_STAT(self
):
216 status
= 'STAT = ' + self
.extract_vars(STAT_REG
, self
.miso_bytes
[0][0])
217 self
.put(self
.miso_bytes
[0][1], self
.miso_bytes
[0][2],
218 self
.out_ann
, [Ann
.REG_RD
, [status
]])
220 def process_cmd(self
):
221 cmd
, cmd_name
, cmd_hnd
= '', '', None
223 for byte
in self
.mosi_bytes
:
224 cmd
+= hex(byte
[0]) + ' '
226 cmd
= self
.mosi_bytes
[0][0]
228 if (cmd
& 0xF0) == 0x00:
229 cmd_name
, cmd_hnd
= 'CMD: W_CONFIG (WC)', self
.handle_WC
230 elif (cmd
& 0xF0) == 0x10:
231 cmd_name
, cmd_hnd
= 'CMD: R_CONFIG (RC)', self
.handle_RC
233 cmd_name
, cmd_hnd
= 'CMD: W_TX_PAYLOAD (WTP)', self
.handle_WTP
235 cmd_name
, cmd_hnd
= 'CMD: R_TX_PAYLOAD (RTP)', self
.handle_RTP
237 cmd_name
, cmd_hnd
= 'CMD: W_TX_ADDRESS (WTA)', self
.handle_WTA
239 cmd_name
, cmd_hnd
= 'CMD: R_TX_ADDRESS (RTA)', self
.handle_RTA
241 cmd_name
, cmd_hnd
= 'CMD: R_RX_PAYLOAD (RRP)', self
.handle_RRP
242 elif (cmd
& 0xF0 == 0x80):
243 cmd_name
, cmd_hnd
= 'CMD: CHANNEL_CONFIG (CC)', self
.handle_CC
245 # Report command name.
246 self
.put(self
.cmd_samples
['ss'], self
.cmd_samples
['es'],
247 self
.out_ann
, [Ann
.CMD
, [cmd_name
]])
249 # Handle status byte.
253 if cmd_hnd
is not None:
256 def set_cs_status(self
, sample
, asserted
):
257 if self
.cs_asserted
== asserted
:
261 self
.cmd_samples
['ss'] = sample
262 self
.cmd_samples
['es'] = -1
264 self
.cmd_samples
['es'] = sample
266 self
.cs_asserted
= asserted
268 def decode(self
, ss
, es
, data
):
269 ptype
, data1
, data2
= data
271 if ptype
== 'CS-CHANGE':
272 if data1
is None and data2
is None:
273 self
.requirements_met
= False
274 raise ChannelError('CS# pin required.')
276 if data1
is None and data2
== 0:
277 self
.set_cs_status(ss
, True)
279 elif data1
is None and data2
== 1:
280 self
.set_cs_status(ss
, False)
282 elif data1
== 1 and data2
== 0:
283 self
.set_cs_status(ss
, True)
285 elif data1
== 0 and data2
== 1:
286 self
.set_cs_status(ss
, False)
287 if len(self
.mosi_bytes
):
291 elif ptype
== 'DATA':
292 # Ignore traffic if CS is not asserted.
293 if self
.cs_asserted
is False:
296 mosi
, miso
= data1
, data2
297 if miso
is None or mosi
is None:
298 raise ChannelError('Both MISO and MOSI pins required.')
300 self
.mosi_bytes
.append((mosi
, ss
, es
))
301 self
.miso_bytes
.append((miso
, ss
, es
))