// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "esp32-hal-i2c.h" #include "esp32-hal.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "rom/ets_sys.h" #include "soc/i2c_reg.h" #include "soc/i2c_struct.h" #include "soc/dport_reg.h" //#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)) struct i2c_struct_t { i2c_dev_t * dev; #if !CONFIG_DISABLE_HAL_LOCKS xSemaphoreHandle lock; #endif uint8_t num; }; enum { I2C_CMD_RSTART, I2C_CMD_WRITE, I2C_CMD_READ, I2C_CMD_STOP, I2C_CMD_END }; #if CONFIG_DISABLE_HAL_LOCKS #define I2C_MUTEX_LOCK() #define I2C_MUTEX_UNLOCK() static i2c_t _i2c_bus_array[2] = { {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE), 0}, {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE), 1} }; #else #define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS) #define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock) static i2c_t _i2c_bus_array[2] = { {(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE), NULL, 0}, {(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE), NULL, 1} }; #endif i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl) { if(i2c == NULL){ return I2C_ERROR_DEV; } pinMode(scl, 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); return I2C_ERROR_OK; } i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda) { if(i2c == NULL){ return I2C_ERROR_DEV; } pinMode(sda, OUTPUT_OPEN_DRAIN); pinMatrixOutAttach(sda, I2C_SDA_IDX(i2c->num), false, false); pinMatrixInAttach(sda, I2C_SDA_IDX(i2c->num), false); return I2C_ERROR_OK; } i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda) { if(i2c == NULL){ return I2C_ERROR_DEV; } pinMatrixOutDetach(sda, false, false); pinMatrixInDetach(I2C_SDA_IDX(i2c->num), false, false); pinMode(sda, INPUT); return I2C_ERROR_OK; } /* * 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. * */ void 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->dev->command[index].val = 0; i2c->dev->command[index].ack_en = ack_check; i2c->dev->command[index].ack_exp = ack_exp; i2c->dev->command[index].ack_val = ack_val; i2c->dev->command[index].byte_num = byte_num; i2c->dev->command[index].op_code = op_code; } void i2cResetCmd(i2c_t * i2c){ int i; for(i=0;i<16;i++){ i2c->dev->command[i].val = 0; } } void i2cResetFiFo(i2c_t * i2c) { i2c->dev->fifo_conf.tx_fifo_rst = 1; i2c->dev->fifo_conf.tx_fifo_rst = 0; i2c->dev->fifo_conf.rx_fifo_rst = 1; i2c->dev->fifo_conf.rx_fifo_rst = 0; } i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop) { int i; uint8_t index = 0; uint8_t dataLen = len + (addr_10bit?2:1); address = (address << 1); if(i2c == NULL){ return I2C_ERROR_DEV; } I2C_MUTEX_LOCK(); while(dataLen) { uint8_t willSend = (dataLen > 32)?32:dataLen; uint8_t dataSend = willSend; i2cResetFiFo(i2c); i2cResetCmd(i2c); //CMD START i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); //CMD WRITE(ADDRESS + DATA) if(!index) { i2c->dev->fifo_data.data = address & 0xFF; dataSend--; if(addr_10bit) { i2c->dev->fifo_data.data = (address >> 8) & 0xFF; dataSend--; } } i = 0; while(idev->fifo_data.data = data[index++]; } i2cSetCmd(i2c, 1, I2C_CMD_WRITE, willSend, false, false, true); dataLen -= willSend; //CMD STOP or CMD END if there is more data if(dataLen) { i2cSetCmd(i2c, 2, I2C_CMD_END, 0, false, false, false); } else if(sendStop) { i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false); } //Clear Interrupts i2c->dev->int_clr.val = 0xFFFFFFFF; //START Transmission i2c->dev->ctr.trans_start = 1; //WAIT Transmission while(1) { //Bus failed (maybe check for this while waiting? if(i2c->dev->int_raw.arbitration_lost) { //log_e("Bus Fail! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_BUS; } //Bus timeout if(i2c->dev->int_raw.time_out) { //log_e("Bus Timeout! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_TIMEOUT; } //Transmission did not finish and ACK_ERR is set if(i2c->dev->int_raw.ack_err) { //log_e("Ack Error! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_ACK; } if(i2c->dev->ctr.trans_start || i2c->dev->status_reg.bus_busy || !(i2c->dev->int_raw.trans_complete) || !(i2c->dev->command[2].done)) { continue; } else if(i2c->dev->command[2].done) { break; } } } I2C_MUTEX_UNLOCK(); return I2C_ERROR_OK; } i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data, uint8_t len, bool sendStop) { address = (address << 1) | 1; uint8_t addrLen = (addr_10bit?2:1); uint8_t index = 0; uint8_t cmdIdx; uint8_t willRead; if(i2c == NULL){ return I2C_ERROR_DEV; } I2C_MUTEX_LOCK(); i2cResetFiFo(i2c); i2cResetCmd(i2c); //CMD START i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); //CMD WRITE ADDRESS i2c->dev->fifo_data.data = address & 0xFF; if(addr_10bit) { i2c->dev->fifo_data.data = (address >> 8) & 0xFF; } i2cSetCmd(i2c, 1, I2C_CMD_WRITE, addrLen, false, false, true); while(len) { cmdIdx = (index)?0:2; willRead = (len > 32)?32:(len-1); if(cmdIdx){ i2cResetFiFo(i2c); } i2cSetCmd(i2c, cmdIdx++, I2C_CMD_READ, willRead, false, false, false); if((len - willRead) > 1) { i2cSetCmd(i2c, cmdIdx++, I2C_CMD_END, 0, false, false, false); } else { willRead++; i2cSetCmd(i2c, cmdIdx++, I2C_CMD_READ, 1, true, false, false); if(sendStop) { i2cSetCmd(i2c, cmdIdx++, I2C_CMD_STOP, 0, false, false, false); } } //Clear Interrupts i2c->dev->int_clr.val = 0xFFFFFFFF; //START Transmission i2c->dev->ctr.trans_start = 1; //WAIT Transmission while(1) { //Bus failed (maybe check for this while waiting? if(i2c->dev->int_raw.arbitration_lost) { //log_e("Bus Fail! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_BUS; } //Bus timeout if(i2c->dev->int_raw.time_out) { //log_e("Bus Timeout! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_TIMEOUT; } //Transmission did not finish and ACK_ERR is set if(i2c->dev->int_raw.ack_err) { //log_e("Ack Error! Addr: %x", address >> 1); I2C_MUTEX_UNLOCK(); return I2C_ERROR_ACK; } if(i2c->dev->ctr.trans_start || i2c->dev->status_reg.bus_busy || !(i2c->dev->int_raw.trans_complete) || !(i2c->dev->command[cmdIdx-1].done)) { continue; } else if(i2c->dev->command[cmdIdx-1].done) { break; } } int i = 0; while(idev->fifo_data.data; } len -= willRead; } I2C_MUTEX_UNLOCK(); return I2C_ERROR_OK; } i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) { uint32_t period = (APB_CLK_FREQ/clk_speed) / 2; if(i2c == NULL){ return I2C_ERROR_DEV; } I2C_MUTEX_LOCK(); i2c->dev->scl_low_period.scl_low_period = period; i2c->dev->scl_high_period.period = period; i2c->dev->scl_start_hold.time = 50; i2c->dev->scl_rstart_setup.time = 50; i2c->dev->scl_stop_hold.time = 50; i2c->dev->scl_stop_setup.time = 50; i2c->dev->sda_hold.time = 25; i2c->dev->sda_sample.time = 25; I2C_MUTEX_UNLOCK(); return I2C_ERROR_OK; } uint32_t i2cGetFrequency(i2c_t * i2c) { if(i2c == NULL){ return 0; } return APB_CLK_FREQ/(i2c->dev->scl_low_period.scl_low_period+i2c->dev->scl_high_period.period); } /* * mode - 0 = Slave, 1 = Master * slave_addr - I2C Address * addr_10bit_en - enable slave 10bit address mode. * */ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en) { if(i2c_num > 1){ return NULL; } i2c_t * i2c = &_i2c_bus_array[i2c_num]; #if !CONFIG_DISABLE_HAL_LOCKS if(i2c->lock == NULL){ i2c->lock = xSemaphoreCreateMutex(); if(i2c->lock == NULL) { return NULL; } } #endif if(i2c_num == 0) { SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT0_CLK_EN); CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT0_RST); } else { SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,DPORT_I2C_EXT1_CLK_EN); CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,DPORT_I2C_EXT1_RST); } I2C_MUTEX_LOCK(); i2c->dev->ctr.val = 0; i2c->dev->ctr.ms_mode = (slave_addr == 0); i2c->dev->ctr.sda_force_out = 1 ; i2c->dev->ctr.scl_force_out = 1 ; i2c->dev->ctr.clk_en = 1; i2c->dev->timeout.tout = 2000; i2c->dev->fifo_conf.nonfifo_en = 0; i2c->dev->slave_addr.val = 0; if (slave_addr) { i2c->dev->slave_addr.addr = slave_addr; i2c->dev->slave_addr.en_10bit = addr_10bit_en; } I2C_MUTEX_UNLOCK(); return i2c; }