I was having troubles talking to a i2c chip using the current version of libmaple's Wire software I2C implementation (commit as of Oct 20, 2011), and put together this patch that should fix the timing issues I saw, and also implements true multi-byte reads.
Hopefully this helps someone.
--- Wire/Wire.cpp 2011-10-28 14:25:32.000000000 -0600
+++ old/Wire/Wire.cpp 2011-10-28 14:12:33.000000000 -0600
@@ -63,45 +63,48 @@
if (!digitalRead(port.sda)) {
I2C_DELAY;
+ digitalWrite(port.sda, LOW);
digitalWrite(port.scl,LOW);
return true;
} else {
I2C_DELAY;
+ digitalWrite(port.sda, LOW);
digitalWrite(port.scl,LOW);
return false;
}
}
void i2c_send_ack(Port port) {
- I2C_DELAY;
digitalWrite(port.sda,LOW);
- I2C_DELAY;
digitalWrite(port.scl,HIGH);
I2C_DELAY;
digitalWrite(port.scl,LOW);
}
void i2c_send_nack(Port port) {
- I2C_DELAY;
digitalWrite(port.sda,HIGH);
- I2C_DELAY;
digitalWrite(port.scl,HIGH);
+ I2C_DELAY;
+ digitalWrite(port.scl,LOW);
+ digitalWrite(port.sda,LOW);
}
uint8 i2c_shift_in(Port port) {
- uint8 data = 0;
-
- int i;
- for (i=0;i<8;i++) {
- I2C_DELAY;
- digitalWrite(port.scl,HIGH);
- I2C_DELAY;
- data += digitalRead(port.sda) << (7-i);
- I2C_DELAY;
- digitalWrite(port.scl,LOW);
- }
-
- return data;
+ char x = 0;
+ uint8 data = 0;
+ digitalWrite(port.sda,HIGH);
+ for(x=0; x<8; x++) {
+ data <<= 1;
+ do {
+ digitalWrite(port.scl,HIGH);
+ }
+ while((digitalRead(port.scl))==0); // wait for any SCL clock stretching
+ I2C_DELAY;
+ if((digitalRead(port.sda))) data |= 1;
+ digitalWrite(port.scl,LOW);
+ I2C_DELAY;
+ }
+ return data;
}
void i2c_shift_out(Port port, uint8 val) {
@@ -110,9 +113,9 @@
I2C_DELAY;
digitalWrite(port.sda, !!(val & (1 << (7 - i))));
I2C_DELAY;
- digitalWrite(port.scl, HIGH);
+ digitalWrite(port.scl,HIGH);
I2C_DELAY;
- digitalWrite(port.scl, LOW);
+ digitalWrite(port.scl,LOW);
}
}
@@ -161,16 +164,18 @@
uint8 TwoWire::endTransmission(void) {
if (tx_buf_overflow) return EDATA;
- i2c_start(port);
+ if(tx_buf_idx > 0) {
+ i2c_start(port);
- i2c_shift_out(port, (tx_addr << 1) | I2C_WRITE);
- if (!i2c_get_ack(port)) return ENACKADDR;
+ i2c_shift_out(port, (tx_addr << 1) | I2C_WRITE);
+ if (!i2c_get_ack(port)) return ENACKADDR;
- // shift out the address we're transmitting to
- for (uint8 i = 0; i < tx_buf_idx; i++) {
- uint8 ret = writeOneByte(tx_buf[i]);
- if (ret) return ret; // SUCCESS is 0
- }
+ // shift out the address we're transmitting to
+ for (uint8 i = 0; i < tx_buf_idx; i++) {
+ uint8 ret = writeOneByte(tx_buf[i]);
+ if (ret) return ret; // SUCCESS is 0
+ }
+ }
i2c_stop(port);
@@ -179,7 +184,7 @@
return SUCCESS;
}
-uint8 TwoWire::requestFrom(uint8 address, int num_bytes) {
+/*uint8 TwoWire::requestFrom(uint8 address, int num_bytes) {
if (num_bytes > WIRE_BUFSIZ) num_bytes = WIRE_BUFSIZ;
rx_buf_idx = 0;
@@ -189,6 +194,33 @@
else break;
}
return rx_buf_len;
+}*/
+
+uint8 TwoWire::requestFrom(uint8 address, int num_bytes) {
+ if (num_bytes > WIRE_BUFSIZ) num_bytes = WIRE_BUFSIZ;
+
+ rx_buf_idx = 0;
+ rx_buf_len = 0;
+
+ i2c_start(port);
+
+ i2c_shift_out(port, (address << 1) | I2C_READ);
+ if (!i2c_get_ack(port)) return ENACKADDR;
+
+ while (rx_buf_len < num_bytes) {
+ i2c_start(port);
+
+ rx_buf[rx_buf_len++] = i2c_shift_in(port);
+ if(rx_buf_len == num_bytes)
+ i2c_send_nack(port);
+ else
+ i2c_send_ack(port);
+
+ }
+
+ i2c_stop(port);
+
+ return rx_buf_len;
}
uint8 TwoWire::requestFrom(int address, int numBytes) {