Tasmota/lib/libesp32_div/ESP32-HomeKit/src/esp_mfi_i2c.c.0
2021-03-11 14:48:59 +01:00

690 lines
17 KiB
Plaintext

/*
* ESPRESSIF MIT License
*
* Copyright (c) 2020 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <sys/errno.h>
#include <sdkconfig.h>
#ifdef CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/ets_sys.h"
#else
#include "esp32/rom/ets_sys.h"
#endif
#ifdef CONFIG_I2C_SOFTWARE
#include "driver/gpio.h"
#else
#include "driver/i2c.h"
#endif
#include "esp_log.h"
#include "esp_mfi_i2c.h"
static const char *TAG = "mfi_i2c";
#ifndef CONFIG_I2C_SOFTWARE
#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_FREQ_HZ CONFIG_IC2_SPEED
#define I2C_MASTER_RX_BUF_DISABLE 0
#define I2C_MASTER_TX_BUF_DISABLE 0
#define ACK_CHECK_EN 1
#define ACK_VAL 0
#define NACK_VAL 1
/* To use GPIOs 17/18 for the Hardware I2C, replace the numbers 26/27 below
*/
#define I2C_MASTER_SDA_GPIO CONFIG_SDA_GPIO
#define I2C_MASTER_SCL_GPIO CONFIG_SCL_GPIO
#define I2C_MASTER_MAX_READ CONFIG_I2C_MAX_READ_COUNT
#define I2C_MASTER_RETRY_TIMES 500
#define I2C_MASTER_TICKS_TIMES 2 * I2C_MASTER_RETRY_TIMES
#define I2C_MASTER_MAX_RETRY 10
#define I2C_MASTER_INTERNAL_TIMES 8 * I2C_MASTER_RETRY_TIMES
/**
* @brief Initialize I2C information
*/
int esp_mfi_i2c_init(void)
{
int ret = 0;
i2c_config_t conf;
int i2c_master_port = I2C_MASTER_NUM;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_MASTER_SDA_GPIO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_MASTER_SCL_GPIO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
ret = i2c_param_config(i2c_master_port, &conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C parameter initial fail %d", ret);
return -EINVAL;
}
ret = i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C driver initial fail %d", ret);
return -EINVAL;
}
return 0;
}
/**
* @brief write data buffer to slave
*/
int esp_mfi_i2c_write(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
{
uint16_t i;
if (!buff)
return -EINVAL;
ESP_LOGI(TAG, "Writing to HW I2C");
int ret = 0;
i2c_cmd_handle_t cmd = NULL;
i = 0;
do {
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// Send write address of the CP
i2c_master_write_byte(cmd, slvaddr, ACK_CHECK_EN);
// Send register address to slave.
i2c_master_write_byte(cmd, regaddr, ACK_CHECK_EN);
// Send data out.
i2c_master_write(cmd, buff, len, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TICKS_TIMES / portTICK_RATE_MS);
i ++;
i2c_cmd_link_delete(cmd);
ets_delay_us(I2C_MASTER_RETRY_TIMES);
} while(ret != ESP_OK && i < I2C_MASTER_MAX_RETRY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Write data to slave fail %d.", ret);
}
return ret;
}
/**
* @brief read data form slave
*/
int esp_mfi_i2c_read(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
{
uint16_t i, j = 0;
if (!buff)
return -EINVAL;
ESP_LOGI(TAG, "Reading from HW I2C");
int ret = 0;
i2c_cmd_handle_t cmd = NULL;
i = 0;
do {
for (j = 0; j < I2C_MASTER_MAX_READ; j++) {
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// Send write address of the CP
i2c_master_write_byte(cmd, slvaddr, ACK_CHECK_EN);
// Send register address to slave.
i2c_master_write_byte(cmd, regaddr, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TICKS_TIMES / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {
break;
} else {
ets_delay_us(I2C_MASTER_INTERNAL_TIMES);
}
}
ets_delay_us(I2C_MASTER_INTERNAL_TIMES);
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, slvaddr + 1, ACK_CHECK_EN);
if (len == 1)
i2c_master_read_byte(cmd, buff, NACK_VAL);
else {
i2c_master_read(cmd, buff, len - 1, ACK_VAL);
i2c_master_read_byte(cmd, buff + len - 1, NACK_VAL);
}
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TICKS_TIMES / portTICK_RATE_MS);
i ++;
i2c_cmd_link_delete(cmd);
} while (ret != ESP_OK && i < I2C_MASTER_MAX_RETRY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Read data from slave fail %d.", ret);
}
return ret;
}
#else
#define MFI_CP_BUSY_RETRY_TIMES CONFIG_I2C_RETRY_COUNT
#define GPIO_Pin_26 GPIO_SEL_26
#define GPIO_Pin_27 GPIO_SEL_27
#define gpio_set_output_level(a, b) gpio_set_level(a, b)
#define gpio_get_input_level(a) gpio_get_level(a)
#define GPIO_Mode_Input_OutOD GPIO_MODE_INPUT_OUTPUT_OD
#define GPIO_PullDown_DIS GPIO_PULLDOWN_DISABLE
#define GPIO_PullUp_EN GPIO_PULLUP_ENABLE
#define GPIO_Mode_Out_OD GPIO_MODE_OUTPUT_OD
#define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO26_U
#define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO27_U
#define I2C_MASTER_SDA_GPIO 26
#define I2C_MASTER_SDA_PIN GPIO_Pin_26
#define I2C_MASTER_SDA_FUNC FUNC_GPIO26_GPIO26
#define I2C_MASTER_SCL_GPIO 27
#define I2C_MASTER_SCL_PIN GPIO_Pin_27
#define I2C_MASTER_SCL_FUNC FUNC_GPIO27_GPIO27
#define I2C_MASTER_SDA_HIGH_SCL_HIGH() \
gpio_set_output_level(I2C_MASTER_SDA_GPIO,1); \
gpio_set_output_level(I2C_MASTER_SCL_GPIO,1);
#define I2C_MASTER_SDA_HIGH_SCL_LOW() \
gpio_set_output_level(I2C_MASTER_SDA_GPIO,1); \
gpio_set_output_level(I2C_MASTER_SCL_GPIO,0);
#define I2C_MASTER_SDA_LOW_SCL_HIGH() \
gpio_set_output_level(I2C_MASTER_SDA_GPIO,0); \
gpio_set_output_level(I2C_MASTER_SCL_GPIO,1);
#define I2C_MASTER_SDA_LOW_SCL_LOW() \
gpio_set_output_level(I2C_MASTER_SDA_GPIO,0); \
gpio_set_output_level(I2C_MASTER_SCL_GPIO,0);
#define i2c_master_wait ets_delay_us
static void i2c_master_init(void);
static void i2c_master_stop(void);
static uint8_t s_last_sda;
static uint8_t s_last_scl;
/**
* @brief set I2C SDA and SCL bit value for half CLK cycle
*/
static void i2c_master_setDC(uint8_t SDA, uint8_t SCL)
{
SDA &= 0x01;
SCL &= 0x01;
s_last_sda = SDA;
s_last_scl = SCL;
if ((0 == SDA) && (0 == SCL)) {
I2C_MASTER_SDA_LOW_SCL_LOW();
} else if ((0 == SDA) && (1 == SCL)) {
I2C_MASTER_SDA_LOW_SCL_HIGH();
} else if ((1 == SDA) && (0 == SCL)) {
I2C_MASTER_SDA_HIGH_SCL_LOW();
} else {
I2C_MASTER_SDA_HIGH_SCL_HIGH();
}
}
/**
* @brief get I2C SDA bit value
*/
static uint8_t i2c_master_getDC(void)
{
uint8_t sda_out;
sda_out = gpio_get_input_level(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO));
return sda_out;
}
/**
* @brief initialize I2C bus to enable i2c operations
*/
static void i2c_master_init(void)
{
uint8_t i;
i2c_master_setDC(1, 0);
i2c_master_wait(5);
// when SCL = 0, toggle SDA to clear up
i2c_master_setDC(0, 0) ;
i2c_master_wait(5);
i2c_master_setDC(1, 0) ;
i2c_master_wait(5);
// set data_cnt to max value
for (i = 0; i < 28; i++) {
i2c_master_setDC(1, 0);
i2c_master_wait(5); // sda 1, scl 0
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
}
// reset all
i2c_master_stop();
}
/**
* @brief configure SDA and SCL GPIO to open-drain output mode
*/
static void i2c_master_gpio_init(void)
{
gpio_config_t io_config;
io_config.intr_type = GPIO_PIN_INTR_DISABLE;
io_config.mode = GPIO_MODE_INPUT_OUTPUT_OD;
io_config.pin_bit_mask = I2C_MASTER_SDA_PIN;
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_config.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_config);
io_config.intr_type = GPIO_PIN_INTR_DISABLE;
io_config.mode = GPIO_MODE_OUTPUT_OD;
io_config.pin_bit_mask = I2C_MASTER_SCL_PIN;
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_config.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_config);
gpio_pullup_en(I2C_MASTER_SDA_GPIO);
gpio_pullup_en(I2C_MASTER_SCL_GPIO);
gpio_pulldown_dis(I2C_MASTER_SDA_GPIO);
gpio_pulldown_dis(I2C_MASTER_SCL_GPIO);
i2c_master_init();
}
/**
* @brief set I2C to start operation
*/
static void i2c_master_start(void)
{
i2c_master_setDC(1, s_last_scl);
i2c_master_wait(5);
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
i2c_master_setDC(0, 1);
i2c_master_wait(5); // sda 0, scl 1
}
/**
* @brief set i2c to stop sending state
*/
static void i2c_master_stop(void)
{
i2c_master_wait(5);
i2c_master_setDC(0, s_last_scl);
i2c_master_wait(5); // sda 0
i2c_master_setDC(0, 1);
i2c_master_wait(5); // sda 0, scl 1
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
}
/**
* @brief set ack to i2c bus as level value
*/
static void i2c_master_setAck(uint8_t level)
{
i2c_master_setDC(s_last_sda, 0);
i2c_master_wait(5);
i2c_master_setDC(level, 0);
i2c_master_wait(5); // sda level, scl 0
i2c_master_setDC(level, 1);
i2c_master_wait(8); // sda level, scl 1
i2c_master_setDC(level, 0);
i2c_master_wait(5); // sda level, scl 0
i2c_master_setDC(1, 0);
i2c_master_wait(5);
}
/**
* @brief confirm if peer send ack
*/
static uint8_t i2c_master_getAck(void)
{
uint8_t retVal;
i2c_master_setDC(s_last_sda, 0);
i2c_master_wait(5);
i2c_master_setDC(1, 0);
i2c_master_wait(5);
i2c_master_setDC(1, 1);
i2c_master_wait(5);
retVal = i2c_master_getDC();
i2c_master_wait(5);
i2c_master_setDC(1, 0);
i2c_master_wait(5);
return retVal;
}
/**
* @brief get I2C response
*/
static bool i2c_master_checkAck(void)
{
if (i2c_master_getAck()) {
return false;
} else {
return true;
}
}
/**
* @brief send I2C ack signal
*/
static void i2c_master_send_ack(void)
{
i2c_master_setAck(0x0);
}
/**
* @brief send I2C not ack signal
*/
static void i2c_master_send_nack(void)
{
i2c_master_setAck(0x1);
}
/**
* @brief read byte from i2c bus
*/
static uint8_t i2c_master_readByte(void)
{
uint8_t retVal = 0;
uint8_t k, i;
i2c_master_wait(5);
i2c_master_setDC(s_last_sda, 0);
i2c_master_wait(5); // sda 1, scl 0
for (i = 0; i < 8; i++) {
i2c_master_wait(5);
i2c_master_setDC(1, 0);
i2c_master_wait(5); // sda 1, scl 0
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
k = i2c_master_getDC();
i2c_master_wait(5);
if (i == 7) {
i2c_master_wait(3);
}
k <<= (7 - i);
retVal |= k;
}
i2c_master_setDC(1, 0);
i2c_master_wait(5); // sda 1, scl 0
return retVal;
}
/**
* @brief write byte to i2c
*/
static void i2c_master_writeByte(uint8_t wrdata)
{
uint8_t dat;
int8_t i;
i2c_master_wait(5);
i2c_master_setDC(s_last_sda, 0);
i2c_master_wait(5);
for (i = 7; i >= 0; i--) {
dat = wrdata >> i;
i2c_master_setDC(dat, 0);
i2c_master_wait(5);
i2c_master_setDC(dat, 1);
i2c_master_wait(5);
if (i == 0) {
i2c_master_wait(3); ////
}
i2c_master_setDC(dat, 0);
i2c_master_wait(5);
}
}
/**
* @brief write byte to i2c and get check ack
*/
static bool i2c_write_byte(uint8_t data, uint8_t iter)
{
if (iter == 0) {
iter = 1;
}
while (iter--) {
i2c_master_writeByte(data);
if (i2c_master_getAck()) {
i2c_master_stop();
ets_delay_us(500); // Wait 500us and retry.
i2c_master_start();
} else {
return true;
}
}
i2c_master_stop();
return false;
}
/**
* @brief write byte to target register of target I2C slave
*/
static void i2c_write_reg(uint8_t slave_add, uint8_t reg_add, uint16_t data)
{
i2c_master_start();
if (false == i2c_write_byte(slave_add, 30)) {
ESP_LOGE(TAG, "1Slave is busy, [%02x]TIME OUT\n", slave_add);
}
if (false == i2c_write_byte(reg_add, 30)) {
ESP_LOGE(TAG, "2Slave is busy, [%02x]TIME OUT\n", reg_add);
}
if (false == i2c_write_byte((data >> 8), 30)) {
ESP_LOGE(TAG, "3Slave is busy, [%02x]TIME OUT\n", data);
}
if (false == i2c_write_byte((data & 0xFF), 30)) {
ESP_LOGE(TAG, "3Slave is busy, [%02x]TIME OUT\n", data);
}
i2c_master_stop();
}
/**
* @brief a block of data to I2C
*/
static void i2s_write_arr(const uint8_t *data, size_t len)
{
if (data == NULL || len <= 0) return;
i2c_master_start();
int i = 0;
for (i = 0; i < len; ++i) {
if (false == i2c_write_byte(data[i], 30)) {
ESP_LOGE(TAG, "Slave is busy, [%02x]TIME OUT\n", data[i]);
}
}
i2c_master_stop();
}
/**
* @brief Initialize I2C information
*/
int esp_mfi_i2c_init(void)
{
i2c_master_gpio_init();
return 0;
}
/**
* @brief Finish I2C information
*/
int esp_mfi_i2c_end(void)
{
return 0;
}
/**
* @brief write one byte data to slave
*/
static int esp_mfi_i2c_write_byte(uint8_t data, uint16_t retrytimes)
{
uint16_t times = retrytimes;
if (0 == times) {
times = 1;
}
while (times--) {
i2c_master_writeByte(data);
if (i2c_master_getAck()) {
i2c_master_wait(500); /**< Wait 500us and retry */
i2c_master_stop();
i2c_master_wait(500); /**< Wait 500us and retry */
i2c_master_start();
} else {
return 0;
}
}
i2c_master_stop();
return -ETIME;
}
/**
* @brief write data buffer to slave
*/
int esp_mfi_i2c_write(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
{
uint16_t i;
if (!buff)
return -EINVAL;
ESP_LOGI(TAG, "Writing to SW I2C");
i2c_master_start();
/**< Send write address of the CP */
if (esp_mfi_i2c_write_byte(slvaddr, MFI_CP_BUSY_RETRY_TIMES) != 0) {
ESP_LOGE(TAG, "Slave is busy, time out.");
return -ETIME;
}
/**< Send register address to slave */
if (esp_mfi_i2c_write_byte(regaddr, 0) != 0) {
ESP_LOGE(TAG, "Write register address[0x%02x] to slave.", regaddr);
return -ENODATA;
}
/**< Send data out */
for (i = 0; i < len; ++i) {
if (esp_mfi_i2c_write_byte(*buff++, 0) != 0) {
ESP_LOGE(TAG, "Write data[0x%02x] to slave.", *buff);
return -ENODATA;
}
}
i2c_master_stop();
return 0;
}
/**
* @brief read data form slave
*/
int esp_mfi_i2c_read(uint8_t slvaddr, uint8_t regaddr, uint8_t *buff, uint32_t len)
{
uint16_t i;
if (!buff)
return -EINVAL;
ESP_LOGI(TAG, "Reading from SW I2C");
i2c_master_start();
/**< Send write address of the CP */
if (esp_mfi_i2c_write_byte(slvaddr, MFI_CP_BUSY_RETRY_TIMES) != 0) {
ESP_LOGE(TAG, "Slave is busy, time out.");
return -ETIME;
}
/**< Send register address to slave */
if (esp_mfi_i2c_write_byte(regaddr, 0) != 0) {
ESP_LOGE(TAG, "Write register address[0x%02x] to slave.", regaddr);
return -ENODATA;
}
i2c_master_stop();
i2c_master_wait(4000);
/**< Send read address of the CP */
if (esp_mfi_i2c_write_byte((slvaddr + 1), MFI_CP_BUSY_RETRY_TIMES) != 0) {
ESP_LOGE(TAG, "Slave is busy, time out.");
return -ETIME;
}
for (i = 0; i < len; ++i) {
buff[i] = i2c_master_readByte();
i2c_master_setAck((i == (len - 1)) ? 1 : 0);
}
i2c_master_stop();
return 0;
}
#endif