spi_host_cxx.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #pragma once
  7. #if __cpp_exceptions
  8. #include <exception>
  9. #include <memory>
  10. #include <chrono>
  11. #include <vector>
  12. #include <list>
  13. #include <future>
  14. #include "system_cxx.hpp"
  15. #include "spi_cxx.hpp"
  16. namespace idf {
  17. /**
  18. * @brief Exception which is thrown in the context of SPI Transactions.
  19. */
  20. struct SPITransferException : public SPIException {
  21. SPITransferException(esp_err_t error);
  22. };
  23. class SPIDevice;
  24. class SPIDeviceHandle;
  25. /**
  26. * @brief Describes and encapsulates the transaction.
  27. *
  28. * @note This class is intended to be used internally by the SPI C++ classes, but not publicly.
  29. * Furthermore, currently only one transaction per time can be handled. If you need to
  30. * send several transactions in parallel, you need to build your own mechanism around a
  31. * FreeRTOS task and a queue.
  32. */
  33. class SPITransactionDescriptor {
  34. friend class SPIDeviceHandle;
  35. public:
  36. /**
  37. * @brief Create a SPITransactionDescriptor object, describing a full duplex transaction.
  38. *
  39. * @param data_to_send The data sent to the SPI device. It can be dummy data if a read-only
  40. * transaction is intended. Its length determines the length of both write and read operation.
  41. * @param handle to the internal driver handle
  42. * @param pre_callback If non-empty, this callback will be called directly before the transaction.
  43. * @param post_callback If non-empty, this callback will be called directly after the transaction.
  44. * @param user_data optional data which will be accessible in the callbacks declared above
  45. */
  46. SPITransactionDescriptor(const std::vector<uint8_t> &data_to_send,
  47. SPIDeviceHandle *handle,
  48. std::function<void(void *)> pre_callback = nullptr,
  49. std::function<void(void *)> post_callback = nullptr,
  50. void* user_data = nullptr);
  51. /**
  52. * @brief Deinitialize and delete all data of the transaction.
  53. *
  54. * @note This destructor must not becalled before the transaction is finished by the driver.
  55. */
  56. ~SPITransactionDescriptor();
  57. SPITransactionDescriptor(const SPITransactionDescriptor&) = delete;
  58. SPITransactionDescriptor operator=(const SPITransactionDescriptor&) = delete;
  59. /**
  60. * @brief Queue the transaction asynchronously.
  61. */
  62. void start();
  63. /**
  64. * @brief Synchronously (blocking) wait for the result and return the result data or throw an exception.
  65. *
  66. * @return The data read from the SPI device. Its length is the length of \c data_to_send passed in the
  67. * constructor.
  68. * @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
  69. * transaction descriptor for some reason. In the former case, the error code is the one from the
  70. * underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
  71. */
  72. std::vector<uint8_t> get();
  73. /**
  74. * @brief Wait until the asynchronous operation is done.
  75. *
  76. * @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
  77. * transaction descriptor for some reason. In the former case, the error code is the one from the
  78. * underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
  79. */
  80. void wait();
  81. /**
  82. * @brief Wait for a result of the transaction up to timeout ms.
  83. *
  84. * @param timeout Maximum timeout value for waiting
  85. *
  86. * @return true if result is available, false if wait timed out
  87. *
  88. * @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
  89. * transaction descriptor for some reason. In the former case, the error code is the one from the
  90. * underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
  91. */
  92. bool wait_for(const std::chrono::milliseconds &timeout);
  93. private:
  94. /**
  95. * Private descriptor data.
  96. */
  97. void *private_transaction_desc;
  98. /**
  99. * Private device data.
  100. */
  101. SPIDeviceHandle *device_handle;
  102. /**
  103. * @brief If non-empty, this callback will be called directly before the transaction.
  104. */
  105. std::function<void(void *)> pre_callback;
  106. /**
  107. * @brief If non-empty, this callback will be called directly after the transaction.
  108. */
  109. std::function<void(void *)> post_callback;
  110. /**
  111. * Buffer in spi_transaction_t is const, so we have to declare it here because we want to
  112. * allocate and delete it.
  113. */
  114. uint8_t *tx_buffer;
  115. /**
  116. * @brief User data which will be provided in the callbacks.
  117. */
  118. void *user_data;
  119. /**
  120. * Tells if data has been received, i.e. the transaction has finished and the result can be acquired.
  121. */
  122. bool received_data;
  123. /**
  124. * Tells if the transaction has been initiated and is at least in-flight, if not finished.
  125. */
  126. bool started;
  127. };
  128. /**
  129. * @brief SPIFuture for asynchronous transaction results, mostly equivalent to std::future.
  130. *
  131. * This re-implementation is necessary as std::future is incompatible with the IDF SPI driver interface.
  132. */
  133. class SPIFuture {
  134. public:
  135. /**
  136. * @brief Create an invalid future.
  137. */
  138. SPIFuture();
  139. /**
  140. * @brief Create a valid future with \c transaction as shared state.
  141. *
  142. * @param transaction the shared transaction state
  143. */
  144. SPIFuture(std::shared_ptr<SPITransactionDescriptor> transaction);
  145. SPIFuture(const SPIFuture &other) = delete;
  146. /**
  147. * @brief Move constructor as in std::future, leaves \c other invalid.
  148. *
  149. * @param other object to move from, will become invalid during this constructor
  150. */
  151. SPIFuture(SPIFuture &&other) noexcept;
  152. /**
  153. * @brief Move assignment as in std::future, leaves \c other invalid.
  154. *
  155. * @param other object to move from, will become invalid during this constructor
  156. * @return A reference to the newly created SPIFuture object
  157. */
  158. SPIFuture &operator=(SPIFuture&& other) noexcept;
  159. /**
  160. * @brief Wait until the asynchronous operation is done and return the result or throw and exception.
  161. *
  162. * @throws std::future_error if this future is not valid.
  163. * @throws SPIException in case of an error of the underlying driver or if the driver returns a wrong
  164. * transaction descriptor for some reason. In the former case, the error code is the one from the
  165. * underlying driver, in the latter case, the error code is ESP_ERR_INVALID_STATE.
  166. * @return The result of the asynchronous SPI transaction.
  167. */
  168. std::vector<uint8_t> get();
  169. /**
  170. * @brief Wait for a result up to timeout ms.
  171. *
  172. * @param timeout Maximum timeout value for waiting
  173. *
  174. * @return std::future_status::ready if result is available, std::future_status::timeout if wait timed out
  175. */
  176. std::future_status wait_for(std::chrono::milliseconds timeout);
  177. /**
  178. * @brief Wait for a result indefinitely.
  179. */
  180. void wait();
  181. /**
  182. * @return true if this future is valid, otherwise false.
  183. */
  184. bool valid() const noexcept;
  185. private:
  186. /**
  187. * The SPITransactionDescriptor, which is the shared state of this future.
  188. */
  189. std::shared_ptr<SPITransactionDescriptor> transaction;
  190. /**
  191. * Indicates if this future is valid.
  192. */
  193. bool is_valid;
  194. };
  195. /**
  196. * @brief Represents an device on an initialized Master Bus.
  197. */
  198. class SPIDevice {
  199. public:
  200. /**
  201. * @brief Create and initialize a device on the master bus corresponding to spi_host.
  202. *
  203. * @param cs The pin number of the chip select signal for the device to create.
  204. * @param spi_host the spi_host (bus) to which the device shall be attached.
  205. * @param frequency The devices frequency. this frequency will be set during transactions to the device which will be
  206. * created.
  207. * @param transaction_queue_size The of the transaction queue of this device. This determines how many
  208. * transactions can be queued at the same time. Currently, it is set to 1 since the
  209. * implementation exclusively acquires the bus for each transaction. This may change in the future.
  210. */
  211. SPIDevice(SPINum spi_host,
  212. CS cs,
  213. Frequency frequency = Frequency::MHz(1),
  214. QueueSize transaction_queue_size = QueueSize(1u));
  215. SPIDevice(const SPIDevice&) = delete;
  216. SPIDevice operator=(const SPIDevice&) = delete;
  217. /**
  218. * @brief De-initializes and destroys the device.
  219. *
  220. * @warning Behavior is undefined if a device is destroyed while there is still an ongoing transaction
  221. * from that device.
  222. */
  223. ~SPIDevice();
  224. /**
  225. * @brief Queue a transfer to this device.
  226. *
  227. * This method creates a full-duplex transfer to the device represented by the current instance of this class.
  228. * It then queues that transfer and returns a "future" object. The future object will become ready once
  229. * the transfer finishes.
  230. *
  231. * @param data_to_send Data which will be sent to the device. The length of the data determines the length
  232. * of the full-deplex transfer. I.e., the same amount of bytes will be received from the device.
  233. * @param pre_callback If non-empty, this callback will be called directly before the transaction.
  234. * If empty, it will be ignored.
  235. * @param post_callback If non-empty, this callback will be called directly after the transaction.
  236. * If empty, it will be ignored.
  237. * @param user_data This pointer will be sent to pre_callback and/or pre_callback, if any of them is non-empty.
  238. *
  239. * @return a future object which will become ready once the transfer has finished. See also \c SPIFuture.
  240. */
  241. SPIFuture transfer(const std::vector<uint8_t> &data_to_send,
  242. std::function<void(void *)> pre_callback = nullptr,
  243. std::function<void(void *)> post_callback = nullptr,
  244. void* user_data = nullptr);
  245. /**
  246. * @brief Queue a transfer to this device like \c transfer, but using begin/end iterators instead of a
  247. * data vector.
  248. *
  249. * This method is equivalent to \c transfer(), except for the parameters.
  250. *
  251. * @param begin Iterator to the begin of the data which will be sent to the device.
  252. * @param end Iterator to the end of the data which will be sent to the device.
  253. * This iterator determines the length of the data and hence the length of the full-deplex transfer.
  254. * I.e., the same amount of bytes will be received from the device.
  255. * @param pre_callback If non-empty, this callback will be called directly before the transaction.
  256. * If empty, it will be ignored.
  257. * @param post_callback If non-empty, this callback will be called directly after the transaction.
  258. * If empty, it will be ignored.
  259. * @param user_data This pointer will be sent to pre_callback and/or pre_callback, if any of them is non-empty.
  260. *
  261. * @return a future object which will become ready once the transfer has finished. See also \c SPIFuture.
  262. */
  263. template<typename IteratorT>
  264. SPIFuture transfer(IteratorT begin,
  265. IteratorT end,
  266. std::function<void(void *)> pre_callback = nullptr,
  267. std::function<void(void *)> post_callback = nullptr,
  268. void* user_data = nullptr);
  269. private:
  270. /**
  271. * Private device data.
  272. */
  273. SPIDeviceHandle *device_handle;
  274. /**
  275. * Saves the current transaction descriptor in case the user's loses its future with the other
  276. * reference to the transaction.
  277. */
  278. std::shared_ptr<SPITransactionDescriptor> current_transaction;
  279. };
  280. /**
  281. * @brief Represents an SPI Master Bus.
  282. */
  283. class SPIMaster {
  284. public:
  285. /*
  286. * @brief Create an SPI Master Bus.
  287. *
  288. * @param host The SPI host (bus) which should be used. ESP chips have a number of different possible SPI hosts,
  289. * each of which will create its own bus. Consult the datasheet and TRM on which host to choose.
  290. * @param mosi The pin number for the MOSI signal of this bus.
  291. * @param miso The pin number for the MISO signal of this bus.
  292. * @param sclk The pin number for the clock signal of this bus.
  293. * @param dma_config The DMA configuration for this bus, see \c DMAConfig.
  294. * @param max_transfer_size The maximum transfer size in bytes.
  295. *
  296. * @throws SPIException with IDF error code if the underlying driver fails.
  297. */
  298. explicit SPIMaster(SPINum host,
  299. const MOSI &mosi,
  300. const MISO &miso,
  301. const SCLK &sclk,
  302. SPI_DMAConfig dma_config = SPI_DMAConfig::AUTO(),
  303. SPITransferSize max_transfer_size = SPITransferSize::default_size());
  304. /*
  305. * @brief Create an SPI Master Bus.
  306. *
  307. * @param host The SPI host (bus) which should be used. ESP chips have a number of different possible SPI hosts,
  308. * each of which will create its own bus. Consult the datasheet and TRM on which host to choose.
  309. * @param mosi The pin number for the MOSI signal of this bus.
  310. * @param miso The pin number for the MISO signal of this bus.
  311. * @param sclk The pin number for the clock signal of this bus.
  312. * @param qspiwp The pin number for the QSPIWP signal of this bus.
  313. * @param qspihd The pin number for the QSPIHD signal of this bus.
  314. * @param dma_config The DMA configuration for this bus, see \c DMAConfig.
  315. * @param max_transfer_size The maximum transfer size in bytes.
  316. *
  317. * @throws SPIException with IDF error code if the underlying driver fails.
  318. */
  319. explicit SPIMaster(SPINum host,
  320. const MOSI &mosi,
  321. const MISO &miso,
  322. const SCLK &sclk,
  323. const QSPIWP &qspiwp,
  324. const QSPIHD &qspihd,
  325. SPI_DMAConfig dma_config = SPI_DMAConfig::AUTO(),
  326. SPITransferSize max_transfer_size = SPITransferSize::default_size());
  327. SPIMaster(const SPIMaster&) = delete;
  328. SPIMaster operator=(const SPIMaster&) = delete;
  329. SPIMaster(SPIMaster&&) = default;
  330. SPIMaster &operator=(SPIMaster&&) = default;
  331. /*
  332. * @brief De-initializes and destroys the SPI Master Bus.
  333. *
  334. * @note Devices created before which try to initialize an exception after the bus is destroyed will throw
  335. * and exception.
  336. */
  337. virtual ~SPIMaster();
  338. /**
  339. * @brief Create a representation of a device on this bus.
  340. *
  341. * @param cs The pin number for the CS (chip select) signal to talk to the device.
  342. * @param f The frequency used to talk to the device.
  343. */
  344. std::shared_ptr<SPIDevice> create_dev(CS cs, Frequency frequency = Frequency::MHz(1));
  345. private:
  346. /**
  347. * @brief Host identifier for internal use.
  348. */
  349. SPINum spi_host;
  350. };
  351. template<typename IteratorT>
  352. SPIFuture SPIDevice::transfer(IteratorT begin,
  353. IteratorT end,
  354. std::function<void(void *)> pre_callback,
  355. std::function<void(void *)> post_callback,
  356. void* user_data)
  357. {
  358. std::vector<uint8_t> write_data;
  359. write_data.assign(begin, end);
  360. return transfer(write_data, pre_callback, post_callback, user_data);
  361. }
  362. }
  363. #endif