I am trying to write a driver for the CC3200's I2C library in master mode. I want to use interrupts - no polling. However, my code does not work when only a single byte is written to the slave - the message is sent on the bus, but the I2C_MASTER_INT_TX_FIFO_EMPTY interrupt fires constantly. If I try to write multiple bytes of data, then the I2C_MASTER_INT_TX_FIFO_REQ interrupt fires once per byte, which is what I want (although other interrupts fire too). I must figure this out so I can do Write-Then-Read operations.
Below is an excerpt from my driver, containing the initialization and read functions, as well as interrupt service routine and applicable global variables. Anyone see something I'm doing wrong?
#define I2C_READ 1 // address bit to read from I2C device #define I2C_WRITE 0 // address bit to write to I2C device volatile uint8_t *txDataPtr; // pointer to TX data volatile uint32_t txByteCount; // num bytes to TX volatile uint32_t txBytesDone; // num bytes sent volatile uint8_t *rxDataPtr; // pointer to RX data volatile uint32_t rxByteCount; // num bytes to RX volatile uint32_t rxBytesDone; // num bytes received static uint8_t i2cAddress; // I2C address of slave /****************************************************************************** * * Function: I2C_ISR * * Description: This is the interrupt service routine registered to the * CC3200's I2C peripheral. * *****************************************************************************/ static void I2C_ISR(void) { uint32_t isrSourceMask; // bit-flags for interrupt source // Identify the source of the interrupt by reading Masked Interrupt Status. // The second argument of this function allows us to specify if we want // to mask the results so only enabled interrupts are returned to us. isrSourceMask = I2CMasterIntStatusEx(I2CA0_BASE, TRUE); // Search for a bit that is set, and deal with it. Other bits will trigger // the ISR after this function returns, and will be dealt with later. So // this block sets the precedence of the interrupt sources. // Also, we must clear the interrupt bit of anything we service, or it will // cause another interrupt once this function exits. if (isrSourceMask & I2C_MASTER_INT_RX_FIFO_REQ) { // Clear the interrupt flag. MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_RX_FIFO_REQ); // Process received data. if (ISMASKCLEAR(MAP_I2CFIFOStatus(I2CA0_BASE), I2C_FIFO_RX_EMPTY)) { // Fetch data from the FIFO. MAP_I2CFIFODataGetNonBlocking(I2CA0_BASE, (uint8_t *)&rxDataPtr[rxBytesDone]); // Increment the counter. if (rxByteCount > rxBytesDone) rxBytesDone++; // If we're done reading... if (rxByteCount == rxBytesDone) { // If we're done, sent STOP. if (txByteCount == 0) MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH); // If we're doing a Read-Then-Write, configure the FIFO // and then send a re-START. else { // Set the I2C address + WRITE bit. MAP_I2CMasterSlaveAddrSet(I2CA0_BASE, i2cAddress, I2C_WRITE); // Set the length of the operation. MAP_I2CMasterBurstLengthSet(I2CA0_BASE, txByteCount); // Place the first byte into the FIFO. MAP_I2CFIFODataPutNonBlocking(I2CA0_BASE, txDataPtr[0]); // Initiate I2C write operation. MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_FIFO_BURST_SEND_START | I2C_MASTER_CMD_FIFO_BURST_SEND_ERROR_STOP); } } } } else if (isrSourceMask & I2C_MASTER_INT_TX_FIFO_REQ) { // Clear the interrupt flag. MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_TX_FIFO_REQ); // Initiate sending of more data (maybe?). if (MAP_I2CFIFOStatus(I2CA0_BASE) & I2C_FIFO_TX_EMPTY) { MAP_I2CFIFODataPutNonBlocking(I2CA0_BASE, txDataPtr[txBytesDone]); if (txByteCount > txBytesDone) txBytesDone++; // If we're done with the write... if (txByteCount == txBytesDone) { // If we're done, send STOP. if (rxByteCount == 0) MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH); // If we're doing a Write-Then-Read, configure the FIFO // and then send a re-START. else { // Set the I2C address + READ bit. MAP_I2CMasterSlaveAddrSet(I2CA0_BASE, i2cAddress, I2C_READ); // Set the length of the operation. MAP_I2CMasterBurstLengthSet(I2CA0_BASE, rxByteCount); // Initiate I2C read operation. MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_FIFO_BURST_RECEIVE_START | I2C_MASTER_CMD_FIFO_BURST_RECEIVE_ERROR_STOP); } } } } // These interrupts are not used in this sample. else if (isrSourceMask & I2C_MASTER_INT_RX_FIFO_FULL) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_RX_FIFO_FULL); else if (isrSourceMask & I2C_MASTER_INT_TX_FIFO_EMPTY) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_TX_FIFO_EMPTY); // This interrupt is disabled in my example code. else if (isrSourceMask & I2C_MASTER_INT_ARB_LOST) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_ARB_LOST); // This interrupt fires whenever a START condition occurs. else if (isrSourceMask & I2C_MASTER_INT_START) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_START); // This interrupt fires whenever a STOP condition occurs. else if (isrSourceMask & I2C_MASTER_INT_STOP) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_STOP); // This interrupt fires whenever a NACK condition occurs. else if (isrSourceMask & I2C_MASTER_INT_NACK) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_NACK); // This interrupt seems to never fire. else if (isrSourceMask & I2C_MASTER_INT_TIMEOUT) MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_TIMEOUT); // This interrupt will fire every time there is a transfer of data. // I'm using it to check for errors, and then reset the bus if an error // occurs. else if (isrSourceMask & I2C_MASTER_INT_DATA) { // Clear the interrupt flag. MAP_I2CMasterIntClearEx(I2CA0_BASE, I2C_MASTER_INT_DATA); // Check for errors. if(MAP_I2CMasterErr(I2CA0_BASE) != I2C_MASTER_ERR_NONE) { // Send STOP to reset the bus. MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_BURST_SEND_STOP); } } } /****************************************************************************** * * Function: I2CInit * * Description: Initialize the specified I2C with the provided settings. * If the I2C was initialized previously, I2CDisable() must be * called before re-initializing. * * Parameters: slaveAddress - address of device we wish to talk to * fastModeFlag - setting for fast mode * *****************************************************************************/ void I2CInit(uint8_t slaveAddress, bool fastModeFlag) { // Enable clock to I2C. MAP_PRCMPeripheralClkEnable(PRCM_I2CA0, PRCM_RUN_MODE_CLK); // Reset the I2C peripheral. MAP_PRCMPeripheralReset(PRCM_I2CA0); // Initialize the I2C peripheral. // (Also enables master mode.) MAP_I2CMasterInitExpClk(I2CA0_BASE, I2C_CLOCK_FREQ_HZ, fastModeFlag); // Disable master mode (because Enable function will turn it on later). MAP_I2CMasterDisable(I2CA0_BASE); // Set the time-out. Not to be used with breakpoints. MAP_I2CMasterTimeoutSet(I2CA0_BASE, I2C_TIMEOUT_VAL); // Store address for later. i2cAddress = slaveAddress; // Enable the I2C master module. MAP_I2CMasterEnable(I2CA0_BASE); // Register software ISR with the DriverLib. MAP_I2CIntRegister(I2CA0_BASE, I2C_ISR); // Clear all interrupts. MAP_I2CMasterIntClearEx(I2CA0_BASE, MAP_I2CMasterIntStatusEx(I2CA0_BASE, FALSE)); // Enable interrupts. MAP_I2CMasterIntEnableEx(I2CA0_BASE, // I2C_MASTER_INT_RX_FIFO_FULL | // RX FIFO is full I2C_MASTER_INT_RX_FIFO_REQ | // RX FIFO service required // I2C_MASTER_INT_TX_FIFO_EMPTY | // TX FIFO is empty I2C_MASTER_INT_TX_FIFO_REQ | // TX FIFO service required // I2C_MASTER_INT_NACK | // no acknowledgment received // I2C_MASTER_INT_START | // START sequence received // I2C_MASTER_INT_STOP | // STOP sequence received I2C_MASTER_INT_TIMEOUT | // timeout I2C_MASTER_INT_DATA | // data transaction complete I2C_MASTER_INT_RX_FIFO_REQ | // FIFO I2C_MASTER_INT_TX_FIFO_REQ | I2C_MASTER_INT_TX_FIFO_EMPTY | I2C_MASTER_INT_RX_FIFO_FULL); // Flush FIFOs. MAP_I2CTxFIFOFlush(I2CA0_BASE); MAP_I2CRxFIFOFlush(I2CA0_BASE); // Enable FIFOs. MAP_I2CTxFIFOConfigSet(I2CA0_BASE, I2C_FIFO_CFG_TX_MASTER | I2C_FIFO_CFG_TX_TRIG_1); MAP_I2CRxFIFOConfigSet(I2CA0_BASE, I2C_FIFO_CFG_RX_MASTER | I2C_FIFO_CFG_RX_TRIG_1); } /****************************************************************************** * * Function: I2CWrite * * Description: Perform a read transaction on the bus (as a master). * * Parameters: dataArray - array of bytes to write to device * count - number of bytes to write * *****************************************************************************/ void I2CWrite(uint8_t *dataArray, uint32_t count) { // Set TX array start address. txDataPtr = dataArray; // Set TX byte count & reset count of bytes sent. // Note that before we finish this function, we'll have sent 1 byte. txByteCount = count; txBytesDone = 1; // Indicate that we're not reading. rxByteCount = 0; // Set the I2C address + WRITE bit. MAP_I2CMasterSlaveAddrSet(I2CA0_BASE, i2cAddress, I2C_WRITE); // Set the length of the operation. MAP_I2CMasterBurstLengthSet(I2CA0_BASE, count); // Place the first byte into the FIFO. MAP_I2CFIFODataPutNonBlocking(I2CA0_BASE, txDataPtr[0]); // Initiate I2C write operation. MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_FIFO_BURST_SEND_START | I2C_MASTER_CMD_FIFO_BURST_SEND_ERROR_STOP); }