//New version WH, Tested OK for Start and Repeated Start //Old version was Wrong: Calls i2c_start without setting address, i2c_do_read continues before checking status, status check for wrong value int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) { int count, status; //Store the address+RD and then generate STA I2C_DAT(obj) = address | 0x01; i2c_start(obj); // Wait for completion of STA and Sending of SlaveAddress+RD and first Read byte i2c_wait_SI(obj); status = i2c_status(obj); if (status == 0x03) { // NAK on SlaveAddress i2c_stop(obj); return I2C_ERROR_NO_SLAVE; } // Read in all except last byte for (count = 0; count < (length-1); count++) { // Wait for it to arrive, note that first byte read after address+RD is already waiting i2c_wait_SI(obj); status = i2c_status(obj); if (status != 0x01) { // RX RDY i2c_stop(obj); return count; } data[count] = I2C_DAT(obj) & 0xFF; // Store read byte obj->i2c->MSTCTL = (1 << 0); // Send ACK and Continue to read } // Read final byte // Wait for it to arrive i2c_wait_SI(obj); status = i2c_status(obj); if (status != 0x01) { // RX RDY i2c_stop(obj); return count; } data[count] = I2C_DAT(obj) & 0xFF; // Store final read byte // If not repeated start, send stop. if (stop) { i2c_stop(obj); // Also sends NAK for last read byte } else { repeated_start = 1; } return length; }
//New version WH, Tested OK for Start and Repeated Start //Old version was Wrong: Calls i2c_start without setting address first int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) { int i, status; //Store the address+/WR and then generate STA I2C_DAT(obj) = address & 0xFE; i2c_start(obj); // Wait for completion of STA and Sending of SlaveAddress+/WR i2c_wait_SI(obj); status = i2c_status(obj); if (status == 0x03) { // NAK SlaveAddress i2c_stop(obj); return I2C_ERROR_NO_SLAVE; } //Write all bytes for (i=0; i<length; i++) { status = i2c_do_write(obj, data[i], 0); if (status != 0x02) { // TX RDY. Handles a Slave NAK on datawrite i2c_stop(obj); return i; } } // If not repeated start, send stop. if (stop) { i2c_stop(obj); } else { repeated_start = 1; } return length; }
int i2c_slave_read(i2c_t *obj, char *data, int length) { int count = 0; int status; do { i2c_clear_SI(obj); i2c_wait_SI(obj); status = i2c_status(obj); if((status == 0x80) || (status == 0x90)) { data[count] = I2C_DAT(obj) & 0xFF; } count++; } while (((status == 0x80) || (status == 0x90) || (status == 0x060) || (status == 0x70)) && (count < length)); // Clear old status and wait for Serial Interrupt. i2c_clear_SI(obj); i2c_wait_SI(obj); // Obtain new status. status = i2c_status(obj); if(status != 0xA0) { i2c_stop(obj); } i2c_clear_SI(obj); return count; }
static inline int i2c_do_read(i2c_t *obj, int last) { // wait for it to arrive i2c_wait_SI(obj); if (!last) obj->i2c->MSTCTL = (1 << 0); // return the data return (I2C_DAT(obj) & 0xFF); }
static inline int i2c_do_write(i2c_t *obj, int value, uint8_t addr) { // write the data I2C_DAT(obj) = value; if (!addr) obj->i2c->MSTCTL = (1 << 0); // wait and return status i2c_wait_SI(obj); return i2c_status(obj); }
static inline int i2c_do_write(i2c_t *obj, int value, uint8_t addr) { // write the data I2C_DAT(obj) = value; // clear SI to init a send i2c_clear_SI(obj); // wait and return status i2c_wait_SI(obj); return i2c_status(obj); }
//Spec says: first check Idle and status is Ok static inline int i2c_do_write(i2c_t *obj, int value, uint8_t addr) { // write the data I2C_DAT(obj) = value; if (!addr) obj->i2c->MSTCTL = (1 << 0); //Set continue for data. Should not be set for addr since that uses STA // wait and return status i2c_wait_SI(obj); return i2c_status(obj); }
static inline int i2c_do_read(i2c_t *obj, int last) { // we are in state 0x40 (SLA+R tx'd) or 0x50 (data rx'd and ack) if(last) { i2c_conclr(obj, 0, 0, 0, 1); // send a NOT ACK } else { i2c_conset(obj, 0, 0, 0, 1); // send a ACK } // accept byte i2c_clear_SI(obj); // wait for it to arrive i2c_wait_SI(obj); // return the data return (I2C_DAT(obj) & 0xFF); }
int i2c_slave_read(i2c_t *obj, char *data, int length) { int count = 0; int status; do { i2c_clear_SI(obj); i2c_wait_SI(obj); status = i2c_status(obj); if((status == 0x80) || (status == 0x90)) { data[count] = I2C_DAT(obj) & 0xFF; } count++; } while (((status == 0x80) || (status == 0x90) || (status == 0x060) || (status == 0x70)) && (count < length)); if(status != 0xA0) { i2c_stop(obj); } i2c_clear_SI(obj); return count; }
//========================================================================== // transmit a buffer to a device //========================================================================== cyg_uint32 cyg_lpc2xxx_i2c_tx(const cyg_i2c_device *dev, cyg_bool send_start, const cyg_uint8 *tx_data, cyg_uint32 count, cyg_bool send_stop) { cyg_lpc2xxx_i2c_extra* extra = (cyg_lpc2xxx_i2c_extra*)dev->i2c_bus->i2c_extra; extra->i2c_addr = dev->i2c_address << 1; extra->i2c_count = count; extra->i2c_txbuf = tx_data; // // for a repeated start the SI bit has to be reset // if we continue a previous transfer, load the next byte // if(send_start) { SET_CON(extra, CON_STA); if (I2C_FLAG_ACT == extra->i2c_flag) { CLR_CON(extra, CON_SI); } } else { HAL_WRITE_UINT8(I2C_DAT(extra), *(extra->i2c_txbuf)); extra->i2c_txbuf++; CLR_CON(extra, CON_SI); } extra->i2c_flag = 0; // // the isr will do most of the work, and the dsr will signal when an // error occured or the transfer finished // cyg_drv_mutex_lock(&extra->i2c_lock); cyg_drv_dsr_lock(); cyg_drv_interrupt_unmask(I2C_ISRVEC(extra)); while(!(extra->i2c_flag & (I2C_FLAG_FINISH | I2C_FLAG_ERROR))) { cyg_drv_cond_wait(&extra->i2c_wait); } cyg_drv_interrupt_mask(I2C_ISRVEC(extra)); cyg_drv_dsr_unlock(); cyg_drv_mutex_unlock(&extra->i2c_lock); // too bad we have no way to tell the caller if(extra->i2c_flag & I2C_FLAG_ERROR) { debug1_printf("I2C TX error flag: %x\n", extra->i2c_flag); extra->i2c_flag = 0; } else { if(send_stop) { SET_CON(extra, CON_STO); CLR_CON(extra, CON_SI | CON_STA); extra->i2c_flag = 0; } else { extra->i2c_flag = I2C_FLAG_ACT; } } count -= extra->i2c_count; extra->i2c_addr = 0; extra->i2c_count = 0; extra->i2c_txbuf = NULL; return count; }
//========================================================================== // The ISR does the actual work. It is not that much work to justify // putting it in the DSR, and it is also not clear whether this would // even work. If an error occurs we try to leave the bus in the same // state as we would if there was no error. //========================================================================== static cyg_uint32 lpc2xxx_i2c_isr(cyg_vector_t vec, cyg_addrword_t data) { cyg_lpc2xxx_i2c_extra* extra = (cyg_lpc2xxx_i2c_extra*)data; cyg_uint8 status; I2C_R8(I2C_STAT(extra), status); switch(status) { case 0x00: // bus error, stop transfer SET_CON(extra, CON_STO); extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUS; break; case 0x08: // START sent, send Addr+R/W case 0x10: // ReSTART sent, send Addr+R/W CLR_CON(extra, CON_STA); I2C_W8(I2C_DAT(extra), extra->i2c_addr); break; case 0x18: // Addr ACKed, send data if(extra->i2c_txbuf == NULL) { extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUF; cyg_drv_interrupt_mask_intunsafe(vec); cyg_drv_interrupt_acknowledge(vec); return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; } I2C_W8(I2C_DAT(extra), *extra->i2c_txbuf); extra->i2c_txbuf++; break; case 0x28: // Data ACKed, send more extra->i2c_count--; if(extra->i2c_count == 0) { extra->i2c_flag = I2C_FLAG_FINISH; cyg_drv_interrupt_mask_intunsafe(vec); cyg_drv_interrupt_acknowledge(vec); return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; } I2C_W8(I2C_DAT(extra), *extra->i2c_txbuf); extra->i2c_txbuf++; break; case 0x50: // Data ACKed, receive more case 0x58: // Data not ACKed, end reception if(extra->i2c_rxbuf == NULL) { extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_BUF; cyg_drv_interrupt_mask_intunsafe(vec); cyg_drv_interrupt_acknowledge(vec); return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; } I2C_R8(I2C_DAT(extra), *extra->i2c_rxbuf); extra->i2c_rxbuf++; extra->i2c_count--; // fall through case 0x40: // Addr ACKed, receive data if(status == 0x58 || extra->i2c_count == 0) { extra->i2c_flag = I2C_FLAG_FINISH; cyg_drv_interrupt_mask_intunsafe(vec); cyg_drv_interrupt_acknowledge(vec); return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; } if((extra->i2c_count == 1) && extra->i2c_rxnak) { CLR_CON(extra, CON_AA); } else { SET_CON(extra, CON_AA); } break; case 0x20: // Addr not ACKed case 0x48: // Addr not ACKed SET_CON(extra, CON_STO); // tranfer failed - force stop extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_ADDR; cyg_drv_interrupt_mask_intunsafe(vec); cyg_drv_interrupt_acknowledge(vec); break; case 0x30: // Data not ACKed SET_CON(extra, CON_STO); // tranfer failed - force stop extra->i2c_count++; extra->i2c_txbuf--; extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_DATA; cyg_drv_interrupt_mask_intunsafe(vec); cyg_drv_interrupt_acknowledge(vec); return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; break; case 0x38: // Arbitration lost extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_LOST; break; default: // lots of unused states extra->i2c_flag = I2C_FLAG_ERROR | I2C_FLAG_UNK; break; } // switch(status) CLR_CON(extra, CON_SI); cyg_drv_interrupt_acknowledge(vec); // // We need to call the DSR only if there is really something to signal, // that means only if extra->i2c_flag != 0 // if (extra->i2c_flag) { return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR; } else { return CYG_ISR_HANDLED; } }