__init__.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. # python-cc1101 - Python Library to Transmit RF Signals via C1101 Transceivers
  2. #
  3. # Copyright (C) 2020 Fabian Peter Hammerle <fabian@hammerle.me>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. import contextlib
  18. import enum
  19. import logging
  20. import math
  21. import typing
  22. import spidev
  23. from cc1101.addresses import (
  24. StrobeAddress,
  25. ConfigurationRegisterAddress,
  26. StatusRegisterAddress,
  27. FIFORegisterAddress,
  28. )
  29. from cc1101.options import PacketLengthMode, SyncMode, ModulationFormat
  30. _LOGGER = logging.getLogger(__name__)
  31. class Pin(enum.Enum):
  32. GDO0 = "GDO0"
  33. class _TransceiveMode(enum.IntEnum):
  34. """
  35. PKTCTRL0.PKT_FORMAT
  36. """
  37. FIFO = 0b00
  38. SYNCHRONOUS_SERIAL = 0b01
  39. RANDOM_TRANSMISSION = 0b10
  40. ASYNCHRONOUS_SERIAL = 0b11
  41. class MainRadioControlStateMachineState(enum.IntEnum):
  42. """
  43. MARCSTATE - Main Radio Control State Machine State
  44. """
  45. # see "Figure 13: Simplified State Diagram"
  46. # and "Figure 25: Complete Radio Control State Diagram"
  47. IDLE = 0x01
  48. STARTCAL = 0x08 # after IDLE
  49. BWBOOST = 0x09 # after STARTCAL
  50. FS_LOCK = 0x0A
  51. RX = 0x0D
  52. RXFIFO_OVERFLOW = 0x11
  53. TX = 0x13
  54. # TXFIFO_UNDERFLOW = 0x16
  55. class CC1101:
  56. # > All transfers on the SPI interface are done
  57. # > most significant bit first.
  58. # > All transactions on the SPI interface start with
  59. # > a header byte containing a R/W bit, a access bit (B),
  60. # > and a 6-bit address (A5 - A0).
  61. # > [...]
  62. # > Table 45: SPI Address Space
  63. _WRITE_SINGLE_BYTE = 0x00
  64. # > Registers with consecutive addresses can be
  65. # > accessed in an efficient way by setting the
  66. # > burst bit (B) in the header byte. The address
  67. # > bits (A5 - A0) set the start address in an
  68. # > internal address counter. This counter is
  69. # > incremented by one each new byte [...]
  70. _WRITE_BURST = 0x40
  71. _READ_SINGLE_BYTE = 0x80
  72. _READ_BURST = 0xC0
  73. # 29.3 Status Register Details
  74. _SUPPORTED_PARTNUM = 0
  75. _SUPPORTED_VERSION = 0x14
  76. _CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ = 26e6
  77. # see "21 Frequency Programming"
  78. # > f_carrier = f_XOSC / 2**16 * (FREQ + CHAN * ((256 + CHANSPC_M) * 2**CHANSPC_E-2))
  79. _FREQUENCY_CONTROL_WORD_HERTZ_FACTOR = _CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ / 2 ** 16
  80. def __init__(self) -> None:
  81. self._spi = spidev.SpiDev()
  82. @staticmethod
  83. def _log_chip_status_byte(chip_status: int) -> None:
  84. # see "10.1 Chip Status Byte" & "Table 23: Status Byte Summary"
  85. _LOGGER.debug(
  86. "chip status byte: CHIP_RDYn=%d STATE=%s FIFO_BYTES_AVAILBLE=%d",
  87. chip_status >> 7,
  88. bin((chip_status >> 4) & 0b111),
  89. chip_status & 0b1111,
  90. )
  91. def _read_single_byte(
  92. self, register: typing.Union[ConfigurationRegisterAddress, FIFORegisterAddress]
  93. ) -> int:
  94. response = self._spi.xfer([register | self._READ_SINGLE_BYTE, 0])
  95. assert len(response) == 2, response
  96. self._log_chip_status_byte(response[0])
  97. return response[1]
  98. def _read_burst(
  99. self,
  100. start_register: typing.Union[ConfigurationRegisterAddress, FIFORegisterAddress],
  101. length: int,
  102. ) -> typing.List[int]:
  103. response = self._spi.xfer([start_register | self._READ_BURST] + [0] * length)
  104. assert len(response) == length + 1, response
  105. self._log_chip_status_byte(response[0])
  106. return response[1:]
  107. def _read_status_register(self, register: StatusRegisterAddress) -> int:
  108. # > For register addresses in the range 0x30-0x3D,
  109. # > the burst bit is used to select between
  110. # > status registers when burst bit is one, and
  111. # > between command strobes when burst bit is
  112. # > zero. [...]
  113. # > Because of this, burst access is not available
  114. # > for status registers and they must be accessed
  115. # > one at a time. The status registers can only be
  116. # > read.
  117. response = self._spi.xfer([register | self._READ_BURST, 0])
  118. assert len(response) == 2, response
  119. self._log_chip_status_byte(response[0])
  120. return response[1]
  121. def _command_strobe(self, register: StrobeAddress) -> None:
  122. # see "10.4 Command Strobes"
  123. _LOGGER.debug("sending command strobe 0x%02x", register)
  124. response = self._spi.xfer([register | self._WRITE_SINGLE_BYTE])
  125. assert len(response) == 1, response
  126. self._log_chip_status_byte(response[0])
  127. def _write_burst(
  128. self,
  129. start_register: typing.Union[ConfigurationRegisterAddress, FIFORegisterAddress],
  130. values: typing.List[int],
  131. ) -> None:
  132. _LOGGER.debug(
  133. "writing burst: start_register=0x%02x values=%s", start_register, values
  134. )
  135. response = self._spi.xfer([start_register | self._WRITE_BURST] + values)
  136. assert len(response) == len(values) + 1, response
  137. self._log_chip_status_byte(response[0])
  138. assert all(v == response[0] for v in response[1:]), response
  139. def _reset(self) -> None:
  140. self._command_strobe(StrobeAddress.SRES)
  141. def _get_symbol_rate_exponent(self) -> int:
  142. """
  143. MDMCFG4.DRATE_E
  144. """
  145. return self._read_single_byte(ConfigurationRegisterAddress.MDMCFG4) & 0b00001111
  146. def _set_symbol_rate_exponent(self, exponent: int):
  147. mdmcfg4 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG4)
  148. mdmcfg4 &= 0b11110000
  149. mdmcfg4 |= exponent
  150. self._write_burst(
  151. start_register=ConfigurationRegisterAddress.MDMCFG4, values=[mdmcfg4]
  152. )
  153. def _get_symbol_rate_mantissa(self) -> int:
  154. """
  155. MDMCFG3.DRATE_M
  156. """
  157. return self._read_single_byte(ConfigurationRegisterAddress.MDMCFG3)
  158. def _set_symbol_rate_mantissa(self, mantissa: int) -> None:
  159. self._write_burst(
  160. start_register=ConfigurationRegisterAddress.MDMCFG3, values=[mantissa]
  161. )
  162. @classmethod
  163. def _symbol_rate_floating_point_to_real(cls, mantissa: int, exponent: int) -> float:
  164. # see "12 Data Rate Programming"
  165. return (
  166. (256 + mantissa)
  167. * (2 ** exponent)
  168. * cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ
  169. / (2 ** 28)
  170. )
  171. @classmethod
  172. def _symbol_rate_real_to_floating_point(cls, real: float) -> typing.Tuple[int, int]:
  173. # see "12 Data Rate Programming"
  174. assert real > 0, real
  175. exponent = math.floor(
  176. math.log2(real / cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ) + 20
  177. )
  178. mantissa = round(
  179. real * 2 ** 28 / cls._CRYSTAL_OSCILLATOR_FREQUENCY_HERTZ / 2 ** exponent
  180. - 256
  181. )
  182. if mantissa == 256:
  183. exponent += 1
  184. mantissa = 0
  185. assert 0 < exponent <= 2 ** 4, exponent
  186. assert mantissa <= 2 ** 8, mantissa
  187. return mantissa, exponent
  188. def get_symbol_rate_baud(self) -> float:
  189. return self._symbol_rate_floating_point_to_real(
  190. mantissa=self._get_symbol_rate_mantissa(),
  191. exponent=self._get_symbol_rate_exponent(),
  192. )
  193. def set_symbol_rate_baud(self, real: float) -> None:
  194. # > The data rate can be set from 0.6 kBaud to 500 kBaud [...]
  195. mantissa, exponent = self._symbol_rate_real_to_floating_point(real)
  196. self._set_symbol_rate_mantissa(mantissa)
  197. self._set_symbol_rate_exponent(exponent)
  198. def get_modulation_format(self) -> ModulationFormat:
  199. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  200. return ModulationFormat((mdmcfg2 >> 4) & 0b111)
  201. def _set_modulation_format(self, modulation_format: ModulationFormat) -> None:
  202. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  203. mdmcfg2 &= ~(modulation_format << 4)
  204. mdmcfg2 |= modulation_format << 4
  205. self._write_burst(ConfigurationRegisterAddress.MDMCFG2, [mdmcfg2])
  206. def enable_manchester_code(self) -> None:
  207. """
  208. MDMCFG2.MANCHESTER_EN
  209. Enable manchester encoding & decoding.
  210. """
  211. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  212. mdmcfg2 |= 0b1000
  213. self._write_burst(ConfigurationRegisterAddress.MDMCFG2, [mdmcfg2])
  214. def get_sync_mode(self) -> SyncMode:
  215. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  216. return SyncMode(mdmcfg2 & 0b11)
  217. def set_sync_mode(self, mode: SyncMode) -> None:
  218. """
  219. MDMCFG2.SYNC_MODE
  220. see "14.3 Byte Synchronization"
  221. """
  222. mdmcfg2 = self._read_single_byte(ConfigurationRegisterAddress.MDMCFG2)
  223. mdmcfg2 &= 0b11111100
  224. mdmcfg2 |= mode
  225. self._write_burst(ConfigurationRegisterAddress.MDMCFG2, [mdmcfg2])
  226. def _set_power_amplifier_setting_index(self, setting_index: int) -> None:
  227. """
  228. FREND0.PA_POWER
  229. > This value is an index to the PATABLE,
  230. > which can be programmed with up to 8 different PA settings.
  231. > In OOK/ASK mode, this selects the PATABLE index to use
  232. > when transmitting a '1'.
  233. > PATABLE index zero is used in OOK/ASK when transmitting a '0'.
  234. > The PATABLE settings from index 0 to the PA_POWER value are
  235. > used for > ASK TX shaping, [...]
  236. see "Figure 32: Shaping of ASK Signal"
  237. > If OOK modulation is used, the logic 0 and logic 1 power levels
  238. > shall be programmed to index 0 and 1 respectively.
  239. """
  240. frend0 = self._read_single_byte(ConfigurationRegisterAddress.FREND0)
  241. frend0 &= 0b000
  242. frend0 |= setting_index
  243. self._write_burst(ConfigurationRegisterAddress.FREND0, [setting_index])
  244. def __enter__(self) -> "CC1101":
  245. # https://docs.python.org/3/reference/datamodel.html#object.__enter__
  246. self._spi.open(0, 0)
  247. self._spi.max_speed_hz = 55700 # empirical
  248. self._reset()
  249. partnum = self._read_status_register(StatusRegisterAddress.PARTNUM)
  250. if partnum != self._SUPPORTED_PARTNUM:
  251. raise ValueError(
  252. "unexpected chip part number {} (expected: {})".format(
  253. partnum, self._SUPPORTED_PARTNUM
  254. )
  255. )
  256. version = self._read_status_register(StatusRegisterAddress.VERSION)
  257. if version != self._SUPPORTED_VERSION:
  258. raise ValueError(
  259. "unexpected chip version number {} (expected: {})".format(
  260. version, self._SUPPORTED_VERSION
  261. )
  262. )
  263. # 6:4 MOD_FORMAT: OOK (default: 2-FSK)
  264. self._set_modulation_format(ModulationFormat.ASK_OOK)
  265. self._set_power_amplifier_setting_index(1)
  266. self._disable_data_whitening()
  267. # 7:6 unused
  268. # 5:4 FS_AUTOCAL: calibrate when going from IDLE to RX or TX
  269. # 3:2 PO_TIMEOUT: default
  270. # 1 PIN_CTRL_EN: default
  271. # 0 XOSC_FORCE_ON: default
  272. self._write_burst(ConfigurationRegisterAddress.MCSM0, [0b010100])
  273. marcstate = self.get_main_radio_control_state_machine_state()
  274. if marcstate != MainRadioControlStateMachineState.IDLE:
  275. raise ValueError("expected marcstate idle (actual: {})".format(marcstate))
  276. return self
  277. def __exit__(self, exc_type, exc_value, traceback): # -> typing.Literal[False]
  278. # https://docs.python.org/3/reference/datamodel.html#object.__exit__
  279. self._spi.close()
  280. return False
  281. def get_main_radio_control_state_machine_state(
  282. self,
  283. ) -> MainRadioControlStateMachineState:
  284. return MainRadioControlStateMachineState(
  285. self._read_status_register(StatusRegisterAddress.MARCSTATE)
  286. )
  287. def get_marc_state(self) -> MainRadioControlStateMachineState:
  288. """
  289. alias for get_main_radio_control_state_machine_state()
  290. """
  291. return self.get_main_radio_control_state_machine_state()
  292. @classmethod
  293. def _frequency_control_word_to_hertz(cls, control_word: typing.List[int]) -> float:
  294. return (
  295. int.from_bytes(control_word, byteorder="big", signed=False)
  296. * cls._FREQUENCY_CONTROL_WORD_HERTZ_FACTOR
  297. )
  298. @classmethod
  299. def _hertz_to_frequency_control_word(cls, hertz: float) -> typing.List[int]:
  300. return list(
  301. round(hertz / cls._FREQUENCY_CONTROL_WORD_HERTZ_FACTOR).to_bytes(
  302. length=3, byteorder="big", signed=False
  303. )
  304. )
  305. def _get_base_frequency_control_word(self) -> typing.List[int]:
  306. # > The base or start frequency is set by the 24 bitfrequency
  307. # > word located in the FREQ2, FREQ1, FREQ0 registers.
  308. return self._read_burst(
  309. start_register=ConfigurationRegisterAddress.FREQ2, length=3
  310. )
  311. def _set_base_frequency_control_word(self, control_word: typing.List[int]) -> None:
  312. self._write_burst(
  313. start_register=ConfigurationRegisterAddress.FREQ2, values=control_word
  314. )
  315. def get_base_frequency_hertz(self) -> float:
  316. return self._frequency_control_word_to_hertz(
  317. self._get_base_frequency_control_word()
  318. )
  319. def set_base_frequency_hertz(self, freq: float) -> None:
  320. self._set_base_frequency_control_word(
  321. self._hertz_to_frequency_control_word(freq)
  322. )
  323. def __str__(self) -> str:
  324. attrs = (
  325. "marcstate={}".format(
  326. self.get_main_radio_control_state_machine_state().name.lower()
  327. ),
  328. "base_frequency={:.2f}MHz".format(
  329. self.get_base_frequency_hertz() / 10 ** 6
  330. ),
  331. "symbol_rate={:.2f}kBaud".format(self.get_symbol_rate_baud() / 1000),
  332. "modulation_format={}".format(self.get_modulation_format().name),
  333. "sync_mode={}".format(self.get_sync_mode().name),
  334. "packet_length{}{}B".format(
  335. "≤"
  336. if self.get_packet_length_mode() == PacketLengthMode.VARIABLE
  337. else "=",
  338. self.get_packet_length_bytes(),
  339. ),
  340. )
  341. return "CC1101({})".format(", ".join(attrs))
  342. def get_configuration_register_values(
  343. self,
  344. start_register: ConfigurationRegisterAddress = min(
  345. ConfigurationRegisterAddress
  346. ),
  347. end_register: ConfigurationRegisterAddress = max(ConfigurationRegisterAddress),
  348. ) -> typing.Dict[ConfigurationRegisterAddress, int]:
  349. assert start_register <= end_register, (start_register, end_register)
  350. values = self._read_burst(
  351. start_register=start_register, length=end_register - start_register + 1
  352. )
  353. return {
  354. ConfigurationRegisterAddress(start_register + i): v
  355. for i, v in enumerate(values)
  356. }
  357. def get_packet_length_bytes(self) -> int:
  358. """
  359. PKTLEN
  360. Packet length in fixed packet length mode,
  361. maximum packet length in variable packet length mode.
  362. > In variable packet length mode, [...]
  363. > any packet received with a length byte
  364. > with a value greater than PKTLEN will be discarded.
  365. """
  366. return self._read_single_byte(ConfigurationRegisterAddress.PKTLEN)
  367. def set_packet_length_bytes(self, packet_length: int) -> None:
  368. """
  369. see get_packet_length_bytes()
  370. """
  371. assert 1 <= packet_length <= 255, "unsupported packet length {}".format(
  372. packet_length
  373. )
  374. self._write_burst(
  375. start_register=ConfigurationRegisterAddress.PKTLEN, values=[packet_length]
  376. )
  377. def _disable_data_whitening(self):
  378. """
  379. PKTCTRL0.WHITE_DATA
  380. see "15.1 Data Whitening"
  381. > By setting PKTCTRL0.WHITE_DATA=1 [default],
  382. > all data, except the preamble and the sync word
  383. > will be XOR-ed with a 9-bit pseudo-random (PN9)
  384. > sequence before being transmitted.
  385. """
  386. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  387. pktctrl0 &= 0b10111111
  388. self._write_burst(
  389. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  390. )
  391. def disable_checksum(self) -> None:
  392. """
  393. PKTCTRL0.CRC_EN
  394. Disable automatic 2-byte cyclic redundancy check (CRC) sum
  395. appending in TX mode and checking in RX mode.
  396. See "Figure 19: Packet Format".
  397. """
  398. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  399. pktctrl0 &= 0b11111011
  400. self._write_burst(
  401. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  402. )
  403. def _get_transceive_mode(self) -> _TransceiveMode:
  404. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  405. return _TransceiveMode((pktctrl0 >> 4) & 0b11)
  406. def _set_transceive_mode(self, mode: _TransceiveMode) -> None:
  407. _LOGGER.info("changing transceive mode to %s", mode.name)
  408. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  409. pktctrl0 &= ~0b00110000
  410. pktctrl0 |= mode << 4
  411. self._write_burst(
  412. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  413. )
  414. def get_packet_length_mode(self) -> PacketLengthMode:
  415. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  416. return PacketLengthMode(pktctrl0 & 0b11)
  417. def set_packet_length_mode(self, mode: PacketLengthMode) -> None:
  418. pktctrl0 = self._read_single_byte(ConfigurationRegisterAddress.PKTCTRL0)
  419. pktctrl0 &= 0b11111100
  420. pktctrl0 |= mode
  421. self._write_burst(
  422. start_register=ConfigurationRegisterAddress.PKTCTRL0, values=[pktctrl0]
  423. )
  424. def _flush_tx_fifo_buffer(self) -> None:
  425. # > Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states.
  426. _LOGGER.debug("flushing tx fifo buffer")
  427. self._command_strobe(StrobeAddress.SFTX)
  428. def transmit(self, payload: bytes) -> None:
  429. """
  430. The most significant bit is transmitted first.
  431. In variable packet length mode,
  432. a byte indicating the packet's length will be prepended.
  433. > In variable packet length mode,
  434. > the packet length is configured by the first byte [...].
  435. > The packet length is defined as the payload data,
  436. > excluding the length byte and the optional CRC.
  437. from "15.2 Packet Format"
  438. Call .set_packet_length_mode(cc1101.PacketLengthMode.FIXED)
  439. to switch to fixed packet length mode.
  440. """
  441. # see "15.2 Packet Format"
  442. # > In variable packet length mode, [...]
  443. # > The first byte written to the TXFIFO must be different from 0.
  444. packet_length_mode = self.get_packet_length_mode()
  445. packet_length = self.get_packet_length_bytes()
  446. if packet_length_mode == PacketLengthMode.VARIABLE:
  447. if not payload:
  448. raise ValueError("empty payload {!r}".format(payload))
  449. if len(payload) > packet_length:
  450. raise ValueError(
  451. "payload exceeds maximum payload length of {} bytes".format(
  452. packet_length
  453. )
  454. + "\nsee .get_packet_length_bytes()"
  455. + "\npayload: {!r}".format(payload)
  456. )
  457. payload = int.to_bytes(len(payload), length=1, byteorder="big") + payload
  458. elif (
  459. packet_length_mode == PacketLengthMode.FIXED
  460. and len(payload) != packet_length
  461. ):
  462. raise ValueError(
  463. "expected payload length of {} bytes, got {}".format(
  464. packet_length, len(payload)
  465. )
  466. + "\nsee .set_packet_length_mode() and .get_packet_length_bytes()"
  467. + "\npayload: {!r}".format(payload)
  468. )
  469. marcstate = self.get_main_radio_control_state_machine_state()
  470. if marcstate != MainRadioControlStateMachineState.IDLE:
  471. raise Exception(
  472. "device must be idle before transmission (current marcstate: {})".format(
  473. marcstate.name
  474. )
  475. )
  476. self._flush_tx_fifo_buffer()
  477. self._write_burst(FIFORegisterAddress.TX, list(payload))
  478. _LOGGER.info(
  479. "transmitting 0x%s (%r)",
  480. "".join("{:02x}".format(b) for b in payload),
  481. payload,
  482. )
  483. self._command_strobe(StrobeAddress.STX)
  484. @contextlib.contextmanager
  485. def asynchronous_transmission(self) -> typing.Iterator[Pin]:
  486. """
  487. see "27.1 Asynchronous Serial Operation"
  488. >>> with cc1101.CC1101() as transceiver:
  489. >>> transceiver.set_base_frequency_hertz(433.92e6)
  490. >>> transceiver.set_symbol_rate_baud(600)
  491. >>> print(transceiver)
  492. >>> with transceiver.asynchronous_transmission():
  493. >>> # send digital signal to GDO0 pin
  494. """
  495. self._set_transceive_mode(_TransceiveMode.ASYNCHRONOUS_SERIAL)
  496. self._command_strobe(StrobeAddress.STX)
  497. try:
  498. # > In TX, the GDO0 pin is used for data input (TX data).
  499. yield Pin.GDO0
  500. finally:
  501. self._command_strobe(StrobeAddress.SIDLE)
  502. self._set_transceive_mode(_TransceiveMode.FIFO)