From 9c7cc86793e6c710554942b61556b7382100fb9a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 16 Dec 2016 23:41:04 +0800 Subject: [PATCH] 1. modify i2c_set_pin function 2. update example comments and other minor changes 3. rename API: i2c_cmd_link_create/i2c_cmd_link_delete (+4 squashed commits) Squashed commits: [2e0ac3e] 1. coding style: add one space after condition key words. 2. modify i2c.h, use gpio_num_t instead of int, improve comments of return values 3. add i2c index in index.rst 4. add readme for i2c example [4991d92] update i2c.doc [88b672e] driver: i2c 1. add mux and spin lock to run in a thread-safe way. 2. modify example code [4eb15fe] driver: i2c code 1. add i2c master code 2. add i2c slave code 3. add i2c example code 4. add DRAM_ATTR for I2C array --- components/driver/i2c.c | 1037 +++++++++++++++++++++ components/driver/include/driver/i2c.h | 514 ++++++++++ components/esp32/include/soc/i2c_reg.h | 2 + components/esp32/include/soc/i2c_struct.h | 6 +- docs/api/i2c.rst | 82 ++ docs/index.rst | 1 + examples/18_i2c/Makefile | 9 + examples/18_i2c/README.md | 29 + examples/18_i2c/main/component.mk | 3 + examples/18_i2c/main/i2c_test.c | 303 ++++++ 10 files changed, 1983 insertions(+), 3 deletions(-) create mode 100644 components/driver/i2c.c create mode 100644 components/driver/include/driver/i2c.h create mode 100644 docs/api/i2c.rst create mode 100644 examples/18_i2c/Makefile create mode 100644 examples/18_i2c/README.md create mode 100644 examples/18_i2c/main/component.mk create mode 100644 examples/18_i2c/main/i2c_test.c diff --git a/components/driver/i2c.c b/components/driver/i2c.c new file mode 100644 index 000000000..311f01516 --- /dev/null +++ b/components/driver/i2c.c @@ -0,0 +1,1037 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "soc/i2c_struct.h" +#include "soc/i2c_reg.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +static const char* I2C_TAG = "i2c"; +#define I2C_CHECK(a, str, ret) if(!(a)) { \ + ESP_LOGE(I2C_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } + +static portMUX_TYPE i2c_spinlock[I2C_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +/* DRAM_ATTR is required to avoid I2C array placed in flash, due to accessed from ISR */ +static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; + + +#define I2C_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define I2C_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define I2C_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define I2C_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +#define I2C_DRIVER_ERR_STR "i2c driver install error" +#define I2C_DRIVER_MALLOC_ERR_STR "i2c driver malloc error" +#define I2C_NUM_ERROR_STR "i2c number error" +#define I2C_ADDR_ERROR_STR "i2c null address error" +#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed" +#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too short for slave mode" +#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error" +#define I2C_SEM_ERR_STR "i2c semaphore error" +#define I2C_BUF_ERR_STR "i2c ringbuffer error" +#define I2C_MASTER_MODE_ERR_STR "Only allowed in master mode" +#define I2C_MODE_SLAVE_ERR_STR "Only allowed in slave mode" +#define I2C_CMD_MALLOC_ERR_STR "i2c command link malloc error" +#define I2C_TRANS_MODE_ERR_STR "i2c trans mode error" +#define I2C_MODE_ERR_STR "i2c mode error" +#define I2C_SDA_IO_ERR_STR "sda gpio number error" +#define I2C_SCL_IO_ERR_STR "scl gpio number error" +#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" +#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin do not support internal pull-up" +#define I2C_FIFO_FULL_THRESH_VAL (28) +#define I2C_FIFO_EMPTY_THRESH_VAL (5) + +typedef struct { + uint8_t byte_num; /*!< cmd byte number */ + uint8_t ack_en; /*!< ack check enable */ + uint8_t ack_exp; /*!< expected ack level to get */ + uint8_t ack_val; /*!< ack value to send */ + uint8_t* data; /*!< data address */ + uint8_t byte_cmd; /*!< to save cmd for one byte command mode */ + i2c_opmode_t op_code; /*!< haredware cmd type */ +}i2c_cmd_t; + +typedef struct i2c_cmd_link{ + i2c_cmd_t cmd; /*!< command in current cmd link */ + struct i2c_cmd_link *next; /*!< next cmd link */ +} i2c_cmd_link_t; + +typedef struct { + i2c_cmd_link_t* head; /*!< head of the command link */ + i2c_cmd_link_t* cur; /*!< last node of the command link */ + i2c_cmd_link_t* free; /*!< the first node to free of the command link */ +} i2c_cmd_desc_t; + +typedef enum { + I2C_STATUS_READ, /*!< read status for current master command */ + I2C_STATUS_WRITE, /*!< write status for current master command */ + I2C_STATUS_IDLE, /*!< idle status for current master command */ + I2C_STATUS_ACK_ERROR, /*!< ack error status for current master command */ + I2C_STATUS_DONE, /*!< I2C command done */ +} i2c_status_t; + +typedef struct { + int i2c_num; /*!< I2C port number */ + int mode; /*!< I2C mode, master or slave */ + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + + int cmd_idx; /*!< record current command index, for master mode */ + int status; /*!< record current command status, for master mode */ + int rx_cnt; /*!< record current read index, for master mode */ + uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ + i2c_cmd_desc_t cmd_link; /*!< I2C command link */ + xSemaphoreHandle cmd_sem; /*!< semaphore to sync command status */ + xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */ + size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ + size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ + + xSemaphoreHandle slv_rx_mux; /*!< slave rx buffer mux */ + xSemaphoreHandle slv_tx_mux; /*!< slave tx buffer mux */ + size_t rx_buf_length; /*!< rx buffer length */ + RingbufHandle_t rx_ring_buf; /*!< rx ringbuffer handler of slave mode */ + size_t tx_buf_length; /*!< tx buffer length */ + RingbufHandle_t tx_ring_buf; /*!< tx ringbuffer handler of slave mode */ +} i2c_obj_t; + +static i2c_obj_t *p_i2c_obj[I2C_NUM_MAX] = {0}; +static void i2c_isr_handler_default(void* arg); +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num); + +/* + For i2c master mode, we don't need to use a buffer for the data, the APIs will execute the master commands +and return after all of the commands have been sent out or when error occurs. So when we send master commands, +we should free or modify the source data only after the i2c_master_cmd_begin function returns. + For i2c slave mode, we need a data buffer to stash the sending and receiving data, because the hardware fifo +has only 32 bytes. +*/ + +esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len, + int intr_alloc_flags) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(mode == I2C_MODE_MASTER || ( slv_rx_buf_len > 100 || slv_tx_buf_len > 100 ), I2C_SLAVE_BUFFER_LEN_ERR_STR, + ESP_ERR_INVALID_ARG); + uint32_t intr_mask = 0; + if (p_i2c_obj[i2c_num] == NULL) { + p_i2c_obj[i2c_num] = (i2c_obj_t*) calloc(1, sizeof(i2c_obj_t)); + if (p_i2c_obj[i2c_num] == NULL) { + ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR); + return ESP_FAIL; + } + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->i2c_num = i2c_num; + p_i2c->mode = mode; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->status = I2C_STATUS_IDLE; + + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + + if (mode == I2C_MODE_SLAVE) { + //we only use ringbuffer for slave mode. + if (slv_rx_buf_len > 0) { + p_i2c->rx_ring_buf = xRingbufferCreate(slv_rx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->rx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->rx_buf_length = slv_rx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (slv_tx_buf_len > 0) { + p_i2c->tx_ring_buf = xRingbufferCreate(slv_tx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->tx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->tx_buf_length = slv_tx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + p_i2c->slv_rx_mux = xSemaphoreCreateMutex(); + p_i2c->slv_tx_mux = xSemaphoreCreateMutex(); + if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_rx_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + intr_mask |= ( I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M ); + } else { + //semaphore to sync sending process, because we only have 32 bytes for hardware fifo. + p_i2c->cmd_sem = xSemaphoreCreateBinary(); + p_i2c->cmd_mux = xSemaphoreCreateMutex(); + if (p_i2c->cmd_sem == NULL || p_i2c->cmd_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + //command link + p_i2c->cmd_link.cur = NULL; + p_i2c->cmd_link.head = NULL; + p_i2c->cmd_link.free = NULL; + + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + } else { + ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR); + return ESP_FAIL; + } + //hook isr handler + i2c_isr_register(i2c_num, i2c_isr_handler_default, p_i2c_obj[i2c_num], intr_alloc_flags, &p_i2c_obj[i2c_num]->intr_handle); + intr_mask |= ( I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M ); + SET_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + return ESP_OK; + + err: + //Some error has happened. Free/destroy all allocated things and return ESP_FAIL. + if (p_i2c_obj[i2c_num]) { + if (p_i2c_obj[i2c_num]->rx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->rx_ring_buf); + p_i2c_obj[i2c_num]->rx_ring_buf = NULL; + p_i2c_obj[i2c_num]->rx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->tx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->tx_ring_buf); + p_i2c_obj[i2c_num]->tx_ring_buf = NULL; + p_i2c_obj[i2c_num]->tx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->cmd_sem) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_sem); + } + if (p_i2c_obj[i2c_num]->cmd_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); + } + if (p_i2c_obj[i2c_num]->slv_rx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_rx_mux); + } + if (p_i2c_obj[i2c_num]->slv_tx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_tx_mux); + } + } + free(p_i2c_obj[i2c_num]); + return ESP_FAIL; +} + +esp_err_t i2c_driver_delete(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + if (p_i2c->cmd_mux) { + xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); + vSemaphoreDelete(p_i2c->cmd_mux); + } + if (p_i2c->cmd_sem) { + vSemaphoreDelete(p_i2c->cmd_sem); + } + if (p_i2c->slv_rx_mux) { + vSemaphoreDelete(p_i2c->slv_rx_mux); + } + if (p_i2c->slv_tx_mux) { + vSemaphoreDelete(p_i2c->slv_tx_mux); + } + + if (p_i2c->rx_ring_buf) { + vRingbufferDelete(p_i2c->rx_ring_buf); + p_i2c->rx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (p_i2c->tx_ring_buf) { + vRingbufferDelete(p_i2c->tx_ring_buf); + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + uint32_t intr_mask = I2C_MASTER_TRAN_COMP_INT_ENA_M | + I2C_TIME_OUT_INT_ENA_M | + I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_TX_SEND_EMPTY_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_RX_REC_FULL_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M; + CLEAR_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + esp_intr_free(p_i2c->intr_handle); + p_i2c->intr_handle = NULL; + free(p_i2c_obj[i2c_num]); + p_i2c_obj[i2c_num] = NULL; + return ESP_OK; +} + +esp_err_t i2c_reset_tx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_reset_rx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +static void i2c_isr_handler_default(void* arg) +{ + i2c_obj_t* p_i2c = (i2c_obj_t*) arg; + int i2c_num = p_i2c->i2c_num; + uint32_t status = I2C[i2c_num]->int_status.val; + int idx = 0; + portBASE_TYPE HPTaskAwoken = pdFALSE; + while (status != 0) { + status = I2C[i2c_num]->int_status.val; + if (status & I2C_TX_SEND_EMPTY_INT_ST_M) { + I2C[i2c_num]->int_clr.tx_send_empty = 1; + } else if (status & I2C_RX_REC_FULL_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_rec_full = 1; + } else if (status & I2C_ACK_ERR_INT_ST_M) { + I2C[i2c_num]->int_clr.ack_err = 1; + if (p_i2c->mode == I2C_MODE_MASTER) { + p_i2c_obj[i2c_num]->status = I2C_STATUS_ACK_ERROR; + I2C[i2c_num]->int_clr.ack_err = 1; + //get error ack value from slave device, stop the commands + i2c_master_cmd_begin_static(i2c_num); + } + } else if (status & I2C_TRANS_START_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_start = 1; + } else if (status & I2C_TIME_OUT_INT_ST_M) { + I2C[i2c_num]->int_clr.time_out = 1; + } else if (status & I2C_TRANS_COMPLETE_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_complete = 1; + if (p_i2c->mode == I2C_MODE_SLAVE) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + if (p_i2c->status != I2C_STATUS_ACK_ERROR) { + i2c_master_cmd_begin_static(i2c_num); + } + } + } else if (status & I2C_MASTER_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.master_tran_comp = 1; + } else if (status & I2C_ARBITRATION_LOST_INT_ST_M) { + I2C[i2c_num]->int_clr.arbitration_lost = 1; + } else if (status & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.slave_tran_comp = 1; + } else if (status & I2C_END_DETECT_INT_ST_M) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_RXFIFO_OVF_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_fifo_ovf = 1; + } else if (status & I2C_TXFIFO_EMPTY_INT_ST_M) { + int tx_fifo_rem = I2C_FIFO_LEN - I2C[i2c_num]->status_reg.tx_fifo_cnt; + size_t size = 0; + uint8_t *data = (uint8_t*) xRingbufferReceiveUpToFromISR(p_i2c->tx_ring_buf, &size, tx_fifo_rem); + if (data) { + for (idx = 0; idx < size; idx++) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), data[idx]); + } + vRingbufferReturnItemFromISR(p_i2c->tx_ring_buf, data, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } else { + I2C[i2c_num]->int_ena.tx_fifo_empty = 0; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } + } else if (status & I2C_RXFIFO_FULL_INT_ST_M) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + I2C[i2c_num]->int_clr.val = status; + } + } +} + +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(tx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(rx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = rx_trans_mode; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = tx_trans_mode; //set tx data msb first + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + if (tx_trans_mode) { + *tx_trans_mode = I2C[i2c_num]->ctr.tx_lsb_first; + } + if (rx_trans_mode) { + *rx_trans_mode = I2C[i2c_num]->ctr.rx_lsb_first; + } + return ESP_OK; +} + +esp_err_t i2c_param_config(i2c_port_t i2c_num, i2c_config_t* i2c_conf) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf->mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, + i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); + if (ret != ESP_OK) { + return ret; + } + if (i2c_num == I2C_NUM_0) { + periph_module_enable(PERIPH_I2C0_MODULE); + } else if (i2c_num == I2C_NUM_1) { + periph_module_enable(PERIPH_I2C1_MODULE); + } + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set tx data msb first + I2C[i2c_num]->ctr.ms_mode = i2c_conf->mode; //mode for master or slave + I2C[i2c_num]->ctr.sda_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.scl_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.sample_scl_level = 0; //sample at high level of clock + + if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode + I2C[i2c_num]->slave_addr.addr = i2c_conf->slave.slave_addr; + I2C[i2c_num]->slave_addr.en_10bit = i2c_conf->slave.addr_10bit_en; + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + I2C[i2c_num]->fifo_conf.fifo_addr_cfg_en = 0; + I2C[i2c_num]->fifo_conf.rx_fifo_full_thrhd = I2C_FIFO_FULL_THRESH_VAL; + I2C[i2c_num]->fifo_conf.tx_fifo_empty_thrhd = I2C_FIFO_EMPTY_THRESH_VAL; + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C[i2c_num]->ctr.trans_start = 0; + } else { + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + } + //set frequency + int half_cycle = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) / 2; + I2C[i2c_num]->scl_low_period.period = half_cycle - 1; + I2C[i2c_num]->scl_high_period.period = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) - half_cycle - 1; + //set timing for start signal + I2C[i2c_num]->scl_start_hold.time = half_cycle; + I2C[i2c_num]->scl_rstart_setup.time = half_cycle; + //set timing for stop signal + I2C[i2c_num]->scl_stop_hold.time = half_cycle; + I2C[i2c_num]->scl_stop_setup.time = half_cycle; + //set timing for data + I2C[i2c_num]->sda_hold.time = half_cycle / 2; + I2C[i2c_num]->sda_sample.time = half_cycle / 2; + //set timeout of receving data + I2C[i2c_num]->timeout.tout = 200000; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_high_period.period = high_period; + I2C[i2c_num]->scl_low_period.period = low_period; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (high_period) { + *high_period = I2C[i2c_num]->scl_high_period.period; + } + if (low_period) { + *low_period = I2C[i2c_num]->scl_low_period.period; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_start_hold.time = hold_time; + I2C[i2c_num]->scl_rstart_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_start_hold.time; + } + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_rstart_setup.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_stop_hold.time = hold_time; + I2C[i2c_num]->scl_stop_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_stop_setup.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_stop_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->sda_hold.time = hold_time; + I2C[i2c_num]->sda_sample.time = sample_time; + return ESP_OK; +} + +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (sample_time) { + *sample_time = I2C[i2c_num]->sda_sample.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->sda_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_isr_register(i2c_port_t i2c_num, void (*fn)(void*), void * arg, int intr_alloc_flags, intr_handle_t *handle) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(fn != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + esp_err_t ret; + switch (i2c_num) { + case I2C_NUM_1: + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + case I2C_NUM_0: + default: + ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + } + return ret; +} + +esp_err_t i2c_isr_free(intr_handle_t handle) +{ + return esp_intr_free(handle); +} + +esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_io_num, gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(((GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num))), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + (GPIO_IS_VALID_GPIO(scl_io_num) && mode == I2C_MODE_SLAVE), + I2C_SCL_IO_ERR_STR, + ESP_ERR_INVALID_ARG); + I2C_CHECK((sda_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num)) || + sda_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((scl_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + scl_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + + int sda_in_sig, sda_out_sig, scl_in_sig, scl_out_sig; + switch (i2c_num) { + case I2C_NUM_1: + sda_out_sig = I2CEXT1_SDA_OUT_IDX; + sda_in_sig = I2CEXT1_SDA_IN_IDX; + scl_out_sig = I2CEXT1_SCL_OUT_IDX; + scl_in_sig = I2CEXT1_SCL_IN_IDX; + break; + case I2C_NUM_0: + default: + sda_out_sig = I2CEXT0_SDA_OUT_IDX; + sda_in_sig = I2CEXT0_SDA_IN_IDX; + scl_out_sig = I2CEXT0_SCL_OUT_IDX; + scl_in_sig = I2CEXT0_SCL_IN_IDX; + break; + } + if (sda_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[sda_io_num], PIN_FUNC_GPIO); + gpio_set_direction(sda_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + if (sda_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(sda_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(sda_io_num, GPIO_FLOATING); + } + gpio_matrix_out(sda_io_num, sda_out_sig, 0, 0); + gpio_matrix_in(sda_io_num, sda_in_sig, 0); + } + + if (scl_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[scl_io_num], PIN_FUNC_GPIO); + if (mode == I2C_MODE_MASTER) { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(scl_io_num, scl_out_sig, 0, 0); + } else { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT); + } + if (scl_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(scl_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(scl_io_num, GPIO_FLOATING); + } + gpio_matrix_in(scl_io_num, scl_in_sig, 0); + } + return ESP_OK; +} + +i2c_cmd_handle_t i2c_cmd_link_create() +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) calloc(1, sizeof(i2c_cmd_desc_t)); + return (i2c_cmd_handle_t) cmd_desc; +} + +void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle) +{ + if (cmd_handle == NULL) { + return; + } + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + while (cmd->free) { + i2c_cmd_link_t* ptmp = cmd->free; + cmd->free = cmd->free->next; + free(ptmp); + } + cmd->cur = NULL; + cmd->free = NULL; + cmd->head = NULL; + free(cmd_handle); + return; +} + +static esp_err_t i2c_cmd_link_append(i2c_cmd_handle_t cmd_handle, i2c_cmd_t* cmd) +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) cmd_handle; + if (cmd_desc->head == NULL) { + cmd_desc->head = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->head == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->head; + cmd_desc->free = cmd_desc->head; + } else { + cmd_desc->cur->next = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->cur->next == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->cur->next; + } + memcpy((uint8_t*) &cmd_desc->cur->cmd, (uint8_t*) cmd, sizeof(i2c_cmd_t)); + cmd_desc->cur->next = NULL; + return ESP_OK; + + err: + return ESP_FAIL; +} + +esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_RESTART; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_STOP; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, bool ack_en) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + uint8_t len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = NULL; + cmd.byte_cmd = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + int len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_READ; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_READ; + cmd.data = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE HPTaskAwoken = pdFALSE; + //This should never happen + if (p_i2c->mode == I2C_MODE_SLAVE) { + return; + } + if (p_i2c->status == I2C_STATUS_DONE) { + return; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 0; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } else if (p_i2c->status == I2C_STATUS_READ) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + while (p_i2c->rx_cnt-- > 0) { + *cmd->data++ = READ_PERI_REG(I2C_DATA_APB_REG(i2c_num)); + } + if (cmd->byte_num > 0) { + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + } + if (p_i2c->cmd_link.head == NULL) { + p_i2c->cmd_link.cur = NULL; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } + while (p_i2c->cmd_link.head) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + I2C[i2c_num]->command[p_i2c->cmd_idx].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_en = cmd->ack_en; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_exp = cmd->ack_exp; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx].op_code = cmd->op_code; + if (cmd->op_code == I2C_CMD_WRITE) { + //TODO: to reduce interrupt number + if (cmd->data) { + while (p_i2c->tx_fifo_remain > 0 && cmd->byte_num > 0) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), *cmd->data++); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + } else { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), cmd->byte_cmd); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num -= cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + if (cmd->byte_num > 0) { + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + p_i2c->status = I2C_STATUS_WRITE; + break; + } else if(cmd->op_code == I2C_CMD_READ) { + //TODO: to reduce interrupt number + p_i2c->rx_cnt = cmd->byte_num > p_i2c->rx_fifo_remain ? p_i2c->rx_fifo_remain : cmd->byte_num; + cmd->byte_num -= p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->status = I2C_STATUS_READ; + break; + } else { + } + p_i2c->cmd_idx++; + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + if (p_i2c->cmd_link.head == NULL || p_i2c->cmd_idx >= 15) { + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + p_i2c->status = I2C_STATUS_IDLE; + break; + } + } + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_clr.master_tran_comp = 1; + I2C[i2c_num]->int_ena.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 1; + I2C[i2c_num]->ctr.trans_start = 0; + I2C[i2c_num]->ctr.trans_start = 1; + return; +} + +esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_NOT_INSTALL_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_MASTER, I2C_MASTER_MODE_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret; + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); + if (res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + xSemaphoreTake(p_i2c->cmd_sem, 0); + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + p_i2c->cmd_link.free = cmd->free; + p_i2c->cmd_link.cur = cmd->cur; + p_i2c->cmd_link.head = cmd->head; + p_i2c->status = I2C_STATUS_IDLE; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + + //start send commands, at most 32 bytes one time, isr handler will process the remaining commands. + i2c_master_cmd_begin_static(i2c_num); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xSemaphoreTake(p_i2c->cmd_sem, ticks_to_wait); + if (res == pdFALSE) { + ret = ESP_ERR_TIMEOUT; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + ret = ESP_FAIL; + } else { + ret = ESP_OK; + } + p_i2c->status = I2C_STATUS_DONE; + xSemaphoreGive(p_i2c->cmd_mux); + return ret; +} + +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + + portBASE_TYPE res; + int cnt = 0; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + + res = xSemaphoreTake(p_i2c->slv_tx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xRingbufferSend(p_i2c->tx_ring_buf, data, size, ticks_to_wait); + if (res == pdFALSE) { + cnt = 0; + } else { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + cnt = size; + } + xSemaphoreGive(p_i2c->slv_tx_mux); + return cnt; +} + +static int i2c_slave_read(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + size_t size; + uint8_t* pdata = (uint8_t*) xRingbufferReceiveUpTo(p_i2c->rx_ring_buf, &size, ticks_to_wait, max_size); + if (pdata && size > 0) { + memcpy(data, pdata, size); + vRingbufferReturnItem(p_i2c->rx_ring_buf, pdata); + } + return size; +} + +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + res = xSemaphoreTake(p_i2c->slv_rx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + int cnt = i2c_slave_read(i2c_num, data, max_size, ticks_to_wait); + if (cnt > 0) { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (cnt < max_size && ticks_to_wait > 0) { + cnt += i2c_slave_read(i2c_num, data + cnt, max_size - cnt, ticks_to_wait); + } + } else { + cnt = 0; + } + xSemaphoreGive(p_i2c->slv_rx_mux); + return cnt; +} + + + + diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h new file mode 100644 index 000000000..69b80b1e6 --- /dev/null +++ b/components/driver/include/driver/i2c.h @@ -0,0 +1,514 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_I2C_H_ +#define _DRIVER_I2C_H_ + + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "driver/gpio.h" + +#define I2C_APB_CLK_FREQ APB_CLK_FREQ /*!< I2C source clock is APB clock, 80MHz */ +#define I2C_FIFO_LEN (32) /*!< I2C hardware fifo length */ +typedef enum{ + I2C_MODE_SLAVE = 0, /*!< I2C slave mode */ + I2C_MODE_MASTER, /*!< I2C master mode */ + I2C_MODE_MAX, +}i2c_mode_t; + +typedef enum { + I2C_MASTER_WRITE = 0, /*!< I2C write data */ + I2C_MASTER_READ, /*!< I2C read data */ +} i2c_rw_t; + +typedef enum { + I2C_DATA_MODE_MSB_FIRST = 0, /*!< I2C data msb first */ + I2C_DATA_MODE_LSB_FIRST = 1, /*!< I2C data lsb first */ + I2C_DATA_MODE_MAX +} i2c_trans_mode_t; + +typedef enum{ + I2C_CMD_RESTART = 0, /*!=0) The number of data bytes that pushed to the I2C slave buffer. + */ +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait); + +/** + * @brief I2C slave read data from internal buffer. When I2C slave receive data, isr will copy received data + * from hardware rx fifo to internal ringbuffer. Then users can read from internal ringbuffer. + * @note + * Only call this function in I2C slave mode + * + * @param i2c_num I2C port number + * @param data data pointer to write into internal buffer + * @param max_size Maximum data size to read + * @param ticks_to_wait Maximum waiting ticks + * + * @return + * - ESP_FAIL(-1) Parameter error + * - Others(>=0) The number of data bytes that read from I2C slave buffer. + */ +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait); + +/** + * @brief set I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period clock cycle number during SCL is high level, high_period is a 14 bit value + * @param low_period clock cycle number during SCL is low level, low_period is a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period); + +/** + * @brief get I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period pointer to get clock cycle number during SCL is high level, will get a 14 bit value + * @param low_period pointer to get clock cycle number during SCL is low level, will get a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); + +/** + * @brief set I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock number between the falling-edge of SDA and rising-edge of SCL for start mark, it's a 10-bit value. + * @param hold_time clock num between the falling-edge of SDA and falling-edge of SCL for start mark, it's a 10-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock num between the rising-edge of SCL and the rising-edge of SDA, it's a 10-bit value. + * @param hold_time clock number after the STOP bit's rising-edge, it's a 14-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time. + * @param hold_time pointer to get hold time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time clock number I2C used to sample data on SDA after the rising-edge of SCL, it's a 10-bit value + * @param hold_time clock number I2C used to hold the data after the falling-edge of SCL, it's a 10-bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time); + +/** + * @brief get I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time pointer to get sample time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time); + +/** + * @brief set I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode I2C sending data mode + * @param rx_trans_mode I2C receving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode); + +/** + * @brief get I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode pointer to get I2C sending data mode + * @param rx_trans_mode pointer to get I2C receiving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode); + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_I2C_H_*/ diff --git a/components/esp32/include/soc/i2c_reg.h b/components/esp32/include/soc/i2c_reg.h index d5c9858a9..9693ca5b5 100644 --- a/components/esp32/include/soc/i2c_reg.h +++ b/components/esp32/include/soc/i2c_reg.h @@ -261,6 +261,8 @@ #define I2C_RXFIFO_FULL_THRHD_V 0x1F #define I2C_RXFIFO_FULL_THRHD_S 0 +#define I2C_DATA_APB_REG(i) (0x60013000 + (i) * 0x14000 + 0x001c) + #define I2C_DATA_REG(i) (REG_I2C_BASE(i) + 0x001c) /* I2C_FIFO_RDATA : RO ;bitpos:[7:0] ;default: 8'b0 ; */ /*description: The register represent the byte data read from rxfifo when use apb fifo access*/ diff --git a/components/esp32/include/soc/i2c_struct.h b/components/esp32/include/soc/i2c_struct.h index a29a9c528..ea50d6bee 100644 --- a/components/esp32/include/soc/i2c_struct.h +++ b/components/esp32/include/soc/i2c_struct.h @@ -16,7 +16,7 @@ typedef volatile struct { union { struct { - uint32_t scl_low_period:14; /*This register is used to configure the low level width of SCL clock.*/ + uint32_t period:14; /*This register is used to configure the low level width of SCL clock.*/ uint32_t reserved14: 18; }; uint32_t val; @@ -58,7 +58,7 @@ typedef volatile struct { } status_reg; union { struct { - uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data.*/ + uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ uint32_t reserved20:12; }; uint32_t val; @@ -282,7 +282,7 @@ typedef volatile struct { uint32_t reserved_f4; uint32_t date; /**/ uint32_t reserved_fc; - uint32_t fifo_start_addr; /*This the start address for ram when use apb nonfifo access.*/ + uint32_t ram_data[32]; /*This the start address for ram when use apb nonfifo access.*/ } i2c_dev_t; extern i2c_dev_t I2C0; extern i2c_dev_t I2C1; diff --git a/docs/api/i2c.rst b/docs/api/i2c.rst new file mode 100644 index 000000000..1186e0583 --- /dev/null +++ b/docs/api/i2c.rst @@ -0,0 +1,82 @@ +I2C +=========== + +Overview +-------- + +ESP32 has two I2C controllers which can be set as master mode or slave mode. + +Application Example +------------------- + +I2C master and slave example: `examples/18_i2c `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/i2c.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: I2C_APB_CLK_FREQ +.. doxygendefine:: I2C_FIFO_LEN + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: i2c_cmd_handle_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: i2c_mode_t +.. doxygenenum:: i2c_rw_t +.. doxygenenum:: i2c_trans_mode_t +.. doxygenenum:: i2c_opmode_t +.. doxygenenum:: i2c_port_t +.. doxygenenum:: i2c_addr_mode_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: i2c_config_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: i2c_driver_install +.. doxygenfunction:: i2c_driver_delete +.. doxygenfunction:: i2c_param_config +.. doxygenfunction:: i2c_reset_tx_fifo +.. doxygenfunction:: i2c_reset_rx_fifo +.. doxygenfunction:: i2c_isr_register +.. doxygenfunction:: i2c_isr_free +.. doxygenfunction:: i2c_set_pin +.. doxygenfunction:: i2c_master_start +.. doxygenfunction:: i2c_master_write_byte +.. doxygenfunction:: i2c_master_write +.. doxygenfunction:: i2c_master_read_byte +.. doxygenfunction:: i2c_master_read +.. doxygenfunction:: i2c_master_stop +.. doxygenfunction:: i2c_master_cmd_begin +.. doxygenfunction:: i2c_slave_write_buffer +.. doxygenfunction:: i2c_slave_read +.. doxygenfunction:: i2c_set_period +.. doxygenfunction:: i2s_get_period +.. doxygenfunction:: i2c_set_start_timing +.. doxygenfunction:: i2c_get_start_timing +.. doxygenfunction:: i2c_set_stop_timing +.. doxygenfunction:: i2c_get_stop_timing +.. doxygenfunction:: i2c_set_data_timing +.. doxygenfunction:: i2c_get_data_timing +.. doxygenfunction:: i2c_set_data_mode +.. doxygenfunction:: i2c_get_data_mode +.. doxygenfunction:: i2c_cmd_link_create +.. doxygenfunction:: i2c_cmd_link_delete + diff --git a/docs/index.rst b/docs/index.rst index ba25b4969..9c2b0643f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -107,6 +107,7 @@ Contents: LED Control Remote Control Timer + I2C Pulse Counter Sigma-delta Modulation SPI Flash and Partition APIs diff --git a/examples/18_i2c/Makefile b/examples/18_i2c/Makefile new file mode 100644 index 000000000..d575e29b9 --- /dev/null +++ b/examples/18_i2c/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := i2c + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/18_i2c/README.md b/examples/18_i2c/README.md new file mode 100644 index 000000000..7a88894bf --- /dev/null +++ b/examples/18_i2c/README.md @@ -0,0 +1,29 @@ +# I2C Example + + +* This example will show you how to use I2C module by running two tasks on i2c bus: + + * read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + +* Pin assignment: + + * slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + +* Connection: + + * connect GPIO18 with GPIO25 + * connect GPIO19 with GPIO26 + * connect sda/scl of sensor with GPIO18/GPIO19 + * no need to add external pull-up resistors, driver will enable internal pull-up resistors. + +* Test items: + + * read the sensor data, if connected. + * i2c master(ESP32) will write data to i2c slave(ESP32). + * i2c master(ESP32) will read data from i2c slave(ESP32). diff --git a/examples/18_i2c/main/component.mk b/examples/18_i2c/main/component.mk new file mode 100644 index 000000000..44bd2b527 --- /dev/null +++ b/examples/18_i2c/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/18_i2c/main/i2c_test.c b/examples/18_i2c/main/i2c_test.c new file mode 100644 index 000000000..2ef6d75b2 --- /dev/null +++ b/examples/18_i2c/main/i2c_test.c @@ -0,0 +1,303 @@ +/* i2c - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "driver/i2c.h" + +/** + * TEST CODE BRIEF + * + * This example will show you how to use I2C module by running two tasks on i2c bus: + * + * - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + * + * Pin assignment: + * + * - slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * - master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + * + * Connection: + * + * - connect GPIO18 with GPIO25 + * - connect GPIO19 with GPIO26 + * - connect sda/scl of sensor with GPIO18/GPIO19 + * - no need to add external pull-up resistors, driver will enable internal pull-up resistors. + * + * Test items: + * + * - read the sensor data, if connected. + * - i2c master(ESP32) will write data to i2c slave(ESP32). + * - i2c master(ESP32) will read data from i2c slave(ESP32). + */ + +#define DATA_LENGTH 512 /*! 1) { + i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); + } + i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief Test code to write esp-i2c-slave + * Master device write data to slave(both esp32), + * the data will be stored in slave buffer. + * We can read them out from slave buffer. + * + * ___________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | + * --------|---------------------------|----------------------|------| + * + */ +esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief test code to write esp-i2c-slave + * + * 1. set mode + * _________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop | + * --------|---------------------------|---------------------|------| + * 2. wait more than 24 ms + * 3. read data + * ______________________________________________________________________________________ + * | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop | + * --------|---------------------------|--------------------|--------------------|------| + */ +esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN); + i2c_master_stop(cmd); + int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ret; + } + vTaskDelay(30 / portTICK_RATE_MS); + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN); + i2c_master_read_byte(cmd, data_h, ACK_VAL); + i2c_master_read_byte(cmd, data_l, NACK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ESP_FAIL; + } + return ESP_OK; +} + +/** + * @brief i2c master initialization + */ +void i2c_master_init() +{ + int i2c_master_port = I2C_MASTER_NUM; + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = I2C_MASTER_SDA_IO; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = I2C_MASTER_SCL_IO; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + i2c_param_config(i2c_master_port, &conf); + i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); +} + +/** + * @brief i2c slave initialization + */ +void i2c_slave_init() +{ + int i2c_slave_port = I2C_SLAVE_NUM; + i2c_config_t conf_slave; + conf_slave.sda_io_num = I2C_SLAVE_SDA_IO; + conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.scl_io_num = I2C_SLAVE_SCL_IO; + conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.mode = I2C_MODE_SLAVE; + conf_slave.slave.addr_10bit_en = 0; + conf_slave.slave.slave_addr = ESP_SLAVE_ADDR; + i2c_param_config(i2c_slave_port, &conf_slave); + i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0); +} + +/** + * @brief test function to show buffer + */ +void disp_buf(uint8_t* buf, int len) +{ + int i; + for (i = 0; i < len; i++) { + printf("%02x ", buf[i]); + if (( i + 1 ) % 16 == 0) { + printf("\n"); + } + } + printf("\n"); +} + +void i2c_test_task(void* arg) +{ + int i = 0; + int ret; + uint32_t task_idx = (uint32_t) arg; + uint8_t* data = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH); + uint8_t sensor_data_h, sensor_data_l; + + while (1) { + ret = i2c_master_sensor_test( I2C_MASTER_NUM, &sensor_data_h, &sensor_data_l); + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx); + printf("*******************\n"); + if (ret == ESP_OK) { + printf("data_h: %02x\n", sensor_data_h); + printf("data_l: %02x\n", sensor_data_l); + printf("sensor val: %f\n", ( sensor_data_h << 8 | sensor_data_l ) / 1.2); + } else { + printf("No ack, sensor not connected...skip...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + + //--------------------------------------------------- + for (i = 0; i < DATA_LENGTH; i++) { + data[i] = i; + } + size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + if (d_size == 0) { + printf("i2c slave tx buffer full\n"); + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH); + } else { + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ FROM SLAVE\n", task_idx); + printf("*******************\n"); + printf("====TASK[%d] Slave buffer data ====\n", task_idx); + disp_buf(data, d_size); + if (ret == ESP_OK) { + printf("====TASK[%d] Master read ====\n", task_idx); + disp_buf(data_rd, d_size); + } else { + printf("Master read slave error, IO not connected...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + //--------------------------------------------------- + int size; + for (i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i + 10; + } + //we need to fill the slave buffer so that master can read later + ret = i2c_master_write_slave( I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + size = i2c_slave_read_buffer( I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER WRITE TO SLAVE\n", task_idx); + printf("*******************\n"); + printf("----TASK[%d] Master write ----\n", task_idx); + disp_buf(data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + printf("----TASK[%d] Slave read: [%d] bytes ----\n", task_idx, size); + disp_buf(data, size); + } else { + printf("TASK[%d] Master write slave error, IO not connected....\n", task_idx); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + } +} + +void app_main() +{ + print_mux = xSemaphoreCreateMutex(); + i2c_slave_init(); + i2c_master_init(); + + xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL); + xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL); +} +