i2c_cxx.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /*
  2. * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #pragma once
  7. #ifndef __cpp_exceptions
  8. #error I2C class can only be used when __cpp_exceptions is enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
  9. #endif
  10. #include <exception>
  11. #include <memory>
  12. #include <chrono>
  13. #include <vector>
  14. #include <list>
  15. #include <future>
  16. #include "sdkconfig.h"
  17. #include "esp_exception.hpp"
  18. #include "system_cxx.hpp"
  19. #include "gpio_cxx.hpp"
  20. namespace idf {
  21. /**
  22. * @brief Check if the provided numerical value is a valid I2C address.
  23. *
  24. * @param addr raw number to be checked.
  25. * @return ESP_OK if \c addr is a valid I2C address, otherwise ESP_ERR_INVALID_ARG.
  26. */
  27. esp_err_t check_i2c_addr(uint32_t addr) noexcept;
  28. struct I2CException : public ESPException {
  29. I2CException(esp_err_t error);
  30. };
  31. struct I2CTransferException : public I2CException {
  32. I2CTransferException(esp_err_t error);
  33. };
  34. /**
  35. * @brief Represents a valid SDA signal pin number.
  36. */
  37. class SDA_type;
  38. using SDA_GPIO = GPIONumBase<class SDA_type>;
  39. /**
  40. * @brief Represents a valid SCL signal pin number.
  41. */
  42. class SCL_type;
  43. using SCL_GPIO = GPIONumBase<class SCL_type>;
  44. /**
  45. * @brief Valid representation of I2C number.
  46. *
  47. * A chip can have multiple I2C interfaces, each identified by a bus number, subsequently called I2C number.
  48. * Instances of this class are guaranteed to always contain a valid I2C number.
  49. */
  50. class I2CNumber : public StrongValueComparable<uint32_t> {
  51. /**
  52. * Construct a valid representation of the I2C number.
  53. *
  54. * This constructor is private because the it can only be accessed but the static creation methods below.
  55. * This guarantees that an instance of I2CNumber always carries a valid number.
  56. */
  57. constexpr explicit I2CNumber(uint32_t number) : StrongValueComparable<uint32_t>(number) { }
  58. public:
  59. /**
  60. * @brief create an I2C number representing the first I2C bus of the chip.
  61. */
  62. constexpr static I2CNumber I2C0() {
  63. return I2CNumber(0);
  64. }
  65. #if CONFIG_SOC_I2C_NUM == 2
  66. /**
  67. * @brief create an I2C number representing the second I2C bus of the chip.
  68. */
  69. constexpr static I2CNumber I2C1() {
  70. return I2CNumber(1);
  71. }
  72. #endif
  73. /**
  74. * Retrieves the valid numerical representation of the I2C number.
  75. */
  76. uint32_t get_num();
  77. };
  78. /**
  79. * @brief Valid representation of I2C address.
  80. *
  81. * Instances of this class are guaranteed to always contain a valid I2C address.
  82. */
  83. class I2CAddress : public StrongValueComparable<uint8_t> {
  84. public:
  85. /**
  86. *
  87. */
  88. explicit I2CAddress(uint8_t addr);
  89. /**
  90. * Retrieves the valid numerical representation of the I2C adress.
  91. */
  92. uint8_t get_addr();
  93. };
  94. /**
  95. * @brief Low-level I2C transaction descriptor
  96. *
  97. * This class records and decribes a low-level transaction. Users use the methods (except \c execute_transfer)
  98. * to record the transaction. Afterwards, the transaction will be executed by calling \c execute_transfer,
  99. * which blocks until the transaction is finished.
  100. *
  101. * @note This is a low-level class, which only exists due to the underlying I2C driver. All data referenced in
  102. * read and write calls must not be changed and must stay allocated until at least \c execute_transfer
  103. * has finished.
  104. */
  105. class I2CCommandLink {
  106. public:
  107. /**
  108. * @brief Allocate and create the transaction descriptor.
  109. */
  110. I2CCommandLink();
  111. /**
  112. * @brief Delete the transaction descriptor, de-allocate all resources.
  113. */
  114. ~I2CCommandLink();
  115. I2CCommandLink(const I2CCommandLink&) = delete;
  116. I2CCommandLink operator=(const I2CCommandLink&) = delete;
  117. /**
  118. * @brief Record a start signal on the I2C bus.
  119. */
  120. void start();
  121. /**
  122. * @brief Record a write of the vector \c bytes on the I2C bus.
  123. *
  124. * @param[in] bytes The data to be written. Must stay allocated until execute_transfer has finished or
  125. * destructor of this class has been called.
  126. * @param[in] expect_ack If acknowledgement shall be requested after each written byte, pass true,
  127. * otherwise false.
  128. */
  129. void write(const std::vector<uint8_t> &bytes, bool expect_ack = true);
  130. /**
  131. * @brief Record a one-byte-write on the I2C bus.
  132. *
  133. * @param[in] byte The data to be written. No restrictions apply.
  134. * @param[in] expect_ack If acknowledgement shall be requested after writing the byte, pass true,
  135. * otherwise false.
  136. */
  137. void write_byte(uint8_t byte, bool expect_ack = true);
  138. /**
  139. * @brief Record a read of the size of vector \c bytes on the I2C bus.
  140. *
  141. * @param[in] bytes Vector with the size of the data to be read (in bytes). Must stay allocated until
  142. * execute_transfer has finished or destructor of this class has been called.
  143. * @param[in] expect_ack If acknowledgement shall be requested after each written byte, pass true,
  144. * otherwise false.
  145. */
  146. void read(std::vector<uint8_t> &bytes);
  147. /**
  148. * @brief Record a stop command on the I2C bus.
  149. */
  150. void stop();
  151. /**
  152. * @brief Execute the transaction and wait until it has finished.
  153. *
  154. * This method will issue the transaction with the operations in the order in which they have been recorded
  155. * before.
  156. *
  157. * @param i2c_num I2C bus number on the chip.
  158. * @param driver_timeout Timeout for this transaction.
  159. */
  160. void execute_transfer(I2CNumber i2c_num, std::chrono::milliseconds driver_timeout);
  161. private:
  162. /**
  163. * @brief Internal driver data.
  164. */
  165. void *handle;
  166. };
  167. /**
  168. * Superclass for all transfer objects which are accepted by \c I2CMaster::transfer().
  169. */
  170. template<typename TReturn>
  171. class I2CTransfer {
  172. public:
  173. /**
  174. * Helper typedef to facilitate type resolution during calls to I2CMaster::transfer().
  175. */
  176. typedef TReturn TransferReturnT;
  177. /**
  178. * @param driver_timeout The timeout used for calls like i2c_master_cmd_begin() to the underlying driver.
  179. */
  180. I2CTransfer(std::chrono::milliseconds driver_timeout_arg = std::chrono::milliseconds(1000));
  181. virtual ~I2CTransfer() { }
  182. /**
  183. * Do all general parts of the I2C transfer:
  184. * - initialize the command link
  185. * - issuing a start to the command link queue
  186. * - calling \c queue_cmd() in the subclass to issue specific commands to the command link queue
  187. * - issuing a stop to the command link queue
  188. * - executing the assembled commands on the I2C bus
  189. * - calling \c process_result() to process the results of the commands or calling process_exception() if
  190. * there was an exception
  191. * - deleting the command link
  192. * This method is normally called by I2CMaster, but can also be used stand-alone if the bus corresponding to
  193. * \c i2c_num has be initialized.
  194. *
  195. * @throws I2CException for any particular I2C error
  196. */
  197. TReturn do_transfer(I2CNumber i2c_num, I2CAddress i2c_addr);
  198. protected:
  199. /**
  200. * Implementation of the I2C command is implemented by subclasses.
  201. * The I2C command handle is initialized already at this stage.
  202. * The first action is issuing the I2C address and the read/write bit, depending on what the subclass implements.
  203. * On error, this method has to throw an instance of I2CException.
  204. *
  205. * @param handle the initialized command handle of the I2C driver.
  206. * @param i2c_addr The slave's I2C address.
  207. *
  208. * @throw I2CException
  209. */
  210. virtual void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) = 0;
  211. /**
  212. * Implementation of whatever neccessary action after successfully sending the I2C command.
  213. * On error, this method has to throw an instance of I2CException.
  214. *
  215. * @throw I2CException
  216. */
  217. virtual TReturn process_result() = 0;
  218. /**
  219. * For some calls to the underlying driver (e.g. \c i2c_master_cmd_begin() ), this general timeout will be passed.
  220. */
  221. std::chrono::milliseconds driver_timeout;
  222. };
  223. /**
  224. * @brief Super class for any I2C master or slave
  225. */
  226. class I2CBus {
  227. public:
  228. /*
  229. * @brief Initialize I2C master bus.
  230. *
  231. * Initialize and install the bus driver in master mode.
  232. *
  233. * @param i2c_number The I2C port number.
  234. */
  235. explicit I2CBus(I2CNumber i2c_number);
  236. /**
  237. * @brief uninstall the bus driver.
  238. */
  239. virtual ~I2CBus();
  240. /**
  241. * The I2C port number.
  242. */
  243. const I2CNumber i2c_num;
  244. };
  245. /**
  246. * @brief Simple I2C Master object
  247. *
  248. * This class provides to ways to issue I2C read and write requests. The simplest way is to use \c sync_write() and
  249. * sync_read() to write and read, respectively. As the name suggests, they block during the whole transfer.
  250. * For all asynchrounous transfers as well as combined write-read transfers, use \c transfer().
  251. */
  252. class I2CMaster : public I2CBus {
  253. public:
  254. /**
  255. * Initialize and install the driver of an I2C master peripheral.
  256. *
  257. * Initialize and install the bus driver in master mode. Pullups will be enabled for both pins. If you want a
  258. * different configuration, use configure() and i2c_set_pin() of the underlying driver to disable one or both
  259. * pullups.
  260. *
  261. * @param i2c_number The number of the I2C device.
  262. * @param scl_gpio GPIO number of the SCL line.
  263. * @param sda_gpio GPIO number of the SDA line.
  264. * @param clock_speed The master clock speed.
  265. * @param scl_pullup Enable SCL pullup.
  266. * @param sda_pullup Enable SDA pullup.
  267. *
  268. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  269. */
  270. explicit I2CMaster(I2CNumber i2c_number,
  271. SCL_GPIO scl_gpio,
  272. SDA_GPIO sda_gpio,
  273. Frequency clock_speed,
  274. bool scl_pullup = true,
  275. bool sda_pullup = true);
  276. /**
  277. * Delete the driver.
  278. */
  279. virtual ~I2CMaster();
  280. /**
  281. * Issue an asynchronous I2C transfer which is executed in the background.
  282. *
  283. * This method uses a C++ \c std::future as mechanism to wait for the asynchronous return value.
  284. * The return value can be accessed with \c future::get(). \c future::get() also synchronizes with the thread
  285. * doing the work in the background, i.e. it waits until the return value has been issued.
  286. *
  287. * The actual implementation is delegated to the TransferT object. It will be given the I2C number to work
  288. * with.
  289. *
  290. * Requirements for TransferT: It should implement or imitate the interface of I2CTransfer.
  291. *
  292. * @param xfer The transfer to execute. What the transfer does, depends on it's implementation in
  293. * \c TransferT::do_transfer(). It also determines the future template of this function, indicated by
  294. * \c TransferT::TransferReturnT.
  295. *
  296. * @param i2c_addr The address of the I2C slave device targeted by the transfer.
  297. *
  298. * @return A future with \c TransferT::TransferReturnT. It depends on which template type is used for xfer.
  299. * In case of a simple write (I2CWrite), it's future<void>.
  300. * In case of a read (I2CRead), it's future<vector<uint8_t> > corresponding to the length of the read
  301. * operation.
  302. * If TransferT is a combined transfer with repeated reads (I2CComposed), then the return type is
  303. * future<vector<vector<uint8_t> > >, a vector of results corresponding to the queued read operations.
  304. *
  305. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  306. * @throws std::exception for failures in libstdc++
  307. */
  308. template<typename TransferT>
  309. std::future<typename TransferT::TransferReturnT> transfer(I2CAddress i2c_addr, std::shared_ptr<TransferT> xfer);
  310. /**
  311. * Do a synchronous write.
  312. *
  313. * All data in data will be written to the I2C device with i2c_addr at once.
  314. * This method will block until the I2C write is complete.
  315. *
  316. * @param i2c_addr The address of the I2C device to which the data shall be sent.
  317. * @param data The data to send (size to be sent is determined by data.size()).
  318. *
  319. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  320. * @throws std::exception for failures in libstdc++
  321. */
  322. void sync_write(I2CAddress i2c_addr, const std::vector<uint8_t> &data);
  323. /**
  324. * Do a synchronous read.
  325. * This method will block until the I2C read is complete.
  326. *
  327. * n_bytes bytes of data will be read from the I2C device with i2c_addr.
  328. * While reading the last byte, the master finishes the reading by sending a NACK, before issuing a stop.
  329. *
  330. * @param i2c_addr The address of the I2C device from which to read.
  331. * @param n_bytes The number of bytes to read.
  332. *
  333. * @return the read bytes
  334. *
  335. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  336. * @throws std::exception for failures in libstdc++
  337. */
  338. std::vector<uint8_t> sync_read(I2CAddress i2c_addr, size_t n_bytes);
  339. /**
  340. * Do a simple synchronous write-read transfer.
  341. *
  342. * First, \c write_data will be written to the bus, then a number of \c read_n_bytes will be read from the bus
  343. * with a repeated start condition. The slave device is determined by \c i2c_addr.
  344. * While reading the last byte, the master finishes the reading by sending a NACK, before issuing a stop.
  345. * This method will block until the I2C transfer is complete.
  346. *
  347. * @param i2c_addr The address of the I2C device from which to read.
  348. * @param write_data The data to write to the bus before reading.
  349. * @param read_n_bytes The number of bytes to read.
  350. *
  351. * @return the read bytes
  352. *
  353. * @throws I2CException with the corrsponding esp_err_t return value if something goes wrong
  354. * @throws std::exception for failures in libstdc++
  355. */
  356. std::vector<uint8_t> sync_transfer(I2CAddress i2c_addr,
  357. const std::vector<uint8_t> &write_data,
  358. size_t read_n_bytes);
  359. };
  360. #if CONFIG_SOC_I2C_SUPPORT_SLAVE
  361. /**
  362. * @brief Responsible for initialization and de-initialization of an I2C slave peripheral.
  363. */
  364. class I2CSlave : public I2CBus {
  365. public:
  366. /**
  367. * Initialize and install the driver of an I2C slave peripheral.
  368. *
  369. * Initialize and install the bus driver in slave mode. Pullups will be enabled for both pins. If you want a
  370. * different configuration, use configure() and i2c_set_pin() of the underlying driver to disable one or both
  371. * pullups.
  372. *
  373. * @param i2c_number The number of the I2C device.
  374. * @param scl_gpio GPIO number of the SCL line.
  375. * @param sda_gpio GPIO number of the SDA line.
  376. * @param slave_addr The address of the slave device on the I2C bus.
  377. * @param rx_buf_len Receive buffer length.
  378. * @param tx_buf_len Transmit buffer length.
  379. * @param scl_pullup Enable SCL pullup.
  380. * @param sda_pullup Enable SDA pullup.
  381. *
  382. * @throws
  383. */
  384. I2CSlave(I2CNumber i2c_number,
  385. SCL_GPIO scl_gpio,
  386. SDA_GPIO sda_gpio,
  387. I2CAddress slave_addr,
  388. size_t rx_buf_len,
  389. size_t tx_buf_len,
  390. bool scl_pullup = true,
  391. bool sda_pullup = true);
  392. /**
  393. * Delete the driver.
  394. */
  395. virtual ~I2CSlave();
  396. /**
  397. * Schedule a raw data write once master is ready.
  398. *
  399. * The data is saved in a buffer, waiting for the master to pick it up.
  400. */
  401. virtual int write_raw(const uint8_t* data, size_t data_len, std::chrono::milliseconds timeout);
  402. /**
  403. * Read raw data from the bus.
  404. *
  405. * The data is read directly from the buffer. Hence, it has to be written already by master.
  406. */
  407. virtual int read_raw(uint8_t* buffer, size_t buffer_len, std::chrono::milliseconds timeout);
  408. };
  409. #endif // CONFIG_SOC_I2C_SUPPORT_SLAVE
  410. /**
  411. * Implementation for simple I2C writes, which can be executed by \c I2CMaster::transfer().
  412. * It stores the bytes to be written as a vector.
  413. */
  414. class I2CWrite : public I2CTransfer<void> {
  415. public:
  416. /**
  417. * @param bytes The bytes which should be written.
  418. * @param driver_timeout The timeout used for calls like i2c_master_cmd_begin() to the underlying driver.
  419. */
  420. I2CWrite(const std::vector<uint8_t> &bytes, std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  421. protected:
  422. /**
  423. * Write the address and set the read bit to 0 to issue the address and request a write.
  424. * Then write the bytes.
  425. *
  426. * @param handle The initialized I2C command handle.
  427. * @param i2c_addr The I2C address of the slave.
  428. */
  429. void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) override;
  430. /**
  431. * Set the value of the promise to unblock any callers waiting on it.
  432. */
  433. void process_result() override;
  434. private:
  435. /**
  436. * The bytes to write.
  437. */
  438. std::vector<uint8_t> bytes;
  439. };
  440. /**
  441. * Implementation for simple I2C reads, which can be executed by \c I2CMaster::transfer().
  442. * It stores the bytes to be read as a vector to be returned later via a future.
  443. */
  444. class I2CRead : public I2CTransfer<std::vector<uint8_t> > {
  445. public:
  446. /**
  447. * @param The number of bytes to read.
  448. * @param driver_timeout The timeout used for calls like i2c_master_cmd_begin() to the underlying driver.
  449. */
  450. I2CRead(size_t size, std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  451. protected:
  452. /**
  453. * Write the address and set the read bit to 1 to issue the address and request a read.
  454. * Then read into bytes.
  455. *
  456. * @param handle The initialized I2C command handle.
  457. * @param i2c_addr The I2C address of the slave.
  458. */
  459. void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) override;
  460. /**
  461. * Set the return value of the promise to unblock any callers waiting on it.
  462. */
  463. std::vector<uint8_t> process_result() override;
  464. private:
  465. /**
  466. * The bytes to read.
  467. */
  468. std::vector<uint8_t> bytes;
  469. };
  470. /**
  471. * This kind of transfer uses repeated start conditions to chain transfers coherently.
  472. * In particular, this can be used to chain multiple single write and read transfers into a single transfer with
  473. * repeated starts as it is commonly done for I2C devices.
  474. * The result is a vector of vectors representing the reads in the order of how they were added using add_read().
  475. */
  476. class I2CComposed : public I2CTransfer<std::vector<std::vector<uint8_t> > > {
  477. public:
  478. I2CComposed(std::chrono::milliseconds driver_timeout = std::chrono::milliseconds(1000));
  479. /**
  480. * Add a read to the chain.
  481. *
  482. * @param size The size of the read in bytes.
  483. */
  484. void add_read(size_t size);
  485. /**
  486. * Add a write to the chain.
  487. *
  488. * @param bytes The bytes to write; size will be bytes.size()
  489. */
  490. void add_write(std::vector<uint8_t> bytes);
  491. protected:
  492. /**
  493. * Write all chained transfers, including a repeated start issue after each but the last transfer.
  494. *
  495. * @param handle The initialized I2C command handle.
  496. * @param i2c_addr The I2C address of the slave.
  497. */
  498. void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) override;
  499. /**
  500. * Creates the vector with the vectors from all reads.
  501. */
  502. std::vector<std::vector<uint8_t> > process_result() override;
  503. private:
  504. class CompTransferNode {
  505. public:
  506. virtual ~CompTransferNode() { }
  507. virtual void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) = 0;
  508. virtual void process_result(std::vector<std::vector<uint8_t> > &read_results) { }
  509. };
  510. class CompTransferNodeRead : public CompTransferNode {
  511. public:
  512. CompTransferNodeRead(size_t size) : bytes(size) { }
  513. void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) override;
  514. void process_result(std::vector<std::vector<uint8_t> > &read_results) override;
  515. private:
  516. std::vector<uint8_t> bytes;
  517. };
  518. class CompTransferNodeWrite : public CompTransferNode {
  519. public:
  520. CompTransferNodeWrite(std::vector<uint8_t> bytes) : bytes(bytes) { }
  521. void queue_cmd(I2CCommandLink &handle, I2CAddress i2c_addr) override;
  522. private:
  523. std::vector<uint8_t> bytes;
  524. };
  525. /**
  526. * The chained transfers.
  527. */
  528. std::list<std::shared_ptr<CompTransferNode> > transfer_list;
  529. };
  530. template<typename TReturn>
  531. I2CTransfer<TReturn>::I2CTransfer(std::chrono::milliseconds driver_timeout_arg)
  532. : driver_timeout(driver_timeout_arg) { }
  533. template<typename TReturn>
  534. TReturn I2CTransfer<TReturn>::do_transfer(I2CNumber i2c_num, I2CAddress i2c_addr)
  535. {
  536. I2CCommandLink cmd_link;
  537. queue_cmd(cmd_link, i2c_addr);
  538. cmd_link.stop();
  539. cmd_link.execute_transfer(i2c_num, driver_timeout);
  540. return process_result();
  541. }
  542. template<typename TransferT>
  543. std::future<typename TransferT::TransferReturnT> I2CMaster::transfer(I2CAddress i2c_addr, std::shared_ptr<TransferT> xfer)
  544. {
  545. if (!xfer) throw I2CException(ESP_ERR_INVALID_ARG);
  546. return std::async(std::launch::async, [this](std::shared_ptr<TransferT> xfer, I2CAddress i2c_addr) {
  547. return xfer->do_transfer(i2c_num, i2c_addr);
  548. }, xfer, i2c_addr);
  549. }
  550. } // idf