refactor(i2c): refactor i2c driver for idf

This commit is contained in:
XiongYu
2018-06-26 14:27:03 +08:00
parent 588fdad231
commit a5cdc760b4
6 changed files with 1249 additions and 0 deletions

View File

@ -0,0 +1,618 @@
// Copyright 2018-2025 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
// Temporary use the FreeRTOS critical function
#include "FreeRTOS.h"
#define ENTER_CRITICAL() portENTER_CRITICAL()
#define EXIT_CRITICAL() portEXIT_CRITICAL()
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); \
}
#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_TIMEING_VAL_ERR_STR "i2c timing value error"
#define I2C_ADDR_ERROR_STR "i2c null address error"
#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed"
#define I2C_MASTER_MODE_ERR_STR "Only allowed in master 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 does not support internal pull-up"
#define I2C_ACK_TYPE_ERR_STR "i2c ack type error"
#define I2C_DATA_LEN_ERR_STR "i2c data read length error"
#define I2C_IO_INIT_LEVEL (1)
#define I2C_CMD_ALIVE_INTERVAL_TICK (1000 / portTICK_PERIOD_MS)
#define I2C_ACKERR_CNT_MAX (10)
#define i2c_master_wait os_delay_us
typedef struct {
uint8_t byte_num; /*!< cmd byte number */
struct {
uint8_t en: 1; /*!< ack check enable */
uint8_t exp: 1; /*!< expected ack level to get */
uint8_t val: 1; /*!< ack value to send */
} ack;
uint8_t *data; /*!< data address */
uint8_t byte_cmd; /*!< to save cmd for one byte command mode */
i2c_opmode_t op_code; /*!< 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_TIMEOUT, /*!< I2C bus status error, and operation timeout */
} i2c_status_t;
typedef struct {
i2c_port_t i2c_num; /*!< I2C port number */
i2c_mode_t mode; /*!< I2C mode, master */
int status; /*!< record current command status, for master mode */
i2c_cmd_desc_t cmd_link; /*!< I2C command link */
} i2c_obj_t;
typedef union {
struct {
uint8_t scl: 1;
uint8_t sda: 1;
};
uint8_t val;
} i2c_last_state_t;
static i2c_obj_t *p_i2c_obj[I2C_NUM_MAX] = {0};
static i2c_config_t *i2c_config[I2C_NUM_MAX] = {NULL};
static i2c_last_state_t *i2c_last_state[I2C_NUM_MAX] = {NULL};
static void i2c_master_set_dc(i2c_port_t i2c_num, uint8_t sda, uint8_t scl)
{
i2c_last_state[i2c_num]->val = ((sda & 0x1) << 1) | (scl & 0x1);
gpio_set_level(i2c_config[i2c_num]->sda_io_num, i2c_last_state[i2c_num]->sda);
gpio_set_level(i2c_config[i2c_num]->scl_io_num, i2c_last_state[i2c_num]->scl);
}
static uint8_t i2c_master_get_dc(i2c_port_t i2c_num)
{
uint8_t sda_out;
sda_out = gpio_get_level(i2c_config[i2c_num]->sda_io_num);
return sda_out;
}
/*
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.
*/
esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode)
{
I2C_CHECK((i2c_num >= 0) && (i2c_num < I2C_NUM_MAX), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
if (p_i2c_obj[i2c_num] == NULL) {
p_i2c_obj[i2c_num] = (i2c_obj_t *) heap_caps_calloc(1, sizeof(i2c_obj_t), MALLOC_CAP_8BIT);
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->status = I2C_STATUS_IDLE;
p_i2c->cmd_link.cur = NULL;
p_i2c->cmd_link.head = NULL;
p_i2c->cmd_link.free = NULL;
} else {
ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR);
return ESP_FAIL;
}
if (i2c_config[i2c_num] == NULL) {
i2c_config[i2c_num] = (i2c_config_t *) heap_caps_calloc(1, sizeof(i2c_config_t), MALLOC_CAP_8BIT);
if (i2c_config[i2c_num] == NULL) {
ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR);
heap_caps_free(p_i2c_obj[i2c_num]);
p_i2c_obj[i2c_num] = NULL;
return ESP_FAIL;
}
memset(i2c_config[i2c_num], 0, sizeof(i2c_config_t));
} else {
ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR);
return ESP_FAIL;
}
if (i2c_last_state[i2c_num] == NULL) {
i2c_last_state[i2c_num] = (i2c_last_state_t *) heap_caps_calloc(1, sizeof(i2c_last_state_t), MALLOC_CAP_8BIT);
if (i2c_last_state[i2c_num] == NULL) {
ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR);
heap_caps_free(p_i2c_obj[i2c_num]);
p_i2c_obj[i2c_num] = NULL;
heap_caps_free(i2c_config[i2c_num]);
i2c_config[i2c_num] = NULL;
return ESP_FAIL;
}
memset(i2c_last_state[i2c_num], 0, sizeof(i2c_last_state_t));
} else {
ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR);
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t i2c_driver_delete(i2c_port_t i2c_num)
{
I2C_CHECK((i2c_num >= 0) && (i2c_num < I2C_NUM_MAX), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(i2c_last_state[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL);
I2C_CHECK(i2c_config[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL);
I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL);
heap_caps_free(i2c_last_state[i2c_num]);
i2c_last_state[i2c_num] = NULL;
heap_caps_free(i2c_config[i2c_num]);
i2c_config[i2c_num] = NULL;
heap_caps_free(p_i2c_obj[i2c_num]);
p_i2c_obj[i2c_num] = NULL;
return ESP_OK;
}
esp_err_t i2c_set_pin(i2c_port_t i2c_num, int sda_io_num, int scl_io_num, gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode)
{
I2C_CHECK((i2c_num >= 0) && (i2c_num < I2C_NUM_MAX), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
gpio_config_t io_conf;
if (sda_io_num >= 0) {
// disable interrupt
io_conf.intr_type = GPIO_INTR_DISABLE;
// set as output mode
io_conf.mode = GPIO_MODE_OUTPUT_OD;
// bit mask of the pins that you want to set
io_conf.pin_bit_mask = (1ULL << sda_io_num);
// disable pull-down mode
io_conf.pull_down_en = 0;
// disable pull-up mode
io_conf.pull_up_en = sda_pullup_en;
// configure GPIO with the given settings
ESP_ERROR_CHECK(gpio_config(&io_conf));
ESP_ERROR_CHECK(gpio_set_level(sda_io_num, 1));
}
if (scl_io_num >= 0) {
// disable interrupt
io_conf.intr_type = GPIO_INTR_DISABLE;
// set as output mode
io_conf.mode = GPIO_MODE_OUTPUT_OD;
// bit mask of the pins that you want to set
io_conf.pin_bit_mask = (1ULL << scl_io_num);
// disable pull-down mode
io_conf.pull_down_en = 0;
// disable pull-up mode
io_conf.pull_up_en = scl_pullup_en;
// configure GPIO with the given settings
ESP_ERROR_CHECK(gpio_config(&io_conf));
ESP_ERROR_CHECK(gpio_set_level(scl_io_num, 1));
}
return ESP_OK;
}
esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf)
{
I2C_CHECK((i2c_num >= 0) && (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;
}
memcpy((i2c_config_t *)(i2c_config[i2c_num]), (i2c_config_t *)i2c_conf, sizeof(i2c_config_t));
return ESP_OK;
}
i2c_cmd_handle_t i2c_cmd_link_create()
{
i2c_cmd_desc_t *cmd_desc = (i2c_cmd_desc_t *) heap_caps_calloc(1, sizeof(i2c_cmd_desc_t), MALLOC_CAP_8BIT);
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;
heap_caps_free(ptmp);
}
cmd->cur = NULL;
cmd->free = NULL;
cmd->head = NULL;
heap_caps_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 *) heap_caps_calloc(1, sizeof(i2c_cmd_link_t), MALLOC_CAP_8BIT);
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 *) heap_caps_calloc(1, sizeof(i2c_cmd_link_t), MALLOC_CAP_8BIT);
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_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_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;
}
static esp_err_t i2c_master_read_static(i2c_cmd_handle_t cmd_handle, uint8_t *data, size_t data_len, i2c_ack_type_t ack)
{
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, i2c_ack_type_t 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_CHECK(ack < I2C_MASTER_ACK_MAX, I2C_ACK_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
i2c_cmd_t cmd;
cmd.ack.en = 0;
cmd.ack.exp = 0;
cmd.ack.val = ((ack == I2C_MASTER_LAST_NACK) ? I2C_MASTER_NACK : (ack & 0x1));
cmd.byte_num = 1;
cmd.op_code = I2C_CMD_READ;
cmd.data = 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, i2c_ack_type_t 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_CHECK(ack < I2C_MASTER_ACK_MAX, I2C_ACK_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
I2C_CHECK(data_len > 0, I2C_DATA_LEN_ERR_STR, ESP_ERR_INVALID_ARG);
if (ack != I2C_MASTER_LAST_NACK) {
return i2c_master_read_static(cmd_handle, data, data_len, ack);
} else {
if (data_len == 1) {
return i2c_master_read_byte(cmd_handle, data, I2C_MASTER_NACK);
} else {
esp_err_t ret;
// first read (data_len - 1) byte data with ACK,than read the end byte with NACK.
if ((ret = i2c_master_read_static(cmd_handle, data, data_len - 1, I2C_MASTER_ACK)) != ESP_OK) {
return ret;
}
return i2c_master_read_byte(cmd_handle, data + data_len - 1, I2C_MASTER_NACK);
}
}
}
static void i2c_master_cmd_begin_static(i2c_port_t i2c_num)
{
i2c_obj_t *p_i2c = p_i2c_obj[i2c_num];
i2c_cmd_t *cmd;
uint8_t dat;
uint8_t len;
int8_t i, k;
uint8_t retVal;
// This should never happen
if (p_i2c->mode != I2C_MODE_MASTER) {
return;
}
while (p_i2c->cmd_link.head) {
cmd = &p_i2c->cmd_link.head->cmd;
switch (cmd->op_code) {
case (I2C_CMD_RESTART): {
i2c_master_set_dc(i2c_num, 1, i2c_last_state[i2c_num]->scl);
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(1); // sda 1, scl 1
i2c_master_set_dc(i2c_num, 0, 1);
i2c_master_wait(1); // sda 0, scl 1
}
break;
case (I2C_CMD_WRITE): {
p_i2c->status = I2C_STATUS_WRITE;
for (len = 0; len < cmd->byte_num; len++) {
dat = 0;
retVal = 0;
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0);
for (i = 7; i >= 0; i--) {
if (cmd->byte_num == 1) {
dat = (cmd->byte_cmd) >> i;
} else {
dat = ((uint8_t) * (cmd->data + len)) >> i;
}
i2c_master_set_dc(i2c_num, dat, 0);
i2c_master_wait(1);
i2c_master_set_dc(i2c_num, dat, 1);
i2c_master_wait(2);
if (i == 0) {
i2c_master_wait(1); // wait slaver ack
}
i2c_master_set_dc(i2c_num, dat, 0);
}
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0);
i2c_master_set_dc(i2c_num, 1, 0);
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(1);
retVal = i2c_master_get_dc(i2c_num);
i2c_master_wait(1);
i2c_master_set_dc(i2c_num, 1, 0);
if (cmd->ack.en == 1) {
if ((retVal & 0x01) != cmd->ack.exp) {
p_i2c->status = I2C_STATUS_ACK_ERROR;
return ;
}
}
}
}
break;
case (I2C_CMD_READ): {
p_i2c->status = I2C_STATUS_READ;
for (len = 0; len < cmd->byte_num; len++) {
retVal = 0;
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0);
for (i = 0; i < 8; i++) {
i2c_master_set_dc(i2c_num, 1, 0);
i2c_master_wait(2);
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(1); // sda 1, scl 1
k = i2c_master_get_dc(i2c_num);
i2c_master_wait(1);
if (i == 7) {
i2c_master_wait(1);
}
k <<= (7 - i);
retVal |= k;
}
i2c_master_set_dc(i2c_num, 1, 0);
memcpy((uint8_t *)(cmd->data + len), (uint8_t *)&retVal, 1);
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0);
i2c_master_set_dc(i2c_num, cmd->ack.val, 0);
i2c_master_set_dc(i2c_num, cmd->ack.val, 1);
i2c_master_wait(4); // sda level, scl 1
i2c_master_set_dc(i2c_num, cmd->ack.val, 0);
i2c_master_set_dc(i2c_num, 1, 0);
i2c_master_wait(1);
}
}
break;
case (I2C_CMD_STOP): {
i2c_master_wait(1);
i2c_master_set_dc(i2c_num, 0, i2c_last_state[i2c_num]->scl);
i2c_master_set_dc(i2c_num, 0, 1);
i2c_master_wait(2); // sda 0, scl 1
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(2); // sda 1, scl 1
}
break;
}
p_i2c->cmd_link.head = p_i2c->cmd_link.head->next;
}
p_i2c->status = I2C_STATUS_DONE;
return;
}
esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait)
{
I2C_CHECK((i2c_num >= 0) && (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);
i2c_obj_t *p_i2c = p_i2c_obj[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;
ENTER_CRITICAL();
// start send commands, at most 32 bytes one time, isr handler will process the remaining commands.
i2c_master_cmd_begin_static(i2c_num);
EXIT_CRITICAL();
// TODO: Timeout check
if (p_i2c->status == I2C_STATUS_DONE) {
return ESP_OK;
} else {
return ESP_FAIL;
}
}

View File

@ -0,0 +1,268 @@
// Copyright 2018-2025 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.
#pragma once
#include <stdint.h>
#include "FreeRTOS.h"
#include "esp_err.h"
#include "gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
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_CMD_RESTART = 0, /*!< I2C restart command */
I2C_CMD_WRITE, /*!< I2C write command */
I2C_CMD_READ, /*!< I2C read command */
I2C_CMD_STOP, /*!< I2C stop command */
} i2c_opmode_t;
typedef enum {
I2C_NUM_0 = 0, /*!< I2C port 0 */
I2C_NUM_MAX
} i2c_port_t;
typedef enum {
I2C_MASTER_ACK = 0x0, /*!< I2C ack for each byte read */
I2C_MASTER_NACK = 0x1, /*!< I2C nack for each byte read */
I2C_MASTER_LAST_NACK = 0x2, /*!< I2C nack for the last byte*/
I2C_MASTER_ACK_MAX,
} i2c_ack_type_t;
/**
* @brief I2C initialization parameters
*/
typedef struct {
i2c_mode_t mode; /*!< I2C mode */
gpio_num_t sda_io_num; /*!< GPIO number for I2C sda signal */
gpio_pullup_t sda_pullup_en; /*!< Internal GPIO pull mode for I2C sda signal*/
gpio_num_t scl_io_num; /*!< GPIO number for I2C scl signal */
gpio_pullup_t scl_pullup_en; /*!< Internal GPIO pull mode for I2C scl signal*/
} i2c_config_t;
typedef void *i2c_cmd_handle_t; /*!< I2C command handle */
/**
* @brief I2C driver install
*
* @param i2c_num I2C port number
* @param mode I2C mode( master or slave )
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL Driver install error
*/
esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode);
/**
* @brief I2C driver delete
*
* @param i2c_num I2C port number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_driver_delete(i2c_port_t i2c_num);
/**
* @brief I2C parameter initialization
*
* @note It must be used after calling i2c_driver_install
*
* @param i2c_num I2C port number
* @param i2c_conf pointer to I2C parameter settings
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);
/**
* @brief Configure GPIO signal for I2C sck and sda
*
* @param i2c_num I2C port number
* @param sda_io_num GPIO number for I2C sda signal
* @param scl_io_num GPIO number for I2C scl signal
* @param sda_pullup_en Whether to enable the internal pullup for sda pin
* @param scl_pullup_en Whether to enable the internal pullup for scl pin
* @param mode I2C mode
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_set_pin(i2c_port_t i2c_num, int sda_io_num, int scl_io_num,
gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode);
/**
* @brief Create and init I2C command link
* @note
* Before we build I2C command link, we need to call i2c_cmd_link_create() to create
* a command link.
* After we finish sending the commands, we need to call i2c_cmd_link_delete() to
* release and return the resources.
*
* @return i2c command link handler
*/
i2c_cmd_handle_t i2c_cmd_link_create();
/**
* @brief Free I2C command link
* @note
* Before we build I2C command link, we need to call i2c_cmd_link_create() to create
* a command link.
* After we finish sending the commands, we need to call i2c_cmd_link_delete() to
* release and return the resources.
*
* @param cmd_handle I2C command handle
*/
void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle);
/**
* @brief Queue command for I2C master to generate a start signal
* @note
* Only call this function in I2C master mode
* Call i2c_master_cmd_begin() to send all queued commands
*
* @param cmd_handle I2C cmd link
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle);
/**
* @brief Queue command for I2C master to write one byte to I2C bus
* @note
* Only call this function in I2C master mode
* Call i2c_master_cmd_begin() to send all queued commands
*
* @param cmd_handle I2C cmd link
* @param data I2C one byte command to write to bus
* @param ack_en enable ack check for master
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en);
/**
* @brief Queue command for I2C master to write buffer to I2C bus
* @note
* Only call this function in I2C master mode
* Call i2c_master_cmd_begin() to send all queued commands
*
* @param cmd_handle I2C cmd link
* @param data data to send
* @param data_len data length
* @param ack_en enable ack check for master
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t *data, size_t data_len, bool ack_en);
/**
* @brief Queue command for I2C master to read one byte from I2C bus
* @note
* Only call this function in I2C master mode
* Call i2c_master_cmd_begin() to send all queued commands
*
* @param cmd_handle I2C cmd link
* @param data pointer accept the data byte
* @param ack ack value for read command
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t *data, i2c_ack_type_t ack);
/**
* @brief Queue command for I2C master to read data from I2C bus
* @note
* Only call this function in I2C master mode
* Call i2c_master_cmd_begin() to send all queued commands
*
* @param cmd_handle I2C cmd link
* @param data data buffer to accept the data from bus
* @param data_len read data length
* @param ack ack value for read command
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t *data, size_t data_len, i2c_ack_type_t ack);
/**
* @brief Queue command for I2C master to generate a stop signal
* @note
* Only call this function in I2C master mode
* Call i2c_master_cmd_begin() to send all queued commands
*
* @param cmd_handle I2C cmd link
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle);
/**
* @brief I2C master send queued commands.
* This function will trigger sending all queued commands.
* The task will be blocked until all the commands have been sent out.
* The I2C APIs are not thread-safe, if you want to use one I2C port in different tasks,
* you need to take care of the multi-thread issue.
* @note
* Only call this function in I2C master mode
*
* @param i2c_num I2C port number
* @param cmd_handle I2C command handler
* @param ticks_to_wait maximum wait ticks.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
*/
esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait);
#ifdef __cplusplus
}
#endif

View File

@ -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

View File

@ -0,0 +1,91 @@
# _I2C Example_
* This example will show you how to use I2C module:
* read external i2c sensor, here we use a MPU6050 sensor for instance.
## Pin assignment
* master:
* GPIO14 is assigned as the data signal of i2c master port
* GPIO2 is assigned as the clock signal of i2c master port
## How to use example
### Hardware Required
* Connection:
* connect sda/scl of sensor with GPIO14/GPIO2
* no need to add external pull-up resistors, driver will enable internal pull-up resistors.
### Configure the project
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (0) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
I (0) gpio: GPIO[2]| InputEn: 0| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
I (0) main: *******************
I (0) main: who_am_i: 68
I (0) main: TEMP: 26.51
I (0) main: sensor_data[0]: -1288
I (0) main: sensor_data[1]: 8796
I (0) main: sensor_data[2]: 11088
I (0) main: sensor_data[3]: -3408
I (0) main: sensor_data[4]: -223
I (0) main: sensor_data[5]: 67
I (0) main: sensor_data[6]: -11
I (0) main: error_count: 0
I (0) main: *******************
I (0) main: who_am_i: 68
I (0) main: TEMP: 26.55
I (0) main: sensor_data[0]: -1224
I (0) main: sensor_data[1]: 8748
I (0) main: sensor_data[2]: 11084
I (0) main: sensor_data[3]: -3392
I (0) main: sensor_data[4]: -318
I (0) main: sensor_data[5]: 235
I (0) main: sensor_data[6]: 21
I (0) main: error_count: 0
```

View File

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@ -0,0 +1,260 @@
/* I2C example
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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_err.h"
#include "driver/i2c.h"
static const char *TAG = "main";
/**
* 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 MPU6050 sensor for instance.
* - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP8266 chip.
*
* Pin assignment:
*
* - master:
* GPIO14 is assigned as the data signal of i2c master port
* GPIO2 is assigned as the clock signal of i2c master port
*
* Connection:
*
* - connect sda/scl of sensor with GPIO14/GPIO2
* - no need to add external pull-up resistors, driver will enable internal pull-up resistors.
*
* Test items:
*
* - read the sensor data, if connected.
*/
#define I2C_EXAMPLE_MASTER_SCL_IO 2 /*!< gpio number for I2C master clock */
#define I2C_EXAMPLE_MASTER_SDA_IO 14 /*!< gpio number for I2C master data */
#define I2C_EXAMPLE_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */
#define I2C_EXAMPLE_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define I2C_EXAMPLE_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
#define MPU6050_SENSOR_ADDR 0x68 /*!< slave address for MPU6050 sensor */
#define MPU6050_CMD_START 0x41 /*!< Command to set measure mode */
#define MPU6050_WHO_AM_I 0x75 /*!< Command to read WHO_AM_I reg */
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#define LAST_NACK_VAL 0x2 /*!< I2C last_nack value */
/**
* Define the mpu6050 register address:
*/
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B
#define WHO_AM_I 0x75 /*!< Command to read WHO_AM_I reg */
/**
* @brief i2c master initialization
*/
static esp_err_t i2c_example_master_init()
{
int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
conf.sda_pullup_en = 0;
conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
conf.scl_pullup_en = 0;
ESP_ERROR_CHECK(i2c_driver_install(i2c_master_port, conf.mode));
ESP_ERROR_CHECK(i2c_param_config(i2c_master_port, &conf));
return ESP_OK;
}
/**
* @brief test code to write mpu6050
*
* 1. send data
* ___________________________________________________________________________________________________
* | start | slave_addr + wr_bit + ack | write reg_address + ack | write data_len byte + ack | stop |
* --------|---------------------------|-------------------------|----------------------------|------|
*
* @param i2c_num I2C port number
* @param reg_address slave reg address
* @param data data to send
* @param data_len data length
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
*/
static esp_err_t i2c_example_master_mpu6050_write(i2c_port_t i2c_num, uint8_t reg_address, uint8_t *data, size_t data_len)
{
int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, MPU6050_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_address, ACK_CHECK_EN);
i2c_master_write(cmd, data, data_len, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/**
* @brief test code to read mpu6050
*
* 1. send reg address
* ______________________________________________________________________
* | start | slave_addr + wr_bit + ack | write reg_address + ack | stop |
* --------|---------------------------|-------------------------|------|
*
* 2. read data
* ___________________________________________________________________________________
* | start | slave_addr + wr_bit + ack | read data_len byte + ack(last nack) | stop |
* --------|---------------------------|--------------------------------------|------|
*
* @param i2c_num I2C port number
* @param reg_address slave reg address
* @param data data to read
* @param data_len data length
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
*/
static esp_err_t i2c_example_master_mpu6050_read(i2c_port_t i2c_num, uint8_t reg_address, uint8_t *data, size_t data_len)
{
int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, MPU6050_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_address, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
return ret;
}
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, MPU6050_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
i2c_master_read(cmd, data, data_len, LAST_NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
static esp_err_t i2c_example_master_mpu6050_init(i2c_port_t i2c_num)
{
uint8_t cmd_data;
vTaskDelay(100 / portTICK_RATE_MS);
i2c_example_master_init();
cmd_data = 0x00; // reset mpu6050
ESP_ERROR_CHECK(i2c_example_master_mpu6050_write(i2c_num, PWR_MGMT_1, &cmd_data, 1));
cmd_data = 0x07; // Set the SMPRT_DIV
ESP_ERROR_CHECK(i2c_example_master_mpu6050_write(i2c_num, SMPLRT_DIV, &cmd_data, 1));
cmd_data = 0x06; // Set the Low Pass Filter
ESP_ERROR_CHECK(i2c_example_master_mpu6050_write(i2c_num, CONFIG, &cmd_data, 1));
cmd_data = 0x18; // Set the GYRO range
ESP_ERROR_CHECK(i2c_example_master_mpu6050_write(i2c_num, GYRO_CONFIG, &cmd_data, 1));
cmd_data = 0x01; // Set the ACCEL range
ESP_ERROR_CHECK(i2c_example_master_mpu6050_write(i2c_num, ACCEL_CONFIG, &cmd_data, 1));
return ESP_OK;
}
static void i2c_task_example(void *arg)
{
uint8_t sensor_data[14];
uint8_t who_am_i, i;
double Temp;
static uint32_t error_count = 0;
int ret;
i2c_example_master_mpu6050_init(I2C_EXAMPLE_MASTER_NUM);
while (1) {
who_am_i = 0;
i2c_example_master_mpu6050_read(I2C_EXAMPLE_MASTER_NUM, WHO_AM_I, &who_am_i, 1);
if (0x68 != who_am_i) {
error_count++;
}
memset(sensor_data, 0, 14);
ret = i2c_example_master_mpu6050_read(I2C_EXAMPLE_MASTER_NUM, ACCEL_XOUT_H, sensor_data, 14);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "*******************\n");
ESP_LOGI(TAG, "WHO_AM_I: 0x%02x\n", who_am_i);
Temp = 36.53 + ((double)(int16_t)((sensor_data[6] << 8) | sensor_data[7]) / 340);
ESP_LOGI(TAG, "TEMP: %d.%d\n", (uint16_t)Temp, (uint16_t)(Temp * 100) % 100);
for (i = 0; i < 7; i++) {
ESP_LOGI(TAG, "sensor_data[%d]: %d\n", i, (int16_t)((sensor_data[i * 2] << 8) | sensor_data[i * 2 + 1]));
}
ESP_LOGI(TAG, "error_count: %d\n", error_count);
} else {
ESP_LOGE(TAG, "No ack, sensor not connected...skip...\n");
}
vTaskDelay(100 / portTICK_RATE_MS);
}
i2c_driver_delete(I2C_EXAMPLE_MASTER_NUM);
}
void app_main(void)
{
//start i2c task
xTaskCreate(i2c_task_example, "i2c_task_example", 2048, NULL, 10, NULL);
}