Don't like the SPI protocol, when you read from an I2C device, you also need to send some register address to the device.
There are some helper functions to illustrate the process.
long long rx_ind = 0;
uint16_t rx_buf[300];
static int gi2c_read_blocking_internal(i2c_inst_t *i2c, uint8_t addr, uint8_t *dst, size_t len, bool nostop,
check_timeout_fn timeout_check, timeout_state_t *ts) {
invalid_params_if(I2C, addr >= 0x80); // 7-bit addresses
invalid_params_if(I2C, i2c_reserved_addr(addr));
invalid_params_if(I2C, len == 0);
invalid_params_if(I2C, ((int)len) < 0);
int kk = rx_ind;
i2c->hw->enable = 0;
i2c->hw->tar = addr;
i2c->hw->enable = 1;
bool abort = false;
bool timeout = false;
uint32_t abort_reason;
int byte_ctr;
int ilen = (int)len;
for (byte_ctr = 0; byte_ctr < ilen; ++byte_ctr) {
bool first = byte_ctr == 0;
bool last = byte_ctr == ilen - 1;
while (!i2c_get_write_available(i2c))
tight_loop_contents();
rx_buf[rx_ind++] =
bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB |
I2C_IC_DATA_CMD_CMD_BITS;
i2c->hw->data_cmd =
bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB |
I2C_IC_DATA_CMD_CMD_BITS; // -> 1 for read
do {
abort_reason = i2c->hw->tx_abrt_source;
abort = (bool) i2c->hw->clr_tx_abrt;
if (timeout_check) {
timeout = timeout_check(ts);
abort |= timeout;
}
} while (!abort && !i2c_get_read_available(i2c));
if (abort)
break;
*dst++ = (uint8_t) i2c->hw->data_cmd;
rx_buf[rx_ind++] = *(dst-1);
}
int rval;
if (abort) {
if (timeout)
rval = PICO_ERROR_TIMEOUT;
else if (!abort_reason || abort_reason & I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS) {
// No reported errors - seems to happen if there is nothing connected to the bus.
// Address byte not acknowledged
rval = PICO_ERROR_GENERIC;
} else {
// panic("Unknown abort from I2C instance @%08x: %08x\n", (uint32_t) i2c->hw, abort_reason);
rval = PICO_ERROR_GENERIC;
}
} else {
rval = byte_ctr;
}
i2c->restart_on_next = nostop;
do{
printf("rx %#08x\n", rx_buf[kk++]);
} while (rx_ind > kk);
return rval;
}
long long tx_ind = 0;
uint16_t tx_buf[300];
static int gi2c_write_blocking_internal(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop,
check_timeout_fn timeout_check, struct timeout_state *ts) {
invalid_params_if(I2C, addr >= 0x80); // 7-bit addresses
invalid_params_if(I2C, i2c_reserved_addr(addr));
// Synopsys hw accepts start/stop flags alongside data items in the same
// FIFO word, so no 0 byte transfers.
invalid_params_if(I2C, len == 0);
invalid_params_if(I2C, ((int)len) < 0);
int kk = tx_ind;
i2c->hw->enable = 0;
i2c->hw->tar = addr;
i2c->hw->enable = 1;
bool abort = false;
bool timeout = false;
uint32_t abort_reason = 0;
int byte_ctr;
int ilen = (int)len;
for (byte_ctr = 0; byte_ctr < ilen; ++byte_ctr) {
bool first = byte_ctr == 0;
bool last = byte_ctr == ilen - 1;
tx_buf[tx_ind++] =
bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB |
*src;
i2c->hw->data_cmd =
bool_to_bit(first && i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
bool_to_bit(last && !nostop) << I2C_IC_DATA_CMD_STOP_LSB |
*src++;
// Wait until the transmission of the address/data from the internal
// shift register has completed. For this to function correctly, the
// TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag
// was set in i2c_init.
do {
if (timeout_check) {
timeout = timeout_check(ts);
abort |= timeout;
}
tight_loop_contents();
} while (!timeout && !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_EMPTY_BITS));
// If there was a timeout, don't attempt to do anything else.
if (!timeout) {
abort_reason = i2c->hw->tx_abrt_source;
if (abort_reason) {
// Note clearing the abort flag also clears the reason, and
// this instance of flag is clear-on-read! Note also the
// IC_CLR_TX_ABRT register always reads as 0.
i2c->hw->clr_tx_abrt;
abort = true;
}
if (abort || (last && !nostop)) {
// If the transaction was aborted or if it completed
// successfully wait until the STOP condition has occured.
// TODO Could there be an abort while waiting for the STOP
// condition here? If so, additional code would be needed here
// to take care of the abort.
do {
if (timeout_check) {
timeout = timeout_check(ts);
abort |= timeout;
}
tight_loop_contents();
} while (!timeout && !(i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_STOP_DET_BITS));
// If there was a timeout, don't attempt to do anything else.
if (!timeout) {
i2c->hw->clr_stop_det;
}
}
}
// Note the hardware issues a STOP automatically on an abort condition.
// Note also the hardware clears RX FIFO as well as TX on abort,
// because we set hwparam IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.
if (abort)
break;
}
int rval;
// A lot of things could have just happened due to the ingenious and
// creative design of I2C. Try to figure things out.
if (abort) {
if (timeout)
rval = PICO_ERROR_TIMEOUT;
else if (!abort_reason || abort_reason & I2C_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK_BITS) {
// No reported errors - seems to happen if there is nothing connected to the bus.
// Address byte not acknowledged
rval = PICO_ERROR_GENERIC;
} else if (abort_reason & I2C_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK_BITS) {
// Address acknowledged, some data not acknowledged
rval = byte_ctr;
} else {
//panic("Unknown abort from I2C instance @%08x: %08x\n", (uint32_t) i2c->hw, abort_reason);
rval = PICO_ERROR_GENERIC;
}
} else {
rval = byte_ctr;
}
// nostop means we are now at the end of a *message* but not the end of a *transfer*
i2c->restart_on_next = nostop;
do{
printf("tx %#08x\n", tx_buf[kk++]);
} while (tx_ind > kk);
return rval;
}
int gi2c_write_blocking(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop) {
return gi2c_write_blocking_internal(i2c, addr, src, len, nostop, NULL, NULL);
}
int gi2c_read_blocking(i2c_inst_t *i2c, uint8_t addr, uint8_t *dst, size_t len, bool nostop) {
return gi2c_read_blocking_internal(i2c, addr, dst, len, nostop, NULL, NULL);
}
// Write 1 byte to the specified register
int reg_write( i2c_inst_t *i2c,
const uint addr,
const uint8_t reg,
uint8_t *buf,
const uint8_t nbytes) {
int num_bytes_read = 0;
uint8_t msg[nbytes + 1];
// Check to make sure caller is sending 1 or more bytes
if (nbytes < 1) {
return 0;
}
// Append register address to front of data packet
msg[0] = reg;
for (int i = 0; i < nbytes; i++) {
msg[i + 1] = buf[i];
}
printf("write func.\n");
// Write data to register(s) over I2C
gi2c_write_blocking(i2c, addr, msg, (nbytes + 1), false);
return num_bytes_read;
}
// Read byte(s) from specified register. If nbytes > 1, read from consecutive
// registers.
int reg_read( i2c_inst_t *i2c,
const uint addr,
const uint8_t reg,
uint8_t *buf,
const uint8_t nbytes) {
int num_bytes_read = 0;
// Check to make sure caller is asking for 1 or more bytes
if (nbytes < 1) {
return 0;
}
printf("write func.\n");
// Read data from register(s) over I2C
gi2c_write_blocking(i2c, addr, ®, 1, true);
printf("read func.\n");
num_bytes_read = gi2c_read_blocking(i2c, addr, buf, nbytes, false);
return num_bytes_read;
}
In gi2c_read_blocking_internal
function, you can see first you need to write the i2c->hw->data_cmd
to control the stop, restart... states in I2C protocol, and then you can read from i2c->hw->data_cmd
to get information from I2C device.
In reg_read
function, first send the register address by call gi2c_write_blocking
, and then you can read the register by call gi2c_read_blocking
After look up the whole process, you need two dma channel trigger simultaneously to simulate the reading from I2C device process(one for write i2c->hw->data_cmd
and one for read i2c->hw->data_cmd
).