Skip to content

Commit e03c445

Browse files
Initial revision of the CC1101 SPI Analyzer
1 parent 37dc059 commit e03c445

File tree

6 files changed

+496
-1
lines changed

6 files changed

+496
-1
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @Andrei-Basarab

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Python generated files
2+
**/__pycache__

CC1101SpiProtocol.py

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
2+
from copy import deepcopy
3+
4+
5+
# Table 43: Configuration Registers Overview
6+
CONFIG_REGISTERS = {
7+
0x00: {"register": "IOCFG2", "description": "GDO2 output pin configuration"},
8+
0x01: {"register": "IOCFG1", "description": "GDO1 output pin configuration"},
9+
0x02: {"register": "IOCFG0", "description": "GDO0 output pin configuration"},
10+
0x03: {"register": "FIFOTHR", "description": "RX FIFO and TX FIFO thresholds"},
11+
0x04: {"register": "SYNC1", "description": "Sync word, high byte"},
12+
0x05: {"register": "SYNC0", "description": "Sync word, low byte"},
13+
0x06: {"register": "PKTLEN", "description": "Packet length"},
14+
0x07: {"register": "PKTCTRL1", "description": "Packet automation control"},
15+
0x08: {"register": "PKTCTRL0", "description": "Packet automation control"},
16+
0x09: {"register": "ADDR", "description": "Device address"},
17+
0x0A: {"register": "CHANNR", "description": "Channel number"},
18+
0x0B: {"register": "FSCTRL1", "description": "Frequency synthesizer control"},
19+
0x0C: {"register": "FSCTRL0", "description": "Frequency synthesizer control"},
20+
0x0D: {"register": "FREQ2", "description": "Frequency control word, high byte"},
21+
0x0E: {"register": "FREQ1", "description": "Frequency control word, middle byte"},
22+
0x0F: {"register": "FREQ0", "description": "Frequency control word, low byte"},
23+
0x10: {"register": "MDMCFG4", "description": "Modem configuration"},
24+
0x11: {"register": "MDMCFG3", "description": "Modem configuration"},
25+
0x12: {"register": "MDMCFG2", "description": "Modem configuration"},
26+
0x13: {"register": "MDMCFG1", "description": "Modem configuration"},
27+
0x14: {"register": "MDMCFG0", "description": "Modem configuration"},
28+
0x15: {"register": "DEVIATN", "description": "Modem deviation setting"},
29+
0x16: {"register": "MCSM2", "description": "Main Radio Control State Machine configuration"},
30+
0x17: {"register": "MCSM1", "description": "Main Radio Control State Machine configuration"},
31+
0x18: {"register": "MCSM0", "description": "Main Radio Control State Machine configuration"},
32+
0x19: {"register": "FOCCFG", "description": "Frequency Offset Compensation configuration"},
33+
0x1A: {"register": "BSCFG", "description": "Bit Synchronization configuration"},
34+
0x1B: {"register": "AGCCTRL2", "description": "AGC control"},
35+
0x1C: {"register": "AGCCTRL1", "description": "AGC control"},
36+
0x1D: {"register": "AGCCTRL0", "description": "AGC control"},
37+
0x1E: {"register": "WOREVT1", "description": "High byte Event 0 timeout"},
38+
0x1F: {"register": "WOREVT0", "description": "Low byte Event 0 timeout"},
39+
0x20: {"register": "WORCTRL", "description": "Wake On Radio control"},
40+
0x21: {"register": "FREND1", "description": "Front end RX configuration"},
41+
0x22: {"register": "FREND0", "description": "Front end TX configuration"},
42+
0x23: {"register": "FSCAL3", "description": "Frequency synthesizer calibration"},
43+
0x24: {"register": "FSCAL2", "description": "Frequency synthesizer calibration"},
44+
0x25: {"register": "FSCAL1", "description": "Frequency synthesizer calibration"},
45+
0x26: {"register": "FSCAL0", "description": "Frequency synthesizer calibration"},
46+
0x27: {"register": "RCCTRL1", "description": "RC oscillator configuration"},
47+
0x28: {"register": "RCCTRL0", "description": "RC oscillator configuration"},
48+
0x29: {"register": "FSTEST", "description": "Frequency synthesizer calibration control"},
49+
0x2A: {"register": "PTEST", "description": "Production test"},
50+
0x2B: {"register": "AGCTEST", "description": "AGC test"},
51+
0x2C: {"register": "TEST2", "description": "Various test settings"},
52+
0x2D: {"register": "TEST1", "description": "Various test settings"},
53+
0x2E: {"register": "TEST0", "description": "Various test settings"}
54+
}
55+
56+
# Table 42: Command Strobes
57+
COMMAND_REGISTERS = {
58+
0x30: {"register": "SRES", "description": "Reset chip."},
59+
0x31: {"register": "SFSTXON", "description": "Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA): Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround)."},
60+
0x32: {"register": "SXOFF", "description": "Turn off crystal oscillator."},
61+
0x33: {"register": "SCAL", "description": "Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without setting manual calibration mode (MCSM0.FS_AUTOCAL=0)"},
62+
0x34: {"register": "SRX", "description": "Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1."},
63+
0x35: {"register": "STX", "description": "In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled: Only go to TX if channel is clear."},
64+
0x36: {"register": "SIDLE", "description": "Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable."},
65+
0x38: {"register": "SWOR", "description": "Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if WORCTRL.RC_PD=0."},
66+
0x39: {"register": "SPWD", "description": "Enter power down mode when CSn goes high."},
67+
0x3A: {"register": "SFRX", "description": "Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states."},
68+
0x3B: {"register": "SFTX", "description": "Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states."},
69+
0x3C: {"register": "SWORRST", "description": "Reset real time clock to Event1 value."},
70+
0x3D: {"register": "SNOP", "description": "No operation. May be used to get access to the chip status byte."},
71+
}
72+
73+
# Table 44: Status Registers Overview
74+
STATUS_REGISTERS = {
75+
0x30: {"register": "PARTNUM", "description": "Part number for CC1101"},
76+
0x31: {"register": "VERSION", "description": "Current version number"},
77+
0x32: {"register": "FREQEST", "description": "Frequency Offset Estimate"},
78+
0x33: {"register": "LQI", "description": "Demodulator estimate for Link Quality"},
79+
0x34: {"register": "RSSI", "description": "Received signal strength indication"},
80+
0x35: {"register": "MARCSTATE", "description": "Control state machine state"},
81+
0x36: {"register": "WORTIME1", "description": "High byte of WOR timer"},
82+
0x37: {"register": "WORTIME0", "description": "Low byte of WOR timer"},
83+
0x38: {"register": "PKTSTATUS", "description": "Current GDOx status and packet status"},
84+
0x39: {"register": "VCO_VC_DAC", "description": "Current setting from PLL calibration module"},
85+
0x3A: {"register": "TXBYTES", "description": "Underflow and number of bytes in the TX FIFO"},
86+
0x3B: {"register": "RXBYTES", "description": "Overflow and number of bytes in the RX FIFO"},
87+
0x3C: {"register": "RCCTRL1_STATUS", "description": "Last RC oscillator calibration result"},
88+
0x3D: {"register": "RCCTRL0_STATUS", "description": "Last RC oscillator calibration result"},
89+
}
90+
91+
# 0x35 (0xF5): MARCSTATE – Main Radio Control State Machine State
92+
# MARC_STATE[4:0] - R - Main Radio Control FSM State
93+
# Note: it is not possible to read back the SLEEP or XOFF state numbers because setting CSn low will make the chip enter the IDLE mode from the SLEEP or XOFF states.
94+
MARC_STATE = {
95+
# Value: {State name, State (Figure 25, page 50) }
96+
0x00: {"state_name": "SLEEP", "state": "SLEEP"},
97+
0x01: {"state_name": "IDLE", "state": "IDLE"},
98+
0x02: {"state_name": "XOFF", "state": "XOFF"},
99+
0x03: {"state_name": "VCOON_MC", "state": "MANCAL"},
100+
0x04: {"state_name": "REGON_MC", "state": "MANCAL"},
101+
0x05: {"state_name": "MANCAL", "state": "MANCAL"},
102+
0x06: {"state_name": "VCOON", "state": "FS_WAKEUP"},
103+
0x07: {"state_name": "REGON", "state": "FS_WAKEUP"},
104+
0x08: {"state_name": "STARTCAL", "state": "CALIBRATE"},
105+
0x09: {"state_name": "BWBOOST", "state": "SETTLING"},
106+
0x0A: {"state_name": "FS_LOCK", "state": "SETTLING"},
107+
0x0B: {"state_name": "IFADCON", "state": "SETTLING"},
108+
0x0C: {"state_name": "ENDCAL", "state": "CALIBRATE"},
109+
0x0D: {"state_name": "RX", "state": "RX"},
110+
0x0E: {"state_name": "RX_END", "state": "RX"},
111+
0x0F: {"state_name": "RX_RST", "state": "RX"},
112+
0x10: {"state_name": "TXRX_SWITCH", "state": "TXRX_SETTLING"},
113+
0x11: {"state_name": "RXFIFO_OVERFLOW", "state": "RXFIFO_OVERFLOW"},
114+
0x12: {"state_name": "FSTXON", "state": "FSTXON"},
115+
0x13: {"state_name": "TX", "state": "TX"},
116+
0x14: {"state_name": "TX_END", "state": "TX"},
117+
0x15: {"state_name": "RXTX_SWITCH", "state": "RXTX_SETTLING"},
118+
0x16: {"state_name": "TXFIFO_UNDERFLOW", "state": "TXFIFO_UNDERFLOW"},
119+
}
120+
121+
MULTI_BYTE_REGISTERS = {
122+
0x3E: {"register": "PATABLE", "description": "PA Table"},
123+
0x3F: {"register": "TX/RX FIFO", "description": "Tx / Rx FIFO"},
124+
}
125+
126+
# Table 23: Status Byte Summary (bit masks)
127+
STATUS_BYTE = {
128+
0x80: {"name": "CHIP_RDYn", "description": "Stays high until power and crystal have stabilized. Should always be low when using the SPI interface."},
129+
0x70: {"name": "STATE", "description": "Indicates the current main state machine mode"},
130+
0x0F: {"name": "FIFO_BYTES_AVAILABLE", "description": "The number of bytes available in the RX FIFO or free bytes in the TX FIFO"},
131+
}
132+
133+
STATE_BITS = {
134+
0b000: {"state": "IDLE", "description": "IDLE state (Also reported for some transitional states instead of SETTLING or CALIBRATE)"},
135+
0b001: {"state": "RX", "description": "Receive mode"},
136+
0b010: {"state": "TX", "description": "Transmit mode"},
137+
0b011: {"state": "FSTXON", "description": "Fast TX ready"},
138+
0b100: {"state": "CALIBRATE", "description": "Frequency synthesizer calibration is running"},
139+
0b101: {"state": "SETTLING", "description": "PLL is settling"},
140+
0b110: {"state": "RXFIFO_OVERFLOW", "description": "RX FIFO has overflowed. Read out any useful data, then flush the FIFO with SFRX"},
141+
0b111: {"state": "TXFIFO_UNDERFLOW", "description": "TX FIFO has underflowed. Acknowledge with SFTX"},
142+
}
143+
144+
145+
class ProtocolFrameType:
146+
REGISTER = "register"
147+
COMMAND = "cmd"
148+
STATUS = "status"
149+
PA_TABLE = "pa table"
150+
FIFO = "fifo"
151+
ERROR = "protocol error"
152+
153+
class CC1101SpiProtocol:
154+
PROTOCOL_MSG = {
155+
"request": None,
156+
"response": None,
157+
}
158+
REQUEST = {
159+
"type": None,
160+
"access": None,
161+
"burst": None,
162+
"register": None,
163+
"data": None,
164+
"description": None,
165+
}
166+
RESPONSE = {
167+
"status": None,
168+
"data": None,
169+
}
170+
STATUS = {
171+
"chip_rdy": None,
172+
"state": None,
173+
"fifo_bytes_available": None,
174+
}
175+
176+
def __init__(self):
177+
pass
178+
179+
def process_frame(self, protocol_frame):
180+
protocol_msg = deepcopy(self.PROTOCOL_MSG)
181+
182+
if len(protocol_frame) > 0:
183+
# Interpret Request
184+
protocol_msg["request"] = self.interpret_request(self.get_mosi_data(protocol_frame))
185+
186+
if self.is_read_access(protocol_frame):
187+
# Interpret Response
188+
protocol_msg["response"] = self.interpret_response(self.get_miso_data(protocol_frame))
189+
return protocol_msg
190+
191+
def is_read_access(self, protocol_frame):
192+
return (protocol_frame[0]["mosi"] & 0x80) != 0
193+
194+
def get_mosi_data(self, protocol_frame):
195+
return [x["mosi"] for x in protocol_frame]
196+
197+
def get_miso_data(self, protocol_frame):
198+
return [x["miso"] for x in protocol_frame]
199+
200+
def is_read(self, data_byte):
201+
return True if (data_byte & 0x80) != 0 else False
202+
203+
def is_write(self, data_byte):
204+
return True if (data_byte & 0x80) == 0 else False
205+
206+
def is_burst(self, data_byte):
207+
return True if (data_byte & 0x40) != 0 else False
208+
209+
def interpret_register(self, data_byte):
210+
register = None
211+
address = data_byte & 0x3F
212+
frame_type = None
213+
214+
if address < 0x30:
215+
frame_type = ProtocolFrameType.REGISTER
216+
register = CONFIG_REGISTERS[address]
217+
elif address == 0x3E:
218+
frame_type = ProtocolFrameType.PA_TABLE
219+
register = MULTI_BYTE_REGISTERS[address]
220+
elif address == 0x3F:
221+
frame_type = ProtocolFrameType.FIFO
222+
register = MULTI_BYTE_REGISTERS[address]
223+
elif self.is_read(data_byte) and self.is_burst(data_byte):
224+
frame_type = ProtocolFrameType.STATUS
225+
register = STATUS_REGISTERS[address]
226+
else:
227+
frame_type = ProtocolFrameType.COMMAND
228+
register = COMMAND_REGISTERS[address]
229+
230+
return frame_type, register["register"], register["description"]
231+
232+
def interpret_request(self, data):
233+
request = deepcopy(self.REQUEST)
234+
235+
# Access mode
236+
request["access"] = "W" if self.is_write(data[0]) else "R"
237+
request["burst"] = "B" if self.is_burst(data[0]) else ""
238+
239+
# Register address
240+
request["type"], request["register"], request["description"] = self.interpret_register(data[0])
241+
242+
# Data Byte
243+
if len(data) > 1:
244+
request["data"] = data[1:]
245+
246+
return request
247+
248+
def interpret_status(self, status_byte):
249+
status = deepcopy(self.STATUS)
250+
status["chip_rdy"] = False if (status_byte & 0x80) != 0 else True
251+
status["state"] = STATE_BITS[(status_byte & 0x70) >> 4]["state"]
252+
status["fifo_bytes_available"] = (status_byte & 0x0F)
253+
return status
254+
255+
def interpret_response(self, data):
256+
response = deepcopy(self.RESPONSE)
257+
258+
# Status Byte
259+
response["status"] = self.interpret_status(data[0])
260+
261+
# Data byte
262+
response["data"] = data[1:]
263+
264+
return response

0 commit comments

Comments
 (0)