gpio_exp.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. /* GDS Example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include <string.h>
  8. #include <stdlib.h>
  9. #include "freertos/FreeRTOS.h"
  10. #include "freertos/task.h"
  11. #include "freertos/queue.h"
  12. #include "esp_task.h"
  13. #include "esp_log.h"
  14. #include "driver/gpio.h"
  15. #include "driver/i2c.h"
  16. #include "gpio_exp.h"
  17. typedef struct gpio_exp_s {
  18. uint32_t first, last;
  19. union gpio_exp_phy_u phy;
  20. uint32_t shadow;
  21. TickType_t age;
  22. SemaphoreHandle_t mutex;
  23. uint32_t r_mask, w_mask;
  24. uint32_t pullup, pulldown;
  25. struct {
  26. gpio_exp_isr handler;
  27. void *arg;
  28. } isr[4];
  29. struct gpio_exp_model_s const *model;
  30. } gpio_exp_t;
  31. typedef struct {
  32. enum { ASYNC_WRITE } type;
  33. int gpio;
  34. int level;
  35. gpio_exp_t *expander;
  36. } async_request_t;
  37. static const char TAG[] = "gpio expander";
  38. static void IRAM_ATTR intr_isr_handler(void* arg);
  39. static gpio_exp_t* find_expander(gpio_exp_t *expander, int *gpio);
  40. static void pca9535_set_direction(gpio_exp_t* self);
  41. static int pca9535_read(gpio_exp_t* self);
  42. static void pca9535_write(gpio_exp_t* self);
  43. static void pca85xx_set_direction(gpio_exp_t* self);
  44. static int pca85xx_read(gpio_exp_t* self);
  45. static void pca85xx_write(gpio_exp_t* self);
  46. static void mcp23017_init(gpio_exp_t* self);
  47. static void mcp23017_set_pull_mode(gpio_exp_t* self);
  48. static void mcp23017_set_direction(gpio_exp_t* self);
  49. static int mcp23017_read(gpio_exp_t* self);
  50. static void mcp23017_write(gpio_exp_t* self);
  51. static void async_handler(void *arg);
  52. static esp_err_t i2c_write_byte(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg, uint8_t val);
  53. static uint8_t i2c_read_byte(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg);
  54. static uint16_t i2c_read_word(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg);
  55. static esp_err_t i2c_write_word(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg, uint16_t data);
  56. static const struct gpio_exp_model_s {
  57. char *model;
  58. gpio_int_type_t trigger;
  59. void (*init)(gpio_exp_t* self);
  60. int (*read)(gpio_exp_t* self);
  61. void (*write)(gpio_exp_t* self);
  62. void (*set_direction)(gpio_exp_t* self);
  63. void (*set_pull_mode)(gpio_exp_t* self);
  64. } registered[] = {
  65. { .model = "pca9535",
  66. .trigger = GPIO_INTR_NEGEDGE,
  67. .set_direction = pca9535_set_direction,
  68. .read = pca9535_read,
  69. .write = pca9535_write, },
  70. { .model = "pca85xx",
  71. .trigger = GPIO_INTR_NEGEDGE,
  72. .set_direction = pca85xx_set_direction,
  73. .read = pca85xx_read,
  74. .write = pca85xx_write, },
  75. { .model = "mcp23017",
  76. .trigger = GPIO_INTR_NEGEDGE,
  77. .init = mcp23017_init,
  78. .set_direction = mcp23017_set_direction,
  79. .set_pull_mode = mcp23017_set_pull_mode,
  80. .read = mcp23017_read,
  81. .write = mcp23017_write, },
  82. };
  83. static EXT_RAM_ATTR uint8_t n_expanders;
  84. static EXT_RAM_ATTR QueueHandle_t async_queue;
  85. static EXT_RAM_ATTR gpio_exp_t expanders[4];
  86. /******************************************************************************
  87. * Retrieve base from an expander reference
  88. */
  89. uint32_t gpio_exp_get_base(gpio_exp_t *expander) {
  90. return expander->first;
  91. }
  92. /******************************************************************************
  93. * Retrieve reference from a GPIO
  94. */
  95. gpio_exp_t *gpio_exp_get_expander(int gpio) {
  96. int _gpio = gpio;
  97. return find_expander(NULL, &_gpio);
  98. }
  99. /******************************************************************************
  100. * Create an I2C expander
  101. */
  102. gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) {
  103. gpio_exp_t *expander = expanders + n_expanders;
  104. if (config->base < GPIO_NUM_MAX || n_expanders == sizeof(expanders)/sizeof(gpio_exp_t)) {
  105. ESP_LOGE(TAG, "Base %d GPIO must be at least %d for %s or too many expanders %d", config->base, GPIO_NUM_MAX, config->model, n_expanders);
  106. return NULL;
  107. }
  108. // See if we know that model (expanders is zero-initialized)
  109. for (int i = 0; !expander->model && i < sizeof(registered)/sizeof(struct gpio_exp_model_s); i++) {
  110. if (strcasestr(config->model, registered[i].model)) expander->model = registered + i;
  111. }
  112. // well... try again
  113. if (!expander->model) {
  114. ESP_LOGE(TAG,"Unknown GPIO expansion chip %s", config->model);
  115. return NULL;
  116. }
  117. n_expanders++;
  118. expander->first = config->base;
  119. expander->last = config->base + config->count - 1;
  120. expander->mutex = xSemaphoreCreateMutex();
  121. memcpy(&expander->phy, &config->phy, sizeof(union gpio_exp_phy_u));
  122. if (expander->model->init) expander->model->init(expander);
  123. // create a task to handle asynchronous requests (only write at this time)
  124. if (!async_queue) {
  125. // we allocate TCB but stack is staic to avoid SPIRAM fragmentation
  126. StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  127. static EXT_RAM_ATTR StackType_t xStack[2*1024] __attribute__ ((aligned (4)));
  128. async_queue = xQueueCreate(4, sizeof(async_request_t));
  129. xTaskCreateStatic(async_handler, "gpio_expander", sizeof(xStack), NULL, ESP_TASK_PRIO_MIN + 1, xStack, xTaskBuffer);
  130. }
  131. // set interrupt if possible
  132. if (config->intr > 0) {
  133. gpio_pad_select_gpio(config->intr);
  134. gpio_set_direction(config->intr, GPIO_MODE_INPUT);
  135. switch (expander->model->trigger) {
  136. case GPIO_INTR_NEGEDGE:
  137. case GPIO_INTR_LOW_LEVEL:
  138. gpio_set_pull_mode(config->intr, GPIO_PULLUP_ONLY);
  139. break;
  140. case GPIO_INTR_POSEDGE:
  141. case GPIO_INTR_HIGH_LEVEL:
  142. gpio_set_pull_mode(config->intr, GPIO_PULLDOWN_ONLY);
  143. break;
  144. default:
  145. gpio_set_pull_mode(config->intr, GPIO_PULLUP_PULLDOWN);
  146. break;
  147. }
  148. gpio_set_intr_type(config->intr, expander->model->trigger);
  149. gpio_isr_handler_add(config->intr, intr_isr_handler, expander);
  150. gpio_intr_enable(config->intr);
  151. }
  152. ESP_LOGI(TAG, "Create GPIO expander at base %u with INT %u at @%x on port %d", config->base, config->intr, config->phy.addr, config->phy.port);
  153. return expander;
  154. }
  155. /******************************************************************************
  156. * Add ISR handler
  157. */
  158. bool gpio_exp_add_isr(gpio_exp_isr isr, void *arg, gpio_exp_t *expander) {
  159. xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY));
  160. for (int i = 0; i < sizeof(expander->isr)/sizeof(*expander->isr); i++) {
  161. if (!expander->isr[i].handler) {
  162. expander->isr[i].handler = isr;
  163. expander->isr[i].arg = arg;
  164. ESP_LOGI(TAG, "Added new ISR for expander base %d", expander->first);
  165. xSemaphoreGive(expander->mutex);
  166. return true;
  167. }
  168. }
  169. xSemaphoreGive(expander->mutex);
  170. ESP_LOGE(TAG, "No room left to add new ISR");
  171. return false;
  172. }
  173. /******************************************************************************
  174. * Set GPIO direction
  175. */
  176. esp_err_t gpio_exp_set_direction(int gpio, gpio_mode_t mode, gpio_exp_t *expander) {
  177. if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG;
  178. xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY));
  179. if (mode == GPIO_MODE_INPUT) {
  180. expander->r_mask |= 1 << gpio;
  181. expander->age = ~xTaskGetTickCount();
  182. } else {
  183. expander->w_mask |= 1 << gpio;
  184. }
  185. if (expander->r_mask & expander->w_mask) {
  186. xSemaphoreGive(expander->mutex);
  187. ESP_LOGE(TAG, "GPIO %d on expander base %u can't be r/w", gpio, expander->first);
  188. return ESP_ERR_INVALID_ARG;
  189. }
  190. // most expanders want unconfigured GPIO to be set to output
  191. if (expander->model->set_direction) expander->model->set_direction(expander);
  192. xSemaphoreGive(expander->mutex);
  193. return ESP_OK;
  194. }
  195. /******************************************************************************
  196. * Get GPIO level with cache
  197. */
  198. int gpio_exp_get_level(int gpio, uint32_t age, gpio_exp_t *expander) {
  199. if ((expander = find_expander(expander, &gpio)) == NULL) return -1;
  200. uint32_t now = xTaskGetTickCount();
  201. // this is a little risk here but that avoids calling scheduler if we are cached
  202. if (now - expander->age >= pdMS_TO_TICKS(age)) {
  203. if (xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50)) == pdFALSE) return -1;
  204. expander->shadow = expander->model->read(expander);
  205. expander->age = now;
  206. xSemaphoreGive(expander->mutex);
  207. }
  208. ESP_LOGD(TAG, "Get level for GPIO %u => read %x", expander->first + gpio, expander->shadow);
  209. return (expander->shadow >> gpio) & 0x01;
  210. }
  211. /******************************************************************************
  212. * Set GPIO level with cache
  213. */
  214. esp_err_t gpio_exp_set_level(int gpio, int level, bool direct, gpio_exp_t *expander) {
  215. if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG;
  216. uint32_t mask = 1 << gpio;
  217. // limited risk with lack of semaphore here
  218. if ((expander->w_mask & mask) == 0) {
  219. ESP_LOGW(TAG, "GPIO %d is not set for output", expander->first + gpio);
  220. return ESP_ERR_INVALID_ARG;
  221. }
  222. if (direct) {
  223. xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY));
  224. level = level ? mask : 0;
  225. mask &= expander->shadow;
  226. // only write if shadow not up to date
  227. if ((mask ^ level) && expander->model->write) {
  228. expander->shadow = (expander->shadow & ~(mask | level)) | level;
  229. expander->model->write(expander);
  230. }
  231. xSemaphoreGive(expander->mutex);
  232. ESP_LOGD(TAG, "Set level %x for GPIO %u => wrote %x", level, expander->first + gpio, expander->shadow);
  233. } else {
  234. async_request_t request = { .gpio = gpio, .level = level, .type = ASYNC_WRITE, .expander = expander };
  235. if (xQueueSend(async_queue, &request, 0) == pdFALSE) return ESP_ERR_INVALID_RESPONSE;
  236. }
  237. return ESP_OK;
  238. }
  239. /******************************************************************************
  240. * Set GPIO pullmode
  241. */
  242. esp_err_t gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, gpio_exp_t *expander) {
  243. if ((expander = find_expander(expander, &gpio)) != NULL && expander->model->set_pull_mode) {
  244. expander->pullup &= ~(1 << gpio);
  245. expander->pulldown &= ~(1 << gpio);
  246. if (mode == GPIO_PULLUP_ONLY || mode == GPIO_PULLUP_PULLDOWN) expander->pullup |= 1 << gpio;
  247. if (mode == GPIO_PULLDOWN_ONLY || mode == GPIO_PULLUP_PULLDOWN) expander->pulldown |= 1 << gpio;
  248. expander->model->set_pull_mode(expander);
  249. return ESP_OK;
  250. }
  251. return ESP_ERR_INVALID_ARG;
  252. }
  253. /******************************************************************************
  254. * Enumerate modified GPIO
  255. */
  256. void gpio_exp_enumerate(gpio_exp_enumerator enumerator, gpio_exp_t *expander) {
  257. uint32_t value = expander->model->read(expander) ^ expander->shadow;
  258. uint8_t clz;
  259. // memorize newly read value and just update if requested
  260. xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50));
  261. expander->shadow ^= value;
  262. xSemaphoreGive(expander->mutex);
  263. if (!enumerator) return;
  264. // now we have a bitmap of all modified GPIO sinnce last call
  265. for (int gpio = 0; value; value <<= (clz + 1)) {
  266. clz = __builtin_clz(value);
  267. gpio += clz;
  268. enumerator(expander->first + 31 - gpio, (expander->shadow >> (31 - gpio)) & 0x01, expander);
  269. }
  270. }
  271. /******************************************************************************
  272. * Wrapper function
  273. */
  274. esp_err_t gpio_set_pull_mode_u(int gpio, gpio_pull_mode_t mode) {
  275. if (gpio < GPIO_NUM_MAX) return gpio_set_pull_mode(gpio, mode);
  276. return gpio_exp_set_pull_mode(gpio, mode, NULL);
  277. }
  278. esp_err_t gpio_set_direction_u(int gpio, gpio_mode_t mode) {
  279. if (gpio < GPIO_NUM_MAX) return gpio_set_direction(gpio, mode);
  280. return gpio_exp_set_direction(gpio, mode, NULL);
  281. }
  282. int gpio_get_level_u(int gpio) {
  283. if (gpio < GPIO_NUM_MAX) return gpio_get_level(gpio);
  284. return gpio_exp_get_level(gpio, 50, NULL);
  285. }
  286. esp_err_t gpio_set_level_u(int gpio, int level) {
  287. if (gpio < GPIO_NUM_MAX) return gpio_set_level(gpio, level);
  288. return gpio_exp_set_level(gpio, level, false, NULL);
  289. }
  290. /****************************************************************************************
  291. * Find the expander related to base
  292. */
  293. static gpio_exp_t* find_expander(gpio_exp_t *expander, int *gpio) {
  294. for (int i = 0; !expander && i < n_expanders; i++) {
  295. if (*gpio >= expanders[i].first && *gpio <= expanders[i].last) expander = expanders + i;
  296. }
  297. // normalize GPIO number
  298. if (expander && *gpio >= expanders->first) *gpio -= expanders->first;
  299. return expander;
  300. }
  301. /****************************************************************************************
  302. * PCA9535 family : direction, read and write
  303. */
  304. static void pca9535_set_direction(gpio_exp_t* self) {
  305. i2c_write_word(self->phy.port, self->phy.addr, 0x06, self->r_mask);
  306. }
  307. static int pca9535_read(gpio_exp_t* self) {
  308. return i2c_read_word(self->phy.port, self->phy.addr, 0x00);
  309. }
  310. static void pca9535_write(gpio_exp_t* self) {
  311. i2c_write_word(self->phy.port, self->phy.addr, 0x02, self->shadow);
  312. }
  313. /****************************************************************************************
  314. * PCA85xx family : read and write
  315. */
  316. static void pca85xx_set_direction(gpio_exp_t* self) {
  317. // all inputs must be set to 1 (open drain) and output are left open as well
  318. i2c_write_word(self->phy.port, self->phy.addr, 0xff, self->r_mask | self->w_mask);
  319. }
  320. static int pca85xx_read(gpio_exp_t* self) {
  321. return i2c_read_word(self->phy.port, self->phy.addr, 0xff);
  322. }
  323. static void pca85xx_write(gpio_exp_t* self) {
  324. // all input must be set to 1 (open drain)
  325. i2c_write_word(self->phy.port, self->phy.addr, 0xff, self->shadow | self->r_mask);
  326. }
  327. /****************************************************************************************
  328. * MCP23017 family : init, direction, read and write
  329. */
  330. static void mcp23017_init(gpio_exp_t* self) {
  331. /*
  332. 0111 x10x = same bank, mirrot single int, no sequentµial, open drain, active low
  333. not sure about this funny change of mapping of the control register itself, really?
  334. */
  335. i2c_write_byte(self->phy.port, self->phy.addr, 0x05, 0x74);
  336. i2c_write_byte(self->phy.port, self->phy.addr, 0x0a, 0x74);
  337. // no interrupt on comparison or on change
  338. i2c_write_word(self->phy.port, self->phy.addr, 0x04, 0x00);
  339. i2c_write_word(self->phy.port, self->phy.addr, 0x08, 0x00);
  340. }
  341. static void mcp23017_set_direction(gpio_exp_t* self) {
  342. // default to input and set real input to generate interrupt
  343. i2c_write_word(self->phy.port, self->phy.addr, 0x00, ~self->w_mask);
  344. i2c_write_word(self->phy.port, self->phy.addr, 0x04, self->r_mask);
  345. }
  346. static void mcp23017_set_pull_mode(gpio_exp_t* self) {
  347. i2c_write_word(self->phy.port, self->phy.addr, 0x0c, self->pullup);
  348. }
  349. static int mcp23017_read(gpio_exp_t* self) {
  350. // read the pin value, not the stored one @interrupt
  351. return i2c_read_word(self->phy.port, self->phy.addr, 0x12);
  352. }
  353. static void mcp23017_write(gpio_exp_t* self) {
  354. i2c_write_word(self->phy.port, self->phy.addr, 0x12, self->shadow);
  355. }
  356. /****************************************************************************************
  357. * INTR low-level handler
  358. */
  359. static void IRAM_ATTR intr_isr_handler(void* arg)
  360. {
  361. gpio_exp_t *expander = (gpio_exp_t*) arg;
  362. BaseType_t woken = pdFALSE;
  363. for (int i = 0; i < sizeof(expander->isr)/sizeof(*expander->isr); i++) {
  364. if (expander->isr[i].handler) woken |= expander->isr[i].handler(expander->isr[i].arg);
  365. }
  366. if (woken) portYIELD_FROM_ISR();
  367. ESP_EARLY_LOGD(TAG, "INTR for expander %u", expander->first);
  368. }
  369. /****************************************************************************************
  370. * Async task
  371. */
  372. void async_handler(void *arg) {
  373. while (1) {
  374. esp_err_t err;
  375. async_request_t request;
  376. if (!xQueueReceive(async_queue, &request, portMAX_DELAY)) continue;
  377. switch (request.type) {
  378. case ASYNC_WRITE:
  379. err = gpio_exp_set_level(request.gpio, request.level, true, request.expander);
  380. if (err != ESP_OK) ESP_LOGW(TAG, "Can't execute async GPIO %d write request (%d)", request.gpio, err);
  381. break;
  382. default:
  383. break;
  384. }
  385. }
  386. }
  387. /****************************************************************************************
  388. *
  389. */
  390. static esp_err_t i2c_write_byte(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg, uint8_t val) {
  391. i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  392. i2c_master_start(cmd);
  393. i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
  394. i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
  395. i2c_master_write_byte(cmd, val, I2C_MASTER_NACK);
  396. i2c_master_stop(cmd);
  397. esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
  398. i2c_cmd_link_delete(cmd);
  399. if (ret != ESP_OK) {
  400. ESP_LOGW(TAG, "I2C write failed");
  401. }
  402. return ret;
  403. }
  404. /****************************************************************************************
  405. * I2C read one byte
  406. */
  407. static uint8_t i2c_read_byte(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg) {
  408. uint8_t data = 0xff;
  409. i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  410. i2c_master_start(cmd);
  411. i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
  412. i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
  413. i2c_master_start(cmd);
  414. i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
  415. i2c_master_read_byte(cmd, &data, I2C_MASTER_NACK);
  416. i2c_master_stop(cmd);
  417. esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
  418. i2c_cmd_link_delete(cmd);
  419. if (ret != ESP_OK) {
  420. ESP_LOGW(TAG, "I2C read failed");
  421. }
  422. return data;
  423. }
  424. /****************************************************************************************
  425. * I2C read 16 bits word
  426. */
  427. static uint16_t i2c_read_word(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg) {
  428. uint16_t data = 0xffff;
  429. i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  430. i2c_master_start(cmd);
  431. i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
  432. // when using a register, write it's value then the device address again
  433. if (reg != 0xff) {
  434. i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
  435. i2c_master_start(cmd);
  436. i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
  437. }
  438. i2c_master_read(cmd, (uint8_t*) &data, 2, I2C_MASTER_NACK);
  439. i2c_master_stop(cmd);
  440. esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
  441. i2c_cmd_link_delete(cmd);
  442. if (ret != ESP_OK) {
  443. ESP_LOGW(TAG, "I2C read failed");
  444. }
  445. return data;
  446. }
  447. /****************************************************************************************
  448. * I2C write 16 bits word
  449. */
  450. static esp_err_t i2c_write_word(uint8_t i2c_port, uint8_t i2c_addr, uint8_t reg, uint16_t data) {
  451. i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  452. i2c_master_start(cmd);
  453. i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
  454. if (reg != 0xff) i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
  455. i2c_master_write(cmd, (uint8_t*) &data, 2, I2C_MASTER_NACK);
  456. i2c_master_stop(cmd);
  457. esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
  458. i2c_cmd_link_delete(cmd);
  459. if (ret != ESP_OK) {
  460. ESP_LOGW(TAG, "I2C write failed");
  461. }
  462. return ret;
  463. }