mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-21 00:56:38 +08:00
refactor(i2c): refactor i2c driver for idf
This commit is contained in:
618
components/esp8266/driver/i2c.c
Normal file
618
components/esp8266/driver/i2c.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
268
components/esp8266/include/driver/i2c.h
Normal file
268
components/esp8266/include/driver/i2c.h
Normal 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
|
9
examples/peripherals/i2c/Makefile
Normal file
9
examples/peripherals/i2c/Makefile
Normal 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
|
||||||
|
|
91
examples/peripherals/i2c/README.md
Normal file
91
examples/peripherals/i2c/README.md
Normal 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
|
||||||
|
|
||||||
|
```
|
3
examples/peripherals/i2c/main/component.mk
Normal file
3
examples/peripherals/i2c/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#
|
||||||
|
# Main Makefile. This is basically the same as a component makefile.
|
||||||
|
#
|
260
examples/peripherals/i2c/main/user_main.c
Normal file
260
examples/peripherals/i2c/main/user_main.c
Normal 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);
|
||||||
|
}
|
Reference in New Issue
Block a user