void I2C_Master_read(uint8_t slave_address, uint32_t * rxBuffer , uint32_t rxlength) { uint32_t rxCount; rxCount = rxlength; // Set Slave Address TWI1->TWI_MMR |= TWI_MMR_DADR(slave_address); // Set Master read TWI1->TWI_MMR |= TWI_MMR_MREAD; // Set Stop Bit prior to start if rxlength is equal to 1 if(rxlength == 1) TWI1->TWI_CR |= TWI_CR_STOP; // Start Communication TWI1->TWI_CR |= TWI_CR_START; // Receive data until previous to last datum while(rxCount > 1) { // Wait until byte received while(!(TWI1->TWI_SR & TWI_SR_RXRDY)); // store received data in rxBuffer *rxBuffer++ = TWI1->TWI_RHR; rxCount --; } // Set Stop Bit if rxlength was higher than 1 if(rxlength > 1) TWI1->TWI_CR |= TWI_CR_STOP; // Wait until last byte received while(!(TWI1->TWI_SR & TWI_SR_RXRDY)); // store last datum in rxBuffer *rxBuffer++ = TWI1->TWI_RHR; while(!(TWI1->TWI_SR & TWI_SR_TXCOMP)); }
/** * @brief Transmits data via the I2C bus as master. * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. * This is hardware restriction. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] addr slave device address * @param[in] txbuf pointer to the transmit buffer * @param[in] txbytes number of bytes to be transmitted * @param[out] rxbuf pointer to the receive buffer * @param[in] rxbytes number of bytes to be received * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_INFINITE no timeout. * . * @return The operation status. * @retval RDY_OK if the function succeeded. * @retval RDY_RESET if one or more I2C errors occurred, the errors can * be retrieved using @p i2cGetErrors(). * @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a * timeout the driver must be stopped and restarted * because the bus is in an uncertain state</b>. * * @notapi */ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, const uint8_t *txbuf, size_t txbytes, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { (void)i2cp; (void)addr; (void)txbuf; (void)txbytes; (void)rxbuf; (void)rxbytes; (void)timeout; i2cdef_t i2c = i2cp->i2c; uint32_t status; /* Set write mode, slave address and 3 internal address byte lengths */ i2c->TWI_MMR = 0; i2c->TWI_MMR = TWI_MMR_DADR(addr->chip) | ((addr->len << TWI_MMR_IADRSZ_Pos) & TWI_MMR_IADRSZ_Msk); /* Set internal address for remote chip */ i2c->TWI_IADR = 0; i2c->TWI_IADR = twi_mk_addr(addr->addr, addr->len); /* Send all bytes */ while (txbytes > 0) { status = i2c->TWI_SR; if (status & TWI_SR_NACK) { return RDY_RESET; } if (!(status & TWI_SR_TXRDY)) { continue; } i2c->TWI_THR = *txbuf++; txbytes--; } while (1) { status = i2c->TWI_SR; if (status & TWI_SR_NACK) { return RDY_RESET; } if (status & TWI_SR_TXRDY) { break; } } i2c->TWI_CR = TWI_CR_STOP; while (!(i2c->TWI_SR & TWI_SR_TXCOMP)) { } return RDY_OK; }
void twi_send(Twi *this_twi, wbuff *nwb, uint32_t address) { this_twi->TWI_MMR = TWI_MMR_DADR(address) | TWI_MMR_IADRSZ_1_BYTE; this_twi->TWI_IADR = TWI_IADR_IADR(nwb->buff[0]); this_twi->TWI_CR = TWI_CR_MSEN; this_twi->TWI_THR = nwb->buff[1]; this_twi->TWI_CR = TWI_CR_STOP; }
/** * \brief Read multiple bytes from a TWI compatible slave device. * * \note This function will NOT return until all data has been read or error occurs. * * \param p_twi Pointer to a TWI instance. * \param p_packet Packet information and data (see \ref twi_packet_t). * * \return TWI_SUCCESS if all bytes were read, error code otherwise. */ uint32_t twi_master_read(Twi *p_twi, twi_packet_t *p_packet) { uint32_t status; uint32_t cnt = p_packet->length; uint8_t *buffer = p_packet->buffer; uint8_t stop_sent = 0; /* Check argument */ if (cnt == 0) { return TWI_INVALID_ARGUMENT; } /* Set read mode, slave address and 3 internal address byte lengths */ p_twi->TWI_MMR = 0; p_twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(p_packet->chip) | ((p_packet->addr_length << TWI_MMR_IADRSZ_Pos) & TWI_MMR_IADRSZ_Msk); /* Set internal address for remote chip */ p_twi->TWI_IADR = 0; p_twi->TWI_IADR = twi_mk_addr(p_packet->addr, p_packet->addr_length); /* Send a START condition */ if (cnt == 1) { p_twi->TWI_CR = TWI_CR_START | TWI_CR_STOP; stop_sent = 1; } else { p_twi->TWI_CR = TWI_CR_START; stop_sent = 0; } while (cnt > 0) { status = p_twi->TWI_SR; if (status & TWI_SR_NACK) { return TWI_RECEIVE_NACK; } /* Last byte ? */ if (cnt == 1 && !stop_sent) { p_twi->TWI_CR = TWI_CR_STOP; stop_sent = 1; } if (!(status & TWI_SR_RXRDY)) { continue; } *buffer++ = p_twi->TWI_RHR; cnt--; } while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) { } p_twi->TWI_SR; return TWI_SUCCESS; }
/** * \brief Write multiple bytes to a TWI compatible slave device. * * \note This function will NOT return until all data has been written or error occurred. * * \param p_twi Pointer to a TWI instance. * \param p_packet Packet information and data (see \ref twi_packet_t). * * \return TWI_SUCCESS if all bytes were written, error code otherwise. */ uint32_t twi_master_write(Twi *p_twi, twi_packet_t *p_packet) { uint32_t status; uint32_t cnt = p_packet->length; uint8_t *buffer = p_packet->buffer; /* Check argument */ if (cnt == 0) { return TWI_INVALID_ARGUMENT; } /* Set write mode, slave address and 3 internal address byte lengths */ p_twi->TWI_MMR = 0; p_twi->TWI_MMR = TWI_MMR_DADR(p_packet->chip) | ((p_packet->addr_length << TWI_MMR_IADRSZ_Pos) & TWI_MMR_IADRSZ_Msk); /* Set internal address for remote chip */ p_twi->TWI_IADR = 0; p_twi->TWI_IADR = twi_mk_addr(p_packet->addr, p_packet->addr_length); /* Send all bytes */ while (cnt > 0) { status = p_twi->TWI_SR; if (status & TWI_SR_NACK) { return TWI_RECEIVE_NACK; } if (!(status & TWI_SR_TXRDY)) { continue; } p_twi->TWI_THR = *buffer++; cnt--; } while (1) { status = p_twi->TWI_SR; if (status & TWI_SR_NACK) { return TWI_RECEIVE_NACK; } if (status & TWI_SR_TXRDY) { break; } } p_twi->TWI_CR = TWI_CR_STOP; while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) { } return TWI_SUCCESS; }
static void write_msg_start(Twi *const twi, struct twi_msg *msg, u8_t daddr) { /* Set slave address and number of internal address bytes. */ twi->TWI_MMR = TWI_MMR_DADR(daddr); /* Write first data byte on I2C bus */ twi->TWI_THR = msg->buf[msg->idx++]; /* Enable Transmit Ready and Transmission Completed interrupts */ twi->TWI_IER = TWI_IER_TXRDY | TWI_IER_TXCOMP | TWI_IER_NACK; }
/** * @brief Receives data via the I2C bus as master. * @details Number of receiving bytes must be more than 1 on STM32F1x. This is * hardware restriction. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] addr slave device address * @param[out] rxbuf pointer to the receive buffer * @param[in] rxbytes number of bytes to be received * @param[in] timeout the number of ticks before the operation timeouts, * the following special values are allowed: * - @a TIME_INFINITE no timeout. * . * @return The operation status. * @retval RDY_OK if the function succeeded. * @retval RDY_RESET if one or more I2C errors occurred, the errors can * be retrieved using @p i2cGetErrors(). * @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a * timeout the driver must be stopped and restarted * because the bus is in an uncertain state</b>. * * @notapi */ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes, systime_t timeout) { (void)i2cp; (void)addr; (void)rxbuf; (void)rxbytes; (void)timeout; uint32_t status; uint32_t stop_sent; i2cdef_t i2c = i2cp->i2c; /* Set read mode, slave address and 3 internal address byte lengths */ i2c->TWI_MMR = 0; i2c->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(addr->chip) | ((addr->len << TWI_MMR_IADRSZ_Pos) & TWI_MMR_IADRSZ_Msk); /* Set internal address for remote chip */ i2c->TWI_IADR = 0; i2c->TWI_IADR = twi_mk_addr(addr->addr, addr->len); /* Send a START condition */ if (rxbytes = 1) { i2c->TWI_CR = TWI_CR_START | TWI_CR_STOP; while (!(i2c->TWI_SR & TWI_SR_RXRDY)); *rxbuf = i2c->TWI_RHR; } else { i2c->TWI_PTCR = TWI_PTCR_RXTDIS | TWI_PTCR_TXTDIS; i2c->TWI_IER = TWI_IER_ENDRX | TWI_IER_NACK | TWI_IER_OVRE; chSysLock(); i2cp->thread = chThdSelf(); i2c->TWI_RNPR = 0; i2c->TWI_RNCR = 0; i2c->TWI_RPR = (uint32_t) rxbuf; i2c->TWI_RCR = rxbytes - 2; i2c->TWI_PTCR = TWI_PTCR_RXTEN; i2cp->curbuf = rxbuf + (rxbytes - 2); i2c->TWI_CR = TWI_CR_START; chSchGoSleepS(THD_STATE_SUSPENDED); return chThdSelf()->p_u.rdymsg; } return RDY_OK; }
/************************************************************************* Issues a start condition and sends address and transfer direction. If device is busy, use ack polling to wait until device is ready Input: address and transfer direction of I2C device *************************************************************************/ void HAL::i2cStartWait(unsigned char address_and_direction) { uint32_t twiDirection = address_and_direction & 1; uint32_t address = address_and_direction >> 1; while(!(TWI_INTERFACE->TWI_SR & TWI_SR_TXCOMP)); // set to master mode TWI_INTERFACE->TWI_CR = TWI_CR_MSEN | TWI_CR_SVDIS; // set master mode register with no internal address TWI_INTERFACE->TWI_MMR = 0; TWI_INTERFACE->TWI_MMR = (twiDirection << 12) | TWI_MMR_IADRSZ_NONE | TWI_MMR_DADR(address); }
static void read_msg_start(Twi *const twi, struct twi_msg *msg, u8_t daddr) { u32_t twi_cr_stop; /* Set slave address and number of internal address bytes */ twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(daddr); /* In single data byte read the START and STOP must both be set */ twi_cr_stop = (msg->len == 1) ? TWI_CR_STOP : 0; /* Start the transfer by sending START condition */ twi->TWI_CR = TWI_CR_START | twi_cr_stop; /* Enable Receive Ready and Transmission Completed interrupts */ twi->TWI_IER = TWI_IER_RXRDY | TWI_IER_TXCOMP | TWI_IER_NACK; }
/************************************************************************* Issues a start condition and sends address and transfer direction. return 0 = device accessible, 1= failed to access device *************************************************************************/ unsigned char HAL::i2cStart(unsigned char address_and_direction) { uint32_t twiDirection = address_and_direction & 1; uint32_t address = address_and_direction >> 1; // set master mode register with no internal address TWI_INTERFACE->TWI_MMR = 0; TWI_INTERFACE->TWI_MMR = (twiDirection << 12) | TWI_MMR_IADRSZ_NONE | TWI_MMR_DADR(address); TWI_INTERFACE->TWI_CR = TWI_CR_MSEN | TWI_CR_SVDIS; //set master mode disable slave mode if (twiDirection) TWI_INTERFACE->TWI_CR = TWI_CR_START; //send Start Bit for receiving data // returning readiness to send/recieve not device accessibility // return value not used in code anyway return !(TWI_INTERFACE->TWI_SR & TWI_SR_TXCOMP); }
static int eeprom_read(uint32_t EE_DEV_ADDR, uint32_t ee_off, void * buf, uint32_t size) { uint8_t *bufptr = (uint8_t *)buf; uint32_t status; uint32_t count; /* Clean out any old status and received byte. */ status = RD4HW(AT91RM92_TWI_BASE, TWI_SR); status = RD4HW(AT91RM92_TWI_BASE, TWI_RHR); /* Set the TWI Master Mode Register */ WR4HW(AT91RM92_TWI_BASE, TWI_MMR, TWI_MMR_DADR(EE_DEV_ADDR) | TWI_MMR_IADRSZ(2) | TWI_MMR_MREAD); /* Set TWI Internal Address Register */ WR4HW(AT91RM92_TWI_BASE, TWI_IADR, ee_off); /* Start transfer */ WR4HW(AT91RM92_TWI_BASE, TWI_CR, TWI_CR_START); status = RD4HW(AT91RM92_TWI_BASE, TWI_SR); while (size-- > 1){ /* Wait until Receive Holding Register is full */ count = 1000000; while (!(RD4HW(AT91RM92_TWI_BASE, TWI_SR) & TWI_SR_RXRDY) && --count != 0) continue; if (count <= 0) return -1; /* Read and store byte */ *bufptr++ = (uint8_t)RD4HW(AT91RM92_TWI_BASE, TWI_RHR); } WR4HW(AT91RM92_TWI_BASE, TWI_CR, TWI_CR_STOP); status = RD4HW(AT91RM92_TWI_BASE, TWI_SR); /* Wait until transfer is finished */ while (!(RD4HW(AT91RM92_TWI_BASE, TWI_SR) & TWI_SR_TXCOMP)) continue; /* Read last byte */ *bufptr = (uint8_t)RD4HW(AT91RM92_TWI_BASE, TWI_RHR); return 0; }
/************************************************************************* Issues a start condition and sends address and transfer direction. If device is busy, use ack polling to wait until device is ready Input: address and transfer direction of I2C device *************************************************************************/ void HAL::i2cStartWait(unsigned char address_and_direction) { uint32_t twiDirection = address_and_direction & 1; uint32_t address = address_and_direction >> 1; while (!(TWI_INTERFACE->TWI_SR & TWI_SR_TXCOMP)); // set master mode register with no internal address TWI_INTERFACE->TWI_MMR = 0; TWI_INTERFACE->TWI_MMR = (twiDirection << 12) | TWI_MMR_IADRSZ_NONE | TWI_MMR_DADR(address); TWI_INTERFACE->TWI_CR = TWI_CR_MSEN | TWI_CR_SVDIS; //set master mode disable slave mode if (twiDirection) TWI_INTERFACE->TWI_CR = TWI_CR_START;//send Start Bit for receiving data }
void I2C_Master_write(uint8_t slave_address, uint32_t * txBuffer , uint32_t txlength, uint8_t stop_enable) { uint32_t txCount; txCount = txlength; // Set Slave Address TWI1->TWI_MMR |= TWI_MMR_DADR(slave_address); // Set Master write TWI1->TWI_MMR &= ~(TWI_MMR_MREAD); while (txCount) { // Wait until TWI ready to transmit while(!(TWI1->TWI_SR & TWI_SR_TXRDY)); // store received data in rxBuffer TWI1->TWI_THR = * txBuffer++ ; txCount --; } // Set Stop Bit if enabled if (stop_enable) { TWI1->TWI_CR |= TWI_CR_STOP; while(!(TWI1->TWI_SR & TWI_SR_TXCOMP)); } }
{ #if EEPROM_AVAILABLE == EEPROM_I2C uint32_t twiDirection = address_and_direction & 1; uint32_t address = address_and_direction >> 1; // if 1 byte address, eeprom uses lower address bits for pos > 255 if (EEPROM_ADDRSZ_BYTES == TWI_MMR_IADRSZ_1_BYTE) { address |= pos >> 8; pos &= 0xFF; } // set master mode register with internal address TWI_INTERFACE->TWI_MMR = 0; TWI_INTERFACE->TWI_MMR = (twiDirection << 12) | EEPROM_ADDRSZ_BYTES | TWI_MMR_DADR(address); // write internal address register TWI_INTERFACE->TWI_IADR = 0; TWI_INTERFACE->TWI_IADR = TWI_IADR_IADR(pos); if (twiDirection) TWI_INTERFACE->TWI_CR = TWI_CR_START;//send Start Bit for receiving data #endif } /************************************************************************* Terminates the data transfer and releases the I2C bus *************************************************************************/ void HAL::i2cStop(void) { while ( (TWI_INTERFACE->TWI_SR & TWI_SR_TXRDY) != TWI_SR_TXRDY);//wait for transmission finished
static int at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct at91_twi_softc *sc; int i, len, err; uint32_t rdwr; uint8_t *buf; uint32_t sr; sc = device_get_softc(dev); err = 0; AT91_TWI_LOCK(sc); for (i = 0; i < nmsgs; i++) { /* * The linux atmel driver doesn't use the internal device * address feature of twi. A separate i2c message needs to * be written to use this. * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html * for details. Upon reflection, we could use this as an * optimization, but it is unclear the code bloat will * result in faster/better operations. */ rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0; WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr); len = msgs[i].len; buf = msgs[i].buf; /* zero byte transfers aren't allowed */ if (len == 0 || buf == NULL) { err = EINVAL; goto out; } if (len == 1 && msgs[i].flags & IIC_M_RD) WR4(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP); else WR4(sc, TWI_CR, TWI_CR_START); if (msgs[i].flags & IIC_M_RD) { sr = RD4(sc, TWI_SR); while (!(sr & TWI_SR_TXCOMP)) { if ((sr = RD4(sc, TWI_SR)) & TWI_SR_RXRDY) { len--; *buf++ = RD4(sc, TWI_RHR) & 0xff; if (len == 1) WR4(sc, TWI_CR, TWI_CR_STOP); } } if (len > 0 || (sr & TWI_SR_NACK)) { err = ENXIO; // iic nack convention goto out; } } else { while (len--) { if ((err = at91_twi_wait(sc, TWI_SR_TXRDY))) goto out; WR4(sc, TWI_THR, *buf++); } WR4(sc, TWI_CR, TWI_CR_STOP); } if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP))) break; } out: if (err) { WR4(sc, TWI_CR, TWI_CR_SWRST); WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); WR4(sc, TWI_CWGR, sc->cwgr); } AT91_TWI_UNLOCK(sc); return (err); }
/** * \ingroup freertos_twi_peripheral_control_group * \brief Initiate a completely asynchronous multi-byte write operation on a TWI * peripheral. * * freertos_twi_write_packet_async() is an ASF specific FreeRTOS driver function. * It configures the TWI peripheral DMA controller (PDC) to transmit data on the * TWI port, then returns. freertos_twi_write_packet_async() does not wait for * the transmission to complete before returning. * * The FreeRTOS TWI driver is initialized using a call to * freertos_twi_master_init(). The freertos_driver_parameters.options_flags * parameter passed into the initialization function defines the driver behavior. * freertos_twi_write_packet_async() can only be used if the * freertos_driver_parameters.options_flags parameter passed to the initialization * function had the WAIT_TX_COMPLETE bit clear. It can also only be used if packet * length is more than 1. * * freertos_twi_write_packet_async() is an advanced function and readers are * recommended to also reference the application note and examples that * accompany the FreeRTOS ASF drivers. freertos_twi_write_packet() is a version * that does not exit until the PDC transfer is complete, but still allows other * RTOS tasks to execute while the transmission is in progress. * * The FreeRTOS ASF driver both installs and handles the TWI PDC interrupts. * Users do not need to concern themselves with interrupt handling, and must * not install their own interrupt handler. * * \param p_twi The handle to the TWI peripheral returned by the * freertos_twi_master_init() call used to initialise the peripheral. * \param p_packet Structure that defines the TWI transfer parameters, such * as the I2C chip being addressed, the source data location, and the number * of bytes to transmit. twi_packet_t is a standard ASF type (it is not * FreeRTOS specific). * \param block_time_ticks The FreeRTOS ASF TWI driver is initialized using a * call to freertos_twi_master_init(). The * freertos_driver_parameters.options_flags parameter passed to the * initialization function defines the driver behavior. If * freertos_driver_parameters.options_flags had the USE_TX_ACCESS_MUTEX bit * set, then the driver will only write to the TWI peripheral if it has * first gained exclusive access to it. block_time_ticks specifies the * maximum amount of time the driver will wait to get exclusive access * before aborting the write operation. Other tasks will execute during any * waiting time. block_time_ticks is specified in RTOS tick periods. To * specify a block time in milliseconds, divide the milliseconds value by * portTICK_RATE_MS, and pass the result in block_time_ticks. * portTICK_RATE_MS is defined by FreeRTOS. * \param notification_semaphore The RTOS task that calls the transmit * function exits the transmit function as soon as the transmission starts. * The data being transmitted by the PDC must not be modified until after * the transmission has completed. The PDC interrupt (handled internally by * the FreeRTOS ASF driver) 'gives' the semaphore when the PDC transfer * completes. The notification_semaphore therefore provides a mechanism for * the calling task to know when the PDC has finished accessing the data. * The calling task can call standard FreeRTOS functions to block on the * semaphore until the PDC interrupt occurs. Other RTOS tasks will execute * while the the calling task is in the Blocked state. The semaphore must * be created using the FreeRTOS vSemaphoreCreateBinary() API function * before it is used as a parameter. * * \return ERR_INVALID_ARG is returned if an input parameter is invalid. * ERR_TIMEOUT is returned if block_time_ticks passed before exclusive * access to the TWI peripheral could be obtained. STATUS_OK is returned if * the PDC was successfully configured to perform the TWI write operation. */ status_code_t freertos_twi_write_packet_async(freertos_twi_if p_twi, twi_packet_t *p_packet, portTickType block_time_ticks, xSemaphoreHandle notification_semaphore) { status_code_t return_value; portBASE_TYPE twi_index; Twi *twi_base; uint32_t internal_address = 0; twi_base = (Twi *) p_twi; twi_index = get_pdc_peripheral_details(all_twi_definitions, MAX_TWIS, (void *) twi_base); /* Don't do anything unless a valid TWI pointer was used. */ if ((twi_index < MAX_TWIS) && (p_packet->length > 0)) { return_value = freertos_obtain_peripheral_access_mutex( &(tx_dma_control[twi_index]), &block_time_ticks); if (return_value == STATUS_OK) { /* Set write mode and slave address. */ twi_base->TWI_MMR = 0; twi_base->TWI_MMR = TWI_MMR_DADR(p_packet->chip) | ((p_packet->addr_length << TWI_MMR_IADRSZ_Pos) & TWI_MMR_IADRSZ_Msk); /* Set internal address if any. */ if (p_packet->addr_length > 0) { internal_address = p_packet->addr[0]; if (p_packet->addr_length > 1) { internal_address <<= 8; internal_address |= p_packet->addr[1]; } if (p_packet->addr_length > 2) { internal_address <<= 8; internal_address |= p_packet->addr[2]; } } twi_base->TWI_IADR = internal_address; if (p_packet->length == 1) { uint32_t status; uint32_t timeout_counter = 0; /* Do not handle errors for short packets in interrupt handler */ twi_disable_interrupt( all_twi_definitions[twi_index].peripheral_base_address, IER_ERROR_INTERRUPTS); /* Send start condition */ twi_base->TWI_THR = *((uint8_t*)(p_packet->buffer)); while (1) { status = twi_base->TWI_SR; if (status & TWI_SR_NACK) { /* Re-enable interrupts */ twi_enable_interrupt( all_twi_definitions[twi_index].peripheral_base_address, IER_ERROR_INTERRUPTS); /* Release semaphore */ xSemaphoreGive(tx_dma_control[twi_index].peripheral_access_mutex); return ERR_BUSY; } if (status & TWI_SR_TXRDY) { break; } /* Check timeout condition. */ if (++timeout_counter >= TWI_TIMEOUT_COUNTER) { return_value = ERR_TIMEOUT; break; } } twi_base->TWI_CR = TWI_CR_STOP; /* Wait for TX complete */ while (!(twi_base->TWI_SR & TWI_SR_TXCOMP)) { /* Check timeout condition. */ if (++timeout_counter >= TWI_TIMEOUT_COUNTER) { return_value = ERR_TIMEOUT; break; } } /* Re-enable interrupts */ twi_enable_interrupt( all_twi_definitions[twi_index].peripheral_base_address, IER_ERROR_INTERRUPTS); /* Release semaphores */ xSemaphoreGive(tx_dma_control[twi_index].peripheral_access_mutex); if (return_value != ERR_TIMEOUT) { if (tx_dma_control[twi_index].transaction_complete_notification_semaphore != NULL) { xSemaphoreGive(tx_dma_control[twi_index].transaction_complete_notification_semaphore); } } } else { twis[twi_index].buffer = p_packet->buffer; twis[twi_index].length = p_packet->length; freertos_start_pdc_tx(&(tx_dma_control[twi_index]), p_packet->buffer, p_packet->length - 1, all_twi_definitions[twi_index].pdc_base_address, notification_semaphore); /* Catch the end of transmission so the access mutex can be returned, and the task notified (if it supplied a notification semaphore). The interrupt can be enabled here because the ENDTX signal from the PDC to the peripheral will have been de-asserted when the next transfer was configured. */ twi_enable_interrupt(twi_base, TWI_IER_ENDTX); return_value = freertos_optionally_wait_transfer_completion( &(tx_dma_control[twi_index]), notification_semaphore, block_time_ticks); } } } else { return_value = ERR_INVALID_ARG; } return return_value; }
/** * \ingroup freertos_twi_peripheral_control_group * \brief Initiate a completely asynchronous multi-byte read operation on an TWI * peripheral. * * freertos_twi_read_packet_async() is an ASF specific FreeRTOS driver function. * It configures the TWI peripheral DMA controller (PDC) to read data from the * TWI port, then returns. freertos_twi_read_packet_async() does not wait for * the reception to complete before returning. * * The FreeRTOS ASF TWI driver is initialized using a call to * freertos_twi_master_init(). The freertos_driver_parameters.options_flags * parameter passed into the initialization function defines the driver behavior. * freertos_twi_read_packet_async() can only be used if the * freertos_driver_parameters.options_flags parameter passed to the initialization * function had the WAIT_RX_COMPLETE bit clear. The function can also only be used * if the length of the packet is more that two. If less, it will block until the * transfer is done. * * freertos_twi_read_packet_async() is an advanced function and readers are * recommended to also reference the application note and examples that * accompany the FreeRTOS ASF drivers. freertos_twi_read_packet() is a version * that does not exit until the PDC transfer is complete, but still allows other * RTOS tasks to execute while the transmission is in progress. * * The FreeRTOS ASF driver both installs and handles the TWI PDC interrupts. * Users do not need to concern themselves with interrupt handling, and must * not install their own interrupt handler. * * \param p_twi The handle to the TWI port returned by the * freertos_twi_master_init() call used to initialise the port. * \param p_packet Structure that defines the TWI transfer parameters, such * as the I2C chip being addressed, the destination for the data being read, * and the number of bytes to read. twi_packet_t is a standard ASF type (it * is not FreeRTOS specific). * \param block_time_ticks The FreeRTOS ASF TWI driver is initialized using a * call to freertos_twi_master_init(). The * freertos_driver_parameters.options_flags parameter passed to the * initialization function defines the driver behavior. If * freertos_driver_parameters.options_flags had the USE_RX_ACCESS_MUTEX bit * set, then the driver will only read from the TWI peripheral if it has * first gained exclusive access to it. block_time_ticks specifies the * maximum amount of time the driver will wait to get exclusive access * before aborting the read operation. Other tasks will execute during any * waiting time. block_time_ticks is specified in RTOS tick periods. To * specify a block time in milliseconds, divide the milliseconds value by * portTICK_RATE_MS, and pass the result in block_time_ticks. * portTICK_RATE_MS is defined by FreeRTOS. * \param notification_semaphore The RTOS task that calls the receive * function exits the receive function as soon as the reception starts. * The data being received by the PDC cannot normally be processed until * after the reception has completed. The PDC interrupt (handled internally * by the FreeRTOS ASF driver) 'gives' the semaphore when the PDC transfer * completes. The notification_semaphore therefore provides a mechanism for * the calling task to know when the PDC has read the requested number of * bytes. The calling task can call standard FreeRTOS functions to block on * the semaphore until the PDC interrupt occurs. Other RTOS tasks will * execute while the the calling task is in the Blocked state. The * semaphore must be created using the FreeRTOS vSemaphoreCreateBinary() API * function before it is used as a parameter. * * \return ERR_INVALID_ARG is returned if an input parameter is invalid. * ERR_TIMEOUT is returned if block_time_ticks passed before exclusive * access to the TWI peripheral could be obtained. STATUS_OK is returned if * the PDC was successfully configured to perform the TWI read operation. */ status_code_t freertos_twi_read_packet_async(freertos_twi_if p_twi, twi_packet_t *p_packet, portTickType block_time_ticks, xSemaphoreHandle notification_semaphore) { status_code_t return_value; portBASE_TYPE twi_index; Twi *twi_base; uint32_t internal_address = 0; twi_base = (Twi *) p_twi; twi_index = get_pdc_peripheral_details(all_twi_definitions, MAX_TWIS, (void *) twi_base); /* Don't do anything unless a valid TWI pointer was used. */ if ((twi_index < MAX_TWIS) && (p_packet->length > 0)) { /* Because the peripheral is half duplex, there is only one access mutex and the rx uses the tx mutex. */ return_value = freertos_obtain_peripheral_access_mutex( &(tx_dma_control[twi_index]), &block_time_ticks); if (return_value == STATUS_OK) { /* Ensure Rx is already empty. */ twi_read_byte(twi_base); /* Set read mode and slave address. */ twi_base->TWI_MMR = 0; twi_base->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR( p_packet->chip) | ((p_packet->addr_length << TWI_MMR_IADRSZ_Pos) & TWI_MMR_IADRSZ_Msk); /* Set internal address if any. */ if (p_packet->addr_length) { internal_address = p_packet->addr [0]; if (p_packet->addr_length > 1) { internal_address <<= 8; internal_address |= p_packet->addr[1]; } if (p_packet->addr_length > 2) { internal_address <<= 8; internal_address |= p_packet->addr[2]; } } twi_base->TWI_IADR = internal_address; if (p_packet->length <= 2) { /* Do not handle errors for short packets in interrupt handler */ twi_disable_interrupt( all_twi_definitions[twi_index].peripheral_base_address, IER_ERROR_INTERRUPTS); /* Cannot use PDC transfer, use normal transfer */ uint8_t stop_sent = 0; uint32_t cnt = p_packet->length; uint32_t status; uint8_t *buffer = p_packet->buffer; uint32_t timeout_counter = 0; /* Start the transfer. */ if (cnt == 1) { twi_base->TWI_CR = TWI_CR_START | TWI_CR_STOP; stop_sent = 1; } else { twi_base->TWI_CR = TWI_CR_START; } while (cnt > 0) { status = twi_base->TWI_SR; if (status & TWI_SR_NACK) { /* Re-enable interrupts */ twi_enable_interrupt( all_twi_definitions[twi_index].peripheral_base_address, IER_ERROR_INTERRUPTS); /* Release semaphore */ xSemaphoreGive(tx_dma_control[twi_index].peripheral_access_mutex); return ERR_BUSY; } /* Last byte ? */ if (cnt == 1 && !stop_sent) { twi_base->TWI_CR = TWI_CR_STOP; stop_sent = 1; } if (!(status & TWI_SR_RXRDY)) { if (++timeout_counter >= TWI_TIMEOUT_COUNTER) { return_value = ERR_TIMEOUT; break; } continue; } *buffer++ = twi_base->TWI_RHR; cnt--; timeout_counter = 0; } timeout_counter = 0; /* Wait for stop to be sent */ while (!(twi_base->TWI_SR & TWI_SR_TXCOMP)) { /* Check timeout condition. */ if (++timeout_counter >= TWI_TIMEOUT_COUNTER) { return_value = ERR_TIMEOUT; break; } } /* Re-enable interrupts */ twi_enable_interrupt( all_twi_definitions[twi_index].peripheral_base_address, IER_ERROR_INTERRUPTS); /* Release semaphores */ xSemaphoreGive(tx_dma_control[twi_index].peripheral_access_mutex); if (return_value != ERR_TIMEOUT) { if (rx_dma_control[twi_index].transaction_complete_notification_semaphore != NULL) { xSemaphoreGive(rx_dma_control[twi_index].transaction_complete_notification_semaphore); } } } else { /* Start the PDC reception. */ twis[twi_index].buffer = p_packet->buffer; twis[twi_index].length = p_packet->length; freertos_start_pdc_rx(&(rx_dma_control[twi_index]), p_packet->buffer, (p_packet->length)-2, all_twi_definitions[twi_index].pdc_base_address, notification_semaphore); /* Start the transfer. */ twi_base->TWI_CR = TWI_CR_START; /* Catch the end of reception so the access mutex can be returned, and the task notified (if it supplied a notification semaphore). The interrupt can be enabled here because the ENDRX signal from the PDC to the peripheral will have been de-asserted when the next transfer was configured. */ twi_enable_interrupt(twi_base, TWI_IER_ENDRX); return_value = freertos_optionally_wait_transfer_completion( &(rx_dma_control[twi_index]), notification_semaphore, block_time_ticks); } } } else { return_value = ERR_INVALID_ARG; } return return_value; }
int32_t i2c_send_sequence( uint8_t ch, uint16_t *sequence, uint32_t sequence_length, uint8_t *received_data, void ( *callback_fn )( void* ), void *user_data ) { int32_t result = 0; volatile I2C_Channel *channel = &( i2c_channels[ch - ISSI_I2C_FirstBus_define] ); uint8_t address; #if defined(_kinetis_) uint8_t status; volatile uint8_t *I2C_C1 = (uint8_t*)(&I2C0_C1) + i2c_offset[ch]; volatile uint8_t *I2C_S = (uint8_t*)(&I2C0_S) + i2c_offset[ch]; volatile uint8_t *I2C_D = (uint8_t*)(&I2C0_D) + i2c_offset[ch]; #elif defined(_sam_) Twi *twi_dev = twi_devs[ch]; #endif if ( channel->status == I2C_BUSY ) { return -1; } // Check if there are back-to-back errors // in succession if ( channel->last_error > 5 ) { warn_msg("I2C Bus Error: "); printInt8( ch ); print(" errors: "); printInt32( channel->error_count ); print( NL ); } // Debug /* for ( uint8_t c = 0; c < sequence_length; c++ ) { printHex( sequence[c] ); print(" "); } print(NL); */ channel->sequence = sequence; channel->sequence_end = sequence + sequence_length; channel->received_data = received_data; channel->status = I2C_BUSY; channel->txrx = I2C_WRITING; channel->callback_fn = callback_fn; channel->user_data = user_data; // reads_ahead does not need to be initialized #if defined(_kinetis_) // Acknowledge the interrupt request, just in case *I2C_S |= I2C_S_IICIF; *I2C_C1 = ( I2C_C1_IICEN | I2C_C1_IICIE ); // Generate a start condition and prepare for transmitting. *I2C_C1 |= ( I2C_C1_MST | I2C_C1_TX ); status = *I2C_S; if ( status & I2C_S_ARBL ) { warn_print("Arbitration lost"); result = -1; goto i2c_send_sequence_cleanup; } // Write the first (address) byte. address = *channel->sequence++; *I2C_D = address; // Everything is OK. return result; i2c_send_sequence_cleanup: // Record error, and reset last error counter channel->error_count++; channel->last_error++; // Generate STOP and disable further interrupts. *I2C_C1 &= ~( I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX ); channel->status = I2C_ERROR; #elif defined(_sam_) // Convert 8 bit address to 7bit + RW address = *channel->sequence++; //print("Address: "); //printHex( address ); //print( NL ); uint8_t mread = address & 1; address >>= 1; // Set slave address twi_dev->TWI_MMR = TWI_MMR_DADR(address) | (mread ? TWI_MMR_MREAD : 0); // Enable interrupts twi_dev->TWI_IER = TWI_IER_RXRDY | TWI_IER_TXRDY | TWI_IER_TXCOMP | TWI_IER_ARBLST; //twi_dev->TWI_IDR = 0xFFFFFFFF; // Generate a start condition twi_dev->TWI_CR |= TWI_CR_START; // Fire off the first read or write. // The first (address) byte is automatically trasmitted before any data // Arbitration errors will be handled in the isr i2c_isr(ch); // Everything is OK. return result; #endif return result; }