static void eeprom_poll_ack(HW_I2C_ID id) { /* * Writing to 24LC256 EEPROM fills internal buffer with data and starts actual write cycle * once it received STOP condition for a write command. Another read or write can be handled * by EEPROM only after it completes write cycle and this can be checked by ACK pooling * (described in datasheet) which basically is sending write command to EEPROM as long as it * does not return ACK - once ACK is received, it means write cycle has completed. */ do { /* * Make sure TX abort is reset, this is because at the beginning EEPROM will not ACK * address byte so we'll have a TX abort set in controller. */ hw_i2c_reset_int_tx_abort(id); /* * We only need to check if EEPROM returns ACK for address byte, however we cannot * simply send address byte since it's controller who takes care of this once TX FIFO * is filled with data. A simple solution is to send 'dummy' byte which will be * ignored by EEPROM but will make I2C controller to generate START condition and * send address byte. */ hw_i2c_write_byte(id, 0xAA); /* * Before transmission status can be checked, we have to be sure that controller * finished sending 'dummy' byte and received ACK (or not). */ while (hw_i2c_is_master_busy(id)) { } /* * No check it transmission was successful - if EEPROM did not ACK address byte, * appropriate abort source will be set. */ } while (hw_i2c_get_abort_source(id) & HW_I2C_ABORT_7B_ADDR_NO_ACK); }
BOOL hw_i2c_master_tx(const uint8_t *buf, uint8_t buf_len) { BOOL error = FALSE; cpu_disable_int(); #if USE_HARDWARE_I2C while ((buf_len > 0) && !error) { TWDR = *buf++; TWCR = TWICR_TWEN | TWICR_TWINT; while ((TWCR & TWICR_TWINT) == 0); error = (TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK; buf_len--; } #else while ((buf_len > 0) && !error) { hw_i2c_write_byte(*buf++); error = hw_i2c_read_bit() != 0; buf_len--; } #endif cpu_enable_int(); return !error; }
static void fm75_write_reg(uint8_t reg, const uint8_t *val, uint8_t len) { size_t wr_status = 0; HW_I2C_ABORT_SOURCE abrt_src = HW_I2C_ABORT_NONE; /* * The first writing byte informs to which register rest data will be written. */ hw_i2c_write_byte(HW_I2C1, reg); wr_status = hw_i2c_write_buffer_sync(HW_I2C1, val, len, &abrt_src, HW_I2C_F_WAIT_FOR_STOP); if ((wr_status < (ssize_t)len) || (abrt_src != HW_I2C_ABORT_NONE)) { printf("fm75 write failure: %u" NEWLINE, abrt_src); } }
static void fm75_read_reg(uint8_t reg, uint8_t *val, uint8_t len) { size_t rd_status = 0; HW_I2C_ABORT_SOURCE abrt_src = HW_I2C_ABORT_NONE; /* * Before reading values from sensor registers we need to send one byte information to it * to inform which sensor register will be read now. */ hw_i2c_write_byte(HW_I2C1, reg); rd_status = hw_i2c_read_buffer_sync(HW_I2C1, val, len, &abrt_src, HW_I2C_F_NONE); if ((rd_status < (size_t)len) || (abrt_src != HW_I2C_ABORT_NONE)) { printf("fm75 read failure: %u" NEWLINE, abrt_src); } }
bool hw_i2c_write_buffer(HW_I2C_ID id, const uint8_t *data, uint16_t len, hw_i2c_complete_cb cb, void *cb_data, bool wait_for_stop) { if (!data) { return false; } if (!cb) { while (len--) { while (!hw_i2c_is_tx_fifo_not_full(id)); hw_i2c_write_byte(id, *data); data++; if (hw_i2c_get_abort_source(id)) { return false; } } while (!hw_i2c_is_tx_fifo_empty(id)); while (hw_i2c_is_master_busy(id)); if (hw_i2c_get_abort_source(id)) { return false; } } else { struct i2c *i2c = get_i2c(id); i2c->tx_state.data = data; i2c->tx_state.len = len; i2c->tx_state.num = 0; i2c->tx_state.cb = cb; i2c->tx_state.cb_data = cb_data; i2c->tx_state.flags = wait_for_stop? HW_I2C_F_WAIT_FOR_STOP : HW_I2C_F_NONE; hw_i2c_reset_int_tx_abort(id); if (wait_for_stop) { hw_i2c_reset_int_stop_detected(id); } hw_i2c_register_int(id, intr_write_buffer_handler, HW_I2C_INT_TX_EMPTY | (wait_for_stop ? HW_I2C_INT_STOP_DETECTED : 0) | HW_I2C_INT_TX_ABORT); /* we want TX_EMPTY as soon as FIFO is not completely full */ hw_i2c_set_tx_fifo_threshold(id, I2C_FIFO_DEPTH - 1); } return true; }
BOOL hw_i2c_master_start(uint8_t address, hw_i2c_rdwr_t hw_i2c_rd_wr) { #if USE_HARDWARE_I2C BOOL error = FALSE; uint8_t twsr; cpu_disable_int(); TWCR = TWICR_TWEN | TWICR_TWINT | TWICR_TWSTA; while ((TWCR & TWICR_TWINT) == 0); twsr = TWSR & TW_STATUS_MASK; if ((twsr != TW_START) && (twsr != TW_REP_START)) { error = TRUE; } else { TWDR = (address & 0xfe) | hw_i2c_rd_wr; TWCR = TWICR_TWEN | TWICR_TWINT; while ((TWCR & TWICR_TWINT) == 0); twsr = TWSR & TW_STATUS_MASK; if (hw_i2c_rd_wr == hw_i2c_rd) { error = twsr != TW_MR_SLA_ACK; } else { error = twsr != TW_MT_SLA_ACK; } } cpu_enable_int(); return !error; #else cpu_disable_int(); delay_T2(); if (hold_bus) { sda_hi(); delay_T2(); scl_hi(); delay_T2(); } sda_low(); delay_T2(); scl_low(); hold_bus = TRUE; hw_i2c_write_byte((address & 0xfe) | hw_i2c_rd_wr); return hw_i2c_read_bit() == 0; // will call cpu_enable_int() #endif }
static void intr_write_buffer_handler(HW_I2C_ID id, uint16_t mask) { struct i2c *i2c = get_i2c(id); struct tx_state *txs = &i2c->tx_state; if (!txs->data || mask == 0) { return; } if (mask & HW_I2C_INT_TX_ABORT) { tx_reply(id, false); /* clear abort */ hw_i2c_reset_int_tx_abort(id); return; } if (mask & HW_I2C_INT_STOP_DETECTED) { tx_reply(id, txs->num == txs->len); hw_i2c_reset_int_stop_detected(id); return; } if (!(mask & HW_I2C_INT_TX_EMPTY)) { tx_reply(id, false); return; } while (txs->num < txs->len && hw_i2c_is_tx_fifo_not_full(id)) { hw_i2c_write_byte(id, txs->data[txs->num]); txs->num++; } /* * trigger reply when all data were written to TX FIFO and either TX FIFO is empty * (controller will generate STOP condition on bus) or caller requested immediate callback * (caller can continue with another transfer immediately). */ if (txs->num == txs->len) { if (txs->flags & HW_I2C_F_WAIT_FOR_STOP) { hw_i2c_set_int_mask(id, hw_i2c_get_int_mask(id) & ~HW_I2C_INT_TX_EMPTY); } else { tx_reply(id, true); } } }
size_t hw_i2c_write_buffer_sync(HW_I2C_ID id, const uint8_t *data, uint16_t len, HW_I2C_ABORT_SOURCE *abrt_code, uint32_t flags) { HW_I2C_ABORT_SOURCE ret = HW_I2C_ABORT_NONE; size_t offst = 0; if (!data || len == 0) { ret = HW_I2C_ABORT_SW_ERROR; } else { while (len--) { while (!hw_i2c_is_tx_fifo_not_full(id)); hw_i2c_write_byte(id, data[offst++]); ret = hw_i2c_get_abort_source(id); if (ret) { break; } } if (!ret && (flags & HW_I2C_F_WAIT_FOR_STOP)) { while (!hw_i2c_is_tx_fifo_empty(id)); while (hw_i2c_is_master_busy(id)); ret = hw_i2c_get_abort_source(id); } } if (abrt_code) { *abrt_code = ret; } if (ret) { hw_i2c_reset_int_tx_abort(id); } return offst; }