From c8e11dc1741a79a1d31b3c9b9e70c79272ae0f38 Mon Sep 17 00:00:00 2001 From: Skydev Date: Fri, 3 Feb 2023 15:48:32 +0200 Subject: [PATCH] Update ESP32 platform file copies to v2, fix sha --- src/sensors/Wire.cpp | 634 +++++++++--- src/sensors/Wire.h | 104 +- src/sensors/esp32-hal-gpio.c | 367 ++++--- src/sensors/esp32-hal-gpio.h | 46 +- src/sensors/esp32-hal-i2c.c | 1816 +++++++--------------------------- src/sensors/esp32-hal-i2c.h | 53 +- src/sensors/esp32-hal-log.h | 116 ++- src/web/WebSockets.cpp | 6 +- 8 files changed, 1201 insertions(+), 1941 deletions(-) diff --git a/src/sensors/Wire.cpp b/src/sensors/Wire.cpp index d3197e9..ae05e27 100644 --- a/src/sensors/Wire.cpp +++ b/src/sensors/Wire.cpp @@ -20,6 +20,7 @@ Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support Modified Nov 2017 by Chuck Todd (ctodd@cableone.net) - ESP32 ISR Support + Modified Nov 2021 by Hristo Gochkov to support ESP-IDF API */ extern "C" { @@ -29,6 +30,7 @@ extern "C" { } #include "esp32-hal-i2c.h" +#include "esp32-hal-i2c-slave.h" #include "Wire.h" #include "Arduino.h" @@ -36,29 +38,35 @@ TwoWire::TwoWire(uint8_t bus_num) :num(bus_num & 1) ,sda(-1) ,scl(-1) - ,i2c(NULL) + ,bufferSize(I2C_BUFFER_LENGTH) // default Wire Buffer Size + ,rxBuffer(NULL) ,rxIndex(0) ,rxLength(0) - ,rxQueued(0) - ,txIndex(0) + ,txBuffer(NULL) ,txLength(0) ,txAddress(0) - ,txQueued(0) - ,transmitting(0) - ,last_error(I2C_ERROR_OK) ,_timeOutMillis(50) + ,nonStop(false) +#if !CONFIG_DISABLE_HAL_LOCKS + ,nonStopTask(NULL) + ,lock(NULL) +#endif + ,is_slave(false) + ,user_onRequest(NULL) + ,user_onReceive(NULL) {} TwoWire::~TwoWire() { - flush(); - if(i2c) { - i2cRelease(i2c); - i2c=NULL; + end(); +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock != NULL){ + vSemaphoreDelete(lock); } +#endif } -void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) +bool TwoWire::initPins(int sdaPin, int sclPin) { if(sdaPin < 0) { // default param passed if(num == 0) { @@ -69,8 +77,12 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } } else { if(sda==-1) { +#ifdef WIRE1_PIN_DEFINED + sdaPin = SDA1; +#else log_e("no Default SDA Pin for Second Peripheral"); - return; //no Default pin for Second Peripheral + return false; //no Default pin for Second Peripheral +#endif } else { sdaPin = sda; // reuse prior pin } @@ -86,8 +98,12 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } } else { if(scl == -1) { +#ifdef WIRE1_PIN_DEFINED + sclPin = SCL1; +#else log_e("no Default SCL Pin for Second Peripheral"); - return; //no Default pin for Second Peripheral + return false; //no Default pin for Second Peripheral +#endif } else { sclPin = scl; // reuse prior pin } @@ -96,133 +112,436 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) sda = sdaPin; scl = sclPin; - i2c = i2cInit(num, sdaPin, sclPin, frequency); - if(!i2c) { - return; + return true; +} + +bool TwoWire::setPins(int sdaPin, int sclPin) +{ +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock == NULL){ + lock = xSemaphoreCreateMutex(); + if(lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return false; + } + } + //acquire lock + if(xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return false; + } +#endif + if(!i2cIsInit(num)){ + initPins(sdaPin, sclPin); + } else { + log_e("bus already initialized. change pins only when not."); } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return !i2cIsInit(num); +} - flush(); +bool TwoWire::allocateWireBuffer(void) +{ + // or both buffer can be allocated or none will be + if (rxBuffer == NULL) { + rxBuffer = (uint8_t *)malloc(bufferSize); + if (rxBuffer == NULL) { + log_e("Can't allocate memory for I2C_%d rxBuffer", num); + return false; + } + } + if (txBuffer == NULL) { + txBuffer = (uint8_t *)malloc(bufferSize); + if (txBuffer == NULL) { + log_e("Can't allocate memory for I2C_%d txBuffer", num); + freeWireBuffer(); // free rxBuffer for safety! + return false; + } + } + // in case both were allocated before, they must have the same size. All good. + return true; +} +void TwoWire::freeWireBuffer(void) +{ + if (rxBuffer != NULL) { + free(rxBuffer); + rxBuffer = NULL; + } + if (txBuffer != NULL) { + free(txBuffer); + txBuffer = NULL; + } } -void TwoWire::setTimeOut(uint16_t timeOutMillis) +size_t TwoWire::setBufferSize(size_t bSize) { - _timeOutMillis = timeOutMillis; + // Maximum size .... HEAP limited ;-) + if (bSize < 32) { // 32 bytes is the I2C FIFO Len for ESP32/S2/S3/C3 + log_e("Minimum Wire Buffer size is 32 bytes"); + return 0; + } + +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock == NULL){ + lock = xSemaphoreCreateMutex(); + if(lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return 0; + } + } + //acquire lock + if(xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return 0; + } +#endif + // allocateWireBuffer allocates memory for both pointers or just free them + if (rxBuffer != NULL || txBuffer != NULL) { + // if begin() has been already executed, memory size changes... data may be lost. We don't care! :^) + if (bSize != bufferSize) { + // we want a new buffer size ... just reset buffer pointers and allocate new ones + freeWireBuffer(); + bufferSize = bSize; + if (!allocateWireBuffer()) { + // failed! Error message already issued + bSize = 0; // returns error + log_e("Buffer allocation failed"); + } + } // else nothing changes, all set! + } else { + // no memory allocated yet, just change the size value - allocation in begin() + bufferSize = bSize; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); + +#endif + return bSize; } -uint16_t TwoWire::getTimeOut() +// Slave Begin +bool TwoWire::begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency) { - return _timeOutMillis; + bool started = false; +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock == NULL){ + lock = xSemaphoreCreateMutex(); + if(lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return false; + } + } + //acquire lock + if(xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return false; + } +#endif + if(is_slave){ + log_w("Bus already started in Slave Mode."); + started = true; + goto end; + } + if(i2cIsInit(num)){ + log_e("Bus already started in Master Mode."); + goto end; + } + if (!allocateWireBuffer()) { + // failed! Error Message already issued + goto end; + } + if(!initPins(sdaPin, sclPin)){ + goto end; + } + i2cSlaveAttachCallbacks(num, onRequestService, onReceiveService, this); + if(i2cSlaveInit(num, sda, scl, addr, frequency, bufferSize, bufferSize) != ESP_OK){ + log_e("Slave Init ERROR"); + goto end; + } + is_slave = true; + started = true; +end: + if (!started) freeWireBuffer(); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return started; +} + +// Master Begin +bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) +{ + bool started = false; + esp_err_t err = ESP_OK; +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock == NULL){ + lock = xSemaphoreCreateMutex(); + if(lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return false; + } + } + //acquire lock + if(xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return false; + } +#endif + if(is_slave){ + log_e("Bus already started in Slave Mode."); + goto end; + } + if(i2cIsInit(num)){ + log_w("Bus already started in Master Mode."); + started = true; + goto end; + } + if (!allocateWireBuffer()) { + // failed! Error Message already issued + goto end; + } + if(!initPins(sdaPin, sclPin)){ + goto end; + } + err = i2cInit(num, sda, scl, frequency); + started = (err == ESP_OK); + +end: + if (!started) freeWireBuffer(); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return started; + } -void TwoWire::setClock(uint32_t frequency) +bool TwoWire::end() { - i2cSetFrequency(i2c, frequency); + esp_err_t err = ESP_OK; +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock != NULL){ + //acquire lock + if(xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return false; + } +#endif + if(is_slave){ + err = i2cSlaveDeinit(num); + if(err == ESP_OK){ + is_slave = false; + } + } else if(i2cIsInit(num)){ + err = i2cDeinit(num); + } + freeWireBuffer(); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); + } +#endif + return (err == ESP_OK); } -size_t TwoWire::getClock() +uint32_t TwoWire::getClock() { - return i2cGetFrequency(i2c); + uint32_t frequency = 0; +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(lock == NULL || xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + } else { +#endif + if(is_slave){ + log_e("Bus is in Slave Mode"); + } else { + i2cGetClock(num, &frequency); + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); + } +#endif + return frequency; } -/* stickBreaker Nov 2017 ISR, and bigblock 64k-1 - */ -i2c_err_t TwoWire::writeTransmission(uint16_t address, uint8_t *buff, uint16_t size, bool sendStop) +bool TwoWire::setClock(uint32_t frequency) +{ + esp_err_t err = ESP_OK; +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(lock == NULL || xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return false; + } +#endif + if(is_slave){ + log_e("Bus is in Slave Mode"); + err = ESP_FAIL; + } else { + err = i2cSetClock(num, frequency); + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return (err == ESP_OK); +} + +void TwoWire::setTimeOut(uint16_t timeOutMillis) { - last_error = i2cWrite(i2c, address, buff, size, sendStop, _timeOutMillis); - return last_error; + _timeOutMillis = timeOutMillis; } -i2c_err_t TwoWire::readTransmission(uint16_t address, uint8_t *buff, uint16_t size, bool sendStop, uint32_t *readCount) +uint16_t TwoWire::getTimeOut() { - last_error = i2cRead(i2c, address, buff, size, sendStop, _timeOutMillis, readCount); - return last_error; + return _timeOutMillis; } void TwoWire::beginTransmission(uint16_t address) { - transmitting = 1; + if(is_slave){ + log_e("Bus is in Slave Mode"); + return; + } +#if !CONFIG_DISABLE_HAL_LOCKS + if(nonStop && nonStopTask == xTaskGetCurrentTaskHandle()){ + log_e("Unfinished Repeated Start transaction! Expected requestFrom, not beginTransmission! Clearing..."); + //release lock + xSemaphoreGive(lock); + } + //acquire lock + if(lock == NULL || xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return; + } +#endif + nonStop = false; txAddress = address; - txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true) - txLength = txQueued; + txLength = 0; } -/*stickbreaker isr - */ -uint8_t TwoWire::endTransmission(bool sendStop) // Assumes Wire.beginTransaction(), Wire.write() +/* +https://www.arduino.cc/reference/en/language/functions/communication/wire/endtransmission/ +endTransmission() returns: +0: success. +1: data too long to fit in transmit buffer. +2: received NACK on transmit of address. +3: received NACK on transmit of data. +4: other error. +5: timeout +*/ +uint8_t TwoWire::endTransmission(bool sendStop) { - if(transmitting == 1) { - last_error = writeTransmission(txAddress, &txBuffer[txQueued], txLength - txQueued, sendStop); - rxIndex = 0; - rxLength = rxQueued; - rxQueued = 0; - txQueued = 0; // the SendStop=true will restart all Queueing - if(last_error == I2C_ERROR_CONTINUE){ - // txlength is howmany bytes in txbuffer have been use - txQueued = txLength; - } + if(is_slave){ + log_e("Bus is in Slave Mode"); + return 4; + } + if (txBuffer == NULL){ + log_e("NULL TX buffer pointer"); + return 4; + } + esp_err_t err = ESP_OK; + if(sendStop){ + err = i2cWrite(num, txAddress, txBuffer, txLength, _timeOutMillis); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif } else { - last_error = I2C_ERROR_NO_BEGIN; - flush(); + //mark as non-stop + nonStop = true; +#if !CONFIG_DISABLE_HAL_LOCKS + nonStopTask = xTaskGetCurrentTaskHandle(); +#endif } - txIndex = 0; - txLength = 0; - transmitting = 0; - return last_error; + switch(err){ + case ESP_OK: return 0; + case ESP_FAIL: return 2; + case ESP_ERR_TIMEOUT: return 5; + default: break; + } + return 4; } -/* @stickBreaker 11/2017 fix for ReSTART timeout, ISR - */ -uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop) +size_t TwoWire::requestFrom(uint16_t address, size_t size, bool sendStop) { - //use internal Wire rxBuffer, multiple requestFrom()'s may be pending, try to share rxBuffer - uint32_t cnt = rxQueued; // currently queued reads, next available position in rxBuffer - if(cnt < (I2C_BUFFER_LENGTH-1) && (size + cnt) <= I2C_BUFFER_LENGTH) { // any room left in rxBuffer - rxQueued += size; - } else { // no room to receive more! - log_e("rxBuff overflow %d", cnt + size); - cnt = 0; - last_error = I2C_ERROR_MEMORY; - flush(); - return cnt; + if(is_slave){ + log_e("Bus is in Slave Mode"); + return 0; } - - last_error = readTransmission(address, &rxBuffer[cnt], size, sendStop, &cnt); - rxIndex = 0; - rxLength = rxQueued; - rxQueued = 0; - txQueued = 0; // the SendStop=true will restart all Queueing - if(last_error != I2C_ERROR_OK){ - cnt = 0; + if (rxBuffer == NULL || txBuffer == NULL){ + log_e("NULL buffer pointer"); + return 0; + } + esp_err_t err = ESP_OK; + if(nonStop +#if !CONFIG_DISABLE_HAL_LOCKS + && nonStopTask == xTaskGetCurrentTaskHandle() +#endif + ){ + if(address != txAddress){ + log_e("Unfinished Repeated Start transaction! Expected address do not match! %u != %u", address, txAddress); + return 0; + } + nonStop = false; + rxIndex = 0; + rxLength = 0; + err = i2cWriteReadNonStop(num, address, txBuffer, txLength, rxBuffer, size, _timeOutMillis, &rxLength); + if(err){ + log_e("i2cWriteReadNonStop returned Error %d", err); + } + } else { +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(lock == NULL || xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return 0; + } +#endif + rxIndex = 0; + rxLength = 0; + err = i2cRead(num, address, rxBuffer, size, _timeOutMillis, &rxLength); + if(err){ + log_e("i2cRead returned Error %d", err); + } } - return cnt; +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return rxLength; } size_t TwoWire::write(uint8_t data) { - if(transmitting) { - if(txLength >= I2C_BUFFER_LENGTH) { - return 0; - } - txBuffer[txIndex] = data; - ++txIndex; - txLength = txIndex; - return 1; + if (txBuffer == NULL){ + log_e("NULL TX buffer pointer"); + return 0; } - return 0; + if(txLength >= bufferSize) { + return 0; + } + txBuffer[txLength++] = data; + return 1; } size_t TwoWire::write(const uint8_t *data, size_t quantity) { - if(transmitting) { - for(size_t i = 0; i < quantity; ++i) { - if(!write(data[i])) { - return i; - } + for(size_t i = 0; i < quantity; ++i) { + if(!write(data[i])) { + return i; } - return quantity; } - return 0; + return quantity; + } int TwoWire::available(void) @@ -234,9 +553,12 @@ int TwoWire::available(void) int TwoWire::read(void) { int value = -1; + if (rxBuffer == NULL){ + log_e("NULL RX buffer pointer"); + return value; + } if(rxIndex < rxLength) { - value = rxBuffer[rxIndex]; - ++rxIndex; + value = rxBuffer[rxIndex++]; } return value; } @@ -244,6 +566,10 @@ int TwoWire::read(void) int TwoWire::peek(void) { int value = -1; + if (rxBuffer == NULL){ + log_e("NULL RX buffer pointer"); + return value; + } if(rxIndex < rxLength) { value = rxBuffer[rxIndex]; } @@ -254,41 +580,51 @@ void TwoWire::flush(void) { rxIndex = 0; rxLength = 0; - txIndex = 0; txLength = 0; - rxQueued = 0; - txQueued = 0; - i2cFlush(i2c); // cleanup + //i2cFlush(num); // cleanup +} + +size_t TwoWire::requestFrom(uint8_t address, size_t len, bool sendStop) +{ + return requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); +} + +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t len, uint8_t sendStop) +{ + return requestFrom(static_cast(address), static_cast(len), static_cast(sendStop)); } -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t len, uint8_t sendStop) { - return requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop)); + return requestFrom(address, static_cast(len), static_cast(sendStop)); } -uint8_t TwoWire::requestFrom(uint16_t address, uint8_t quantity, uint8_t sendStop) +/* Added to match the Arduino function definition: https://github.com/arduino/ArduinoCore-API/blob/173e8eadced2ad32eeb93bcbd5c49f8d6a055ea6/api/HardwareI2C.h#L39 + * See: https://github.com/arduino-libraries/ArduinoECCX08/issues/25 +*/ +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t len, bool stopBit) { - return requestFrom(address, static_cast(quantity), static_cast(sendStop)); + return requestFrom((uint16_t)address, (size_t)len, stopBit); } -uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t len) { - return requestFrom(static_cast(address), static_cast(quantity), true); + return requestFrom(static_cast(address), static_cast(len), true); } -uint8_t TwoWire::requestFrom(uint16_t address, uint8_t quantity) +uint8_t TwoWire::requestFrom(uint16_t address, uint8_t len) { - return requestFrom(address, static_cast(quantity), true); + return requestFrom(address, static_cast(len), true); } -uint8_t TwoWire::requestFrom(int address, int quantity) +uint8_t TwoWire::requestFrom(int address, int len) { - return requestFrom(static_cast(address), static_cast(quantity), true); + return requestFrom(static_cast(address), static_cast(len), true); } -uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) +uint8_t TwoWire::requestFrom(int address, int len, int sendStop) { - return static_cast(requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop))); + return static_cast(requestFrom(static_cast(address), static_cast(len), static_cast(sendStop))); } void TwoWire::beginTransmission(int address) @@ -306,61 +642,57 @@ uint8_t TwoWire::endTransmission(void) return endTransmission(true); } -uint8_t TwoWire::endTransmission(uint8_t sendStop) +size_t TwoWire::slaveWrite(const uint8_t * buffer, size_t len) { - return endTransmission(static_cast(sendStop)); + return i2cSlaveWrite(num, buffer, len, _timeOutMillis); } -/* stickbreaker Nov2017 better error reporting - */ -uint8_t TwoWire::lastError() +void TwoWire::onReceiveService(uint8_t num, uint8_t* inBytes, size_t numBytes, bool stop, void * arg) { - return (uint8_t)last_error; + TwoWire * wire = (TwoWire*)arg; + if(!wire->user_onReceive){ + return; + } + if (wire->rxBuffer == NULL){ + log_e("NULL RX buffer pointer"); + return; + } + for(uint8_t i = 0; i < numBytes; ++i){ + wire->rxBuffer[i] = inBytes[i]; + } + wire->rxIndex = 0; + wire->rxLength = numBytes; + wire->user_onReceive(numBytes); } -const char ERRORTEXT[] = - "OK\0" - "DEVICE\0" - "ACK\0" - "TIMEOUT\0" - "BUS\0" - "BUSY\0" - "MEMORY\0" - "CONTINUE\0" - "NO_BEGIN\0" - "\0"; - - -char * TwoWire::getErrorText(uint8_t err) +void TwoWire::onRequestService(uint8_t num, void * arg) { - uint8_t t = 0; - bool found = false; - char * message = (char*)&ERRORTEXT; - - while(!found && message[0]) { - found = t == err; - if(!found) { - message = message + strlen(message) + 1; - t++; - } + TwoWire * wire = (TwoWire*)arg; + if(!wire->user_onRequest){ + return; } - if(!found) { - return NULL; - } else { - return message; + if (wire->txBuffer == NULL){ + log_e("NULL TX buffer pointer"); + return; + } + wire->txLength = 0; + wire->user_onRequest(); + if(wire->txLength){ + wire->slaveWrite((uint8_t*)wire->txBuffer, wire->txLength); } } -/*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging - */ -void TwoWire::dumpInts() +void TwoWire::onReceive( void (*function)(int) ) { - i2cDumpInts(num); + user_onReceive = function; } -void TwoWire::dumpI2C() +// sets function called on slave read +void TwoWire::onRequest( void (*function)(void) ) { - i2cDumpI2c(i2c); + user_onRequest = function; } + TwoWire Wire = TwoWire(0); +TwoWire Wire1 = TwoWire(1); diff --git a/src/sensors/Wire.h b/src/sensors/Wire.h index cdf50fd..339b0e2 100644 --- a/src/sensors/Wire.h +++ b/src/sensors/Wire.h @@ -20,18 +20,28 @@ Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support Modified November 2017 by Chuck Todd to use ISR and increase stability. + Modified Nov 2021 by Hristo Gochkov to support ESP-IDF API */ #ifndef TwoWire_h #define TwoWire_h #include +#if !CONFIG_DISABLE_HAL_LOCKS #include "freertos/FreeRTOS.h" -#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#endif #include "Stream.h" -#define STICKBREAKER V0.2.2 -#define I2C_BUFFER_LENGTH 128 +// WIRE_HAS_BUFFER_SIZE means Wire has setBufferSize() +#define WIRE_HAS_BUFFER_SIZE 1 +// WIRE_HAS_END means Wire has end() +#define WIRE_HAS_END 1 + +#ifndef I2C_BUFFER_LENGTH + #define I2C_BUFFER_LENGTH 128 // Default size, if none is set using Wire::setBuffersize(size_t) +#endif typedef void(*user_onRequest)(void); typedef void(*user_onReceive)(uint8_t*, int); @@ -41,57 +51,75 @@ class TwoWire: public Stream uint8_t num; int8_t sda; int8_t scl; - i2c_t * i2c; - uint8_t rxBuffer[I2C_BUFFER_LENGTH]; - uint16_t rxIndex; - uint16_t rxLength; - uint16_t rxQueued; //@stickBreaker + size_t bufferSize; + uint8_t *rxBuffer; + size_t rxIndex; + size_t rxLength; - uint8_t txBuffer[I2C_BUFFER_LENGTH]; - uint16_t txIndex; - uint16_t txLength; + uint8_t *txBuffer; + size_t txLength; uint16_t txAddress; - uint16_t txQueued; //@stickbreaker - - uint8_t transmitting; - /* slave Mode, not yet Stickbreaker - static user_onRequest uReq[2]; - static user_onReceive uRcv[2]; - void onRequestService(void); - void onReceiveService(uint8_t*, int); - */ - i2c_err_t last_error; // @stickBreaker from esp32-hal-i2c.h - uint16_t _timeOutMillis; + + uint32_t _timeOutMillis; + bool nonStop; +#if !CONFIG_DISABLE_HAL_LOCKS + 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 *); + bool initPins(int sdaPin, int sclPin); + bool allocateWireBuffer(void); + void freeWireBuffer(void); public: TwoWire(uint8_t bus_num); ~TwoWire(); - void begin(int sda=-1, int scl=-1, uint32_t frequency=0); + + //call setPins() first, so that begin() can be called without arguments from libraries + bool setPins(int sda, int scl); + + bool begin(int sda, int scl, uint32_t frequency=0); // returns true, if successful init of i2c bus + bool begin(uint8_t slaveAddr, int sda, int scl, uint32_t frequency); + // Explicit Overload for Arduino MainStream API compatibility + inline bool begin() + { + return begin(-1, -1, static_cast(0)); + } + inline bool begin(uint8_t addr) + { + return begin(addr, -1, -1, 0); + } + inline bool begin(int addr) + { + return begin(static_cast(addr), -1, -1, 0); + } + bool end(); - void setClock(uint32_t frequency); // change bus clock without initing hardware - size_t getClock(); // current bus clock rate in hz + size_t setBufferSize(size_t bSize); - void setTimeOut(uint16_t timeOutMillis); + void setTimeOut(uint16_t timeOutMillis); // default timeout of i2c transactions is 50ms uint16_t getTimeOut(); - uint8_t lastError(); - char * getErrorText(uint8_t err); - - //@stickBreaker for big blocks and ISR model - i2c_err_t writeTransmission(uint16_t address, uint8_t* buff, uint16_t size, bool sendStop=true); - i2c_err_t readTransmission(uint16_t address, uint8_t* buff, uint16_t size, bool sendStop=true, uint32_t *readCount=NULL); + bool setClock(uint32_t); + uint32_t getClock(); void beginTransmission(uint16_t address); void beginTransmission(uint8_t address); void beginTransmission(int address); uint8_t endTransmission(bool sendStop); - uint8_t endTransmission(uint8_t sendStop); uint8_t endTransmission(void); + size_t requestFrom(uint16_t address, size_t size, bool sendStop); uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop); + size_t requestFrom(uint8_t address, size_t len, bool stopBit); uint8_t requestFrom(uint16_t address, uint8_t size); uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop); uint8_t requestFrom(uint8_t address, uint8_t size); @@ -128,16 +156,10 @@ class TwoWire: public Stream void onReceive( void (*)(int) ); void onRequest( void (*)(void) ); - - void dumpInts(); - void dumpI2C(); + size_t slaveWrite(const uint8_t *, size_t); }; extern TwoWire Wire; +extern TwoWire Wire1; - -/* -V0.2.2 13APR2018 preserve custom SCL,SDA,Frequency when no parameters passed to begin() -V0.2.1 15MAR2018 Hardware reset, Glitch prevention, adding destructor for second i2c testing -*/ #endif diff --git a/src/sensors/esp32-hal-gpio.c b/src/sensors/esp32-hal-gpio.c index 7c5e02b..f0f99db 100644 --- a/src/sensors/esp32-hal-gpio.c +++ b/src/sensors/esp32-hal-gpio.c @@ -13,244 +13,215 @@ // limitations under the License. #include "esp32-hal-gpio.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "rom/ets_sys.h" -#include "esp_attr.h" -#include "esp_intr.h" -#include "rom/gpio.h" -#include "soc/gpio_reg.h" -#include "soc/io_mux_reg.h" -#include "soc/gpio_struct.h" -#include "soc/rtc_io_reg.h" +#include "hal/gpio_hal.h" +#include "soc/soc_caps.h" -const int8_t esp32_adc2gpio[20] = {36, 37, 38, 39, 32, 33, 34, 35, -1, -1, 4, 0, 2, 15, 13, 12, 14, 27, 25, 26}; +// It fixes lack of pin definition for S3 and for any future SoC +// this function works for ESP32, ESP32-S2 and ESP32-S3 - including the C3, it will return -1 for any pin +#if SOC_TOUCH_SENSOR_NUM > 0 +#include "soc/touch_sensor_periph.h" -const DRAM_ATTR esp32_gpioMux_t esp32_gpioMux[GPIO_PIN_COUNT]={ - {0x44, 11, 11, 1}, - {0x88, -1, -1, -1}, - {0x40, 12, 12, 2}, - {0x84, -1, -1, -1}, - {0x48, 10, 10, 0}, - {0x6c, -1, -1, -1}, - {0x60, -1, -1, -1}, - {0x64, -1, -1, -1}, - {0x68, -1, -1, -1}, - {0x54, -1, -1, -1}, - {0x58, -1, -1, -1}, - {0x5c, -1, -1, -1}, - {0x34, 15, 15, 5}, - {0x38, 14, 14, 4}, - {0x30, 16, 16, 6}, - {0x3c, 13, 13, 3}, - {0x4c, -1, -1, -1}, - {0x50, -1, -1, -1}, - {0x70, -1, -1, -1}, - {0x74, -1, -1, -1}, - {0x78, -1, -1, -1}, - {0x7c, -1, -1, -1}, - {0x80, -1, -1, -1}, - {0x8c, -1, -1, -1}, - {0, -1, -1, -1}, - {0x24, 6, 18, -1}, //DAC1 - {0x28, 7, 19, -1}, //DAC2 - {0x2c, 17, 17, 7}, - {0, -1, -1, -1}, - {0, -1, -1, -1}, - {0, -1, -1, -1}, - {0, -1, -1, -1}, - {0x1c, 9, 4, 9}, - {0x20, 8, 5, 8}, - {0x14, 4, 6, -1}, - {0x18, 5, 7, -1}, - {0x04, 0, 0, -1}, - {0x08, 1, 1, -1}, - {0x0c, 2, 2, -1}, - {0x10, 3, 3, -1} -}; +int8_t digitalPinToTouchChannel(uint8_t pin) +{ + int8_t ret = -1; + if (pin < SOC_GPIO_PIN_COUNT) { + for (uint8_t i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) { + if (touch_sensor_channel_io_map[i] == pin) { + ret = i; + break; + } + } + } + return ret; +} +#else +// No Touch Sensor available +int8_t digitalPinToTouchChannel(uint8_t pin) +{ + return -1; +} +#endif + +#ifdef SOC_ADC_SUPPORTED +#include "soc/adc_periph.h" + +int8_t digitalPinToAnalogChannel(uint8_t pin) +{ + uint8_t channel = 0; + if (pin < SOC_GPIO_PIN_COUNT) { + for (uint8_t i = 0; i < SOC_ADC_PERIPH_NUM; i++) { + for (uint8_t j = 0; j < SOC_ADC_MAX_CHANNEL_NUM; j++) { + if (adc_channel_io_map[i][j] == pin) { + return channel; + } + channel++; + } + } + } + return -1; +} + +int8_t analogChannelToDigitalPin(uint8_t channel) +{ + if (channel >= (SOC_ADC_PERIPH_NUM * SOC_ADC_MAX_CHANNEL_NUM)) { + return -1; + } + uint8_t adc_unit = (channel / SOC_ADC_MAX_CHANNEL_NUM); + uint8_t adc_chan = (channel % SOC_ADC_MAX_CHANNEL_NUM); + return adc_channel_io_map[adc_unit][adc_chan]; +} +#else +// No Analog channels availible +int8_t analogChannelToDigitalPin(uint8_t channel) +{ + return -1; +} +#endif typedef void (*voidFuncPtr)(void); -static voidFuncPtr __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,}; +typedef void (*voidFuncPtrArg)(void*); +typedef struct { + voidFuncPtr fn; + void* arg; + bool functional; +} InterruptHandle_t; +static InterruptHandle_t __pinInterruptHandlers[SOC_GPIO_PIN_COUNT] = {0,}; #include "driver/rtc_io.h" -extern void IRAM_ATTR __pinMode(uint8_t pin, uint8_t mode) +extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) { - - if(!digitalPinIsValid(pin)) { +#ifdef RGB_BUILTIN + if (pin == RGB_BUILTIN){ + __pinMode(RGB_BUILTIN-SOC_GPIO_PIN_COUNT, mode); return; } +#endif - uint32_t rtc_reg = rtc_gpio_desc[pin].reg; - if(mode == ANALOG) { - if(!rtc_reg) { - return;//not rtc pin - } - //lock rtc - uint32_t reg_val = ESP_REG(rtc_reg); - if(reg_val & rtc_gpio_desc[pin].mux){ - return;//already in adc mode - } - reg_val &= ~( - (RTC_IO_TOUCH_PAD1_FUN_SEL_V << rtc_gpio_desc[pin].func) - |rtc_gpio_desc[pin].ie - |rtc_gpio_desc[pin].pullup - |rtc_gpio_desc[pin].pulldown); - ESP_REG(RTC_GPIO_ENABLE_W1TC_REG) = (1 << (rtc_gpio_desc[pin].rtc_num + RTC_GPIO_ENABLE_W1TC_S)); - ESP_REG(rtc_reg) = reg_val | rtc_gpio_desc[pin].mux; - //unlock rtc - ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = ((uint32_t)2 << MCU_SEL_S) | ((uint32_t)2 << FUN_DRV_S) | FUN_IE; + if (!GPIO_IS_VALID_GPIO(pin)) { + log_e("Invalid pin selected"); return; } - - //RTC pins PULL settings - if(rtc_reg) { - //lock rtc - ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux); - if(mode & PULLUP) { - ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pullup) & ~(rtc_gpio_desc[pin].pulldown); - } else if(mode & PULLDOWN) { - ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pulldown) & ~(rtc_gpio_desc[pin].pullup); - } else { - ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown); + + gpio_hal_context_t gpiohal; + gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0); + + gpio_config_t conf = { + .pin_bit_mask = (1ULL<pin[pin].int_type /*!< GPIO interrupt type - previously set */ + }; + if (mode < 0x20) {//io + conf.mode = mode & (INPUT | OUTPUT); + if (mode & OPEN_DRAIN) { + conf.mode |= GPIO_MODE_DEF_OD; } - //unlock rtc - } - - uint32_t pinFunction = 0, pinControl = 0; - - //lock gpio - if(mode & INPUT) { - if(pin < 32) { - GPIO.enable_w1tc = ((uint32_t)1 << pin); - } else { - GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32)); + if (mode & PULLUP) { + conf.pull_up_en = GPIO_PULLUP_ENABLE; } - } else if(mode & OUTPUT) { - if(pin > 33){ - //unlock gpio - return;//pins above 33 can be only inputs - } else if(pin < 32) { - GPIO.enable_w1ts = ((uint32_t)1 << pin); - } else { - GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32)); + if (mode & PULLDOWN) { + conf.pull_down_en = GPIO_PULLDOWN_ENABLE; } } - - if(mode & PULLUP) { - pinFunction |= FUN_PU; - } else if(mode & PULLDOWN) { - pinFunction |= FUN_PD; - } - - pinFunction |= ((uint32_t)2 << FUN_DRV_S);//what are the drivers? - pinFunction |= FUN_IE;//input enable but required for output as well? - - if(mode & (INPUT | OUTPUT)) { - pinFunction |= ((uint32_t)2 << MCU_SEL_S); - } else if(mode == SPECIAL) { - pinFunction |= ((uint32_t)(((pin)==1||(pin)==3)?0:1) << MCU_SEL_S); - } else { - pinFunction |= ((uint32_t)(mode >> 5) << MCU_SEL_S); - } - - ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction; - - if(mode & OPEN_DRAIN) { - pinControl = (1 << GPIO_PIN0_PAD_DRIVER_S); + if(gpio_config(&conf) != ESP_OK) + { + log_e("GPIO config failed"); + return; } - - GPIO.pin[pin].val = pinControl; - //unlock gpio } -extern void IRAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) +extern void ARDUINO_ISR_ATTR __digitalWrite(uint8_t pin, uint8_t val) { - if(val) { - if(pin < 32) { - GPIO.out_w1ts = ((uint32_t)1 << pin); - } else if(pin < 34) { - GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32)); - } - } else { - if(pin < 32) { - GPIO.out_w1tc = ((uint32_t)1 << pin); - } else if(pin < 34) { - GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32)); + #ifdef RGB_BUILTIN + if(pin == RGB_BUILTIN){ + //use RMT to set all channels on/off + const uint8_t comm_val = val != 0 ? RGB_BRIGHTNESS : 0; + neopixelWrite(RGB_BUILTIN, comm_val, comm_val, comm_val); + return; } - } + #endif + gpio_set_level((gpio_num_t)pin, val); } -extern int IRAM_ATTR __digitalRead(uint8_t pin) +extern int ARDUINO_ISR_ATTR __digitalRead(uint8_t pin) { - if(pin < 32) { - return (GPIO.in >> pin) & 0x1; - } else if(pin < 40) { - return (GPIO.in1.val >> (pin - 32)) & 0x1; + return gpio_get_level((gpio_num_t)pin); +} + +static void ARDUINO_ISR_ATTR __onPinInterrupt(void * arg) { + InterruptHandle_t * isr = (InterruptHandle_t*)arg; + if(isr->fn) { + if(isr->arg){ + ((voidFuncPtrArg)isr->fn)(isr->arg); + } else { + isr->fn(); + } } - return 0; } -static intr_handle_t gpio_intr_handle = NULL; +extern void cleanupFunctional(void* arg); -static void IRAM_ATTR __onPinInterrupt(void *arg) +extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional) { - uint32_t gpio_intr_status_l=0; - uint32_t gpio_intr_status_h=0; + static bool interrupt_initialized = false; - gpio_intr_status_l = GPIO.status; - gpio_intr_status_h = GPIO.status1.val; - GPIO.status_w1tc = gpio_intr_status_l;//Clear intr for gpio0-gpio31 - GPIO.status1_w1tc.val = gpio_intr_status_h;//Clear intr for gpio32-39 + if(!interrupt_initialized) { + esp_err_t err = gpio_install_isr_service((int)ARDUINO_ISR_FLAG); + interrupt_initialized = (err == ESP_OK) || (err == ESP_ERR_INVALID_STATE); + } + if(!interrupt_initialized) { + log_e("GPIO ISR Service Failed To Start"); + return; + } - uint8_t pin=0; - if(gpio_intr_status_l) { - do { - if(gpio_intr_status_l & ((uint32_t)1 << pin)) { - if(__pinInterruptHandlers[pin]) { - __pinInterruptHandlers[pin](); - } - } - } while(++pin<32); + // if new attach without detach remove old info + if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) + { + cleanupFunctional(__pinInterruptHandlers[pin].arg); } - if(gpio_intr_status_h) { - pin=32; - do { - if(gpio_intr_status_h & ((uint32_t)1 << (pin - 32))) { - if(__pinInterruptHandlers[pin]) { - __pinInterruptHandlers[pin](); - } - } - } while(++pinlock, portMAX_DELAY) != pdPASS) -#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock) - -static i2c_t _i2c_bus_array[2] = { - {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0}, - {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0} -}; -#endif - -/* - * index - command index (0 to 15) - * op_code - is the command - * byte_num - This register is to store the amounts of data that is read and written. byte_num in RSTART, STOP, END is null. - * ack_val - Each data byte is terminated by an ACK bit used to set the bit level. - * ack_exp - This bit is to set an expected ACK value for the transmitter. - * ack_check - This bit is to decide whether the transmitter checks ACK bit. 1 means yes and 0 means no. - * */ -static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check) -{ - I2C_COMMAND_t cmd; - cmd.val=0; - cmd.ack_en = ack_check; - cmd.ack_exp = ack_exp; - cmd.ack_val = ack_val; - cmd.byte_num = byte_num; - cmd.op_code = op_code; - i2c->dev->command[index].val = cmd.val; -} - -/* Stickbreaker ISR mode debug support - */ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO -#define INTBUFFMAX 64 -static uint32_t intBuff[INTBUFFMAX][3][2]; -static uint32_t intPos[2]= {0,0}; -#endif - -/* Stickbreaker ISR mode debug support - */ -void IRAM_ATTR dumpCmdQueue(i2c_t *i2c) -{ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - uint8_t i=0; - while(i<16) { - I2C_COMMAND_t c; - c.val=i2c->dev->command[i].val; - log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'), - c.op_code, - c.ack_val, - c.ack_exp, - c.ack_en, - c.byte_num); - i++; - } -#endif -} - -/* Stickbreaker ISR mode support - */ -static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) -{ - /* this function is call on initial i2cProcQueue() - or when a I2C_END_DETECT_INT occures - */ - uint16_t cmdIdx = 0; - uint16_t qp = i2c->queuePos; - bool done; - bool needMoreCmds = false; - bool ena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ - bool ena_tx=false; // if we add a Write op, better enable TX_Fifo IRQ - - while(!needMoreCmds&&(qp < i2c->queueCount)) { // check if more possible cmds - if(i2c->dq[qp].ctrl.stopCmdSent) { - qp++; - } else { - needMoreCmds=true; - } - } - //log_e("needMoreCmds=%d",needMoreCmds); - done=(!needMoreCmds)||(cmdIdx>14); - - while(!done) { // fill the command[] until either 0..14 filled or out of cmds and last cmd is STOP - //CMD START - I2C_DATA_QUEUE_t *tdq=&i2c->dq[qp]; // simpler coding - - if((!tdq->ctrl.startCmdSent) && (cmdIdx < 14)) { // has this dq element's START command been added? - // <14 testing if ReSTART END is causeing the Timeout - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); - tdq->ctrl.startCmdSent=1; - done = (cmdIdx>14); - } - - //CMD WRITE ADDRESS - if((!done)&&(tdq->ctrl.startCmdSent)) { // have to leave room for continue, and START must have been sent! - if(!tdq->ctrl.addrCmdSent) { - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, tdq->ctrl.addrReq, false, false, true); //load address in cmdlist, validate (low) ack - tdq->ctrl.addrCmdSent=1; - done =(cmdIdx>14); - ena_tx=true; // tx Data necessary - } - } - - /* Can I have another Sir? - ALL CMD queues must be terminated with either END or STOP. - - If END is used, when refilling the cmd[] next time, no entries from END to [15] can be used. - AND the cmd[] must be filled starting at [0] with commands. Either fill all 15 [0]..[14] and leave the - END in [15] or include a STOP in one of the positions [0]..[14]. Any entries after a STOP are IGNORED byte the StateMachine. - The END operation does not complete until ctr->trans_start=1 has been issued. - - So, only refill from [0]..[14], leave [15] for a continuation if necessary. - As a corrilary, once END exists in [15], you do not need to overwrite it for the - next continuation. It is never modified. But, I update it every time because it might - actually be the first time! - - 23NOV17 START cannot proceed END. if START is in[14], END cannot be in [15]. - so, AND if END is moved to [14], [14] and [15] can nolonger be use for anything other than END. - If a START is found in [14] then a prior READ or WRITE must be expanded so that there is no START element in [14]. - - - */ - if((!done)&&(tdq->ctrl.addrCmdSent)) { //room in command[] for at least One data (read/Write) cmd - uint8_t blkSize=0; // max is 255? does numBytes =0 actually mean 256? haven't tried it. - //log_e("needed=%2d index=%d",*neededRead,cmdIdx); - while(( tdq->cmdBytesNeeded > tdq->ctrl.mode )&&(!done )) { // more bytes needed and room in cmd queue, leave room for END - blkSize = (tdq->cmdBytesNeeded > 255)?255:(tdq->cmdBytesNeeded - tdq->ctrl.mode); // Last read cmd needs different ACK setting, so leave 1 byte remainer on reads - tdq->cmdBytesNeeded -= blkSize; // - if(tdq->ctrl.mode==1) { //read mode - i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, blkSize,false,false,false); // read cmd, this can't be the last read. - ena_rx=true; // need to enable rxFifo IRQ - } else { // write - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_WRITE, blkSize, false, false, true); // check for Nak - ena_tx=true; // need to enable txFifo IRQ - } - done = cmdIdx>14; //have to leave room for END - } - - if(!done) { // buffer is not filled completely - if((tdq->ctrl.mode==1)&&(tdq->cmdBytesNeeded==1)) { //special last read byte NAK - i2cSetCmd(i2c, (cmdIdx)++, I2C_CMD_READ, 1,true,false,false); - // send NAK to mark end of read - tdq->cmdBytesNeeded=0; - done = cmdIdx > 14; - ena_rx=true; - } - } - - tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data - - if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop - if(tdq->ctrl.stop) { //send a stop - i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); - done = cmdIdx > 14; - tdq->ctrl.stopCmdSent = 1; - } else { // dummy a stop because this is a restart - tdq->ctrl.stopCmdSent = 1; - } - } - } - - if((cmdIdx==14)&&(!tdq->ctrl.startCmdSent)) { - // START would have preceded END, causes SM TIMEOUT - // need to stretch out a prior WRITE or READ to two Command[] elements - done = false; // reuse it - uint16_t i = 13; // start working back until a READ/WRITE has >1 numBytes - cmdIdx =15; - // log_e("before Stretch"); - // dumpCmdQueue(i2c); - while(!done) { - i2c->dev->command[i+1].val = i2c->dev->command[i].val; // push it down - if (((i2c->dev->command[i].op_code == 1)||(i2c->dev->command[i].op_code==2))) { - /* just try a num_bytes =0; - &&(i2c->dev->command[i].byte_num>1)){ // found the one to expand - i2c->dev->command[i+1].byte_num =1; - // the -= in the following statment caused unintential consequences. - // The op_code field value changed from 2 to 4, so the manual cludge was needed - // i2c->dev->command[i].byte_num -= 1; - uint32_t temp = i2c->dev->command[i].val; - temp = (temp&0xFFFFFF00) | ((temp & 0xFF)-1); - i2c->dev->command[i].val = temp; - */ - i2c->dev->command[i].byte_num = 0; - done = true; - - } else { - if(i > 0) { - i--; - } else { // unable to stretch, fatal - log_e("invalid CMD[] layout Stretch Failed"); - dumpCmdQueue(i2c); - done = true; - } - } - } - // log_e("after Stretch"); - // dumpCmdQueue(i2c); - - } - - - if(cmdIdx==15) { //need continuation, even if STOP is in 14, it will not matter - // cmd buffer is almost full, Add END as a continuation feature - // log_e("END at %d, left=%d",cmdIdx,neededRead); - i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); - i2c->dev->int_ena.end_detect=1; //maybe? - i2c->dev->int_clr.end_detect=1; //maybe? - done = true; - } - - if(!done) { - if(tdq->ctrl.stopCmdSent) { // this queue element has been completely added to command[] buffer - qp++; - if(qp < i2c->queueCount) { - tdq = &i2c->dq[qp]; - // log_e("inc to next queue=%d",qp); - } else { - done = true; - } - } - } - - }// while(!done) - if(INTS) { // don't want to prematurely enable fifo ints until ISR is ready to handle it. - if(ena_rx) { - i2c->dev->int_ena.rx_fifo_full = 1; - } - if(ena_tx) { - i2c->dev->int_ena.tx_fifo_empty = 1; - } - } -} - -/* Stickbreaker ISR mode support - */ -static void IRAM_ATTR fillTxFifo(i2c_t * i2c) -{ - /* need to test overlapping RX->TX fifo operations, - Currently, this function attempts to queue all possible tx elements into the Fifo. - What happens when WRITE 10, READ 20, Write 10? - (Write Addr, Write 10),(Write addr, Read 20) (Write addr, Write 10). - I know everything will work up to the End of the Read 20, but I am unsure - what will happen to the third command, will the Read 20 overwrite the previously - queued (write addr, write 10) of the Third command? I need to test! - */ - /*11/15/2017 will assume that I cannot queue tx after a READ until READ completes - 11/23/2017 Seems to be a TX fifo problem, the SM sends 0x40 for last rxbyte, I - enable txEmpty, filltx fires, but the SM has already sent a bogus byte out the BUS. - I am going so see if I can overlap Tx/Rx/Tx in the fifo - 12/01/2017 The Fifo's are independent, 32 bytes of tx and 32 bytes of Rx. - overlap is not an issue, just keep them full/empty the status_reg.xx_fifo_cnt - tells the truth. And the INT's fire correctly - */ - uint16_t a=i2c->queuePos; // currently executing dq, - bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31); - uint8_t cnt; - while((a < i2c->queueCount) && !full) { - I2C_DATA_QUEUE_t *tdq = &i2c->dq[a]; - cnt=0; - // add to address to fifo ctrl.addr already has R/W bit positioned correctly - if(tdq->ctrl.addrSent < tdq->ctrl.addrReq) { // need to send address bytes - if(tdq->ctrl.addrReq==2) { //10bit - if(tdq->ctrl.addrSent==0) { //10bit highbyte needs sent - if(!full) { // room in fifo - i2c->dev->fifo_data.val = ((tdq->ctrl.addr>>8)&0xFF); - cnt++; - tdq->ctrl.addrSent=1; //10bit highbyte sent - } - } - full=!(i2c->dev->status_reg.tx_fifo_cnt<31); - - if(tdq->ctrl.addrSent==1) { //10bit Lowbyte needs sent - if(!full) { // room in fifo - i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; - cnt++; - tdq->ctrl.addrSent=2; //10bit lowbyte sent - } - } - } else { // 7bit} - if(tdq->ctrl.addrSent==0) { // 7bit Lowbyte needs sent - if(!full) { // room in fifo - i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF; - cnt++; - tdq->ctrl.addrSent=1; // 7bit lowbyte sent - } - } - } - } - full=!(i2c->dev->status_reg.tx_fifo_cnt<31); - // add write data to fifo - //21NOV2017 might want to look into using local capacity counter instead of reading status_reg - // a double while loop, like emptyRxFifo() - if(tdq->ctrl.mode==0) { // write - if(tdq->ctrl.addrSent == tdq->ctrl.addrReq) { //address has been sent, is Write Mode! - while((!full)&&(tdq->position < tdq->length)) { - i2c->dev->fifo_data.val = tdq->data[tdq->position++]; - cnt++; - full=!(i2c->dev->status_reg.tx_fifo_cnt<31); - } - } - } - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - - // update debug buffer tx counts - cnt += intBuff[intPos[i2c->num]][1][i2c->num]>>16; - intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16); - -#endif - - if(!full) { - a++; // check next buffer for tx - } - } - - if(!full || (a >= i2c->queueCount)) { // disable IRQ, the next dq will re-enable it - i2c->dev->int_ena.tx_fifo_empty=0; - } - - i2c->dev->int_clr.tx_fifo_empty=1; -} - -/* Stickbreaker ISR mode support - */ -static void IRAM_ATTR emptyRxFifo(i2c_t * i2c) -{ - uint32_t d, cnt=0, moveCnt; - I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; - - moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read - if(moveCnt > (tdq->length - tdq->position)) { //makesure they go in this dq - // part of these reads go into the next dq - moveCnt = (tdq->length - tdq->position); - } - - if(tdq->ctrl.mode==1) { // read - while(moveCnt > 0) { - while(moveCnt > 0) { - d = i2c->dev->fifo_data.val; - moveCnt--; - cnt++; - tdq->data[tdq->position++] = (d&0xFF); - } - // see if any more chars showed up while empting Fifo. - moveCnt = i2c->dev->status_reg.rx_fifo_cnt; - if(moveCnt > (tdq->length - tdq->position)) { //makesure they go in this dq - // part of these reads go into the next dq - moveCnt = (tdq->length - tdq->position); - } - } -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - // update Debug rxCount - cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&&0xffFF; - intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt; -#endif - } else { - log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); - // dumpI2c(i2c); - } - //log_e("emptied %d",*index); -} - -static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal) -{ - - switch(eventCode) { - case EVENT_DONE: - i2c->error = I2C_OK; - break; - case EVENT_ERROR_NAK : - i2c->error =I2C_ADDR_NAK; - break; - case EVENT_ERROR_DATA_NAK : - i2c->error =I2C_DATA_NAK; - break; - case EVENT_ERROR_TIMEOUT : - i2c->error = I2C_TIMEOUT; - break; - case EVENT_ERROR_ARBITRATION: - i2c->error = I2C_ARBITRATION; - break; - default : - i2c->error = I2C_ERROR; - } - uint32_t exitCode = EVENT_DONE | eventCode |(Fatal?EVENT_ERROR:0); - - if(i2c->dq[i2c->queuePos].ctrl.mode == 1) { - emptyRxFifo(i2c); // grab last few characters - } - - i2c->dev->int_ena.val = 0; // shutdown interrupts - i2c->dev->int_clr.val = 0x1FFFF; - i2c->stage = I2C_DONE; - i2c->exitCode = exitCode; //true eventcode - - portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; - // try to notify Dispatch we are done, - // else the 50ms timeout will recover the APP, just alittle slower - HPTaskAwoken = pdFALSE; - xResult = xEventGroupSetBitsFromISR(i2c->i2c_event, exitCode, &HPTaskAwoken); - if(xResult == pdPASS) { - if(HPTaskAwoken==pdTRUE) { - portYIELD_FROM_ISR(); - // log_e("Yield to Higher"); - } - } - -} - -static void IRAM_ATTR i2c_isr_handler_default(void* arg) -{ - i2c_t* p_i2c = (i2c_t*) arg; // recover data - uint32_t activeInt = p_i2c->dev->int_status.val&0x1FFF; - - //portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; - - if(p_i2c->stage==I2C_DONE) { //get Out - log_e("eject int=%p, ena=%p",activeInt,p_i2c->dev->int_ena.val); - p_i2c->dev->int_ena.val = 0; - p_i2c->dev->int_clr.val = activeInt; //0x1FFF; - return; - } - while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - if(activeInt==(intBuff[intPos[p_i2c->num]][0][p_i2c->num]&0x1fff)) { - intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (((intBuff[intPos[p_i2c->num]][0][p_i2c->num]>>16)+1)<<16)|activeInt; - } else { - intPos[p_i2c->num]++; - intPos[p_i2c->num] %= INTBUFFMAX; - intBuff[intPos[p_i2c->num]][0][p_i2c->num] = (1<<16) | activeInt; - intBuff[intPos[p_i2c->num]][1][p_i2c->num] = 0; - } - - intBuff[intPos[p_i2c->num]][2][p_i2c->num] = xTaskGetTickCountFromISR(); // when IRQ fired - -#endif - //uint32_t oldInt =activeInt; - - if (activeInt & I2C_TRANS_START_INT_ST_M) { - // p_i2c->byteCnt=0; - if(p_i2c->stage==I2C_STARTUP) { - p_i2c->stage=I2C_RUNNING; - } - - activeInt &=~I2C_TRANS_START_INT_ST_M; - p_i2c->dev->int_ena.trans_start = 1; // already enabled? why Again? - p_i2c->dev->int_clr.trans_start = 1; // so that will trigger after next 'END' - } - - if (activeInt & I2C_TXFIFO_EMPTY_INT_ST) {//should this be before Trans_start? - fillTxFifo(p_i2c); //fillTxFifo will enable/disable/clear interrupt - activeInt&=~I2C_TXFIFO_EMPTY_INT_ST; - } - - if(activeInt & I2C_RXFIFO_FULL_INT_ST) { - emptyRxFifo(p_i2c); - p_i2c->dev->int_clr.rx_fifo_full=1; - p_i2c->dev->int_ena.rx_fifo_full=1; //why? - - activeInt &=~I2C_RXFIFO_FULL_INT_ST; - } - - if(activeInt & I2C_MASTER_TRAN_COMP_INT_ST) { // each byte the master sends/recv - p_i2c->dev->int_clr.master_tran_comp = 1; - - p_i2c->byteCnt++; - - if(p_i2c->byteCnt > p_i2c->dq[p_i2c->queuePos].queueLength) { // simulate Trans_start - - p_i2c->byteCnt -= p_i2c->dq[p_i2c->queuePos].queueLength; - - if(p_i2c->dq[p_i2c->queuePos].ctrl.mode==1) { // grab last characters for this dq - emptyRxFifo(p_i2c); - p_i2c->dev->int_clr.rx_fifo_full=1; - p_i2c->dev->int_ena.rx_fifo_full=1; - } - - p_i2c->queuePos++; //inc to next dq - - if(p_i2c->queuePos < p_i2c->queueCount) { // load next dq address field + data - p_i2c->dev->int_ena.tx_fifo_empty=1; - } - - } - activeInt &=~I2C_MASTER_TRAN_COMP_INT_ST; - } - - if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service - if (p_i2c->mode == I2C_MASTER) { - // log_e("AcK Err byteCnt=%d, queuepos=%d",p_i2c->byteCnt,p_i2c->queuePos); - if(p_i2c->byteCnt==1) { - i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); - } else if((p_i2c->byteCnt == 2) && (p_i2c->dq[p_i2c->queuePos].ctrl.addrReq == 2)) { - i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); - } else { - i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); - } - } - return; - } - - if (activeInt & I2C_TIME_OUT_INT_ST_M) { - // let Gross timeout occur, Slave may release SCL before the configured timeout expires - // the Statemachine only has a 13.1ms max timout, some Devices >500ms - p_i2c->dev->int_clr.time_out =1; - activeInt &=~I2C_TIME_OUT_INT_ST; - } - - if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { - i2cIsrExit(p_i2c,EVENT_DONE,false); - return; // no more work to do - /* - // how does slave mode act? - if (p_i2c->mode == I2C_SLAVE) { // STOP detected - // empty fifo - // dispatch callback - */ - } - - if (activeInt & I2C_ARBITRATION_LOST_INT_ST_M) { //fatal - i2cIsrExit(p_i2c,EVENT_ERROR_ARBITRATION,true); - return; // no more work to do - } - - if (activeInt & I2C_SLAVE_TRAN_COMP_INT_ST_M) { - p_i2c->dev->int_clr.slave_tran_comp = 1; - // need to complete this ! - } - - if (activeInt & I2C_END_DETECT_INT_ST_M) { - p_i2c->dev->int_ena.end_detect = 0; - p_i2c->dev->int_clr.end_detect = 1; - p_i2c->dev->ctr.trans_start=0; - fillCmdQueue(p_i2c,true); // enable interrupts - p_i2c->dev->ctr.trans_start=1; // go for it - activeInt&=~I2C_END_DETECT_INT_ST_M; - } - - if(activeInt) { // clear unhandled if possible? What about Disabling interrupt? - p_i2c->dev->int_clr.val = activeInt; - log_e("unknown int=%x",activeInt); - // disable unhandled IRQ, - p_i2c->dev->int_ena.val = p_i2c->dev->int_ena.val & (~activeInt); - } - - activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened - } -} - -/* Stickbreaker added for ISR 11/2017 -functional with Silicon date=0x16042000 - */ -static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, EventGroupHandle_t event) -{ - // need to grab a MUTEX for exclusive Queue, - // what out if ISR is running? - - if(i2c==NULL) { - return I2C_ERROR_DEV; - } - - I2C_DATA_QUEUE_t dqx; - dqx.data = dataPtr; - dqx.length = dataLen; - dqx.position = 0; - dqx.cmdBytesNeeded = dataLen; - dqx.ctrl.val = 0; - dqx.ctrl.addr = i2cDeviceAddr; - dqx.ctrl.mode = mode; - dqx.ctrl.stop= sendStop; - dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address - dqx.queueLength = dataLen + dqx.ctrl.addrReq; - dqx.queueEvent = event; - - if(event) { // an eventGroup exist, so, initialize it - xEventGroupClearBits(event, EVENT_MASK); // all of them - } - - if(i2c->dq!=NULL) { // expand - //log_i("expand"); - I2C_DATA_QUEUE_t* tq =(I2C_DATA_QUEUE_t*)realloc(i2c->dq,sizeof(I2C_DATA_QUEUE_t)*(i2c->queueCount +1)); - if(tq!=NULL) { // ok - i2c->dq = tq; - memmove(&i2c->dq[i2c->queueCount++],&dqx,sizeof(I2C_DATA_QUEUE_t)); - } else { // bad stuff, unable to allocate more memory! - log_e("realloc Failure"); - return I2C_ERROR_MEMORY; - } - } else { // first Time - //log_i("new"); - i2c->queueCount=0; - i2c->dq =(I2C_DATA_QUEUE_t*)malloc(sizeof(I2C_DATA_QUEUE_t)); - if(i2c->dq!=NULL) { - memmove(&i2c->dq[i2c->queueCount++],&dqx,sizeof(I2C_DATA_QUEUE_t)); - } else { - log_e("malloc failure"); - return I2C_ERROR_MEMORY; - } - } - return I2C_ERROR_OK; -} - -i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event) -{ - return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); -} - -i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event) -{ - //10bit read is kind of weird, first you do a 0byte Write with 10bit - // address, then a ReSTART then a 7bit Read using the the upper 7bit + - // readBit. - - // this might cause an internal register pointer problem with 10bit - // devices, But, Don't have any to test agains. - // this is the Industry Standard specification. - - if((i2cDeviceAddr &0xFC00)==0x7800) { // ten bit read - i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,event); - if(err==I2C_ERROR_OK) { - return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,event); - } else { - return err; - } - } - return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event); -} -// Stickbreaker - -i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis) -{ - /* do the hard stuff here - install ISR if necessary - setup EventGroup - handle bus busy? - */ - //log_e("procQueue i2c=%p",&i2c); - if(readCount){ //total reads accomplished in all queue elements - *readCount = 0; - } - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - if (i2c->dev->status_reg.bus_busy) { // return error, let TwoWire() handle resetting the hardware. - /* if multi master then this if should be changed to this 03/12/2018 - if(multiMaster){// try to let the bus clear by its self - uint32_t timeOutTick = millis(); - while((i2c->dev->status_reg.bus_busy)&&(millis()-timeOutTickdev->status_reg.bus_busy){ // still busy, so die - */ - log_i("Bus busy, reinit"); - return I2C_ERROR_BUSY; - } - - I2C_MUTEX_LOCK(); - /* what about co-existence with SLAVE mode? - Should I check if a slaveMode xfer is in progress and hang - until it completes? - if i2c->stage == I2C_RUNNING or I2C_SLAVE_ACTIVE - */ - i2c->stage = I2C_DONE; // until ready - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - for(uint16_t i=0; inum] = 0; - intBuff[i][1][i2c->num] = 0; - intBuff[i][2][i2c->num] = 0; - } - intPos[i2c->num] = 0; -#endif - // EventGroup is used to signal transmission completion from ISR - // not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request - // if that happens, this call hangs until the timeout period expires, then it continues. - if(!i2c->i2c_event) { - i2c->i2c_event = xEventGroupCreate(); - } - if(i2c->i2c_event) { - xEventGroupClearBits(i2c->i2c_event, 0xFF); - } else { // failed to create EventGroup - log_e("eventCreate failed=%p",i2c->i2c_event); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_MEMORY; - } - - i2c_err_t reason = I2C_ERROR_OK; - i2c->mode = I2C_MASTER; - - i2c->dev->ctr.trans_start=0; // Pause Machine - i2c->dev->timeout.tout = 0xFFFFF; // max 13ms - I2C_FIFO_CONF_t f; - f.val = i2c->dev->fifo_conf.val; - f.rx_fifo_rst = 1; // fifo in reset - f.tx_fifo_rst = 1; // fifo in reset - f.nonfifo_en = 0; // use fifo mode - // need to adjust threshold based on I2C clock rate, at 100k, 30 usually works, - // sometimes the emptyRx() actually moves 31 bytes - // it hasn't overflowed yet, I cannot tell if the new byte is added while - // emptyRX() is executing or before? - f.rx_fifo_full_thrhd = 30; // 30 bytes before INT is issued - f.fifo_addr_cfg_en = 0; // no directed access - i2c->dev->fifo_conf.val = f.val; // post them all - - f.rx_fifo_rst = 0; // release fifo - f.tx_fifo_rst = 0; - i2c->dev->fifo_conf.val = f.val; // post them all - - i2c->dev->int_clr.val = 0xFFFFFFFF; // kill them All! - i2c->dev->ctr.ms_mode = 1; // master! - i2c->queuePos=0; - i2c->byteCnt=0; - uint32_t totalBytes=0; // total number of bytes to be Moved! - // convert address field to required I2C format - while(i2c->queuePos < i2c->queueCount) { // need to push these address modes upstream, to AddQueue - I2C_DATA_QUEUE_t *tdq = &i2c->dq[i2c->queuePos++]; - uint16_t taddr=0; - if(tdq->ctrl.addrReq ==2) { // 10bit address - taddr =((tdq->ctrl.addr >> 7) & 0xFE) - |tdq->ctrl.mode; - taddr = (taddr <<8) || (tdq->ctrl.addr&0xFF); - } else { // 7bit address - taddr = ((tdq->ctrl.addr<<1)&0xFE) - |tdq->ctrl.mode; - } - tdq->ctrl.addr = taddr; // all fixed with R/W bit - totalBytes += tdq->queueLength; // total number of byte to be moved! - } - i2c->queuePos=0; - - fillCmdQueue(i2c,false); // don't enable Tx/RX irq's - // start adding command[], END irq will keep it full - //Data Fifo will be filled after trans_start is issued - - i2c->exitCode=0; - i2c->stage = I2C_STARTUP; // everything configured, now start the I2C StateMachine, and - // As soon as interrupts are enabled, the ISR will start handling them. - // it should receive a TXFIFO_EMPTY immediately, even before it - // receives the TRANS_START - - i2c->dev->int_ena.val = - I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit - I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END - I2C_TIME_OUT_INT_ENA | //(BIT(8)) Trigger by SLAVE SCL stretching, NOT an ERROR - I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit - I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) counts each byte xfer'd, inc's queuePos - I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit - I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled - I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list - I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) unhandled - I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo() - I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo() - - if(!i2c->intr_handle) { // create ISR for either peripheral - // log_i("create ISR %d",i2c->num); - uint32_t ret = 0; - uint32_t flags = ESP_INTR_FLAG_EDGE | //< Edge-triggered interrupt - ESP_INTR_FLAG_IRAM | //< ISR can be called if cache is disabled - ESP_INTR_FLAG_LOWMED; //< Low and medium prio interrupts. These can be handled in C. - - if(i2c->num) { - ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x1FFF, &i2c_isr_handler_default,i2c, &i2c->intr_handle); - } else { - ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x1FFF, &i2c_isr_handler_default,i2c, &i2c->intr_handle); - } - - if(ret!=ESP_OK) { - log_e("install interrupt handler Failed=%d",ret); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_MEMORY; - } - } - - //hang until it completes. - - // how many ticks should it take to transfer totalBytes thru the I2C hardware, - // add user supplied timeOutMillis to Calc Value - - portTickType ticksTimeOut = ((totalBytes*10*1000)/(i2cGetFrequency(i2c))+timeOutMillis)/portTICK_PERIOD_MS; - - //log_e("before startup @tick=%d will wait=%d",xTaskGetTickCount(),ticksTimeOut); - - i2c->dev->ctr.trans_start=1; // go for it - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - portTickType tBefore=xTaskGetTickCount(); -#endif - - uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ticksTimeOut); - - //log_e("after WaitBits=%x @tick=%d",eBits,xTaskGetTickCount()); - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - portTickType tAfter=xTaskGetTickCount(); -#endif - - uint32_t b; - // if xEventGroupSetBitsFromISR() failed, the ISR could have succeeded but never been - // able to mark the success - - if(i2c->exitCode!=eBits) { // try to recover from O/S failure - // log_e("EventGroup Failed:%p!=%p",eBits,i2c->exitCode); - eBits=i2c->exitCode; - } - - if(!(eBits==EVENT_DONE)&&(eBits&~(EVENT_ERROR_NAK|EVENT_ERROR_DATA_NAK|EVENT_ERROR|EVENT_DONE))) { // not only Done, therefore error, exclude ADDR NAK, DATA_NAK -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - i2cDumpI2c(i2c); - i2cDumpInts(i2c->num); -#else - log_n("I2C exitCode=0x%x",eBits); -#endif - } - - if(eBits&EVENT_DONE) { // no gross timeout -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG - uint32_t expected =(totalBytes*10*1000)/i2cGetFrequency(i2c); - if((tAfter-tBefore)>(expected+1)) { //used some of the timeout Period - // expected can be zero due to small packets - log_e("TimeoutRecovery: expected=%ums, actual=%ums",expected,(tAfter-tBefore)); - i2cDumpI2c(i2c); - i2cDumpInts(i2c->num); - } -#endif - switch(i2c->error) { - case I2C_OK : - reason = I2C_ERROR_OK; - break; - case I2C_ERROR : - reason = I2C_ERROR_DEV; - break; - case I2C_ADDR_NAK: - reason = I2C_ERROR_ACK; - break; - case I2C_DATA_NAK: - reason = I2C_ERROR_ACK; - break; - case I2C_ARBITRATION: - reason = I2C_ERROR_BUS; - break; - case I2C_TIMEOUT: - reason = I2C_ERROR_TIMEOUT; - break; - default : - reason = I2C_ERROR_DEV; - } - } else { // GROSS timeout, shutdown ISR , report Timeout - i2c->stage = I2C_DONE; - i2c->dev->int_ena.val =0; - i2c->dev->int_clr.val = 0x1FFF; - if((i2c->queuePos==0)&&(i2c->byteCnt==0)) { // Bus Busy no bytes Moved - reason = I2C_ERROR_BUSY; - eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE; -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - log_e(" Busy Timeout start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); - i2cDumpI2c(i2c); - i2cDumpInts(i2c->num); -#endif - } else { // just a timeout, some data made it out or in. - reason = I2C_ERROR_TIMEOUT; - eBits = eBits | EVENT_ERROR_TIMEOUT|EVENT_ERROR|EVENT_DONE; - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - log_e(" Gross Timeout Dead start=0x%x, end=0x%x, =%d, max=%d error=%d",tBefore,tAfter,(tAfter-tBefore),ticksTimeOut,i2c->error); - i2cDumpI2c(i2c); - i2cDumpInts(i2c->num); -#endif - } - } - - // offloading all EventGroups to dispatch, EventGroups in ISR is not always successful - // 11/20/2017 - // if error, need to trigger all succeeding dataQueue events with the EVENT_ERROR_PREV - - b = 0; - - while(b < i2c->queueCount) { - if(i2c->dq[b].ctrl.mode==1 && readCount) { - *readCount += i2c->dq[b].position; // number of data bytes received - } - if(b < i2c->queuePos) { // before any error - if(i2c->dq[b].queueEvent) { // this data queue element has an EventGroup - xEventGroupSetBits(i2c->dq[b].queueEvent,EVENT_DONE); - } - } else if(b == i2c->queuePos) { // last processed queue - if(i2c->dq[b].queueEvent) { // this data queue element has an EventGroup - xEventGroupSetBits(i2c->dq[b].queueEvent,eBits); - } - } else { // never processed queues - if(i2c->dq[b].queueEvent) { // this data queue element has an EventGroup - xEventGroupSetBits(i2c->dq[b].queueEvent,eBits|EVENT_ERROR_PREV); - } - } - b++; - } - - I2C_MUTEX_UNLOCK(); - return reason; -} - -static void i2cReleaseISR(i2c_t * i2c) -{ - if(i2c->intr_handle) { - esp_intr_free(i2c->intr_handle); - i2c->intr_handle=NULL; - } -} - -static bool i2cCheckLineState(int8_t sda, int8_t scl){ - if(sda < 0 || scl < 0){ - return true;//return true since there is nothing to do - } - // if the bus is not 'clear' try the recommended recovery sequence, START, 9 Clocks, STOP - digitalWrite(sda, HIGH); - digitalWrite(scl, HIGH); - pinMode(sda, PULLUP|OPEN_DRAIN|OUTPUT|INPUT); - pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT|INPUT); - - if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state - log_w("invalid state sda=%d, scl=%d\n", digitalRead(sda), digitalRead(scl)); - digitalWrite(sda, HIGH); - digitalWrite(scl, HIGH); - delayMicroseconds(5); - digitalWrite(sda, LOW); - for(uint8_t a=0; a<9; a++) { - delayMicroseconds(5); - digitalWrite(scl, LOW); - delayMicroseconds(5); - digitalWrite(scl, HIGH); - } - delayMicroseconds(5); - digitalWrite(sda, HIGH); - } - - if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state - log_e("Bus Invalid State, TwoWire() Can't init"); - return false; // bus is busy - } - return true; -} - -i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - digitalWrite(scl, HIGH); - - //- { ODROID-GO QWERTY - if(scl == 12) - pinMode(scl, INPUT | OUTPUT); - else - pinMode(scl, OPEN_DRAIN | PULLUP | INPUT | OUTPUT); - //- } - - pinMatrixOutAttach(scl, I2C_SCL_IDX(i2c->num), false, false); - pinMatrixInAttach(scl, I2C_SCL_IDX(i2c->num), false); - return I2C_ERROR_OK; -} - -i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - pinMatrixOutDetach(scl, false, false); - pinMatrixInDetach(I2C_SCL_IDX(i2c->num), false, false); - pinMode(scl, INPUT | PULLUP); - return I2C_ERROR_OK; -} - -i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - digitalWrite(sda, HIGH); - pinMode(sda, OPEN_DRAIN | PULLUP | INPUT | OUTPUT ); - pinMatrixOutAttach(sda, I2C_SDA_IDX(i2c->num), false, false); - pinMatrixInAttach(sda, I2C_SDA_IDX(i2c->num), false); - return I2C_ERROR_OK; -} - -i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - pinMatrixOutDetach(sda, false, false); - pinMatrixInDetach(I2C_SDA_IDX(i2c->num), false, false); - pinMode(sda, INPUT | PULLUP); - return I2C_ERROR_OK; -} - -/* - * PUBLIC API - * */ -// 24Nov17 only supports Master Mode -i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //before this is called, pins should be detached, else glitch -{ - if(i2c_num > 1) { - return NULL; - } - - i2c_t * i2c = &_i2c_bus_array[i2c_num]; - - if(i2c->sda >= 0){ - i2cDetachSDA(i2c, i2c->sda); - } - if(i2c->scl >= 0){ - i2cDetachSCL(i2c, i2c->scl); - } - i2c->sda = sda; - i2c->scl = scl; - -#if !CONFIG_DISABLE_HAL_LOCKS - if(i2c->lock == NULL) { - i2c->lock = xSemaphoreCreateMutex(); - if(i2c->lock == NULL) { - return NULL; - } - } -#endif - I2C_MUTEX_LOCK(); - - i2cReleaseISR(i2c); // ISR exists, release it before disabling hardware - - if(frequency == 0) {// don't change existing frequency - frequency = i2cGetFrequency(i2c); - if(frequency == 0) { - frequency = 100000L; // default to 100khz - } - } - - if(i2c_num == 0) { - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); //reset hardware - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST);// release reset - } else { - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); //reset Hardware - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); - } - i2c->dev->ctr.val = 0; - i2c->dev->ctr.ms_mode = 1; - i2c->dev->ctr.sda_force_out = 1 ; - i2c->dev->ctr.scl_force_out = 1 ; - i2c->dev->ctr.clk_en = 1; - - //the max clock number of receiving a data - i2c->dev->timeout.tout = 400000;//clocks max=1048575 - //disable apb nonfifo access - i2c->dev->fifo_conf.nonfifo_en = 0; - - i2c->dev->slave_addr.val = 0; - I2C_MUTEX_UNLOCK(); - - i2cSetFrequency(i2c, frequency); // reconfigure - - if(!i2cCheckLineState(i2c->sda, i2c->scl)){ - return NULL; - } - - if(i2c->sda >= 0){ - i2cAttachSDA(i2c, i2c->sda); - } - if(i2c->scl >= 0){ - i2cAttachSCL(i2c, i2c->scl); - } - return i2c; -} - -void i2cRelease(i2c_t *i2c) // release all resources, power down peripheral -{ - I2C_MUTEX_LOCK(); - - if(i2c->sda >= 0){ - i2cDetachSDA(i2c, i2c->sda); - } - if(i2c->scl >= 0){ - i2cDetachSCL(i2c, i2c->scl); - } - - i2cReleaseISR(i2c); - - if(i2c->i2c_event) { - vEventGroupDelete(i2c->i2c_event); - i2c->i2c_event = NULL; - } - - i2cFlush(i2c); - - // reset the I2C hardware and shut off the clock, power it down. - if(i2c->num == 0) { - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); //reset hardware - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); // shutdown hardware - } else { - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); //reset Hardware - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); // shutdown Hardware - } - - I2C_MUTEX_UNLOCK(); -} - -i2c_err_t i2cFlush(i2c_t * i2c) -{ - if(i2c==NULL) { - return I2C_ERROR_DEV; - } - // need to grab a MUTEX for exclusive Queue, - // what out if ISR is running? - i2c_err_t rc=I2C_ERROR_OK; - if(i2c->dq!=NULL) { - // log_i("free"); - // what about EventHandle? - free(i2c->dq); - i2c->dq = NULL; - } - i2c->queueCount=0; - i2c->queuePos=0; - // release Mutex - return rc; -} - -i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis){ - i2c_err_t last_error = i2cAddQueueWrite(i2c, address, buff, size, sendStop, NULL); - - if(last_error == I2C_ERROR_OK) { //queued - if(sendStop) { //now actually process the queued commands, including READs - last_error = i2cProcQueue(i2c, NULL, timeOutMillis); - if(last_error == I2C_ERROR_BUSY) { // try to clear the bus - if(i2cInit(i2c->num, i2c->sda, i2c->scl, 0)) { - last_error = i2cProcQueue(i2c, NULL, timeOutMillis); - } - } - i2cFlush(i2c); - } else { // stop not received, so wait for I2C stop, - last_error = I2C_ERROR_CONTINUE; - } - } - return last_error; -} - -i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount){ - i2c_err_t last_error=i2cAddQueueRead(i2c, address, buff, size, sendStop, NULL); - - if(last_error == I2C_ERROR_OK) { //queued - if(sendStop) { //now actually process the queued commands, including READs - last_error = i2cProcQueue(i2c, readCount, timeOutMillis); - if(last_error == I2C_ERROR_BUSY) { // try to clear the bus - if(i2cInit(i2c->num, i2c->sda, i2c->scl, 0)) { - last_error = i2cProcQueue(i2c, readCount, timeOutMillis); - } - } - i2cFlush(i2c); - } else { // stop not received, so wait for I2C stop, - last_error = I2C_ERROR_CONTINUE; - } - } - return last_error; -} - -i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - - uint32_t period = (APB_CLK_FREQ/clk_speed) / 2; - uint32_t halfPeriod = period/2; - uint32_t quarterPeriod = period/4; - - I2C_MUTEX_LOCK(); - //the clock num during SCL is low level - i2c->dev->scl_low_period.period = period; - //the clock num during SCL is high level - i2c->dev->scl_high_period.period = period; - - //the clock num between the negedge of SDA and negedge of SCL for start mark - i2c->dev->scl_start_hold.time = halfPeriod; - //the clock num between the posedge of SCL and the negedge of SDA for restart mark - i2c->dev->scl_rstart_setup.time = halfPeriod; - - //the clock num after the STOP bit's posedge - i2c->dev->scl_stop_hold.time = halfPeriod; - //the clock num between the posedge of SCL and the posedge of SDA - i2c->dev->scl_stop_setup.time = halfPeriod; - - //the clock num I2C used to hold the data after the negedge of SCL. - i2c->dev->sda_hold.time = quarterPeriod; - //the clock num I2C used to sample data on SDA after the posedge of SCL - i2c->dev->sda_sample.time = quarterPeriod; - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_OK; -} - -uint32_t i2cGetFrequency(i2c_t * i2c) -{ - if(i2c == NULL) { - return 0; - } - uint32_t result = 0; - uint32_t old_count = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); - if(old_count>0) { - result = APB_CLK_FREQ / old_count; - } else { - result = 0; - } - return result; -} - - -/* Stickbreaker ISR mode debug support - */ -void i2cDumpDqData(i2c_t * i2c) -{ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR - uint16_t a=0; - char buff[140]; - I2C_DATA_QUEUE_t *tdq; - while(aqueueCount) { - tdq=&i2c->dq[a]; - log_e("[%d] %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a,tdq->ctrl.addr,(tdq->ctrl.mode)?'R':'W',(tdq->ctrl.stop)?"STOP":"",tdq->data,tdq->length,tdq->position,tdq->queueEvent,(tdq->queueEvent)?xEventGroupGetBits(tdq->queueEvent):0); - uint16_t offset = 0; - while(offsetlength) { - memset(buff,' ',140); - buff[139]='\0'; - uint16_t i = 0,j; - j=sprintf(buff,"0x%04x: ",offset); - while((i<32)&&(offset < tdq->length)) { - char ch = tdq->data[offset]; - sprintf((char*)&buff[(i*3)+41],"%02x ",ch); - if((ch<32)||(ch>126)) { - ch='.'; - } - j+=sprintf((char*)&buff[j],"%c",ch); - buff[j]=' '; - i++; - offset++; - } - log_e("%s",buff); - } - a++; - } -#else - log_n("Enable Core Debug Level \"Error\""); -#endif -} - -void i2cDumpI2c(i2c_t * i2c) -{ - log_e("i2c=%p",i2c); - log_e("dev=%p date=%p",i2c->dev,i2c->dev->date); -#if !CONFIG_DISABLE_HAL_LOCKS - log_e("lock=%p",i2c->lock); -#endif - log_e("num=%d",i2c->num); - log_e("mode=%d",i2c->mode); - log_e("stage=%d",i2c->stage); - log_e("error=%d",i2c->error); - log_e("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0); - log_e("intr_handle=%p",i2c->intr_handle); - log_e("dq=%p",i2c->dq); - log_e("queueCount=%d",i2c->queueCount); - log_e("queuePos=%d",i2c->queuePos); - log_e("byteCnt=%d",i2c->byteCnt); - if(i2c->dq) { - i2cDumpDqData(i2c); - } -} - -void i2cDumpInts(uint8_t num) -{ -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO - uint32_t b; - log_e("%u row count INTR TX RX",num); - for(uint32_t a=1; a<=INTBUFFMAX; a++) { - b=(a+intPos[num])%INTBUFFMAX; - if(intBuff[b][0][num]!=0) { - log_e("[%02d] 0x%04x 0x%04x 0x%04x 0x%04x 0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]); - } - } -#else - log_n("enable Core Debug Level \"Error\""); -#endif -} - -/* todo - 24Nov17 - Need to think about not usings I2C_MASTER_TRAN_COMP_INT_ST to adjust queuePos. This - INT triggers every byte. The only reason to know which byte is being transfered is - the status_reg.tx_fifo_cnt and a .txQueued to do this in the fillRxFifo(). The - same mechanism could work if an error occured in i2cErrorExit(). - */ - +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-i2c.h" +#include "esp32-hal.h" +#if !CONFIG_DISABLE_HAL_LOCKS +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#endif +#include "esp_attr.h" +#include "esp_system.h" +#include "soc/soc_caps.h" +#include "soc/i2c_periph.h" +#include "hal/i2c_hal.h" +#include "hal/i2c_ll.h" +#include "driver/i2c.h" + +typedef volatile struct { + bool initialized; + uint32_t frequency; +#if !CONFIG_DISABLE_HAL_LOCKS + SemaphoreHandle_t lock; +#endif +} i2c_bus_t; + +static i2c_bus_t bus[SOC_I2C_NUM]; + +bool i2cIsInit(uint8_t i2c_num){ + if(i2c_num >= SOC_I2C_NUM){ + return false; + } + return bus[i2c_num].initialized; +} + +esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency){ + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + if(bus[i2c_num].lock == NULL){ + bus[i2c_num].lock = xSemaphoreCreateMutex(); + if(bus[i2c_num].lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return ESP_ERR_NO_MEM; + } + } + //acquire lock + if(xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ESP_FAIL; + } +#endif + if(bus[i2c_num].initialized){ + log_e("bus is already initialized"); + return ESP_FAIL; + } + + if(!frequency){ + frequency = 100000UL; + } else if(frequency > 1000000UL){ + frequency = 1000000UL; + } + log_i("Initialising I2C Master: sda=%d scl=%d freq=%d", sda, scl, frequency); + + i2c_config_t conf = { }; + conf.mode = I2C_MODE_MASTER; + conf.scl_io_num = (gpio_num_t)scl; + conf.sda_io_num = (gpio_num_t)sda; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = frequency; + conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; //Any one clock source that is available for the specified frequency may be choosen + + esp_err_t ret = i2c_param_config((i2c_port_t)i2c_num, &conf); + if (ret != ESP_OK) { + log_e("i2c_param_config failed"); + } else { + ret = i2c_driver_install((i2c_port_t)i2c_num, conf.mode, 0, 0, 0); + if (ret != ESP_OK) { + log_e("i2c_driver_install failed"); + } else { + bus[i2c_num].initialized = true; + bus[i2c_num].frequency = frequency; + //Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2 + i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT); + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cDeinit(uint8_t i2c_num){ + esp_err_t err = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return err; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + } else { + err = i2c_driver_delete((i2c_port_t)i2c_num); + if(err == ESP_OK){ + bus[i2c_num].initialized = false; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return err; +} + +esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t* buff, size_t size, uint32_t timeOutMillis){ + esp_err_t ret = ESP_FAIL; + i2c_cmd_handle_t cmd = NULL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + goto end; + } + + //short implementation does not support zero size writes (example when scanning) PR in IDF? + //ret = i2c_master_write_to_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_RATE_MS); + + ret = ESP_OK; + uint8_t cmd_buff[I2C_LINK_RECOMMENDED_SIZE(1)] = { 0 }; + cmd = i2c_cmd_link_create_static(cmd_buff, I2C_LINK_RECOMMENDED_SIZE(1)); + ret = i2c_master_start(cmd); + if (ret != ESP_OK) { + goto end; + } + ret = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); + if (ret != ESP_OK) { + goto end; + } + if(size){ + ret = i2c_master_write(cmd, buff, size, true); + if (ret != ESP_OK) { + goto end; + } + } + ret = i2c_master_stop(cmd); + if (ret != ESP_OK) { + goto end; + } + ret = i2c_master_cmd_begin((i2c_port_t)i2c_num, cmd, timeOutMillis / portTICK_RATE_MS); + +end: + if(cmd != NULL){ + i2c_cmd_link_delete_static(cmd); + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t* buff, size_t size, uint32_t timeOutMillis, size_t *readCount){ + esp_err_t ret = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + } else { + ret = i2c_master_read_from_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_RATE_MS); + if(ret == ESP_OK){ + *readCount = size; + } else { + *readCount = 0; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cWriteReadNonStop(uint8_t i2c_num, uint16_t address, const uint8_t* wbuff, size_t wsize, uint8_t* rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount){ + esp_err_t ret = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + } else { + ret = i2c_master_write_read_device((i2c_port_t)i2c_num, address, wbuff, wsize, rbuff, rsize, timeOutMillis / portTICK_RATE_MS); + if(ret == ESP_OK){ + *readCount = rsize; + } else { + *readCount = 0; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency){ + esp_err_t ret = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + goto end; + } + if(bus[i2c_num].frequency == frequency){ + ret = ESP_OK; + goto end; + } + if(!frequency){ + frequency = 100000UL; + } else if(frequency > 1000000UL){ + frequency = 1000000UL; + } + // Freq limitation when using different clock sources + #define I2C_CLK_LIMIT_REF_TICK (1 * 1000 * 1000 / 20) /*!< Limited by REF_TICK, no more than REF_TICK/20*/ + #define I2C_CLK_LIMIT_APB (80 * 1000 * 1000 / 20) /*!< Limited by APB, no more than APB/20*/ + #define I2C_CLK_LIMIT_RTC (20 * 1000 * 1000 / 20) /*!< Limited by RTC, no more than RTC/20*/ + #define I2C_CLK_LIMIT_XTAL (40 * 1000 * 1000 / 20) /*!< Limited by RTC, no more than XTAL/20*/ + + typedef struct { + uint8_t character; /*!< I2C source clock characteristic */ + uint32_t clk_freq; /*!< I2C source clock frequency */ + } i2c_clk_alloc_t; + + // i2c clock characteristic, The order is the same as i2c_sclk_t. + static i2c_clk_alloc_t i2c_clk_alloc[I2C_SCLK_MAX] = { + {0, 0}, + #if SOC_I2C_SUPPORT_APB + {0, I2C_CLK_LIMIT_APB}, /*!< I2C APB clock characteristic*/ + #endif + #if SOC_I2C_SUPPORT_XTAL + {0, I2C_CLK_LIMIT_XTAL}, /*!< I2C XTAL characteristic*/ + #endif + #if SOC_I2C_SUPPORT_RTC + {I2C_SCLK_SRC_FLAG_LIGHT_SLEEP | I2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_CLK_LIMIT_RTC}, /*!< I2C 20M RTC characteristic*/ + #endif + #if SOC_I2C_SUPPORT_REF_TICK + {I2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_CLK_LIMIT_REF_TICK}, /*!< I2C REF_TICK characteristic*/ + #endif + }; + + i2c_sclk_t src_clk = I2C_SCLK_DEFAULT; + ret = ESP_OK; + for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) { +#if CONFIG_IDF_TARGET_ESP32S3 + if (clk == I2C_SCLK_RTC) { // RTC clock for s3 is unaccessable now. + continue; + } +#endif + if (frequency <= i2c_clk_alloc[clk].clk_freq) { + src_clk = clk; + break; + } + } + if(src_clk == I2C_SCLK_MAX){ + log_e("clock source could not be selected"); + ret = ESP_FAIL; + } else { + i2c_hal_context_t hal; + hal.dev = I2C_LL_GET_HW(i2c_num); + i2c_hal_set_bus_timing(&(hal), frequency, src_clk); + bus[i2c_num].frequency = frequency; + //Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2 + i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT); + } + +end: +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t * frequency){ + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + return ESP_FAIL; + } + *frequency = bus[i2c_num].frequency; + return ESP_OK; +} diff --git a/src/sensors/esp32-hal-i2c.h b/src/sensors/esp32-hal-i2c.h index 580986e..3fa889f 100644 --- a/src/sensors/esp32-hal-i2c.h +++ b/src/sensors/esp32-hal-i2c.h @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // modified Nov 2017 by Chuck Todd to support Interrupt Driven I/O +// modified Nov 2021 by Hristo Gochkov to support ESP-IDF API #ifndef _ESP32_HAL_I2C_H_ #define _ESP32_HAL_I2C_H_ @@ -22,48 +23,16 @@ extern "C" { #include #include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" - -// External Wire.h equivalent error Codes -typedef enum { - I2C_ERROR_OK=0, - I2C_ERROR_DEV, - I2C_ERROR_ACK, - I2C_ERROR_TIMEOUT, - I2C_ERROR_BUS, - I2C_ERROR_BUSY, - I2C_ERROR_MEMORY, - I2C_ERROR_CONTINUE, - I2C_ERROR_NO_BEGIN -} i2c_err_t; - -struct i2c_struct_t; -typedef struct i2c_struct_t i2c_t; - -i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed); -void i2cRelease(i2c_t *i2c); // free ISR, Free DQ, Power off peripheral clock. Must call i2cInit() to recover -i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis); -i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount); -i2c_err_t i2cFlush(i2c_t *i2c); -i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed); -uint32_t i2cGetFrequency(i2c_t * i2c); - -//Functions below should be used only if well understood -//Might be deprecated and removed in future -i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl); -i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl); -i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda); -i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda); - -//Stickbreakers ISR Support -i2c_err_t i2cProcQueue(i2c_t *i2c, uint32_t *readCount, uint16_t timeOutMillis); -i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); -i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event); - -//stickbreaker debug support -void i2cDumpInts(uint8_t num); -void i2cDumpI2c(i2c_t *i2c); +#include + +esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed); +esp_err_t i2cDeinit(uint8_t i2c_num); +esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency); +esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t * frequency); +esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t* buff, size_t size, uint32_t timeOutMillis); +esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t* buff, size_t size, uint32_t timeOutMillis, size_t *readCount); +esp_err_t i2cWriteReadNonStop(uint8_t i2c_num, uint16_t address, const uint8_t* wbuff, size_t wsize, uint8_t* rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount); +bool i2cIsInit(uint8_t i2c_num); #ifdef __cplusplus } diff --git a/src/sensors/esp32-hal-log.h b/src/sensors/esp32-hal-log.h index 126ebf6..3198387 100644 --- a/src/sensors/esp32-hal-log.h +++ b/src/sensors/esp32-hal-log.h @@ -20,6 +20,7 @@ extern "C" #endif #include "sdkconfig.h" +#include "esp_timer.h" #define ARDUHAL_LOG_LEVEL_NONE (0) #define ARDUHAL_LOG_LEVEL_ERROR (1) @@ -36,6 +37,11 @@ extern "C" #define ARDUHAL_LOG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL #else #define ARDUHAL_LOG_LEVEL CORE_DEBUG_LEVEL +#ifdef USE_ESP_IDF_LOG +#ifndef LOG_LOCAL_LEVEL +#define LOG_LOCAL_LEVEL CORE_DEBUG_LEVEL +#endif +#endif #endif #ifndef CONFIG_ARDUHAL_LOG_COLORS @@ -62,6 +68,8 @@ extern "C" #define ARDUHAL_LOG_COLOR_I ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GREEN) #define ARDUHAL_LOG_COLOR_D ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_CYAN) #define ARDUHAL_LOG_COLOR_V ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GRAY) +#define ARDUHAL_LOG_COLOR_PRINT(letter) log_printf(ARDUHAL_LOG_COLOR_ ## letter) +#define ARDUHAL_LOG_COLOR_PRINT_END log_printf(ARDUHAL_LOG_RESET_COLOR) #else #define ARDUHAL_LOG_COLOR_E #define ARDUHAL_LOG_COLOR_W @@ -69,64 +77,146 @@ extern "C" #define ARDUHAL_LOG_COLOR_D #define ARDUHAL_LOG_COLOR_V #define ARDUHAL_LOG_RESET_COLOR +#define ARDUHAL_LOG_COLOR_PRINT(letter) +#define ARDUHAL_LOG_COLOR_PRINT_END #endif + + const char * pathToFileName(const char * path); int log_printf(const char *fmt, ...); +void log_print_buf(const uint8_t *b, size_t len); #define ARDUHAL_SHORT_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter format ARDUHAL_LOG_RESET_COLOR "\r\n" -#define ARDUHAL_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter "[" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", pathToFileName(__FILE__), __LINE__, __FUNCTION__ +#define ARDUHAL_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter "[%6u][" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", (unsigned long) (esp_timer_get_time() / 1000ULL), pathToFileName(__FILE__), __LINE__, __FUNCTION__ #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE +#ifndef USE_ESP_IDF_LOG #define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#define log_buf_v(b,l) do{ARDUHAL_LOG_COLOR_PRINT(V);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_v(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_v(format, ...) do {ets_printf(LOG_FORMAT(V, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_v(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_VERBOSE);}while(0) +#endif #else -#define log_v(format, ...) +#define log_v(format, ...) do {} while(0) +#define isr_log_v(format, ...) do {} while(0) +#define log_buf_v(b,l) do {} while(0) #endif #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG +#ifndef USE_ESP_IDF_LOG #define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#define log_buf_d(b,l) do{ARDUHAL_LOG_COLOR_PRINT(D);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_d(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_d(format, ...) do {ets_printf(LOG_FORMAT(D, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_d(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_DEBUG);}while(0) +#endif #else -#define log_d(format, ...) +#define log_d(format, ...) do {} while(0) +#define isr_log_d(format, ...) do {} while(0) +#define log_buf_d(b,l) do {} while(0) #endif #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +#ifndef USE_ESP_IDF_LOG #define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#define log_buf_i(b,l) do{ARDUHAL_LOG_COLOR_PRINT(I);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_i(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_i(format, ...) do {ets_printf(LOG_FORMAT(I, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_i(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_INFO);}while(0) +#endif #else -#define log_i(format, ...) +#define log_i(format, ...) do {} while(0) +#define isr_log_i(format, ...) do {} while(0) +#define log_buf_i(b,l) do {} while(0) #endif #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN +#ifndef USE_ESP_IDF_LOG #define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#define log_buf_w(b,l) do{ARDUHAL_LOG_COLOR_PRINT(W);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) #else -#define log_w(format, ...) +#define log_w(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_w(format, ...) do {ets_printf(LOG_FORMAT(W, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_w(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_WARN);}while(0) +#endif +#else +#define log_w(format, ...) do {} while(0) +#define isr_log_w(format, ...) do {} while(0) +#define log_buf_w(b,l) do {} while(0) #endif #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#ifndef USE_ESP_IDF_LOG #define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define log_buf_e(b,l) do{ARDUHAL_LOG_COLOR_PRINT(E);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) #else -#define log_e(format, ...) +#define log_e(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_e(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_e(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_ERROR);}while(0) +#endif +#else +#define log_e(format, ...) do {} while(0) +#define isr_log_e(format, ...) do {} while(0) +#define log_buf_e(b,l) do {} while(0) #endif #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE +#ifndef USE_ESP_IDF_LOG #define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define log_buf_n(b,l) do{ARDUHAL_LOG_COLOR_PRINT(E);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) #else -#define log_n(format, ...) +#define log_n(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_n(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_n(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_ERROR);}while(0) +#endif +#else +#define log_n(format, ...) do {} while(0) +#define isr_log_n(format, ...) do {} while(0) +#define log_buf_n(b,l) do {} while(0) #endif #include "esp_log.h" +#ifdef USE_ESP_IDF_LOG +//#ifndef TAG +//#define TAG "ARDUINO" +//#endif +//#define log_n(format, ...) myLog(ESP_LOG_NONE, format, ##__VA_ARGS__) +#else #ifdef CONFIG_ARDUHAL_ESP_LOG #undef ESP_LOGE #undef ESP_LOGW #undef ESP_LOGI #undef ESP_LOGD #undef ESP_LOGV - -#define ESP_LOGE(tag, ...) log_e(__VA_ARGS__) -#define ESP_LOGW(tag, ...) log_w(__VA_ARGS__) -#define ESP_LOGI(tag, ...) log_i(__VA_ARGS__) -#define ESP_LOGD(tag, ...) log_d(__VA_ARGS__) -#define ESP_LOGV(tag, ...) log_v(__VA_ARGS__) +#undef ESP_EARLY_LOGE +#undef ESP_EARLY_LOGW +#undef ESP_EARLY_LOGI +#undef ESP_EARLY_LOGD +#undef ESP_EARLY_LOGV + +#define ESP_LOGE(tag, format, ...) log_e("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGW(tag, format, ...) log_w("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGI(tag, format, ...) log_i("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGD(tag, format, ...) log_d("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGV(tag, format, ...) log_v("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGE(tag, format, ...) isr_log_e("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGW(tag, format, ...) isr_log_w("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGI(tag, format, ...) isr_log_i("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGD(tag, format, ...) isr_log_d("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGV(tag, format, ...) isr_log_v("[%s] " format, tag, ##__VA_ARGS__) +#endif #endif #ifdef __cplusplus diff --git a/src/web/WebSockets.cpp b/src/web/WebSockets.cpp index 44ddbb1..83f76a7 100644 --- a/src/web/WebSockets.cpp +++ b/src/web/WebSockets.cpp @@ -39,7 +39,11 @@ extern "C" { #ifdef ESP8266 #include #elif defined(ESP32) -#include + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (4, 4, 0) + #include + #else + #include + #endif #else extern "C" {