|
@@ -1,4 +1,5 @@
|
|
|
import enum
|
|
|
+import typing
|
|
|
|
|
|
import spidev
|
|
|
|
|
@@ -24,7 +25,12 @@ class CC1101:
|
|
|
_READ_BURST = 0xC0
|
|
|
|
|
|
class _SPIAddress(enum.IntEnum):
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ FREQ2 = 0x0D
|
|
|
+ FREQ1 = 0x0E
|
|
|
+ FREQ0 = 0x0F
|
|
|
|
|
|
|
|
|
|
|
@@ -44,37 +50,56 @@ class CC1101:
|
|
|
MARCSTATE - Main Radio Control State Machine State
|
|
|
"""
|
|
|
|
|
|
+
|
|
|
IDLE = 0x01
|
|
|
|
|
|
|
|
|
_SUPPORTED_PARTNUM = 0
|
|
|
_SUPPORTED_VERSION = 0x14
|
|
|
|
|
|
+ _CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ = 26e6
|
|
|
+
|
|
|
+
|
|
|
+ _FREQUENCY_CONTROL_WORD_HERTZ_FACTOR = _CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ / 2 ** 16
|
|
|
+
|
|
|
def __init__(self) -> None:
|
|
|
self._spi = spidev.SpiDev()
|
|
|
|
|
|
def _reset(self) -> None:
|
|
|
+
|
|
|
assert self._spi.xfer([self._SPIAddress.SRES, 0]) == [0x0F, 0x0F]
|
|
|
|
|
|
- def _read_burst(self, register: _SPIAddress) -> int:
|
|
|
- response = self._spi.xfer([register | self._READ_BURST, 0])
|
|
|
- assert len(response) == 2, response
|
|
|
- assert response[0] == 0
|
|
|
- return response[1]
|
|
|
+ def _read_burst(self, start_register: _SPIAddress, length: int) -> typing.List[int]:
|
|
|
+ response = self._spi.xfer([start_register | self._READ_BURST] + [0] * length)
|
|
|
+ assert len(response) == length + 1, response
|
|
|
+ assert response[0] == 0, response
|
|
|
+ return response[1:]
|
|
|
+
|
|
|
+ def _read_status_register(self, register: _SPIAddress) -> int:
|
|
|
+ values = self._read_burst(start_register=register, length=1)
|
|
|
+ assert len(values) == 1, values
|
|
|
+ return values[0]
|
|
|
+
|
|
|
+ def _write_burst(
|
|
|
+ self, start_register: _SPIAddress, values: typing.List[int]
|
|
|
+ ) -> None:
|
|
|
+ response = self._spi.xfer([start_register | self._WRITE_BURST] + values)
|
|
|
+ assert len(response) == len(values) + 1, response
|
|
|
+ assert all(v == 0x0F for v in response), response
|
|
|
|
|
|
def __enter__(self) -> "CC1101":
|
|
|
|
|
|
self._spi.open(0, 0)
|
|
|
self._spi.max_speed_hz = 55700
|
|
|
self._reset()
|
|
|
- partnum = self._read_burst(self._SPIAddress.PARTNUM)
|
|
|
+ partnum = self._read_status_register(self._SPIAddress.PARTNUM)
|
|
|
if partnum != self._SUPPORTED_PARTNUM:
|
|
|
raise ValueError(
|
|
|
"unexpected chip part number {} (expected: {})".format(
|
|
|
partnum, self._SUPPORTED_PARTNUM
|
|
|
)
|
|
|
)
|
|
|
- version = self._read_burst(self._SPIAddress.VERSION)
|
|
|
+ version = self._read_status_register(self._SPIAddress.VERSION)
|
|
|
if version != self._SUPPORTED_VERSION:
|
|
|
raise ValueError(
|
|
|
"unexpected chip version number {} (expected: {})".format(
|
|
@@ -93,7 +118,7 @@ class CC1101:
|
|
|
|
|
|
def getMainRadioControlStateMachineState(self) -> MainRadioControlStateMachineState:
|
|
|
return self.MainRadioControlStateMachineState(
|
|
|
- self._read_burst(self._SPIAddress.MARCSTATE)
|
|
|
+ self._read_status_register(self._SPIAddress.MARCSTATE)
|
|
|
)
|
|
|
|
|
|
def getMARCState(self) -> MainRadioControlStateMachineState:
|
|
@@ -101,3 +126,42 @@ class CC1101:
|
|
|
alias for getMainRadioControlStateMachineState()
|
|
|
"""
|
|
|
return self.getMainRadioControlStateMachineState()
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def _frequency_control_word_to_hertz(cls, control_word: typing.List[int]) -> float:
|
|
|
+ return (
|
|
|
+ int.from_bytes(control_word, byteorder="big", signed=False)
|
|
|
+ * cls._FREQUENCY_CONTROL_WORD_HERTZ_FACTOR
|
|
|
+ )
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def _hertz_to_frequency_control_word(cls, hertz: float) -> typing.List[int]:
|
|
|
+ return list(
|
|
|
+ round(hertz / cls._FREQUENCY_CONTROL_WORD_HERTZ_FACTOR).to_bytes(
|
|
|
+ length=3, byteorder="big", signed=False
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+ def _get_base_frequency_control_word(self) -> typing.List[int]:
|
|
|
+
|
|
|
+
|
|
|
+ return self._read_burst(start_register=self._SPIAddress.FREQ2, length=3)
|
|
|
+
|
|
|
+ def _set_base_frequency_control_word(self, control_word: typing.List[int]) -> None:
|
|
|
+ self._write_burst(start_register=self._SPIAddress.FREQ2, values=control_word)
|
|
|
+
|
|
|
+ def get_base_frequency_hertz(self) -> float:
|
|
|
+ return self._frequency_control_word_to_hertz(
|
|
|
+ self._get_base_frequency_control_word()
|
|
|
+ )
|
|
|
+
|
|
|
+ def set_base_frequency_hertz(self, freq: float) -> None:
|
|
|
+ self._set_base_frequency_control_word(
|
|
|
+ self._hertz_to_frequency_control_word(freq)
|
|
|
+ )
|
|
|
+
|
|
|
+ def __str__(self) -> str:
|
|
|
+ return "CC1101(marcstate={}, base_frequency={:.2f}MHz)".format(
|
|
|
+ self.getMainRadioControlStateMachineState().name.lower(),
|
|
|
+ self.get_base_frequency_hertz() / 10 ** 6,
|
|
|
+ )
|