Hopefully fix I2C/Wire

Tested with (all at the same time):
- MPU9250
- HTU21
- TMP102
- BMP180
- AM2320
- SSD1306
This commit is contained in:
me-no-dev 2016-12-14 02:41:22 +02:00
parent e92634a92b
commit c19fc061b0
3 changed files with 71 additions and 25 deletions

View File

@ -158,6 +158,8 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * dat
i2cResetFiFo(i2c); i2cResetFiFo(i2c);
i2cResetCmd(i2c); i2cResetCmd(i2c);
//Clear Interrupts
i2c->dev->int_clr.val = 0xFFFFFFFF;
//CMD START //CMD START
i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false);
@ -174,26 +176,32 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * dat
i = 0; i = 0;
while(i<dataSend) { while(i<dataSend) {
i++; i++;
i2c->dev->fifo_data.data = data[index++]; i2c->dev->fifo_data.val = data[index++];
while(i2c->dev->status_reg.tx_fifo_cnt < i);
} }
i2cSetCmd(i2c, 1, I2C_CMD_WRITE, willSend, false, false, true); i2cSetCmd(i2c, 1, I2C_CMD_WRITE, willSend, false, false, true);
dataLen -= willSend; dataLen -= willSend;
//CMD STOP or CMD END if there is more data //CMD STOP or CMD END if there is more data
if(dataLen) { if(dataLen || !sendStop) {
i2cSetCmd(i2c, 2, I2C_CMD_END, 0, false, false, false); i2cSetCmd(i2c, 2, I2C_CMD_END, 0, false, false, false);
} else if(sendStop) { } else if(sendStop) {
i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false); i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false);
} }
//Clear Interrupts
i2c->dev->int_clr.val = 0xFFFFFFFF;
//START Transmission //START Transmission
i2c->dev->ctr.trans_start = 1; i2c->dev->ctr.trans_start = 1;
//WAIT Transmission //WAIT Transmission
uint32_t startAt = millis();
while(1) { while(1) {
//have been looping for too long
if((millis() - startAt)>50){
//log_e("Timeout! Addr: %x", address >> 1);
I2C_MUTEX_UNLOCK();
return I2C_ERROR_BUS;
}
//Bus failed (maybe check for this while waiting? //Bus failed (maybe check for this while waiting?
if(i2c->dev->int_raw.arbitration_lost) { if(i2c->dev->int_raw.arbitration_lost) {
//log_e("Bus Fail! Addr: %x", address >> 1); //log_e("Bus Fail! Addr: %x", address >> 1);
@ -210,14 +218,12 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * dat
//Transmission did not finish and ACK_ERR is set //Transmission did not finish and ACK_ERR is set
if(i2c->dev->int_raw.ack_err) { if(i2c->dev->int_raw.ack_err) {
//log_e("Ack Error! Addr: %x", address >> 1); //log_w("Ack Error! Addr: %x", address >> 1);
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return I2C_ERROR_ACK; 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)) { if((sendStop && i2c->dev->command[2].done) || !i2c->dev->status_reg.bus_busy){
continue;
} else if(i2c->dev->command[2].done) {
break; break;
} }
} }
@ -248,9 +254,9 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data
i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false); i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false);
//CMD WRITE ADDRESS //CMD WRITE ADDRESS
i2c->dev->fifo_data.data = address & 0xFF; i2c->dev->fifo_data.val = address & 0xFF;
if(addr_10bit) { if(addr_10bit) {
i2c->dev->fifo_data.data = (address >> 8) & 0xFF; i2c->dev->fifo_data.val = (address >> 8) & 0xFF;
} }
i2cSetCmd(i2c, 1, I2C_CMD_WRITE, addrLen, false, false, true); i2cSetCmd(i2c, 1, I2C_CMD_WRITE, addrLen, false, false, true);
@ -279,7 +285,15 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data
i2c->dev->ctr.trans_start = 1; i2c->dev->ctr.trans_start = 1;
//WAIT Transmission //WAIT Transmission
uint32_t startAt = millis();
while(1) { while(1) {
//have been looping for too long
if((millis() - startAt)>50){
//log_e("Timeout! Addr: %x", address >> 1);
I2C_MUTEX_UNLOCK();
return I2C_ERROR_BUS;
}
//Bus failed (maybe check for this while waiting? //Bus failed (maybe check for this while waiting?
if(i2c->dev->int_raw.arbitration_lost) { if(i2c->dev->int_raw.arbitration_lost) {
//log_e("Bus Fail! Addr: %x", address >> 1); //log_e("Bus Fail! Addr: %x", address >> 1);
@ -296,13 +310,12 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data
//Transmission did not finish and ACK_ERR is set //Transmission did not finish and ACK_ERR is set
if(i2c->dev->int_raw.ack_err) { if(i2c->dev->int_raw.ack_err) {
//log_e("Ack Error! Addr: %x", address >> 1); //log_w("Ack Error! Addr: %x", address >> 1);
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return I2C_ERROR_ACK; 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; if(i2c->dev->command[cmdIdx-1].done) {
} else if(i2c->dev->command[cmdIdx-1].done) {
break; break;
} }
} }
@ -310,7 +323,7 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data
int i = 0; int i = 0;
while(i<willRead) { while(i<willRead) {
i++; i++;
data[index++] = i2c->dev->fifo_data.data; data[index++] = i2c->dev->fifo_data.val & 0xFF;
} }
len -= willRead; len -= willRead;
} }
@ -320,24 +333,34 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, bool addr_10bit, uint8_t * data
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed)
{ {
uint32_t period = (APB_CLK_FREQ/clk_speed) / 2;
if(i2c == NULL){ if(i2c == NULL){
return I2C_ERROR_DEV; return I2C_ERROR_DEV;
} }
uint32_t period = (APB_CLK_FREQ/clk_speed) / 2;
uint32_t halfPeriod = period/2;
uint32_t quarterPeriod = period/4;
I2C_MUTEX_LOCK(); I2C_MUTEX_LOCK();
//the clock num during SCL is low level
i2c->dev->scl_low_period.scl_low_period = period; i2c->dev->scl_low_period.scl_low_period = period;
//the clock num during SCL is high level
i2c->dev->scl_high_period.period = period; i2c->dev->scl_high_period.period = period;
i2c->dev->scl_start_hold.time = 50; //the clock num between the negedge of SDA and negedge of SCL for start mark
i2c->dev->scl_rstart_setup.time = 50; 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;
i2c->dev->scl_stop_hold.time = 50; //the clock num after the STOP bit's posedge
i2c->dev->scl_stop_setup.time = 50; 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;
i2c->dev->sda_hold.time = 25; //the clock num I2C used to hold the data after the negedge of SCL.
i2c->dev->sda_sample.time = 25; 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(); I2C_MUTEX_UNLOCK();
return I2C_ERROR_OK; return I2C_ERROR_OK;
} }
@ -389,7 +412,9 @@ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en)
i2c->dev->ctr.scl_force_out = 1 ; i2c->dev->ctr.scl_force_out = 1 ;
i2c->dev->ctr.clk_en = 1; i2c->dev->ctr.clk_en = 1;
//the max clock number of receiving a data
i2c->dev->timeout.tout = 400000;//clocks max=1048575 i2c->dev->timeout.tout = 400000;//clocks max=1048575
//disable apb nonfifo access
i2c->dev->fifo_conf.nonfifo_en = 0; i2c->dev->fifo_conf.nonfifo_en = 0;
i2c->dev->slave_addr.val = 0; i2c->dev->slave_addr.val = 0;
@ -402,4 +427,19 @@ i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en)
return i2c; return i2c;
} }
void i2cInitFix(i2c_t * i2c){
if(i2c == NULL){
return;
}
I2C_MUTEX_LOCK();
i2cResetFiFo(i2c);
i2cResetCmd(i2c);
i2c->dev->int_clr.val = 0xFFFFFFFF;
i2cSetCmd(i2c, 0, I2C_CMD_RSTART, 0, false, false, false);
i2c->dev->fifo_data.data = 0;
i2cSetCmd(i2c, 1, I2C_CMD_WRITE, 1, false, false, false);
i2cSetCmd(i2c, 2, I2C_CMD_STOP, 0, false, false, false);
i2c->dev->ctr.trans_start = 1;
while(!i2c->dev->command[2].done);
I2C_MUTEX_UNLOCK();
}

View File

@ -35,6 +35,10 @@ typedef struct i2c_struct_t i2c_t;
i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en); i2c_t * i2cInit(uint8_t i2c_num, uint16_t slave_addr, bool addr_10bit_en);
//call this after you setup the bus and pins to send empty packet
//required because when pins are attached, they emit pulses that lock the bus
void i2cInitFix(i2c_t * i2c);
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed); i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed);
uint32_t i2cGetFrequency(i2c_t * i2c); uint32_t i2cGetFrequency(i2c_t * i2c);

View File

@ -86,6 +86,8 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
i2cAttachSCL(i2c, scl); i2cAttachSCL(i2c, scl);
flush(); flush();
i2cInitFix(i2c);
} }
void TwoWire::setClock(uint32_t frequency) void TwoWire::setClock(uint32_t frequency)