|
| 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