forked from espressif/arduino-esp32
I2C Slave Implementation
This commit is contained in:
829
cores/esp32/esp32-hal-i2c-slave.c
Executable file
829
cores/esp32/esp32-hal-i2c-slave.c
Executable file
@ -0,0 +1,829 @@
|
||||
// Copyright 2015-2021 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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
#include "rom/gpio.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "soc/i2c_reg.h"
|
||||
#include "soc/i2c_struct.h"
|
||||
#include "hal/i2c_ll.h"
|
||||
#include "esp32-hal-log.h"
|
||||
#include "esp32-hal-i2c-slave.h"
|
||||
|
||||
#define I2C_SLAVE_USE_RX_QUEUE 0 // 1: Queue, 0: RingBuffer
|
||||
|
||||
#if SOC_I2C_NUM > 1
|
||||
#define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0))
|
||||
#define I2C_SDA_IDX(p) ((p==0)?I2CEXT0_SDA_OUT_IDX:((p==1)?I2CEXT1_SDA_OUT_IDX:0))
|
||||
#else
|
||||
#define I2C_SCL_IDX(p) I2CEXT0_SCL_OUT_IDX
|
||||
#define I2C_SDA_IDX(p) I2CEXT0_SDA_OUT_IDX
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define I2C_TXFIFO_WM_INT_ENA I2C_TXFIFO_EMPTY_INT_ENA
|
||||
#define I2C_RXFIFO_WM_INT_ENA I2C_RXFIFO_FULL_INT_ENA
|
||||
#endif
|
||||
|
||||
enum {
|
||||
I2C_SLAVE_EVT_RX, I2C_SLAVE_EVT_TX
|
||||
};
|
||||
|
||||
typedef struct i2c_slave_struct_t {
|
||||
i2c_dev_t * dev;
|
||||
uint8_t num;
|
||||
int8_t sda;
|
||||
int8_t scl;
|
||||
i2c_slave_request_cb_t request_callback;
|
||||
i2c_slave_receive_cb_t receive_callback;
|
||||
void * arg;
|
||||
intr_handle_t intr_handle;
|
||||
TaskHandle_t task_handle;
|
||||
xQueueHandle event_queue;
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
xQueueHandle rx_queue;
|
||||
#else
|
||||
RingbufHandle_t rx_ring_buf;
|
||||
#endif
|
||||
xQueueHandle tx_queue;
|
||||
uint32_t rx_data_count;
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
xSemaphoreHandle lock;
|
||||
#endif
|
||||
} i2c_slave_struct_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t event : 2;
|
||||
uint32_t stop : 1;
|
||||
uint32_t param : 29;
|
||||
};
|
||||
uint32_t val;
|
||||
} i2c_slave_queue_event_t;
|
||||
|
||||
static i2c_slave_struct_t _i2c_bus_array[SOC_I2C_NUM] = {
|
||||
{ &I2C0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
, NULL
|
||||
#endif
|
||||
},
|
||||
#if SOC_I2C_NUM > 1
|
||||
{ &I2C1, 1, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
, NULL
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
#define I2C_SLAVE_MUTEX_LOCK()
|
||||
#define I2C_SLAVE_MUTEX_UNLOCK()
|
||||
#else
|
||||
#define I2C_SLAVE_MUTEX_LOCK() if(i2c->lock){xSemaphoreTake(i2c->lock, portMAX_DELAY);}
|
||||
#define I2C_SLAVE_MUTEX_UNLOCK() if(i2c->lock){xSemaphoreGive(i2c->lock);}
|
||||
#endif
|
||||
|
||||
//-------------------------------------- HAL_LL (Missing Functions) ------------------------------------------------
|
||||
typedef enum {
|
||||
I2C_STRETCH_CAUSE_MASTER_READ,
|
||||
I2C_STRETCH_CAUSE_TX_FIFO_EMPTY,
|
||||
I2C_STRETCH_CAUSE_RX_FIFO_FULL,
|
||||
I2C_STRETCH_CAUSE_MAX
|
||||
} i2c_stretch_cause_t;
|
||||
|
||||
static inline i2c_stretch_cause_t i2c_ll_stretch_cause(i2c_dev_t *hw)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
return hw->sr.stretch_cause;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
return hw->status_reg.stretch_cause;
|
||||
#else
|
||||
return I2C_STRETCH_CAUSE_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void i2c_ll_set_stretch(i2c_dev_t *hw, uint16_t time)
|
||||
{
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
typeof(hw->scl_stretch_conf) scl_stretch_conf;
|
||||
scl_stretch_conf.val = 0;
|
||||
scl_stretch_conf.slave_scl_stretch_en = (time > 0);
|
||||
scl_stretch_conf.stretch_protect_num = time;
|
||||
scl_stretch_conf.slave_scl_stretch_clr = 1;
|
||||
hw->scl_stretch_conf.val = scl_stretch_conf.val;
|
||||
if(time > 0){
|
||||
//enable interrupt
|
||||
hw->int_ena.val |= I2C_SLAVE_STRETCH_INT_ENA;
|
||||
} else {
|
||||
//disable interrupt
|
||||
hw->int_ena.val &= (~I2C_SLAVE_STRETCH_INT_ENA);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void i2c_ll_stretch_clr(i2c_dev_t *hw)
|
||||
{
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
hw->scl_stretch_conf.slave_scl_stretch_clr = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool i2c_ll_slave_addressed(i2c_dev_t *hw)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
return hw->sr.slave_addressed;
|
||||
#else
|
||||
return hw->status_reg.slave_addressed;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool i2c_ll_slave_rw(i2c_dev_t *hw)//not exposed by hal_ll
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
return hw->sr.slave_rw;
|
||||
#else
|
||||
return hw->status_reg.slave_rw;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------- PRIVATE (Function Prototypes) ------------------------------------------------
|
||||
static void i2c_slave_delay_us(uint64_t us);
|
||||
static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode);
|
||||
static bool i2c_slave_check_line_state(int8_t sda, int8_t scl);
|
||||
static bool i2c_slave_attach_gpio(i2c_slave_struct_t * i2c, int8_t sda, int8_t scl);
|
||||
static bool i2c_slave_detach_gpio(i2c_slave_struct_t * i2c);
|
||||
static bool i2c_slave_set_frequency(i2c_slave_struct_t * i2c, uint32_t clk_speed);
|
||||
static bool i2c_slave_send_event(i2c_slave_struct_t * i2c, i2c_slave_queue_event_t* event);
|
||||
static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t * i2c);
|
||||
static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t * i2c, uint32_t len);
|
||||
static size_t i2c_slave_read_rx(i2c_slave_struct_t * i2c, uint8_t * data, size_t len);
|
||||
static void i2c_slave_isr_handler(void* arg);
|
||||
static void i2c_slave_task(void *pv_args);
|
||||
|
||||
|
||||
//=====================================================================================================================
|
||||
//-------------------------------------- Public Functions -------------------------------------------------------------
|
||||
//=====================================================================================================================
|
||||
|
||||
esp_err_t i2c_slave_attach_callbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void * arg){
|
||||
if(num >= SOC_I2C_NUM){
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
i2c_slave_struct_t * i2c = &_i2c_bus_array[num];
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
i2c->request_callback = request_callback;
|
||||
i2c->receive_callback = receive_callback;
|
||||
i2c->arg = arg;
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_slave_deinit(uint8_t num){
|
||||
if(num >= SOC_I2C_NUM){
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
i2c_slave_struct_t * i2c = &_i2c_bus_array[num];
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
i2c_slave_detach_gpio(i2c);
|
||||
i2c_ll_set_slave_addr(i2c->dev, 0, false);
|
||||
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_clr_intsts_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
|
||||
if (i2c->intr_handle) {
|
||||
esp_intr_free(i2c->intr_handle);
|
||||
i2c->intr_handle = NULL;
|
||||
}
|
||||
|
||||
if(i2c->task_handle){
|
||||
vTaskDelete(i2c->task_handle);
|
||||
i2c->task_handle = NULL;
|
||||
}
|
||||
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
if (i2c->rx_queue) {
|
||||
vQueueDelete(i2c->rx_queue);
|
||||
i2c->rx_queue = NULL;
|
||||
}
|
||||
#else
|
||||
if (i2c->rx_ring_buf) {
|
||||
vRingbufferDelete(i2c->rx_ring_buf);
|
||||
i2c->rx_ring_buf = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i2c->tx_queue) {
|
||||
vQueueDelete(i2c->tx_queue);
|
||||
i2c->tx_queue = NULL;
|
||||
}
|
||||
|
||||
if (i2c->event_queue) {
|
||||
vQueueDelete(i2c->event_queue);
|
||||
i2c->event_queue = NULL;
|
||||
}
|
||||
|
||||
i2c->rx_data_count = 0;
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_slave_init(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len) {
|
||||
if(num >= SOC_I2C_NUM){
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (sda < 0 || scl < 0) {
|
||||
log_e("invalid pins sda=%d, scl=%d", sda, scl);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
log_i("Initialising I2C Slave: sda=%d scl=%d freq=%d, addr=0x%x", sda, scl, frequency, slaveID);
|
||||
|
||||
i2c_slave_struct_t * i2c = &_i2c_bus_array[num];
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if(!i2c->lock){
|
||||
i2c->lock = xSemaphoreCreateMutex();
|
||||
if (i2c->lock == NULL) {
|
||||
log_e("RX queue create failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
i2c_slave_deinit(num);
|
||||
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
i2c->rx_queue = xQueueCreate(rx_len, sizeof(uint8_t));
|
||||
if (i2c->rx_queue == NULL) {
|
||||
log_e("RX queue create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
#else
|
||||
i2c->rx_ring_buf = xRingbufferCreate(rx_len, RINGBUF_TYPE_BYTEBUF);
|
||||
if (i2c->rx_ring_buf == NULL) {
|
||||
log_e("RX RingBuf create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
i2c->tx_queue = xQueueCreate(tx_len, sizeof(uint8_t));
|
||||
if (i2c->tx_queue == NULL) {
|
||||
log_e("TX queue create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c->event_queue = xQueueCreate(16, sizeof(i2c_slave_queue_event_t));
|
||||
if (i2c->event_queue == NULL) {
|
||||
log_e("Event queue create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xTaskCreate(i2c_slave_task, "i2c_slave_task", 4096, i2c, 20, &i2c->task_handle);
|
||||
if(i2c->task_handle == NULL){
|
||||
log_e("Event thread create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (frequency == 0) {
|
||||
frequency = 100000L;
|
||||
}
|
||||
frequency = (frequency * 5) / 4;
|
||||
|
||||
if (i2c->num == 0) {
|
||||
periph_module_enable(PERIPH_I2C0_MODULE);
|
||||
#if SOC_I2C_NUM > 1
|
||||
} else {
|
||||
periph_module_enable(PERIPH_I2C1_MODULE);
|
||||
#endif
|
||||
}
|
||||
|
||||
i2c_ll_slave_init(i2c->dev);
|
||||
i2c_ll_set_fifo_mode(i2c->dev, true);
|
||||
i2c_ll_set_slave_addr(i2c->dev, slaveID, false);
|
||||
i2c_ll_set_tout(i2c->dev, 32000);
|
||||
i2c_slave_set_frequency(i2c, frequency);
|
||||
|
||||
if (!i2c_slave_check_line_state(sda, scl)) {
|
||||
log_e("bad pin state");
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c_slave_attach_gpio(i2c, sda, scl);
|
||||
|
||||
if (i2c_ll_is_bus_busy(i2c->dev)) {
|
||||
log_w("Bus busy, reinit");
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_clr_intsts_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_set_fifo_mode(i2c->dev, true);
|
||||
|
||||
if (!i2c->intr_handle) {
|
||||
uint32_t flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED;
|
||||
if(i2c->num == 0) {
|
||||
ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#if SOC_I2C_NUM > 1
|
||||
} else {
|
||||
ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
log_e("install interrupt handler Failed=%d", ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
i2c_ll_rxfifo_rst(i2c->dev);
|
||||
i2c_ll_slave_enable_rx_it(i2c->dev);
|
||||
i2c_ll_set_stretch(i2c->dev, 0x3FF);
|
||||
i2c_ll_update(i2c->dev);
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
i2c_slave_deinit(num);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t i2c_slave_write(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms) {
|
||||
if(num >= SOC_I2C_NUM){
|
||||
log_e("Invalid port num: %u", num);
|
||||
return 0;
|
||||
}
|
||||
size_t to_queue = 0, to_fifo = 0;
|
||||
i2c_slave_struct_t * i2c = &_i2c_bus_array[num];
|
||||
if(!i2c->tx_queue){
|
||||
return 0;
|
||||
}
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
//make sure that tx is idle
|
||||
uint64_t tout_at = esp_timer_get_time() + (timeout_ms * 1000);
|
||||
while(i2c_ll_slave_addressed(i2c->dev) && i2c_ll_slave_rw(i2c->dev)) {
|
||||
// ongoing MASTER READ
|
||||
//wait up to timeout_ms for current transaction to finish
|
||||
vTaskDelay(2);
|
||||
if((uint64_t)esp_timer_get_time() >= tout_at){
|
||||
log_e("TX IDLE WAIT TIMEOUT!");
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
i2c_ll_slave_disable_tx_it(i2c->dev);
|
||||
if (i2c_ll_get_txfifo_len(i2c->dev) < SOC_I2C_FIFO_LEN) {
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
}
|
||||
#endif
|
||||
to_fifo = i2c_ll_get_txfifo_len(i2c->dev);
|
||||
if(len < to_fifo){
|
||||
to_fifo = len;
|
||||
}
|
||||
i2c_ll_write_txfifo(i2c->dev, (uint8_t*)buf, to_fifo);
|
||||
buf += to_fifo;
|
||||
len -= to_fifo;
|
||||
//reset tx_queue
|
||||
xQueueReset(i2c->tx_queue);
|
||||
//write the rest of the bytes to the queue
|
||||
if(len){
|
||||
to_queue = uxQueueSpacesAvailable(i2c->tx_queue);
|
||||
if(len < to_queue){
|
||||
to_queue = len;
|
||||
}
|
||||
for (size_t i = 0; i < to_queue; i++) {
|
||||
if (xQueueSend(i2c->tx_queue, &buf[i], timeout_ms / portTICK_RATE_MS) != pdTRUE) {
|
||||
xQueueReset(i2c->tx_queue);
|
||||
to_queue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//no need to enable TX_EMPTY if tx_queue is empty
|
||||
if(to_queue){
|
||||
i2c_ll_slave_enable_tx_it(i2c->dev);
|
||||
}
|
||||
}
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return to_queue + to_fifo;
|
||||
}
|
||||
|
||||
//=====================================================================================================================
|
||||
//-------------------------------------- Private Functions ------------------------------------------------------------
|
||||
//=====================================================================================================================
|
||||
|
||||
static bool i2c_slave_set_frequency(i2c_slave_struct_t * i2c, uint32_t clk_speed)
|
||||
{
|
||||
if (i2c == NULL) {
|
||||
log_e("no control buffer");
|
||||
return false;
|
||||
}
|
||||
if(clk_speed > 1100000UL){
|
||||
clk_speed = 1100000UL;
|
||||
}
|
||||
|
||||
// Adjust Fifo thresholds based on frequency
|
||||
uint32_t a = (clk_speed / 50000L) + 2;
|
||||
log_d("Fifo thresholds: rx_fifo_full = %d, tx_fifo_empty = %d", SOC_I2C_FIFO_LEN - a, a);
|
||||
|
||||
i2c_clk_cal_t clk_cal;
|
||||
#if SOC_I2C_SUPPORT_APB
|
||||
i2c_ll_cal_bus_clk(APB_CLK_FREQ, clk_speed, &clk_cal);
|
||||
i2c_ll_set_source_clk(i2c->dev, I2C_SCLK_APB); /*!< I2C source clock from APB, 80M*/
|
||||
#elif SOC_I2C_SUPPORT_XTAL
|
||||
i2c_ll_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal);
|
||||
i2c_ll_set_source_clk(i2c->dev, I2C_SCLK_XTAL); /*!< I2C source clock from XTAL, 40M */
|
||||
#endif
|
||||
i2c_ll_set_txfifo_empty_thr(i2c->dev, a);
|
||||
i2c_ll_set_rxfifo_full_thr(i2c->dev, SOC_I2C_FIFO_LEN - a);
|
||||
i2c_ll_set_bus_timing(i2c->dev, &clk_cal);
|
||||
i2c_ll_set_filter(i2c->dev, 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void i2c_slave_delay_us(uint64_t us)
|
||||
{
|
||||
uint64_t m = esp_timer_get_time();
|
||||
if (us) {
|
||||
uint64_t e = (m + us);
|
||||
if (m > e) { //overflow
|
||||
while ((uint64_t)esp_timer_get_time() > e);
|
||||
}
|
||||
while ((uint64_t)esp_timer_get_time() < e);
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode)
|
||||
{
|
||||
gpio_config_t conf = {
|
||||
.pin_bit_mask = 1LL << pin,
|
||||
.mode = mode,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&conf);
|
||||
}
|
||||
|
||||
static bool i2c_slave_check_line_state(int8_t sda, int8_t scl)
|
||||
{
|
||||
if (sda < 0 || scl < 0) {
|
||||
return false;//return false since there is nothing to do
|
||||
}
|
||||
// if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles
|
||||
gpio_set_level(sda, 1);
|
||||
gpio_set_level(scl, 1);
|
||||
i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD);
|
||||
i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD);
|
||||
gpio_set_level(scl, 1);
|
||||
|
||||
if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state
|
||||
log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, gpio_get_level(sda), scl, gpio_get_level(scl));
|
||||
for (uint8_t a=0; a<9; a++) {
|
||||
i2c_slave_delay_us(5);
|
||||
if (gpio_get_level(sda) && gpio_get_level(scl)) { // bus recovered
|
||||
log_w("Recovered after %d Cycles",a);
|
||||
gpio_set_level(sda,0); // start
|
||||
i2c_slave_delay_us(5);
|
||||
for (uint8_t a=0;a<9; a++) {
|
||||
gpio_set_level(scl,1);
|
||||
i2c_slave_delay_us(5);
|
||||
gpio_set_level(scl,0);
|
||||
i2c_slave_delay_us(5);
|
||||
}
|
||||
gpio_set_level(scl,1);
|
||||
i2c_slave_delay_us(5);
|
||||
gpio_set_level(sda,1); // stop
|
||||
break;
|
||||
}
|
||||
gpio_set_level(scl, 0);
|
||||
i2c_slave_delay_us(5);
|
||||
gpio_set_level(scl, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state
|
||||
log_e("Bus Invalid State, Can't init sda=%d, scl=%d",gpio_get_level(sda),gpio_get_level(scl));
|
||||
return false; // bus is busy
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool i2c_slave_attach_gpio(i2c_slave_struct_t * i2c, int8_t sda, int8_t scl)
|
||||
{
|
||||
if (i2c == NULL) {
|
||||
log_e("no control block");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((sda < 0)||( scl < 0)) {
|
||||
log_e("bad pins sda=%d, scl=%d",sda,scl);
|
||||
return false;
|
||||
}
|
||||
|
||||
i2c->scl = scl;
|
||||
gpio_set_level(scl, 1);
|
||||
i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT_OUTPUT_OD);
|
||||
gpio_matrix_out(scl, I2C_SCL_IDX(i2c->num), false, false);
|
||||
gpio_matrix_in(scl, I2C_SCL_IDX(i2c->num), false);
|
||||
|
||||
i2c->sda = sda;
|
||||
gpio_set_level(sda, 1);
|
||||
i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT_OUTPUT_OD);
|
||||
gpio_matrix_out(sda, I2C_SDA_IDX(i2c->num), false, false);
|
||||
gpio_matrix_in(sda, I2C_SDA_IDX(i2c->num), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool i2c_slave_detach_gpio(i2c_slave_struct_t * i2c)
|
||||
{
|
||||
if (i2c == NULL) {
|
||||
log_e("no control Block");
|
||||
return false;
|
||||
}
|
||||
if (i2c->scl >= 0) {
|
||||
gpio_matrix_out(i2c->scl, 0x100, false, false);
|
||||
gpio_matrix_in(0x30, I2C_SCL_IDX(i2c->num), false);
|
||||
i2c_slave_gpio_mode(i2c->scl, GPIO_MODE_INPUT);
|
||||
i2c->scl = -1; // un attached
|
||||
}
|
||||
if (i2c->sda >= 0) {
|
||||
gpio_matrix_out(i2c->sda, 0x100, false, false);
|
||||
gpio_matrix_in(0x30, I2C_SDA_IDX(i2c->num), false);
|
||||
i2c_slave_gpio_mode(i2c->sda, GPIO_MODE_INPUT);
|
||||
i2c->sda = -1; // un attached
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool i2c_slave_send_event(i2c_slave_struct_t * i2c, i2c_slave_queue_event_t* event)
|
||||
{
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
if(i2c->event_queue) {
|
||||
if(xQueueSendFromISR(i2c->event_queue, event, (BaseType_t * const)&pxHigherPriorityTaskWoken) != pdTRUE){
|
||||
log_e("event_queue_full");
|
||||
}
|
||||
}
|
||||
return pxHigherPriorityTaskWoken;
|
||||
}
|
||||
|
||||
static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t * i2c)
|
||||
{
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
uint32_t d = 0, moveCnt = i2c_ll_get_txfifo_len(i2c->dev);
|
||||
while (moveCnt > 0) { // read tx queue until Fifo is full or queue is empty
|
||||
if(xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t * const)&pxHigherPriorityTaskWoken) == pdTRUE){
|
||||
i2c_ll_write_txfifo(i2c->dev, (uint8_t*)&d, 1);
|
||||
moveCnt--;
|
||||
} else {
|
||||
i2c_ll_slave_disable_tx_it(i2c->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pxHigherPriorityTaskWoken;
|
||||
}
|
||||
|
||||
static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t * i2c, uint32_t len)
|
||||
{
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
uint32_t d = 0;
|
||||
#else
|
||||
uint8_t data[SOC_I2C_FIFO_LEN];
|
||||
#endif
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
while (len > 0) {
|
||||
i2c_ll_read_rxfifo(i2c->dev, (uint8_t*)&d, 1);
|
||||
if(xQueueSendFromISR(i2c->rx_queue, &d, (BaseType_t * const)&pxHigherPriorityTaskWoken) != pdTRUE){
|
||||
log_e("rx_queue_full");
|
||||
} else {
|
||||
i2c->rx_data_count++;
|
||||
}
|
||||
if (--len == 0) {
|
||||
len = i2c_ll_get_rxfifo_cnt(i2c->dev);
|
||||
}
|
||||
#else
|
||||
if(len){
|
||||
i2c_ll_read_rxfifo(i2c->dev, data, len);
|
||||
if(xRingbufferSendFromISR(i2c->rx_ring_buf, (void*) data, len, (BaseType_t * const)&pxHigherPriorityTaskWoken) != pdTRUE){
|
||||
log_e("rx_ring_buf_full");
|
||||
} else {
|
||||
i2c->rx_data_count += len;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return pxHigherPriorityTaskWoken;
|
||||
}
|
||||
|
||||
static void i2c_slave_isr_handler(void* arg)
|
||||
{
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
i2c_slave_struct_t * i2c = (i2c_slave_struct_t *) arg; // recover data
|
||||
|
||||
uint32_t activeInt = i2c_ll_get_intsts_mask(i2c->dev);
|
||||
i2c_ll_clr_intsts_mask(i2c->dev, activeInt);
|
||||
uint8_t rx_fifo_len = i2c_ll_get_rxfifo_cnt(i2c->dev);
|
||||
uint8_t tx_fifo_len = SOC_I2C_FIFO_LEN - i2c_ll_get_txfifo_len(i2c->dev);
|
||||
bool slave_rw = i2c_ll_slave_rw(i2c->dev);
|
||||
|
||||
if(activeInt & I2C_RXFIFO_WM_INT_ENA){ // RX FiFo Full
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
i2c_ll_slave_enable_rx_it(i2c->dev);//is this necessary?
|
||||
}
|
||||
|
||||
if(activeInt & I2C_TRANS_COMPLETE_INT_ENA){ // STOP
|
||||
if(rx_fifo_len){ //READ RX FIFO
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
}
|
||||
if(!slave_rw || i2c->rx_data_count){ //WRITE or RepeatedStart
|
||||
//SEND RX Event
|
||||
i2c_slave_queue_event_t event;
|
||||
event.event = I2C_SLAVE_EVT_RX;
|
||||
event.stop = !slave_rw;
|
||||
event.param = i2c->rx_data_count;
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
|
||||
//Zero RX count
|
||||
i2c->rx_data_count = 0;
|
||||
}
|
||||
if(slave_rw){ // READ
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
//SEND TX Event
|
||||
i2c_slave_queue_event_t event;
|
||||
event.event = I2C_SLAVE_EVT_TX;
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
|
||||
#else
|
||||
//reset TX data
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
uint8_t d;
|
||||
while (xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t * const)&pxHigherPriorityTaskWoken) == pdTRUE) ;//flush partial write
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
if(activeInt & I2C_SLAVE_STRETCH_INT_ENA){ // STRETCH
|
||||
i2c_stretch_cause_t cause = i2c_ll_stretch_cause(i2c->dev);
|
||||
if(cause == I2C_STRETCH_CAUSE_MASTER_READ){
|
||||
//on C3 RX data dissapears with repeated start, so we need to get it here
|
||||
if(rx_fifo_len){
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
}
|
||||
//SEND TX Event
|
||||
i2c_slave_queue_event_t event;
|
||||
event.event = I2C_SLAVE_EVT_TX;
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
|
||||
//will clear after execution
|
||||
} else if(cause == I2C_STRETCH_CAUSE_TX_FIFO_EMPTY){
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c);
|
||||
i2c_ll_stretch_clr(i2c->dev);
|
||||
} else if(cause == I2C_STRETCH_CAUSE_RX_FIFO_FULL){
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
i2c_ll_stretch_clr(i2c->dev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(activeInt & I2C_TXFIFO_WM_INT_ENA){ // TX FiFo Empty
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c);
|
||||
}
|
||||
|
||||
if(pxHigherPriorityTaskWoken){
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static size_t i2c_slave_read_rx(i2c_slave_struct_t * i2c, uint8_t * data, size_t len){
|
||||
if(!len){
|
||||
return 0;
|
||||
}
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
uint8_t d = 0;
|
||||
BaseType_t res = pdTRUE;
|
||||
for(size_t i=0; i<len; i++) {
|
||||
if(data){
|
||||
res = xQueueReceive(i2c->rx_queue, &data[i], 0);
|
||||
} else {
|
||||
res = xQueueReceive(i2c->rx_queue, &d, 0);
|
||||
}
|
||||
if (res != pdTRUE) {
|
||||
log_e("Read Queue(%u) Failed", i);
|
||||
len = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (data)?len:0;
|
||||
#else
|
||||
size_t dlen = 0,
|
||||
to_read = len,
|
||||
so_far = 0,
|
||||
available = 0;
|
||||
uint8_t * rx_data = NULL;
|
||||
|
||||
vRingbufferGetInfo(i2c->rx_ring_buf, NULL, NULL, NULL, NULL, &available);
|
||||
if(available < to_read){
|
||||
log_e("Less available than requested. %u < %u", available, len);
|
||||
to_read = available;
|
||||
}
|
||||
|
||||
while(to_read){
|
||||
dlen = 0;
|
||||
rx_data = (uint8_t *)xRingbufferReceiveUpTo(i2c->rx_ring_buf, &dlen, 0, to_read);
|
||||
if(!rx_data){
|
||||
log_e("Receive %u Failed", to_read);
|
||||
return so_far;
|
||||
}
|
||||
if(data){
|
||||
memcpy(data+so_far, rx_data, dlen);
|
||||
}
|
||||
vRingbufferReturnItem(i2c->rx_ring_buf, rx_data);
|
||||
so_far+=dlen;
|
||||
to_read-=dlen;
|
||||
}
|
||||
return (data)?so_far:0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void i2c_slave_task(void *pv_args)
|
||||
{
|
||||
i2c_slave_struct_t * i2c = (i2c_slave_struct_t *)pv_args;
|
||||
i2c_slave_queue_event_t event;
|
||||
size_t len = 0;
|
||||
bool stop = false;
|
||||
uint8_t * data = NULL;
|
||||
for(;;){
|
||||
if(xQueueReceive(i2c->event_queue, &event, portMAX_DELAY) == pdTRUE){
|
||||
// Write
|
||||
if(event.event == I2C_SLAVE_EVT_RX){
|
||||
len = event.param;
|
||||
stop = event.stop;
|
||||
data = (len > 0)?(uint8_t*)malloc(len):NULL;
|
||||
|
||||
if(len && data == NULL){
|
||||
log_e("Malloc (%u) Failed", len);
|
||||
}
|
||||
len = i2c_slave_read_rx(i2c, data, len);
|
||||
if(i2c->receive_callback){
|
||||
i2c->receive_callback(i2c->num, data, len, stop, i2c->arg);
|
||||
}
|
||||
free(data);
|
||||
|
||||
// Read
|
||||
} else if(event.event == I2C_SLAVE_EVT_TX){
|
||||
if(i2c->request_callback){
|
||||
i2c->request_callback(i2c->num, i2c->arg);
|
||||
}
|
||||
i2c_ll_stretch_clr(i2c->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
35
cores/esp32/esp32-hal-i2c-slave.h
Executable file
35
cores/esp32/esp32-hal-i2c-slave.h
Executable file
@ -0,0 +1,35 @@
|
||||
// Copyright 2015-2021 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stddef.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef void (*i2c_slave_request_cb_t) (uint8_t num, void * arg);
|
||||
typedef void (*i2c_slave_receive_cb_t) (uint8_t num, uint8_t * data, size_t len, bool stop, void * arg);
|
||||
esp_err_t i2c_slave_attach_callbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void * arg);
|
||||
|
||||
esp_err_t i2c_slave_init(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len);
|
||||
esp_err_t i2c_slave_deinit(uint8_t num);
|
||||
size_t i2c_slave_write(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
30
libraries/Wire/examples/WireMaster/WireMaster.ino
Normal file
30
libraries/Wire/examples/WireMaster/WireMaster.ino
Normal file
@ -0,0 +1,30 @@
|
||||
#include "Wire.h"
|
||||
|
||||
#define I2C_DEV_ADDR 0x55
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(5000);
|
||||
|
||||
//Write message to the slave
|
||||
Wire.beginTransmission(I2C_DEV_ADDR);
|
||||
Wire.printf("Hello World! %u", i++);
|
||||
uint8_t error = Wire.endTransmission(true);
|
||||
Serial.printf("endTransmission: %u\n", error);
|
||||
|
||||
//Read 16 bytes from the slave
|
||||
error = Wire.requestFrom(I2C_DEV_ADDR, 16);
|
||||
Serial.printf("requestFrom: %u\n", error);
|
||||
if(error){
|
||||
uint8_t temp[error];
|
||||
Wire.readBytes(temp, error);
|
||||
log_print_buf(temp, error);
|
||||
}
|
||||
}
|
28
libraries/Wire/examples/WireScan/WireScan.ino
Normal file
28
libraries/Wire/examples/WireScan/WireScan.ino
Normal file
@ -0,0 +1,28 @@
|
||||
#include "Wire.h"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
byte error, address;
|
||||
int nDevices = 0;
|
||||
|
||||
delay(5000);
|
||||
|
||||
Serial.println("Scanning for I2C devices ...");
|
||||
for(address = 0x01; address < 0x7f; address++){
|
||||
Wire.beginTransmission(address);
|
||||
error = Wire.endTransmission();
|
||||
if (error == 0){
|
||||
Serial.printf("I2C device found at address 0x%02X\n", address);
|
||||
nDevices++;
|
||||
} else if(error != 2){
|
||||
Serial.printf("Error %d at address 0x%02X\n", error, address);
|
||||
}
|
||||
}
|
||||
if (nDevices == 0){
|
||||
Serial.println("No I2C devices found");
|
||||
}
|
||||
}
|
37
libraries/Wire/examples/WireSlave/WireSlave.ino
Normal file
37
libraries/Wire/examples/WireSlave/WireSlave.ino
Normal file
@ -0,0 +1,37 @@
|
||||
#include "Wire.h"
|
||||
|
||||
#define I2C_DEV_ADDR 0x55
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
void onRequest(){
|
||||
Wire.print(i++);
|
||||
Wire.print(" Packets.");
|
||||
Serial.println("onRequest");
|
||||
}
|
||||
|
||||
void onReceive(int len){
|
||||
Serial.printf("onReceive[%d]: ", len);
|
||||
while(Wire.available()){
|
||||
Serial.write(Wire.read());
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
Wire.onReceive(onReceive);
|
||||
Wire.onRequest(onRequest);
|
||||
Wire.begin((uint8_t)I2C_DEV_ADDR);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
char message[64];
|
||||
snprintf(message, 64, "%u Packets.", i++);
|
||||
Wire.slaveWrite((uint8_t *)message, strlen(message));
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
@ -30,6 +30,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include "esp32-hal-i2c.h"
|
||||
#include "esp32-hal-i2c-slave.h"
|
||||
#include "Wire.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
@ -47,6 +48,9 @@ TwoWire::TwoWire(uint8_t bus_num)
|
||||
,nonStopTask(NULL)
|
||||
,lock(NULL)
|
||||
#endif
|
||||
,is_slave(false)
|
||||
,user_onRequest(NULL)
|
||||
,user_onReceive(NULL)
|
||||
{}
|
||||
|
||||
TwoWire::~TwoWire()
|
||||
@ -402,5 +406,102 @@ uint8_t TwoWire::endTransmission(void)
|
||||
return endTransmission(true);
|
||||
}
|
||||
|
||||
bool TwoWire::begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency)
|
||||
{
|
||||
if(!frequency){
|
||||
frequency = 100000;
|
||||
} else if(frequency > 1000000){
|
||||
frequency = 1000000;
|
||||
}
|
||||
|
||||
if(sdaPin < 0) { // default param passed
|
||||
if(num == 0) {
|
||||
if(sda==-1) {
|
||||
sdaPin = SDA; //use Default Pin
|
||||
} else {
|
||||
sdaPin = sda; // reuse prior pin
|
||||
}
|
||||
} else {
|
||||
if(sda==-1) {
|
||||
log_e("no Default SDA Pin for Second Peripheral");
|
||||
return false; //no Default pin for Second Peripheral
|
||||
} else {
|
||||
sdaPin = sda; // reuse prior pin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sclPin < 0) { // default param passed
|
||||
if(num == 0) {
|
||||
if(scl == -1) {
|
||||
sclPin = SCL; // use Default pin
|
||||
} else {
|
||||
sclPin = scl; // reuse prior pin
|
||||
}
|
||||
} else {
|
||||
if(scl == -1) {
|
||||
log_e("no Default SCL Pin for Second Peripheral");
|
||||
return false; //no Default pin for Second Peripheral
|
||||
} else {
|
||||
sclPin = scl; // reuse prior pin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sda = sdaPin;
|
||||
scl = sclPin;
|
||||
i2c_slave_attach_callbacks(num, onRequestService, onReceiveService, this);
|
||||
if(i2c_slave_init(num, sda, scl, addr, frequency, I2C_BUFFER_LENGTH, I2C_BUFFER_LENGTH) != ESP_OK){
|
||||
Serial.println("INIT ERROR");
|
||||
return false;
|
||||
}
|
||||
is_slave = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TwoWire::slaveWrite(const uint8_t * buffer, size_t len)
|
||||
{
|
||||
return i2c_slave_write(num, buffer, len, _timeOutMillis);
|
||||
}
|
||||
|
||||
void TwoWire::onReceiveService(uint8_t num, uint8_t* inBytes, size_t numBytes, bool stop, void * arg)
|
||||
{
|
||||
TwoWire * wire = (TwoWire*)arg;
|
||||
if(!wire->user_onReceive){
|
||||
return;
|
||||
}
|
||||
for(uint8_t i = 0; i < numBytes; ++i){
|
||||
wire->rxBuffer[i] = inBytes[i];
|
||||
}
|
||||
wire->rxIndex = 0;
|
||||
wire->rxLength = numBytes;
|
||||
wire->user_onReceive(numBytes);
|
||||
}
|
||||
|
||||
void TwoWire::onRequestService(uint8_t num, void * arg)
|
||||
{
|
||||
TwoWire * wire = (TwoWire*)arg;
|
||||
if(!wire->user_onRequest){
|
||||
return;
|
||||
}
|
||||
wire->txLength = 0;
|
||||
wire->user_onRequest();
|
||||
if(wire->txLength){
|
||||
wire->slaveWrite((uint8_t*)wire->txBuffer, wire->txLength);
|
||||
}
|
||||
}
|
||||
|
||||
void TwoWire::onReceive( void (*function)(int) )
|
||||
{
|
||||
user_onReceive = function;
|
||||
}
|
||||
|
||||
// sets function called on slave read
|
||||
void TwoWire::onRequest( void (*function)(void) )
|
||||
{
|
||||
user_onRequest = function;
|
||||
}
|
||||
|
||||
|
||||
TwoWire Wire = TwoWire(0);
|
||||
TwoWire Wire1 = TwoWire(1);
|
||||
|
@ -61,6 +61,12 @@ protected:
|
||||
TaskHandle_t nonStopTask;
|
||||
SemaphoreHandle_t lock;
|
||||
#endif
|
||||
private:
|
||||
bool is_slave;
|
||||
void (*user_onRequest)(void);
|
||||
void (*user_onReceive)(int);
|
||||
static void onRequestService(uint8_t, void *);
|
||||
static void onReceiveService(uint8_t, uint8_t*, size_t, bool, void *);
|
||||
|
||||
public:
|
||||
TwoWire(uint8_t bus_num);
|
||||
@ -70,6 +76,7 @@ public:
|
||||
bool setPins(int sda, int scl);
|
||||
|
||||
bool begin(int sda=-1, int scl=-1, uint32_t frequency=0); // returns true, if successful init of i2c bus
|
||||
bool begin(uint8_t slaveAddr, int sda=-1, int scl=-1, uint32_t frequency=0);
|
||||
bool end();
|
||||
|
||||
void setTimeOut(uint16_t timeOutMillis); // default timeout of i2c transactions is 50ms
|
||||
@ -123,6 +130,7 @@ public:
|
||||
|
||||
void onReceive( void (*)(int) );
|
||||
void onRequest( void (*)(void) );
|
||||
size_t slaveWrite(const uint8_t *, size_t);
|
||||
};
|
||||
|
||||
extern TwoWire Wire;
|
||||
|
Reference in New Issue
Block a user