i2c_cxx.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. // Copyright 2020 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #pragma once
  15. #ifndef __cpp_exceptions
  16. #error I2C class can only be used when __cpp_exceptions is enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
  17. #endif
  18. #include <exception>
  19. #include <memory>
  20. #include <chrono>
  21. #include <vector>
  22. #include <list>
  23. #include <future>
  24. #include "driver/i2c.h"
  25. #include "esp_exception.hpp"
  26. namespace idf {
  27. struct I2CException : public ESPException {
  28. I2CException(esp_err_t error);
  29. };
  30. struct I2CTransferException : public I2CException {
  31. I2CTransferException(esp_err_t error);
  32. };
  33. /**
  34. * Superclass for all transfer objects which are accepted by \c I2CMaster::transfer().
  35. */
  36. template<typename TReturn>
  37. class I2CTransfer {
  38. protected:
  39. /**
  40. * Wrapper around i2c_cmd_handle_t, makes it exception-safe.
  41. */
  42. struct I2CCommandLink {
  43. I2CCommandLink();
  44. ~I2CCommandLink();
  45. i2c_cmd_handle_t handle;
  46. };
  47. public:
  48. /**
  49. * Helper typedef to facilitate type resolution during calls to I2CMaster::transfer().
  50. */
  51. typedef TReturn TransferReturnT;
  52. /**
  53. * @param driver_timeout The timeout used for calls like i2c_master_cmd_begin() to the underlying driver.
  54. */
  55. I2CTransfer(std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  56. virtual ~I2CTransfer() { }
  57. /**
  58. * Do all general parts of the I2C transfer:
  59. * - initialize the command link
  60. * - issuing a start to the command link queue
  61. * - calling \c queue_cmd() in the subclass to issue specific commands to the command link queue
  62. * - issuing a stop to the command link queue
  63. * - executing the assembled commands on the I2C bus
  64. * - calling \c process_result() to process the results of the commands or calling process_exception() if
  65. * there was an exception
  66. * - deleting the command link
  67. * This method is normally called by I2CMaster, but can also be used stand-alone if the bus corresponding to
  68. * \c i2c_num has be initialized.
  69. *
  70. * @throws I2CException for any particular I2C error
  71. */
  72. TReturn do_transfer(i2c_port_t i2c_num, uint8_t i2c_addr);
  73. protected:
  74. /**
  75. * Implementation of the I2C command is implemented by subclasses.
  76. * The I2C command handle is initialized already at this stage.
  77. * The first action is issuing the I2C address and the read/write bit, depending on what the subclass implements.
  78. * On error, this method has to throw an instance of I2CException.
  79. *
  80. * @param handle the initialized command handle of the I2C driver.
  81. * @param i2c_addr The slave's I2C address.
  82. *
  83. * @throw I2CException
  84. */
  85. virtual void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) = 0;
  86. /**
  87. * Implementation of whatever neccessary action after successfully sending the I2C command.
  88. * On error, this method has to throw an instance of I2CException.
  89. *
  90. * @throw I2CException
  91. */
  92. virtual TReturn process_result() = 0;
  93. /**
  94. * For some calls to the underlying driver (e.g. \c i2c_master_cmd_begin() ), this general timeout will be passed.
  95. */
  96. const TickType_t driver_timeout;
  97. };
  98. /**
  99. * @brief Super class for any I2C master or slave
  100. */
  101. class I2CBus {
  102. public:
  103. /*
  104. * @brief Initialize I2C master bus.
  105. *
  106. * Initialize and install the bus driver in master mode.
  107. *
  108. * @param i2c_number The I2C port number.
  109. */
  110. I2CBus(i2c_port_t i2c_number);
  111. /**
  112. * @brief uninstall the bus driver.
  113. */
  114. virtual ~I2CBus();
  115. /**
  116. * The I2C port number.
  117. */
  118. const i2c_port_t i2c_num;
  119. };
  120. /**
  121. * @brief Simple I2C Master object
  122. *
  123. * This class provides to ways to issue I2C read and write requests. The simplest way is to use \c sync_write() and
  124. * sync_read() to write and read, respectively. As the name suggests, they block during the whole transfer.
  125. * For all asynchrounous transfers as well as combined write-read transfers, use \c transfer().
  126. */
  127. class I2CMaster : public I2CBus {
  128. public:
  129. /**
  130. * Initialize and install the driver of an I2C master peripheral.
  131. *
  132. * Initialize and install the bus driver in master mode. Pullups will be enabled for both pins. If you want a
  133. * different configuration, use configure() and i2c_set_pin() of the underlying driver to disable one or both
  134. * pullups.
  135. *
  136. * @param i2c_number The number of the I2C device.
  137. * @param scl_gpio GPIO number of the SCL line.
  138. * @param sda_gpio GPIO number of the SDA line.
  139. * @param clock_speed The master clock speed.
  140. * @param scl_pullup Enable SCL pullup.
  141. * @param sda_pullup Enable SDA pullup.
  142. *
  143. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  144. */
  145. I2CMaster(i2c_port_t i2c_number,
  146. int scl_gpio,
  147. int sda_gpio,
  148. uint32_t clock_speed,
  149. bool scl_pullup = true,
  150. bool sda_pullup = true);
  151. /**
  152. * Delete the driver.
  153. */
  154. virtual ~I2CMaster();
  155. /**
  156. * Issue an asynchronous I2C transfer which is executed in the background.
  157. *
  158. * This method uses a C++ \c std::future as mechanism to wait for the asynchronous return value.
  159. * The return value can be accessed with \c future::get(). \c future::get() also synchronizes with the thread
  160. * doing the work in the background, i.e. it waits until the return value has been issued.
  161. *
  162. * The actual implementation is delegated to the TransferT object. It will be given the I2C number to work
  163. * with.
  164. *
  165. * Requirements for TransferT: It should implement or imitate the interface of I2CTransfer.
  166. *
  167. * @param xfer The transfer to execute. What the transfer does, depends on it's implementation in
  168. * \c TransferT::do_transfer(). It also determines the future template of this function, indicated by
  169. * \c TransferT::TransferReturnT.
  170. *
  171. * @param i2c_addr The address of the I2C slave device targeted by the transfer.
  172. *
  173. * @return A future with \c TransferT::TransferReturnT. It depends on which template type is used for xfer.
  174. * In case of a simple write (I2CWrite), it's future<void>.
  175. * In case of a read (I2CRead), it's future<vector<uint8_t> > corresponding to the length of the read
  176. * operation.
  177. * If TransferT is a combined transfer with repeated reads (I2CComposed), then the return type is
  178. * future<vector<vector<uint8_t> > >, a vector of results corresponding to the queued read operations.
  179. *
  180. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  181. * @throws std::exception for failures in libstdc++
  182. */
  183. template<typename TransferT>
  184. std::future<typename TransferT::TransferReturnT> transfer(std::shared_ptr<TransferT> xfer, uint8_t i2c_addr);
  185. /**
  186. * Do a synchronous write.
  187. *
  188. * All data in data will be written to the I2C device with i2c_addr at once.
  189. * This method will block until the I2C write is complete.
  190. *
  191. * @param i2c_addr The address of the I2C device to which the data shall be sent.
  192. * @param data The data to send (size to be sent is determined by data.size()).
  193. *
  194. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  195. * @throws std::exception for failures in libstdc++
  196. */
  197. void sync_write(uint8_t i2c_addr, const std::vector<uint8_t> &data);
  198. /**
  199. * Do a synchronous read.
  200. * This method will block until the I2C read is complete.
  201. *
  202. * n_bytes bytes of data will be read from the I2C device with i2c_addr.
  203. * While reading the last byte, the master finishes the reading by sending a NACK, before issuing a stop.
  204. *
  205. * @param i2c_addr The address of the I2C device from which to read.
  206. * @param n_bytes The number of bytes to read.
  207. *
  208. * @return the read bytes
  209. *
  210. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  211. * @throws std::exception for failures in libstdc++
  212. */
  213. std::vector<uint8_t> sync_read(uint8_t i2c_addr, size_t n_bytes);
  214. /**
  215. * Do a simple asynchronous write-read transfer.
  216. *
  217. * First, \c write_data will be written to the bus, then a number of \c read_n_bytes will be read from the bus
  218. * with a repeated start condition. The slave device is determined by \c i2c_addr.
  219. * While reading the last byte, the master finishes the reading by sending a NACK, before issuing a stop.
  220. * This method will block until the I2C transfer is complete.
  221. *
  222. * @param i2c_addr The address of the I2C device from which to read.
  223. * @param write_data The data to write to the bus before reading.
  224. * @param read_n_bytes The number of bytes to read.
  225. *
  226. * @return the read bytes
  227. *
  228. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  229. * @throws std::exception for failures in libstdc++
  230. */
  231. std::vector<uint8_t> sync_transfer(uint8_t i2c_addr,
  232. const std::vector<uint8_t> &write_data,
  233. size_t read_n_bytes);
  234. };
  235. /**
  236. * @brief Responsible for initialization and de-initialization of an I2C slave peripheral.
  237. */
  238. class I2CSlave : public I2CBus {
  239. public:
  240. /**
  241. * Initialize and install the driver of an I2C slave peripheral.
  242. *
  243. * Initialize and install the bus driver in slave mode. Pullups will be enabled for both pins. If you want a
  244. * different configuration, use configure() and i2c_set_pin() of the underlying driver to disable one or both
  245. * pullups.
  246. *
  247. * @param i2c_number The number of the I2C device.
  248. * @param scl_gpio GPIO number of the SCL line.
  249. * @param sda_gpio GPIO number of the SDA line.
  250. * @param slave_addr The address of the slave device on the I2C bus.
  251. * @param rx_buf_len Receive buffer length.
  252. * @param tx_buf_len Transmit buffer length.
  253. * @param scl_pullup Enable SCL pullup.
  254. * @param sda_pullup Enable SDA pullup.
  255. *
  256. * @throws
  257. */
  258. I2CSlave(i2c_port_t i2c_number,
  259. int scl_gpio,
  260. int sda_gpio,
  261. uint8_t slave_addr,
  262. size_t rx_buf_len,
  263. size_t tx_buf_len,
  264. bool scl_pullup = true,
  265. bool sda_pullup = true);
  266. /**
  267. * Delete the driver.
  268. */
  269. virtual ~I2CSlave();
  270. /**
  271. * Schedule a raw data write once master is ready.
  272. *
  273. * The data is saved in a buffer, waiting for the master to pick it up.
  274. */
  275. virtual int write_raw(const uint8_t* data, size_t data_len, std::chrono::milliseconds timeout);
  276. /**
  277. * Read raw data from the bus.
  278. *
  279. * The data is read directly from the buffer. Hence, it has to be written already by master.
  280. */
  281. virtual int read_raw(uint8_t* buffer, size_t buffer_len, std::chrono::milliseconds timeout);
  282. };
  283. /**
  284. * Implementation for simple I2C writes, which can be executed by \c I2CMaster::transfer().
  285. * It stores the bytes to be written as a vector.
  286. */
  287. class I2CWrite : public I2CTransfer<void> {
  288. public:
  289. /**
  290. * @param bytes The bytes which should be written.
  291. * @param driver_timeout The timeout used for calls like i2c_master_cmd_begin() to the underlying driver.
  292. */
  293. I2CWrite(const std::vector<uint8_t> &bytes, std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  294. protected:
  295. /**
  296. * Write the address and set the read bit to 0 to issue the address and request a write.
  297. * Then write the bytes.
  298. *
  299. * @param handle The initialized I2C command handle.
  300. * @param i2c_addr The I2C address of the slave.
  301. */
  302. void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) override;
  303. /**
  304. * Set the value of the promise to unblock any callers waiting on it.
  305. */
  306. void process_result() override;
  307. private:
  308. /**
  309. * The bytes to write.
  310. */
  311. std::vector<uint8_t> bytes;
  312. };
  313. /**
  314. * Implementation for simple I2C reads, which can be executed by \c I2CMaster::transfer().
  315. * It stores the bytes to be read as a vector to be returned later via a future.
  316. */
  317. class I2CRead : public I2CTransfer<std::vector<uint8_t> > {
  318. public:
  319. /**
  320. * @param The number of bytes to read.
  321. * @param driver_timeout The timeout used for calls like i2c_master_cmd_begin() to the underlying driver.
  322. */
  323. I2CRead(size_t size, std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  324. protected:
  325. /**
  326. * Write the address and set the read bit to 1 to issue the address and request a read.
  327. * Then read into bytes.
  328. *
  329. * @param handle The initialized I2C command handle.
  330. * @param i2c_addr The I2C address of the slave.
  331. */
  332. void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) override;
  333. /**
  334. * Set the return value of the promise to unblock any callers waiting on it.
  335. */
  336. std::vector<uint8_t> process_result() override;
  337. private:
  338. /**
  339. * The bytes to read.
  340. */
  341. std::vector<uint8_t> bytes;
  342. };
  343. /**
  344. * This kind of transfer uses repeated start conditions to chain transfers coherently.
  345. * In particular, this can be used to chain multiple single write and read transfers into a single transfer with
  346. * repeated starts as it is commonly done for I2C devices.
  347. * The result is a vector of vectors representing the reads in the order of how they were added using add_read().
  348. */
  349. class I2CComposed : public I2CTransfer<std::vector<std::vector<uint8_t> > > {
  350. public:
  351. I2CComposed(std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  352. /**
  353. * Add a read to the chain.
  354. *
  355. * @param size The size of the read in bytes.
  356. */
  357. void add_read(size_t size);
  358. /**
  359. * Add a write to the chain.
  360. *
  361. * @param bytes The bytes to write; size will be bytes.size()
  362. */
  363. void add_write(std::vector<uint8_t> bytes);
  364. protected:
  365. /**
  366. * Write all chained transfers, including a repeated start issue after each but the last transfer.
  367. *
  368. * @param handle The initialized I2C command handle.
  369. * @param i2c_addr The I2C address of the slave.
  370. */
  371. void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) override;
  372. /**
  373. * Creates the vector with the vectors from all reads.
  374. */
  375. std::vector<std::vector<uint8_t> > process_result() override;
  376. private:
  377. class CompTransferNode {
  378. public:
  379. virtual ~CompTransferNode() = default;
  380. virtual void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) = 0;
  381. virtual void process_result(std::vector<std::vector<uint8_t> > &read_results) { }
  382. };
  383. class CompTransferNodeRead : public CompTransferNode {
  384. public:
  385. CompTransferNodeRead(size_t size) : bytes(size) { }
  386. void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) override;
  387. void process_result(std::vector<std::vector<uint8_t> > &read_results) override;
  388. private:
  389. std::vector<uint8_t> bytes;
  390. };
  391. class CompTransferNodeWrite : public CompTransferNode {
  392. public:
  393. CompTransferNodeWrite(std::vector<uint8_t> bytes) : bytes(bytes) { }
  394. void queue_cmd(i2c_cmd_handle_t handle, uint8_t i2c_addr) override;
  395. private:
  396. std::vector<uint8_t> bytes;
  397. };
  398. /**
  399. * The chained transfers.
  400. */
  401. std::list<std::shared_ptr<CompTransferNode> > transfer_list;
  402. };
  403. template<typename TReturn>
  404. I2CTransfer<TReturn>::I2CTransfer(std::chrono::milliseconds driver_timeout)
  405. : driver_timeout(driver_timeout.count()) { }
  406. template<typename TReturn>
  407. I2CTransfer<TReturn>::I2CCommandLink::I2CCommandLink()
  408. {
  409. handle = i2c_cmd_link_create();
  410. if (!handle) {
  411. throw I2CException(ESP_ERR_NO_MEM);
  412. }
  413. }
  414. template<typename TReturn>
  415. I2CTransfer<TReturn>::I2CCommandLink::~I2CCommandLink()
  416. {
  417. i2c_cmd_link_delete(handle);
  418. }
  419. template<typename TReturn>
  420. TReturn I2CTransfer<TReturn>::do_transfer(i2c_port_t i2c_num, uint8_t i2c_addr)
  421. {
  422. I2CCommandLink cmd_link;
  423. queue_cmd(cmd_link.handle, i2c_addr);
  424. CHECK_THROW_SPECIFIC(i2c_master_stop(cmd_link.handle), I2CException);
  425. CHECK_THROW_SPECIFIC(i2c_master_cmd_begin(i2c_num, cmd_link.handle, driver_timeout / portTICK_RATE_MS), I2CTransferException);
  426. return process_result();
  427. }
  428. template<typename TransferT>
  429. std::future<typename TransferT::TransferReturnT> I2CMaster::transfer(std::shared_ptr<TransferT> xfer, uint8_t i2c_addr)
  430. {
  431. if (!xfer) throw I2CException(ESP_ERR_INVALID_ARG);
  432. return std::async(std::launch::async, [this](std::shared_ptr<TransferT> xfer, uint8_t i2c_addr) {
  433. return xfer->do_transfer(i2c_num, i2c_addr);
  434. }, xfer, i2c_addr);
  435. }
  436. } // idf