diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index 1e03627feaa..18ef4c115e7 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -14,1915 +14,324 @@ #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" -#include "freertos/event_groups.h" -#include "driver/periph_ctrl.h" -#include "soc/i2c_reg.h" -#include "soc/i2c_struct.h" +#endif #include "esp_attr.h" -#include "esp32-hal-cpu.h" // cpu clock change support 31DEC2018 - #include "esp_system.h" -#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 -#include "soc/dport_reg.h" -#include "esp32/rom/ets_sys.h" -#elif CONFIG_IDF_TARGET_ESP32S2 -#include "soc/dport_reg.h" -#include "esp32s2/rom/ets_sys.h" -#elif CONFIG_IDF_TARGET_ESP32C3 -#include "esp32c3/rom/ets_sys.h" -#else -#error Target CONFIG_IDF_TARGET is not supported -#endif -#else // ESP32 Before IDF 4.0 -#include "rom/ets_sys.h" -#endif - - -#if CONFIG_IDF_TARGET_ESP32 -//#define I2C_DEV(i) (volatile i2c_dev_t *)((i)?DR_REG_I2C1_EXT_BASE:DR_REG_I2C_EXT_BASE) -//#define I2C_DEV(i) ((i2c_dev_t *)(REG_I2C_BASE(i))) -#define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0)) -#define I2C_SDA_IDX(p) ((p==0)?I2CEXT0_SDA_OUT_IDX:((p==1)?I2CEXT1_SDA_OUT_IDX:0)) - -#define DR_REG_I2C_EXT_BASE_FIXED 0x60013000 -#define DR_REG_I2C1_EXT_BASE_FIXED 0x60027000 - -/* Stickbreaker ISR mode debug support - -ENABLE_I2C_DEBUG_BUFFER - Enable debug interrupt history buffer, fifoTx history buffer. - Setting this define will result in 2570 bytes of RAM being used whenever CORE_DEBUG_LEVEL - is higher than WARNING. Unless you are debugging a problem in the I2C subsystem, - I would recommend you leave it commented out. - */ - -//#define ENABLE_I2C_DEBUG_BUFFER - -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) -#define INTBUFFMAX 64 -#define FIFOMAX 512 -static uint32_t intBuff[INTBUFFMAX][3][2]; -static uint32_t intPos[2]= {0,0}; -static uint16_t fifoBuffer[FIFOMAX]; -static uint16_t fifoPos = 0; -#endif - -// start from tools/sdk/include/soc/soc/i2c_struct.h - -typedef union { - struct { - uint32_t byte_num: 8; /*Byte_num represent the number of data need to be send or data need to be received.*/ - uint32_t ack_en: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ - uint32_t ack_exp: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ - uint32_t ack_val: 1; /*ack_check_en ack_exp and ack value are used to control the ack bit.*/ - uint32_t op_code: 3; /*op_code is the command 0:RSTART 1:WRITE 2:READ 3:STOP . 4:END.*/ - uint32_t reserved14: 17; - uint32_t done: 1; /*When command0 is done in I2C Master mode this bit changes to high level.*/ - }; - uint32_t val; -} I2C_COMMAND_t; - -typedef union { - struct { - uint32_t rx_fifo_full_thrhd: 5; - uint32_t tx_fifo_empty_thrhd:5; //Config tx_fifo empty threhd value when using apb fifo access * / - uint32_t nonfifo_en: 1; //Set this bit to enble apb nonfifo access. * / - uint32_t fifo_addr_cfg_en: 1; //When this bit is set to 1 then the byte after address represent the offset address of I2C Slave's ram. * / - uint32_t rx_fifo_rst: 1; //Set this bit to reset rx fifo when using apb fifo access. * / - // chuck while this bit is 1, the RX fifo is held in REST, Toggle it * / - uint32_t tx_fifo_rst: 1; //Set this bit to reset tx fifo when using apb fifo access. * / - // chuck while this bit is 1, the TX fifo is held in REST, Toggle it * / - uint32_t nonfifo_rx_thres: 6; //when I2C receives more than nonfifo_rx_thres data it will produce rx_send_full_int_raw interrupt and update the current offset address of the receiving data.* / - uint32_t nonfifo_tx_thres: 6; //when I2C sends more than nonfifo_tx_thres data it will produce tx_send_empty_int_raw interrupt and update the current offset address of the sending data. * / - uint32_t reserved26: 6; - }; - uint32_t val; -} I2C_FIFO_CONF_t; - -typedef union { - struct { - uint32_t rx_fifo_start_addr: 5; /*This is the offset address of the last receiving data as described in nonfifo_rx_thres_register.*/ - uint32_t rx_fifo_end_addr: 5; /*This is the offset address of the first receiving data as described in nonfifo_rx_thres_register.*/ - uint32_t tx_fifo_start_addr: 5; /*This is the offset address of the first sending data as described in nonfifo_tx_thres register.*/ - uint32_t tx_fifo_end_addr: 5; /*This is the offset address of the last sending data as described in nonfifo_tx_thres register.*/ - uint32_t reserved20: 12; - }; - uint32_t val; - } I2C_FIFO_ST_t; - -// end from tools/sdk/include/soc/soc/i2c_struct.h - -// sync between dispatch(i2cProcQueue) and worker(i2c_isr_handler_default) -typedef enum { - //I2C_NONE=0, - I2C_STARTUP=1, - I2C_RUNNING, - I2C_DONE -} I2C_STAGE_t; - -typedef enum { - I2C_NONE=0, - I2C_MASTER, - I2C_SLAVE, - I2C_MASTERSLAVE -} I2C_MODE_t; - -// internal Error condition -typedef enum { - // I2C_NONE=0, - I2C_OK=1, - I2C_ERROR, - I2C_ADDR_NAK, - I2C_DATA_NAK, - I2C_ARBITRATION, - I2C_TIMEOUT -} I2C_ERROR_t; - -// i2c_event bits for EVENTGROUP bits -// needed to minimize change events, FreeRTOS Daemon overload, so ISR will only set values -// on Exit. Dispatcher will set bits for each dq before/after ISR completion -#define EVENT_ERROR_NAK (BIT(0)) -#define EVENT_ERROR (BIT(1)) -#define EVENT_ERROR_BUS_BUSY (BIT(2)) -#define EVENT_RUNNING (BIT(3)) -#define EVENT_DONE (BIT(4)) -#define EVENT_IN_END (BIT(5)) -#define EVENT_ERROR_PREV (BIT(6)) -#define EVENT_ERROR_TIMEOUT (BIT(7)) -#define EVENT_ERROR_ARBITRATION (BIT(8)) -#define EVENT_ERROR_DATA_NAK (BIT(9)) -#define EVENT_MASK 0x3F - -// control record for each dq entry -typedef union { - struct { - uint32_t addr: 16; // I2C address, if 10bit must have 0x7800 mask applied, else 8bit - uint32_t mode: 1; // transaction direction 0 write, 1 read - uint32_t stop: 1; // sendStop 0 no, 1 yes - uint32_t startCmdSent: 1; // START cmd has been added to command[] - uint32_t addrCmdSent: 1; // addr WRITE cmd has been added to command[] - uint32_t dataCmdSent: 1; // all necessary DATA(READ/WRITE) cmds added to command[] - uint32_t stopCmdSent: 1; // completed all necessary commands - uint32_t addrReq: 2; // number of addr bytes need to send address - uint32_t addrSent: 2; // number of addr bytes added to FIFO - uint32_t reserved_31: 6; - }; - uint32_t val; -} I2C_DATA_CTRL_t; - -// individual dq element -typedef struct { - uint8_t *data; // data pointer for read/write buffer - uint16_t length; // size of data buffer - uint16_t position; // current position for next char in buffer (lock, portMAX_DELAY) != pdPASS) -#define I2C_MUTEX_UNLOCK() xSemaphoreGiveRecursive(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,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,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. - * */ +} i2c_bus_t; +static i2c_bus_t bus[SOC_I2C_NUM]; -/* Stickbreaker ISR mode debug support - */ -static void ARDUINO_ISR_ATTR i2cDumpCmdQueue(i2c_t *i2c) -{ -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR)&&(defined ENABLE_I2C_DEBUG_BUFFER) - static const char * const cmdName[] ={"RSTART","WRITE","READ","STOP","END"}; - uint8_t i=0; - while(i<16) { - I2C_COMMAND_t c; - c.val=i2c->dev->command[i].val; - log_e("[%2d]\t%c\t%s\tval[%d]\texp[%d]\ten[%d]\tbytes[%d]",i,(c.done?'Y':'N'), - cmdName[c.op_code], - c.ack_val, - c.ack_exp, - c.ack_en, - c.byte_num); - i++; +bool i2cIsInit(uint8_t i2c_num){ + if(i2c_num >= SOC_I2C_NUM){ + return false; } -#endif + return bus[i2c_num].initialized; } -/* Stickbreaker ISR mode debug support - */ -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) -static void i2cDumpDqData(i2c_t * i2c) -{ -#if defined (ENABLE_I2C_DEBUG_BUFFER) - uint16_t a=0; - char buff[140]; - I2C_DATA_QUEUE_t *tdq; - int digits=0,lenDigits=0; - a = i2c->queueCount; - while(a>0) { - digits++; - a /= 10; - } - while(aqueueCount) { // find maximum number of len decimal digits for formatting - if (i2c->dq[a].length > lenDigits ) lenDigits = i2c->dq[a].length; - a++; - } - a=0; - while(lenDigits>0){ - a++; - lenDigits /= 10; +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; } - lenDigits = a; - a = 0; - while(aqueueCount) { - tdq=&i2c->dq[a]; - char buf1[10],buf2[10]; - sprintf(buf1,"%0*d",lenDigits,tdq->length); - sprintf(buf2,"%0*d",lenDigits,tdq->position); - log_i("[%0*d] %sbit %x %c %s buf@=%p, len=%s, pos=%s, ctrl=%d%d%d%d%d",digits,a, - (tdq->ctrl.addr>0x100)?"10":"7", - (tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1), - (tdq->ctrl.mode)?'R':'W', - (tdq->ctrl.stop)?"STOP":"", - tdq->data, - buf1,buf2, - tdq->ctrl.startCmdSent,tdq->ctrl.addrCmdSent,tdq->ctrl.dataCmdSent,(tdq->ctrl.stop)?tdq->ctrl.stopCmdSent:0,tdq->ctrl.addrSent - ); - 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_i("%s",buff); - } - a++; - } -#else - log_i("Debug Buffer not Enabled"); -#endif -} -#endif -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO -static void i2cDumpI2c(i2c_t * i2c) -{ - log_e("i2c=%p",i2c); - log_i("dev=%p date=%p",i2c->dev,i2c->dev->date); #if !CONFIG_DISABLE_HAL_LOCKS - log_i("lock=%p",i2c->lock); -#endif - log_i("num=%d",i2c->num); - log_i("mode=%d",i2c->mode); - log_i("stage=%d",i2c->stage); - log_i("error=%d",i2c->error); - log_i("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0); - log_i("intr_handle=%p",i2c->intr_handle); - log_i("dq=%p",i2c->dq); - log_i("queueCount=%d",i2c->queueCount); - log_i("queuePos=%d",i2c->queuePos); - log_i("errorByteCnt=%d",i2c->errorByteCnt); - log_i("errorQueue=%d",i2c->errorQueue); - log_i("debugFlags=0x%08X",i2c->debugFlags); - if(i2c->dq) { - i2cDumpDqData(i2c); - } -} -#endif - -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) -static void i2cDumpInts(uint8_t num) -{ -#if defined (ENABLE_I2C_DEBUG_BUFFER) - uint32_t b; - log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num); - for(uint32_t a=1; a<=INTBUFFMAX; a++) { - b=(a+intPos[num])%INTBUFFMAX; - if(intBuff[b][0][num]!=0) { - log_i("[%02d]\t0x%04x\t0x%04x\t0x%04x\t0x%04x\t0x%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]); + 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; } } -#else - log_i("Debug Buffer not Enabled"); -#endif -} -#endif - -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) -static void ARDUINO_ISR_ATTR i2cDumpStatus(i2c_t * i2c){ - typedef union { - struct { - uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/ - uint32_t slave_rw: 1; /*when in slave mode 1:master read slave 0: master write slave.*/ - uint32_t time_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/ - uint32_t arb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/ - uint32_t bus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/ - uint32_t slave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/ - uint32_t byte_trans: 1; /*This register changes to high level when one byte is transferred.*/ - uint32_t reserved7: 1; - uint32_t rx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/ - uint32_t reserved14: 4; - uint32_t tx_fifo_cnt: 6; /*This register stores the amount of received data in ram.*/ - uint32_t scl_main_state_last: 3; /*This register stores the value of state machine for i2c module. 3'h0: SCL_MAIN_IDLE 3'h1: SCL_ADDRESS_SHIFT 3'h2: SCL_ACK_ADDRESS 3'h3: SCL_RX_DATA 3'h4 SCL_TX_DATA 3'h5:SCL_SEND_ACK 3'h6:SCL_WAIT_ACK*/ - uint32_t reserved27: 1; - uint32_t scl_state_last: 3; /*This register stores the value of state machine to produce SCL. 3'h0: SCL_IDLE 3'h1:SCL_START 3'h2:SCL_LOW_EDGE 3'h3: SCL_LOW 3'h4:SCL_HIGH_EDGE 3'h5:SCL_HIGH 3'h6:SCL_STOP*/ - uint32_t reserved31: 1; - }; - uint32_t val; - } status_reg; - - status_reg sr; - sr.val= i2c->dev->status_reg.val; - - log_i("ack(%d) sl_rw(%d) to(%d) arb(%d) busy(%d) sl(%d) trans(%d) rx(%d) tx(%d) sclMain(%d) scl(%d)",sr.ack_rec,sr.slave_rw,sr.time_out,sr.arb_lost,sr.bus_busy,sr.slave_addressed,sr.byte_trans, sr.rx_fifo_cnt, sr.tx_fifo_cnt,sr.scl_main_state_last, sr.scl_state_last); -} -#endif - -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) -static void i2cDumpFifo(i2c_t * i2c){ -char buf[64]; -uint16_t k = 0; -uint16_t i = fifoPos+1; - i %=FIFOMAX; -while((fifoBuffer[i]==0)&&(i!=fifoPos)){ - i++; - i %=FIFOMAX; -} -if(i != fifoPos){// actual data - do{ - if(fifoBuffer[i] & 0x8000){ // address byte - if(fifoBuffer[i] & 0x100) { // read - if(fifoBuffer[i] & 0x1) { // valid read dev id - k+= sprintf(&buf[k],"READ 0x%02X",(fifoBuffer[i]&0xff)>>1); - } else { // invalid read dev id - k+= sprintf(&buf[k],"Bad READ 0x%02X",(fifoBuffer[i]&0xff)); - } - } else { // write - if(fifoBuffer[i] & 0x1) { // bad write dev id - k+= sprintf(&buf[k],"bad WRITE 0x%02X",(fifoBuffer[i]&0xff)); - } else { // good Write - k+= sprintf(&buf[k],"WRITE 0x%02X",(fifoBuffer[i]&0xff)>>1); - } - } - } else k += sprintf(&buf[k],"% 4X ",fifoBuffer[i]); - - i++; - i %= FIFOMAX; - bool outBuffer=false; - if( fifoBuffer[i] & 0x8000){ - outBuffer=true; - k=0; - } - if((outBuffer)||(k>50)||(i==fifoPos)) log_i("%s",buf); - outBuffer = false; - if(k>50) { - k=sprintf(buf,"-> "); - } - }while( i!= fifoPos); -} -} -#endif - -static void ARDUINO_ISR_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char locus[]){ -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) - if( trigger ){ - log_i("%s",locus); - if(trigger & 1) i2cDumpI2c(i2c); - if(trigger & 2) i2cDumpInts(i2c->num); - if(trigger & 4) i2cDumpCmdQueue(i2c); - if(trigger & 8) i2cDumpStatus(i2c); - if(trigger & 16) i2cDumpFifo(i2c); - } -#endif -} - // end of debug support routines - -/* Start of CPU Clock change Support -*/ - -static void i2cApbChangeCallback(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ - i2c_t* i2c = (i2c_t*) arg; // recover data - if(i2c == NULL) { // point to peripheral control block does not exits - return; - } - uint32_t oldFreq=0; - switch(ev_type){ - case APB_BEFORE_CHANGE : - if(new_apb < 3000000) {// too slow - log_e("apb speed %d too slow",new_apb); - break; - } - I2C_MUTEX_LOCK(); // lock will spin until current transaction is completed - break; - case APB_AFTER_CHANGE : - oldFreq = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); //read old apbCycles - if(oldFreq>0) { // was configured with value - oldFreq = old_apb / oldFreq; - i2cSetFrequency(i2c,oldFreq); - } - I2C_MUTEX_UNLOCK(); - break; - default : - log_e("unk ev %u",ev_type); - I2C_MUTEX_UNLOCK(); - } - return; -} -/* End of CPU Clock change Support -*/ -static void ARDUINO_ISR_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; -} - - -static void ARDUINO_ISR_ATTR fillCmdQueue(i2c_t * i2c, bool INTS) -{ - /* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs - */ - uint16_t cmdIdx = 0; - uint16_t qp = i2c->queuePos; // all queues before queuePos have been completely processed, - // so look start by checking the 'current queue' so see if it needs commands added to - // hardware peripheral command list. walk through each queue entry until all queues have been - // checked - 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) {// marks that all required cmds[] have been added to peripheral - 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? - // (cmdIdx<14) because a START op cannot directly precede an END op, else a time out cascade occurs - i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false); - tdq->ctrl.startCmdSent=1; - } - - //CMD WRITE ADDRESS - if((!done)&&(tdq->ctrl.startCmdSent)) { // have to leave room for continue(END), 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 by 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(END) if necessary. - As a corollary, 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, if END is moved to [14], [14] and [15] can no longer be used 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 - 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 remainder 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.stopCmdSent){ - if(tdq->ctrl.stop) { //send a stop - i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false); - done = cmdIdx > 14; - } - 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; - 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))) { - /* add a dummy read/write cmd[] with num_bytes set to zero,just a place holder in the cmd[]list - */ - 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"); - i2cDumpCmdQueue(i2c); - done = true; - } - } - } - - } - - - 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 - i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_END, 0,false,false,false); - i2c->dev->int_ena.end_detect=1; - i2c->dev->int_clr.end_detect=1; - 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]; - } else { - done = true; - } - } - } - - }// while(!done) - if(INTS) { // don't want to prematurely enable fifo ints until ISR is ready to handle them. - if(ena_rx) { - i2c->dev->int_ena.rx_fifo_full = 1; - } - if(ena_tx) { - i2c->dev->int_ena.tx_fifo_empty = 1; - } + //acquire lock + if(xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ESP_FAIL; } -} - -static void ARDUINO_ISR_ATTR fillTxFifo(i2c_t * i2c) -{ - /* - 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; - bool rxQueueEncountered = false; - 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 -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) - uint16_t a = 0x8000 | tdq->ctrl.addr | (tdq->ctrl.mode<<8); - fifoBuffer[fifoPos++] = a; - fifoPos %= FIFOMAX; #endif - } - } - } - } - // add write data to fifo - if(tdq->ctrl.mode==0) { // write - if(tdq->ctrl.addrSent == tdq->ctrl.addrReq) { //address has been sent, is Write Mode! - uint32_t moveCnt = 32 - i2c->dev->status_reg.tx_fifo_cnt; // how much room in txFifo? - while(( moveCnt>0)&&(tdq->position < tdq->length)) { -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) - fifoBuffer[fifoPos++] = tdq->data[tdq->position]; - fifoPos %= FIFOMAX; -#endif - i2c->dev->fifo_data.val = tdq->data[tdq->position++]; - cnt++; - moveCnt--; - - } - } - } else { // read Queue Encountered, can't update QueuePos past this point, emptyRxfifo will do it - if( ! rxQueueEncountered ) { - rxQueueEncountered = true; - if(a > i2c->queuePos){ - i2c->queuePos = a; - } - } - } -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) - - // 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 - full=!(i2c->dev->status_reg.tx_fifo_cnt<31); - if(!full) { - a++; // check next buffer for address tx, or write data - } + if(bus[i2c_num].initialized){ + log_e("bus is already initialized"); + return ESP_FAIL; } - if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued - i2c->dev->int_ena.tx_fifo_empty= 0; + if(!frequency){ + frequency = 100000UL; + } else if(frequency > 1000000UL){ + frequency = 1000000UL; } - i2c->dev->int_clr.tx_fifo_empty=1; -} - - -static void ARDUINO_ISR_ATTR emptyRxFifo(i2c_t * i2c) -{ - uint32_t d, cnt=0, moveCnt; - - moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read - - while((moveCnt > 0)&&(i2c->queuePos < i2c->queueCount)){ // data to move - - I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut - - while((tdq->position >= tdq->length)&&( i2c->queuePos < i2c->queueCount)){ // find were to store - i2c->queuePos++; - tdq = &i2c->dq[i2c->queuePos]; - } - - if(i2c->queuePos >= i2c->queueCount){ // bad stuff, rx data but no place to put it! - log_e("no Storage location for %d",moveCnt); - // discard - while(moveCnt>0){ - d = i2c->dev->fifo_data.val; - moveCnt--; - cnt++; - } - break; - } - - if(tdq->ctrl.mode == 1){ // read command - if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq - // part of these reads go into the next dq - moveCnt = (tdq->length - tdq->position); - } - } else {// error - log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos); - // discard - while(moveCnt>0){ - d = i2c->dev->fifo_data.val; - moveCnt--; - cnt++; - } - break; - } + 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 - while(moveCnt > 0) { // store data - d = i2c->dev->fifo_data.val; - moveCnt--; - cnt++; - tdq->data[tdq->position++] = (d&0xFF); + 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; } - - moveCnt = i2c->dev->status_reg.rx_fifo_cnt; //any more out there? } - -#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER) - // 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; +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); #endif + return ret; } -static void ARDUINO_ISR_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->queuePos < i2c->queueCount) && (i2c->dq[i2c->queuePos].ctrl.mode == 1)) { - emptyRxFifo(i2c); // grab last few characters - } - i2c->dev->int_ena.val = 0; // shut down interrupts - i2c->dev->int_clr.val = 0x1FFF; - i2c->stage = I2C_DONE; - i2c->exitCode = exitCode; //true eventcode - - portBASE_TYPE HPTaskAwoken = pdFALSE,xResult; - // try to notify Dispatch we are done, - // else the 50ms time out will recover the APP, just a little 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 ARDUINO_ISR_ATTR i2c_update_error_byte_cnt(i2c_t * i2c) -{ -/* i2c_update_error_byte_cnt 07/18/2018 - Only called after an error has occurred, so, most of the time this function is never used. - This function obliterates the need to interrupt monitor each byte transferred, at high bitrates - the byte interrupts were overwhelming the OS. Spurious Interrupts were being generated. - it updates errorByteCnt, errorQueue. - */ - uint16_t a=0; // start at top of DQ, count how many bytes added to tx fifo, and received from rx_fifo. - int16_t bc = 0; - I2C_DATA_QUEUE_t *tdq; - i2c->errorByteCnt = 0; - while( a < i2c->queueCount){ // add up all bytes loaded into fifo's - tdq = &i2c->dq[a]; - i2c->errorByteCnt += tdq->ctrl.addrSent; - i2c->errorByteCnt += tdq->position; - a++; +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; } - // now errorByteCnt contains total bytes moved into and out of FIFO's - // but, there may still be bytes waiting in Fifo's - i2c->errorByteCnt -= i2c->dev->status_reg.tx_fifo_cnt; // waiting to go out; - i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received -// now walk thru DQ again, find which byte is 'current' - bc = i2c->errorByteCnt; - i2c->errorQueue = 0; - while( i2c->errorQueue < i2c->queueCount ){ - tdq = &i2c->dq[i2c->errorQueue]; - if(bc>0){ // not found yet - if( tdq->ctrl.addrSent >= bc){ // in address - bc = -1; // in address - break; - } else { - bc -= tdq->ctrl.addrSent; - if( tdq->length > bc) { // data nak - break; - } else { // count down - bc -= tdq->length; - } - } - } else break; - - i2c->errorQueue++; - } - - i2c->errorByteCnt = bc; - } - -static void ARDUINO_ISR_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&0x7FF; - - if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured - p_i2c->dev->int_ena.val = 0; - p_i2c->dev->int_clr.val = 0x1FFF; -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE - uint32_t raw = p_i2c->dev->int_raw.val; - log_w("eject raw=%p, int=%p",raw,activeInt); -#endif - return; +#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; } - while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change - - #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) - 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 - - if (activeInt & I2C_TRANS_START_INT_ST_M) { - 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_RXFIFO_OVF_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_OVF_INT_ST; - } - - if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service - if (p_i2c->mode == I2C_MASTER) { - i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data - if(p_i2c->errorByteCnt < 0 ) { // address - i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true); - } else { - i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data - } - } - 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; - // since a timeout occurred, capture the rxFifo data - emptyRxFifo(p_i2c); - p_i2c->dev->int_clr.rx_fifo_full=1; - p_i2c->dev->int_ena.rx_fifo_full=1; //why? - - } - - if (activeInt & I2C_TRANS_COMPLETE_INT_ST_M) { - p_i2c->dev->int_clr.trans_complete = 1; - 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 -// 01AUG2018 if another interrupt happened, the OS will schedule another interrupt -// this is the source of spurious interrupts - } -} - -/* 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, bool dataOnly, EventGroupHandle_t event) -{ - // need to grab a MUTEX for exclusive Queue, - // what about 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; - if( dataOnly) { - /* special case to add a queue data only element. - START and devAddr will not be sent, this dq element can have a STOP. - allows multiple buffers to be used for one transaction. - sequence: normal transaction(sendStop==false), [dataonly(sendStop==false)],dataOnly(sendStop==true) - *** Currently only works with WRITE, final byte NAK an READ will cause a fail between dq buffer elements. (in progress 30JUL2018) - */ - dqx.ctrl.startCmdSent = 1; // mark as already sent - dqx.ctrl.addrCmdSent = 1; + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); } else { - dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address - } - dqx.ctrl.addr = i2cDeviceAddr; - dqx.ctrl.mode = mode; - dqx.ctrl.stop= sendStop; - 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,false,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 against. - // this is the Industry Standard specification. - - if((i2cDeviceAddr &0xFC00)==0x7800) { // ten bit read - i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,false,event); - if(err==I2C_ERROR_OK) { - return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,false,event); - } else { - return err; + err = i2c_driver_delete((i2c_port_t)i2c_num); + if(err == ESP_OK){ + bus[i2c_num].initialized = false; } } - return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return err; } -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->debugFlags & 0xff000000) i2cTriggerDumps(i2c,(i2c->debugFlags>>24),"before ProcQueue"); - 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; +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; } - - 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) && (defined ENABLE_I2C_DEBUG_BUFFER) - for(uint16_t i=0; inum] = 0; - intBuff[i][1][i2c->num] = 0; - intBuff[i][2][i2c->num] = 0; +#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; } - intPos[i2c->num] = 0; - fifoPos = 0; - memset(fifoBuffer,0,FIFOMAX); #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->dev->int_clr.val = 0x1FFF; // kill them All! - - i2c->dev->ctr.ms_mode = 1; // master! - i2c->queuePos=0; - i2c->errorByteCnt=0; - i2c->errorQueue = 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->length + tdq->ctrl.addrReq; // total number of byte to be moved! + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + goto end; } - 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_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 - f.nonfifo_tx_thres = 31; - // 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? - // let i2cSetFrequency() set thrhds - // f.rx_fifo_full_thrhd = 30; // 30 bytes before INT is issued - // f.tx_fifo_empty_thrhd = 0; - 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->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 - - uint32_t interruptsEnabled = - 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_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() - - i2c->dev->int_ena.val = interruptsEnabled; - - if(!i2c->intr_handle) { // create ISR for either peripheral - // log_i("create ISR %d",i2c->num); - uint32_t ret = 0; - uint32_t flags = ARDUINO_ISR_FLAG | //< ISR can be called if cache is disabled - ESP_INTR_FLAG_LOWMED | //< Low and medium prio interrupts. These can be handled in C. - ESP_INTR_FLAG_SHARED; //< Reduce resource requirements, Share interrupts - - if(i2c->num) { - ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &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, interruptsEnabled, &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; - } - if( !addApbChangeCallback( i2c, i2cApbChangeCallback)) { - log_e("install apb Callback failed"); - I2C_MUTEX_UNLOCK(); - return I2C_ERROR_DEV; - } + //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; } - //hang until it completes. - - // how many ticks should it take to transfer totalBytes through the I2C hardware, - // add user supplied timeOutMillis to Calculated Value - - portTickType ticksTimeOut = ((totalBytes*10*1000)/(i2cGetFrequency(i2c))+timeOutMillis)/portTICK_PERIOD_MS; - - i2c->dev->ctr.trans_start=1; // go for it - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG - portTickType tBefore=xTaskGetTickCount(); -#endif - - // wait for ISR to complete the transfer, or until timeOut in case of bus fault, hardware problem - - uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ticksTimeOut); - -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG - portTickType tAfter=xTaskGetTickCount(); -#endif - - - // 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_ERROR)||(!(eBits & EVENT_DONE))){ // need accurate errorByteCnt for debug - i2c_update_error_byte_cnt(i2c); - } - - 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); -#endif - } - - if(eBits&EVENT_DONE) { // no gross timeout - 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; - i2c_update_error_byte_cnt(i2c); - if((i2c->errorByteCnt == 0)&&(i2c->errorQueue==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_DEBUG - log_d(" 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_DEBUG - log_d(" 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 - 07/22/2018 - Need to use the queueEvent value to identify transaction blocks, if an error occurs, - all subsequent queue items with the same queueEvent value will receive the EVENT_ERROR_PREV. - But, ProcQue should re-queue queue items that have a different queueEvent value(different transaction) - This change will support multi-thread i2c usage. Use the queueEvent as the transaction event - identifier. - */ - uint32_t 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++; - } - if(i2c->debugFlags & 0x00ff0000) i2cTriggerDumps(i2c,(i2c->debugFlags>>16),"after ProcQueue"); - - 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; - if (!removeApbChangeCallback( i2c, i2cApbChangeCallback)) { - log_e("unable to release apbCallback"); - } - } -} - -static bool i2cCheckLineState(int8_t sda, int8_t scl){ - if(sda < 0 || scl < 0){ - return false;//return false since there is nothing to do + ret = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); + if (ret != ESP_OK) { + goto end; } - // if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles - digitalWrite(sda, HIGH); - digitalWrite(scl, HIGH); - pinMode(sda, PULLUP|OPEN_DRAIN|INPUT); - pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT); - - if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state - log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, digitalRead(sda), scl, digitalRead(scl)); - digitalWrite(scl, HIGH); - for(uint8_t a=0; a<9; a++) { - delayMicroseconds(5); - digitalWrite(scl, LOW); - delayMicroseconds(5); - digitalWrite(scl, HIGH); - if(digitalRead(sda)){ // bus recovered - log_d("Recovered after %d Cycles",a+1); - break; - } + if(size){ + ret = i2c_master_write(cmd, buff, size, true); + if (ret != ESP_OK) { + goto end; } } - - if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state - log_e("Bus Invalid State, TwoWire() Can't init sda=%d, scl=%d",digitalRead(sda),digitalRead(scl)); - 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); - 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; + ret = i2c_master_stop(cmd); + if (ret != ESP_OK) { + goto end; } - 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; -} + ret = i2c_master_cmd_begin((i2c_port_t)i2c_num, cmd, timeOutMillis / portTICK_RATE_MS); -i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; +end: + if(cmd != NULL){ + i2c_cmd_link_delete_static(cmd); } - 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) { -#ifdef ENABLE_I2C_DEBUG_BUFFER - log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); #endif - if(i2c_num > 1) { - return NULL; - } - - i2c_t * i2c = &_i2c_bus_array[i2c_num]; + return ret; +} - // pins should be detached, else glitch - if(i2c->sda >= 0){ - i2cDetachSDA(i2c, i2c->sda); - } - if(i2c->scl >= 0){ - i2cDetachSCL(i2c, i2c->scl); +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; } - i2c->sda = sda; - i2c->scl = scl; - #if !CONFIG_DISABLE_HAL_LOCKS - if(i2c->lock == NULL) { - i2c->lock = xSemaphoreCreateRecursiveMutex(); - if(i2c->lock == NULL) { - return NULL; - } + //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 - 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 + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); } 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); + 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; + } } - return i2c; +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; } -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); +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; } - - i2cReleaseISR(i2c); - - if(i2c->i2c_event) { - vEventGroupDelete(i2c->i2c_event); - i2c->i2c_event = NULL; +#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; } - - 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 +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); } 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; - } - i2cTriggerDumps(i2c,i2c->debugFlags & 0xff, "FLUSH"); - - // 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){ - if((i2c==NULL)||((size>0)&&(buff==NULL))) { // need to have location to store requested data - return I2C_ERROR_DEV; - } - 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){ - if((size == 0)||(i2c == NULL)||(buff==NULL)){ // hardware will hang if no data requested on READ - return I2C_ERROR_DEV; - } - 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; + 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; } } - return last_error; +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; } -#define MIN_I2C_CLKS 100 // minimum ratio between cpu and i2c Bus clocks -#define INTERRUPT_CYCLE_OVERHEAD 16000 // number of cpu clocks necessary to respond to interrupt -i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) -{ - if(i2c == NULL) { - return I2C_ERROR_DEV; - } - uint32_t apb = getApbFrequency(); - uint32_t period = (apb/clk_speed) / 2; - - if((apb/8192 > clk_speed)||(apb/MIN_I2C_CLKS < clk_speed)){ //out of bounds - log_d("i2c freq(%d) out of bounds.vs APB Clock(%d), min=%d, max=%d",clk_speed,apb,(apb/8192),(apb/MIN_I2C_CLKS)); +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(period < (MIN_I2C_CLKS/2) ){ - period = (MIN_I2C_CLKS/2); - clk_speed = apb/(period*2); - log_d("APB Freq too slow, Reducing i2c Freq to %d Hz",clk_speed); - } else if ( period> 4095) { - period = 4095; - clk_speed = apb/(period*2); - log_d("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed); +#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; } -#ifdef ENABLE_I2C_DEBUG_BUFFER - log_v("freq=%dHz",clk_speed); -#endif - uint32_t halfPeriod = period/2; - uint32_t quarterPeriod = period/4; - - I2C_MUTEX_LOCK(); - - I2C_FIFO_CONF_t f; - - f.val = i2c->dev->fifo_conf.val; -/* Adjust Fifo thresholds based on differential between cpu frequency and bus clock. - The fifo_delta is calculated such that at least INTERRUPT_CYCLE_OVERHEAD cpu clocks are - available when a Fifo interrupt is triggered. This allows enough room in the Fifo so that - interrupt latency does not cause a Fifo overflow/underflow event. -*/ -#ifdef ENABLE_I2C_DEBUG_BUFFER - log_v("cpu Freq=%dMhz, i2c Freq=%dHz",getCpuFrequencyMhz(),clk_speed); -#endif - uint32_t fifo_delta = (INTERRUPT_CYCLE_OVERHEAD/((getCpuFrequencyMhz()*1000000 / clk_speed)*10))+1; - if (fifo_delta > 24) fifo_delta=24; - f.rx_fifo_full_thrhd = 32 - fifo_delta; - f.tx_fifo_empty_thrhd = fifo_delta; - i2c->dev->fifo_conf.val = f.val; // set thresholds -#ifdef ENABLE_I2C_DEBUG_BUFFER - log_v("Fifo delta=%d",fifo_delta); #endif - //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; -} + 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 + }; -uint32_t i2cGetFrequency(i2c_t * i2c) -{ - if(i2c == NULL) { - return 0; + 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; + } } - 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 = getApbFrequency() / old_count; + if(src_clk == I2C_SCLK_MAX){ + log_e("clock source could not be selected"); + ret = ESP_FAIL; } else { - result = 0; - } - return result; -} - - -uint32_t i2cDebug(i2c_t * i2c, uint32_t setBits, uint32_t resetBits){ - if(i2c != NULL) { - i2c->debugFlags = ((i2c->debugFlags | setBits) & ~resetBits); - return i2c->debugFlags; - } - return 0; - - } - -uint32_t i2cGetStatus(i2c_t * i2c){ - if(i2c != NULL){ - return i2c->dev->status_reg.val; - } - else return 0; -} -#else -#include "driver/i2c.h" - -#define ACK_CHECK_EN 1 /*!< I2C master will check ack from slave*/ -#define ACK_CHECK_DIS 0 /*!< I2C master will not check ack from slave */ -#define ACK_VAL 0x0 /*!< I2C ack value */ -#define NACK_VAL 0x1 /*!< I2C nack value */ - -struct i2c_struct_t { - i2c_port_t num; -}; - -static i2c_t * i2c_ports[2] = {NULL, NULL}; - -i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed){ - if(i2c_num >= SOC_I2C_NUM){ - return NULL; - } - if(!clk_speed){ - //originally does not change the speed, but getFrequency and setFrequency need to be implemented first. - clk_speed = 100000; - } - i2c_t * out = NULL; - if(i2c_ports[i2c_num] == NULL){ - out = (i2c_t*)malloc(sizeof(i2c_t)); - if(out == NULL){ - log_e("malloc failed"); - return NULL; - } - out->num = (i2c_port_t)i2c_num; - i2c_ports[i2c_num] = out; - } else { - out = i2c_ports[i2c_num]; - i2c_driver_delete((i2c_port_t)i2c_num); - } - - 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 = clk_speed; - esp_err_t ret = i2c_param_config(out->num, &conf); - if (ret != ESP_OK) { - log_e("i2c_param_config failed"); - free(out); - i2c_ports[i2c_num] = NULL; - return NULL; - } - ret = i2c_driver_install(out->num, conf.mode, 0, 0, 0); - if (ret != ESP_OK) { - log_e("i2c_driver_install failed"); - free(out); - i2c_ports[i2c_num] = NULL; - return NULL; + 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; } - return out; -} -i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis){ - esp_err_t ret = ESP_OK; - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); - if(size){ - i2c_master_write(cmd, buff, size, ACK_CHECK_EN); - } - //if send stop? - i2c_master_stop(cmd); - ret = i2c_master_cmd_begin(i2c->num, cmd, timeOutMillis / portTICK_RATE_MS); - i2c_cmd_link_delete(cmd); +end: +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif return ret; } -i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount){ - esp_err_t ret = ESP_OK; - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, ACK_CHECK_EN); - if (size > 1) { - i2c_master_read(cmd, buff, size - 1, ACK_VAL); +esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t * frequency){ + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; } - i2c_master_read_byte(cmd, buff + size - 1, NACK_VAL); - i2c_master_stop(cmd); - ret = i2c_master_cmd_begin(i2c->num, cmd, timeOutMillis / portTICK_RATE_MS); - i2c_cmd_link_delete(cmd); - if(ret == ESP_OK){ - *readCount = size; + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + return ESP_FAIL; } - return ret; -} - -void i2cRelease(i2c_t *i2c){ - log_w(""); - return; -} -i2c_err_t i2cFlush(i2c_t *i2c){ - esp_err_t ret = i2c_reset_tx_fifo(i2c->num); - if(ret != ESP_OK){ - return ret; - } - return i2c_reset_rx_fifo(i2c->num); -} -i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed){ - log_w(""); + *frequency = bus[i2c_num].frequency; return ESP_OK; } -uint32_t i2cGetFrequency(i2c_t * i2c){ - log_w(""); - return 0; -} -uint32_t i2cGetStatus(i2c_t * i2c){ - log_w(""); - return 0; -} - -//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){ - return ESP_FAIL; -} -i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl){ - return ESP_FAIL; -} -i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda){ - return ESP_FAIL; -} -i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda){ - return ESP_FAIL; -} - -#endif /* CONFIG_IDF_TARGET_ESP32 */ - -/* todo - 22JUL18 - need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads - transactions are present in the same queue, and an error occurs, abort all succeeding unserviced transactions - with the same dq.queueEvent value. Succeeding unserviced transactions with different dq.queueEvent values - can be re-queued and processed independently. - 30JUL18 complete data only queue elements, this will allow transfers to use multiple data blocks, - */ - diff --git a/cores/esp32/esp32-hal-i2c.h b/cores/esp32/esp32-hal-i2c.h index 1a696aca7d6..3fa889fbefb 100644 --- a/cores/esp32/esp32-hal-i2c.h +++ b/cores/esp32/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,58 +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); -uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral - -//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 -uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits); -// Debug actions have 3 currently defined locus -// 0xXX------ : at entry of ProcQueue -// 0x--XX---- : at exit of ProcQueue -// 0x------XX : at entry of Flush -// -// bit 0 causes DumpI2c to execute -// bit 1 causes DumpInts to execute -// bit 2 causes DumpCmdqueue to execute -// bit 3 causes DumpStatus to execute -// bit 4 causes DumpFifo to execute +#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/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 92bb182896d..3efead522dd 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -31,6 +31,11 @@ #include "sdkconfig.h" #include "esp_system.h" #include "esp_sleep.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" #ifdef __cplusplus extern "C" { diff --git a/libraries/Wire/doc/i2c_debugging.md b/libraries/Wire/doc/i2c_debugging.md deleted file mode 100644 index 35c6e2e7f17..00000000000 --- a/libraries/Wire/doc/i2c_debugging.md +++ /dev/null @@ -1,276 +0,0 @@ -# Debugging I2C - -With the release of Arduino-ESP32 V1.0.1 the I2C subsystem contains code to exhaustively report communication errors. -* Basic debugging can be enable by setting the *CORE DEBUG LEVEL* at or above *ERROR*. All errors will be directed the the *DEBUG OUTPUT* normally connected to `Serial()`. -* Enhanced debugging can be used to generate specified information at specific positions during the i2c communication sequence. Increase *CORE DEBUG LEVEL* to ***DEBUG*** - -## Enable Debug Buffer -The Enhanced debug features are enabled by uncommenting the `\\#define ENABLE_I2C_DEBUG_BUFFER` at line 45 of `esp32-hal-i2c.c`. -* When Arduino-Esp32 is installed in Windows with Arduino Boards Manager, `esp32-hal-i2c.c` can be found in: -`C:\Users\{user}\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\cores\esp32\` -* When Arduino-Esp32 Development version is installed from GitHub, `esp32-hal-i2c.c` can be found in: -`{arduino Sketch}\hardware\espressif\esp32\cores\esp32\` - - -```c++ -//#define ENABLE_I2C_DEBUG_BUFFER -``` -Change it to: -```c++ -#define ENABLE_I2C_DEBUG_BUFFER -``` -and recompile/upload the resulting code to your ESP32. - -Enabling this `#define` will consume an additional 2570 bytes of RAM and include a commensurate amount of code FLASH. If you see the message `"Debug Buffer not Enabled"` in your console log I would suggest you un-comment the line and regenerate the error. Additional information will be supplied on the log console. - -## Manually controlled Debugging -Manual logging of the i2c control data buffers can be accomplished by using the debug control function of `Wire()`: -```c++ - uint32_t setDebugFlags( uint32_t setBits, uint32_t resetBits); -``` -`setBits`, and `resetBits` manually cause output of the control structures to the log console. They are bit fields that enable/disable the reporting of individual control structures during specific phases of the i2c communications sequence. The 32bit values are divided into four 8bit fields. Currently only five bits are defined. ***If an error is detected during normal operations, the relevant control structure will bit added to the log irrespective of the current debug flags.*** - -* **bit 0** causes DumpI2c to execute -header information about current communications event, -and the dataQueue elements showing the logical i2c transaction commands -* **bit 1** causes DumpInts to execute -Actual sequence of interrupts handled during last communications event, cleared on entry into `ProcQueue()`. -* **bit 2** causes DumpCmdqueue to execute -The last block of commands to the i2c peripheral. -* **bit 3** causes DumpStatus to execute -A descriptive display of the 32bit i2c peripheral status word. -* **bit 4** causes DumpFifo to execute -A buffer listing the sequence of data added to the txFifo of the i2c peripheral. - -Of the four division, only three are currently implemented: -* 0xXX - - - - - - : at entry of ProcQueue (`bitFlags << 24`) -* 0x - - XX - - - - : at exit of ProcQueue (`bitFlags << 16`) -* 0x - - - - - - XX : at entry of Flush (`bitFlags`) - -For example, to display the sequence of Interrupts processed during the i2c communication transaction, **bit 1** would be set, and, since this information on Interrupt usage would only be valid after the communications have completed, the locus would be *at exit of ProcQueue*. The following code would be necessary. -### code -```c++ -uint8_t flag = 1 << 1; // turn on bit 1 -uint32_t debugFlag = flag << 16; // correctly position the 8bits of flag as the second byte of setBits. -Wire.setDebugFlags(debugFlag,0);// resetBits=0 says leave all current setBits as is. - -Wire.requestFrom(id,byteCount); // read byteCount bytes from slave at id - -Wire.setDebugFlags(0,debugFlag); // don't add any new debug, remove debugFlag -``` -### output of log console -``` -[I][esp32-hal-i2c.c:437] i2cTriggerDumps(): after ProcQueue -[I][esp32-hal-i2c.c:346] i2cDumpInts(): 0 row count INTR TX RX Tick -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [01] 0x0001 0x0002 0x0003 0x0000 0x005baac5 -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [02] 0x0001 0x0200 0x0000 0x0000 0x005baac5 -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [03] 0x0001 0x0080 0x0000 0x0008 0x005baac6 -``` -# Debug Log example -### Code -To read eight bytes of data from a DS1307 RTCC -``` - uint32_t debugFlag = 0x001F0000; - uint8_t ID = 0x68; - uint8_t block=8; - - if(debugFlag >0){ - Wire.setDebugFlags(debugFlag,0); - } - - Wire.beginTransmission(ID); - Wire.write(lowByte(addr)); - if((err=Wire.endTransmission(false))!=0) { - Serial.printf(" EndTransmission=%d(%s)",Wire.lastError(),Wire.getErrorText(Wire.lastError())); - - if(err!=2) { - Serial.printf(", resetting\n"); - if( !Wire.begin()) Serial.printf(" Reset Failed\n"); - if(debugFlag >0) Wire.setDebugFlags(0,debugFlag); - return; - } else { - Serial.printf(", No Device present, aborting\n"); - currentCommand= NO_COMMAND; - return; - } - } - err = Wire.requestFrom(ID,block,true); - if(debugFlag >0){ - Wire.setDebugFlags(0,debugFlag); - } -``` -### output of log console -``` -[I][esp32-hal-i2c.c:437] i2cTriggerDumps(): after ProcQueue -[E][esp32-hal-i2c.c:318] i2cDumpI2c(): i2c=0x3ffbdc78 -[I][esp32-hal-i2c.c:319] i2cDumpI2c(): dev=0x60013000 date=0x16042000 -[I][esp32-hal-i2c.c:321] i2cDumpI2c(): lock=0x3ffb843c -[I][esp32-hal-i2c.c:323] i2cDumpI2c(): num=0 -[I][esp32-hal-i2c.c:324] i2cDumpI2c(): mode=1 -[I][esp32-hal-i2c.c:325] i2cDumpI2c(): stage=3 -[I][esp32-hal-i2c.c:326] i2cDumpI2c(): error=1 -[I][esp32-hal-i2c.c:327] i2cDumpI2c(): event=0x3ffb85c4 bits=10 -[I][esp32-hal-i2c.c:328] i2cDumpI2c(): intr_handle=0x3ffb85f4 -[I][esp32-hal-i2c.c:329] i2cDumpI2c(): dq=0x3ffb858c -[I][esp32-hal-i2c.c:330] i2cDumpI2c(): queueCount=2 -[I][esp32-hal-i2c.c:331] i2cDumpI2c(): queuePos=1 -[I][esp32-hal-i2c.c:332] i2cDumpI2c(): errorByteCnt=0 -[I][esp32-hal-i2c.c:333] i2cDumpI2c(): errorQueue=0 -[I][esp32-hal-i2c.c:334] i2cDumpI2c(): debugFlags=0x001F0000 -[I][esp32-hal-i2c.c:288] i2cDumpDqData(): [0] 7bit 68 W buf@=0x3ffc04b2, len=1, pos=1, ctrl=11101 -[I][esp32-hal-i2c.c:306] i2cDumpDqData(): 0x0000: . 00 -[I][esp32-hal-i2c.c:288] i2cDumpDqData(): [1] 7bit 68 R STOP buf@=0x3ffc042c, len=8, pos=8, ctrl=11111 -[I][esp32-hal-i2c.c:306] i2cDumpDqData(): 0x0000: 5P...... 35 50 07 06 13 09 18 00 -[I][esp32-hal-i2c.c:346] i2cDumpInts(): 0 row count INTR TX RX Tick -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [01] 0x0001 0x0002 0x0003 0x0000 0x000073d5 -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [02] 0x0001 0x0200 0x0000 0x0000 0x000073d5 -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [03] 0x0001 0x0080 0x0000 0x0008 0x000073d6 -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 0] Y RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 1] Y WRITE val[0] exp[0] en[1] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 2] Y WRITE val[0] exp[0] en[1] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 3] Y RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 4] Y WRITE val[0] exp[0] en[1] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 5] Y READ val[0] exp[0] en[0] bytes[7] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 6] Y READ val[1] exp[0] en[0] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 7] Y STOP val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 8] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 9] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [10] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [11] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [12] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [13] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [14] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [15] N RSTART val[0] exp[0] en[0] bytes[0] -[I][esp32-hal-i2c.c:385] i2cDumpStatus(): ack(0) sl_rw(0) to(0) arb(0) busy(0) sl(1) trans(0) rx(0) tx(0) sclMain(5) scl(6) -[I][esp32-hal-i2c.c:424] i2cDumpFifo(): WRITE 0x68 0 -[I][esp32-hal-i2c.c:424] i2cDumpFifo(): READ 0x68 -``` -## Explaination of log output -### DumpI2c -``` -[I][esp32-hal-i2c.c:437] i2cTriggerDumps(): after ProcQueue -[E][esp32-hal-i2c.c:318] i2cDumpI2c(): i2c=0x3ffbdc78 -[I][esp32-hal-i2c.c:319] i2cDumpI2c(): dev=0x60013000 date=0x16042000 -[I][esp32-hal-i2c.c:321] i2cDumpI2c(): lock=0x3ffb843c -[I][esp32-hal-i2c.c:323] i2cDumpI2c(): num=0 -[I][esp32-hal-i2c.c:324] i2cDumpI2c(): mode=1 -[I][esp32-hal-i2c.c:325] i2cDumpI2c(): stage=3 -[I][esp32-hal-i2c.c:326] i2cDumpI2c(): error=1 -[I][esp32-hal-i2c.c:327] i2cDumpI2c(): event=0x3ffb85c4 bits=10 -[I][esp32-hal-i2c.c:328] i2cDumpI2c(): intr_handle=0x3ffb85f4 -[I][esp32-hal-i2c.c:329] i2cDumpI2c(): dq=0x3ffb858c -[I][esp32-hal-i2c.c:330] i2cDumpI2c(): queueCount=2 -[I][esp32-hal-i2c.c:331] i2cDumpI2c(): queuePos=1 -[I][esp32-hal-i2c.c:332] i2cDumpI2c(): errorByteCnt=0 -[I][esp32-hal-i2c.c:333] i2cDumpI2c(): errorQueue=0 -[I][esp32-hal-i2c.c:334] i2cDumpI2c(): debugFlags=0x001F0000 -``` -variable | description ----- | ---- -**i2c** | *memory address for control block* -**dev** | *memory address for access to i2c peripheral registers* -**date** | *revision date of peripheral silicon 2016, 42 week* -**lock** | *hal lock handle* -**num** | *0,1 which peripheral is being controlled* -**mode** | *configuration of driver 0=none, 1=MASTER, 2=SLAVE, 3=MASTER and SLAVE* -**stage** | *internal STATE of driver 0=not configured, 1=startup, 2=running, 3=done* -**error** | *internal ERROR status 0=not configured, 1=ok, 2=error, 3=address NAK, 4=data NAK, 5=arbitration loss, 6=timeout* -**event** | *handle for interprocess FreeRTOS eventSemaphore for communication between ISR and APP* -**intr_handle** | *FreeRTOS handle for allocated interrupt* -**dq** | *memory address for data queue control block* -**queueCount** | *number of data operations in queue control block* -**queuePos** | *last executed data block* -**errorByteCnt** | *position in current data block when error occured -1=address byte* -**errorQueue** | *queue that was executing when error occurred* -**debugFlags** | *current specified error bits* - -### DQ data -``` -[I][esp32-hal-i2c.c:288] i2cDumpDqData(): [0] 7bit 68 W buf@=0x3ffc04b2, len=1, pos=1, ctrl=11101 -[I][esp32-hal-i2c.c:306] i2cDumpDqData(): 0x0000: . 00 -[I][esp32-hal-i2c.c:288] i2cDumpDqData(): [1] 7bit 68 R STOP buf@=0x3ffc042c, len=8, pos=8, ctrl=11111 -[I][esp32-hal-i2c.c:306] i2cDumpDqData(): 0x0000: 5P...... 35 50 07 06 13 09 18 00 -``` -variable | description ---- | --- -**[n]** | *index of data queue element* -**i2c address** | *7bit= 7bit i2c slave address, 10bit= 10bit i2c slave address* -**direction** | *W=Write, R=READ* -**STOP** | *command issued a I2C STOP, else if blank, a RESTART was issued by next dq element.* -**buf@** | *pointer to data buffer* -**len** | *length of data buffer* -**pos** | *last position used in buffer* -**ctrl** | *bit field for data queue control, this bits describe if all necessary commands have been added to peripheral command buffer. in order(START,ADDRESS_Write,DATA,STOP,ADDRESS_value* -**0xnnnn** | *data buffer content, displayable followed by HEX, 32 bytes on a line.* - -### DumpInts -``` -[I][esp32-hal-i2c.c:346] i2cDumpInts(): 0 row count INTR TX RX Tick -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [01] 0x0001 0x0002 0x0003 0x0000 0x000073d5 -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [02] 0x0001 0x0200 0x0000 0x0000 0x000073d5 -[I][esp32-hal-i2c.c:350] i2cDumpInts(): [03] 0x0001 0x0080 0x0000 0x0008 0x000073d6 -``` -variable | description ----- | ---- -**row** | *array index* -**count** | *number of consecutive, duplicate interrupts* -**INTR** | *bit value of active interrupt (from ..\esp32\tools\sdk\include\soc\soc\i2c_struct.h)* -**TX** | *number of bytes added to txFifo* -**RX** | *number of bytes read from rxFifo* -**Tick** | *current tick counter from xTaskGetTickCountFromISR()* - -### DumpCmdQueue -``` -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 0] Y RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 1] Y WRITE val[0] exp[0] en[1] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 2] Y WRITE val[0] exp[0] en[1] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 3] Y RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 4] Y WRITE val[0] exp[0] en[1] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 5] Y READ val[0] exp[0] en[0] bytes[7] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 6] Y READ val[1] exp[0] en[0] bytes[1] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 7] Y STOP val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 8] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [ 9] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [10] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [11] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [12] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [13] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [14] N RSTART val[0] exp[0] en[0] bytes[0] -[E][esp32-hal-i2c.c:243] i2cDumpCmdQueue(): [15] N RSTART val[0] exp[0] en[0] bytes[0] -``` -Column | description ----- | ---- -**command** | *RSTART= generate i2c START sequence, WRITE= output byte(s), READ= input byte(s), STOP= generate i2c STOP sequence, END= continuation flag for peripheral to pause execution waiting for a refilled command list* -**val** | *value for ACK bit, 0 = LOW, 1= HIGH* -**exp** | *test of ACK bit 0=no, 1=yes* -**en** | *output of val, 0=no, 1=yes* -**bytes** | *number of byte to send(WRITE) or receive(READ) 1..255* - -### DumpStatus -``` -[I][esp32-hal-i2c.c:385] i2cDumpStatus(): ack(0) sl_rw(0) to(0) arb(0) busy(0) sl(1) trans(0) rx(0) tx(0) sclMain(5) scl(6) -``` -variable | description ----- | ---- -**ack** | *last value for ACK bit* -**sl_rw** | *mode for SLAVE operation 0=write, 1=read* -**to** | *timeout* -**arb** | *arbitration loss* -**busy** | *bus is inuse by other Master, or SLAVE is holding SCL,SDA* -**sl** | *last address on bus was equal to slave_addr* -**trans** | *a byte has moved though peripheral* -**rx** | *count of bytes in rxFifo* -**tx** | *count of bytes in txFifo* -**sclMain** | *state machine for i2c module. 0: SCL_MAIN_IDLE, 1: SCL_ADDRESS_SHIFT, 2: SCL_ACK_ADDRESS, 3: SCL_RX_DATA, 4: SCL_TX_DATA, 5: SCL_SEND_ACK, 6 :SCL_WAIT_ACK* -**scl** | *SCL clock state. 0: SCL_IDLE, 1: SCL_START, 2: SCL_LOW_EDGE, 3: SCL_LOW, 4: SCL_HIGH_EDGE, 5: SCL_HIGH, 6:SCL_STOP* - -### DumpFifo -``` -[I][esp32-hal-i2c.c:424] i2cDumpFifo(): WRITE 0x68 0 -[I][esp32-hal-i2c.c:424] i2cDumpFifo(): READ 0x68 -``` -Mode | datavalues ---- | --- -**WRITE** | the following bytes added to the txFifo are in response to a WRITE command -**READ** | the following bytes added to the txFifo are in response to a READ command - diff --git a/libraries/Wire/keywords.txt b/libraries/Wire/keywords.txt index 3344011d406..a2fbf317e95 100644 --- a/libraries/Wire/keywords.txt +++ b/libraries/Wire/keywords.txt @@ -11,13 +11,14 @@ ####################################### begin KEYWORD2 +end KEYWORD2 setClock KEYWORD2 -setClockStretchLimit KEYWORD2 +getClock KEYWORD2 +setTimeOut KEYWORD2 +getTimeOut KEYWORD2 beginTransmission KEYWORD2 endTransmission KEYWORD2 requestFrom KEYWORD2 -send KEYWORD2 -receive KEYWORD2 onReceive KEYWORD2 onRequest KEYWORD2 @@ -26,6 +27,7 @@ onRequest KEYWORD2 ####################################### Wire KEYWORD2 +TwoWire KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 3acea70ac58..7000e7013bb 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/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" { @@ -36,41 +37,79 @@ TwoWire::TwoWire(uint8_t bus_num) :num(bus_num & 1) ,sda(-1) ,scl(-1) - ,i2c(NULL) ,rxIndex(0) ,rxLength(0) - ,rxQueued(0) - ,txIndex(0) ,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 {} TwoWire::~TwoWire() { - flush(); - if(i2c) { - i2cRelease(i2c); - i2c=NULL; + end(); +#if !CONFIG_DISABLE_HAL_LOCKS + if(lock != NULL){ + vSemaphoreDelete(lock); } +#endif } bool TwoWire::setPins(int sdaPin, int sclPin) { - if(i2c) { - log_e("can not set pins if begin was already called"); +#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; } - sda = sdaPin; - scl = sclPin; - return true; +#endif + if(!i2cIsInit(num)){ + sda = sdaPin; + scl = 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); } 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(i2cIsInit(num)){ + started = true; + goto end; + } if(sdaPin < 0) { // default param passed if(num == 0) { if(sda==-1) { @@ -81,7 +120,7 @@ bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } else { if(sda==-1) { log_e("no Default SDA Pin for Second Peripheral"); - return false; //no Default pin for Second Peripheral + goto end; //no Default pin for Second Peripheral } else { sdaPin = sda; // reuse prior pin } @@ -98,7 +137,7 @@ bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) } else { if(scl == -1) { log_e("no Default SCL Pin for Second Peripheral"); - return false; //no Default pin for Second Peripheral + goto end; //no Default pin for Second Peripheral } else { sclPin = scl; // reuse prior pin } @@ -107,138 +146,172 @@ bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency) sda = sdaPin; scl = sclPin; - i2c = i2cInit(num, sda, scl, frequency); - if(!i2c) { - return false; - } - - flush(); - return true; + err = i2cInit(num, sda, scl, frequency); + started = (err == ESP_OK); -} +end: +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return started; -void TwoWire::setTimeOut(uint16_t timeOutMillis) -{ - _timeOutMillis = timeOutMillis; } -uint16_t TwoWire::getTimeOut() +bool TwoWire::end() { - return _timeOutMillis; + 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(i2cIsInit(num)){ + err = i2cDeinit(num); + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); + } +#endif + return (err == ESP_OK); } -void TwoWire::setClock(uint32_t frequency) +uint32_t TwoWire::getClock() { -#if CONFIG_IDF_TARGET_ESP32S2 - i2c = i2cInit(num, sda, scl, frequency); - if(!i2c) { - return; + 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 + i2cGetClock(num, &frequency); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); } #endif - i2cSetFrequency(i2c, frequency); + return frequency; } -size_t TwoWire::getClock() +bool TwoWire::setClock(uint32_t frequency) { - return i2cGetFrequency(i2c); + 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 + err = i2cSetClock(num, frequency); +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(lock); +#endif + return (err == ESP_OK); } -/* stickBreaker Nov 2017 ISR, and bigblock 64k-1 - */ -i2c_err_t TwoWire::writeTransmission(uint16_t address, uint8_t *buff, uint16_t size, bool sendStop) +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 !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; - last_error = I2C_ERROR_OK; + txLength = 0; } -/*stickbreaker isr - */ -uint8_t TwoWire::endTransmission(bool sendStop) // Assumes Wire.beginTransaction(), Wire.write() +uint8_t TwoWire::endTransmission(bool sendStop) { - if(transmitting == 1) { - // txlength is howmany bytes in txbuffer have been use - last_error = writeTransmission(txAddress, &txBuffer[txQueued], txLength - txQueued, sendStop); - if(last_error == I2C_ERROR_CONTINUE){ - txQueued = txLength; - } else if( last_error == I2C_ERROR_OK){ - rxIndex = 0; - rxLength = rxQueued; - rxQueued = 0; - txQueued = 0; // the SendStop=true will restart all Queueing - } + 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 == I2C_ERROR_CONTINUE)?I2C_ERROR_OK:last_error; // Don't return Continue for compatibility. + 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) { - //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; - } - - last_error = readTransmission(address, &rxBuffer[cnt], size, sendStop, &cnt); - rxIndex = 0; - - rxLength = cnt; - - if( last_error != I2C_ERROR_CONTINUE){ // not a buffered ReSTART operation - // so this operation actually moved data, queuing is done. - rxQueued = 0; - txQueued = 0; // the SendStop=true will restart all Queueing or error condition - } - - if(last_error != I2C_ERROR_OK){ // ReSTART on read does not return any data - cnt = 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); + } 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); } - - 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) { - last_error = I2C_ERROR_MEMORY; - return 0; - } - txBuffer[txIndex] = data; - ++txIndex; - txLength = txIndex; - return 1; + if(txLength >= I2C_BUFFER_LENGTH) { + return 0; } - last_error = I2C_ERROR_NO_BEGIN; // no begin, not transmitting - return 0; + txBuffer[txLength++] = data; + return 1; } size_t TwoWire::write(const uint8_t *data, size_t quantity) @@ -262,8 +335,7 @@ int TwoWire::read(void) { int value = -1; if(rxIndex < rxLength) { - value = rxBuffer[rxIndex]; - ++rxIndex; + value = rxBuffer[rxIndex++]; } return value; } @@ -281,11 +353,8 @@ void TwoWire::flush(void) { rxIndex = 0; rxLength = 0; - txIndex = 0; txLength = 0; - rxQueued = 0; - txQueued = 0; - i2cFlush(i2c); // cleanup + //i2cFlush(num); // cleanup } uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) @@ -333,56 +402,5 @@ uint8_t TwoWire::endTransmission(void) return endTransmission(true); } -/* stickbreaker Nov2017 better error reporting - */ -uint8_t TwoWire::lastError() -{ - return (uint8_t)last_error; -} - -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) -{ - 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++; - } - } - if(!found) { - return NULL; - } else { - return message; - } -} - -/*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging - */ - -uint32_t TwoWire::setDebugFlags( uint32_t setBits, uint32_t resetBits){ - return i2cDebug(i2c,setBits,resetBits); -} - -bool TwoWire::busy(void){ - return ((i2cGetStatus(i2c) & 16 )==16); -} - TwoWire Wire = TwoWire(0); TwoWire Wire1 = TwoWire(1); diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 3cee73104bd..08e52f5d011 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -20,17 +20,20 @@ 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 'V1.1.0' #ifndef I2C_BUFFER_LENGTH #define I2C_BUFFER_LENGTH 128 #endif @@ -43,28 +46,21 @@ 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 rxIndex; + size_t rxLength; uint8_t txBuffer[I2C_BUFFER_LENGTH]; - uint16_t txIndex; - uint16_t txLength; + 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 public: TwoWire(uint8_t bus_num); @@ -74,20 +70,13 @@ class TwoWire: public Stream bool setPins(int sda, int scl); bool begin(int sda=-1, int scl=-1, uint32_t frequency=0); // returns true, if successful init of i2c bus - // calling will attemp to recover hung bus - - void setClock(uint32_t frequency); // change bus clock without initing hardware - size_t getClock(); // current bus clock rate in hz + bool end(); 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); @@ -134,22 +123,9 @@ class TwoWire: public Stream void onReceive( void (*)(int) ); void onRequest( void (*)(void) ); - - uint32_t setDebugFlags( uint32_t setBits, uint32_t resetBits); - bool busy(); }; extern TwoWire Wire; extern TwoWire Wire1; - -/* -V1.1.0 08JAN2019 Support CPU Clock frequency changes -V1.0.2 30NOV2018 stop returning I2C_ERROR_CONTINUE on ReSTART operations, regain compatibility with Arduino libs -V1.0.1 02AUG2018 First Fix after release, Correct ReSTART handling, change Debug control, change begin() - to a function, this allow reporting if bus cannot be initialized, Wire.begin() can be used to recover - a hung bus busy condition. -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