void STM32_I2C_EV_Interrupt (void* param) // Event Interrupt Handler
{
    INTERRUPT_START
    
    // pre:
    // I2C_SR1_SB | I2C_SR1_ADDR | I2C_SR1_BTF | I2C_CR2_ITBUFEN & (I2C_SR1_RXNE | I2C_SR1_TXE)
    
    I2C_HAL_XACTION* xAction = currentI2CXAction;
    I2C_HAL_XACTION_UNIT* unit = currentI2CUnit;
    
    int todo = unit->m_bytesToTransfer;
    int sr1 = I2Cx->SR1;  // read status register
    int sr2 = I2Cx->SR2;  // clear ADDR bit
    int cr1 = I2Cx->CR1;  // initial control register
    
    if (unit->IsReadXActionUnit()) { // read transaction
        if (sr1 & I2C_SR1_SB) { // start bit
            if (todo == 1) {
                I2Cx->CR1 = (cr1 &= ~I2C_CR1_ACK); // last byte nack
            } else if (todo == 2) {
                I2Cx->CR1 = (cr1 |= I2C_CR1_POS); // prepare 2nd byte nack
            }
            UINT8 addr = xAction->m_address << 1; // address bits
            I2Cx->DR = addr + 1; // send header byte with read bit;
        } else {
            if (sr1 & I2C_SR1_ADDR) { // address sent
                if (todo == 1) {
                    I2Cx->CR1 = (cr1 |= I2C_CR1_STOP); // send stop after single byte
                } else if (todo == 2) {
                    I2Cx->CR1 = (cr1 &= ~I2C_CR1_ACK); // last byte nack
                }
            } else {
                while (sr1 & I2C_SR1_RXNE) { // data available
                    if (todo == 2) { // 2 bytes remaining
                        I2Cx->CR1 = (cr1 |= I2C_CR1_STOP); // stop after last byte
                    } else if (todo == 3) { // 3 bytes remaining
                        if (!(sr1 & I2C_SR1_BTF)) break; // assure 2 bytes are received
                        I2Cx->CR1 = (cr1 &= ~I2C_CR1_ACK); // last byte nack
                    }
                    UINT8 data = I2Cx->DR; // read data
                    *(unit->m_dataQueue.Push()) = data; // save data
                    unit->m_bytesTransferred++;
                    unit->m_bytesToTransfer = --todo; // update todo
                    sr1 = I2Cx->SR1;  // update status register copy
                }
            }
            if (todo == 1) {
                I2Cx->CR2 |= I2C_CR2_ITBUFEN; // enable I2C_SR1_RXNE interrupt
            }
        }
    } else { // write transaction
        if (sr1 & I2C_SR1_SB) { // start bit
            UINT8 addr = xAction->m_address << 1; // address bits
            I2Cx->DR = addr; // send header byte with write bit;
        } else {
            while (todo && (sr1 & I2C_SR1_TXE)) {
                I2Cx->DR = *(unit->m_dataQueue.Pop()); // next data byte;
                unit->m_bytesTransferred++;
                unit->m_bytesToTransfer = --todo; // update todo
                sr1 = I2Cx->SR1;  // update status register copy
            }
            if (!(sr1 & I2C_SR1_BTF)) todo++; // last byte not yet sent
        }
    }
    
    if (todo == 0) { // all received or all sent
        if (!xAction->ProcessingLastUnit()) { // start next unit
            I2Cx->CR2 &= ~I2C_CR2_ITBUFEN; // disable I2C_SR1_RXNE interrupt
            currentI2CUnit = xAction->m_xActionUnits[ xAction->m_current++ ];
            I2Cx->CR1 = I2C_CR1_PE | I2C_CR1_START | I2C_CR1_ACK; // send restart
        } else {
            xAction->Signal(I2C_HAL_XACTION::c_Status_Completed); // calls XActionStop()
        }
    }
    
    INTERRUPT_END
}
void STM32_I2C_EV_Interrupt (void* param) // Event Interrupt Handler
{
    INTERRUPT_START
    
    I2C_HAL_XACTION* xAction = currentI2CXAction;
    I2C_HAL_XACTION_UNIT* unit = currentI2CUnit;
    
    int todo = unit->m_bytesToTransfer;
    int sr1 = I2Cx->SR1;  // read status register
    int sr2 = I2Cx->SR2;  // clear ADDR bit
    int cr1 = I2Cx->CR1;  // initial control register
    
    if (unit->IsReadXActionUnit()) { // read transaction
        if (todo > 0) {
            if (sr1 & I2C_SR1_SB) { // start bit
                if (todo == 2) {
                    I2Cx->CR1 = (cr1 |= I2C_CR1_POS); // prepare 2nd byte nack
                }
                UINT8 addr = xAction->m_address << 1; // address bits
                I2Cx->DR = addr + 1; // send header byte with read bit;
            } else {
                if (sr1 & I2C_SR1_ADDR) { // address sent
                    if (todo <= 2) {
                        if (todo == 1) cr1 |= I2C_CR1_STOP; // send stop after single byte
                        I2Cx->CR1 = (cr1 &= ~I2C_CR1_ACK); // last byte nack
                    }
                } else {
                    int n = 0;
                    if (sr1 & I2C_SR1_BTF) { // two bytes available
                        n = (todo & 1) + 1; // todo odd => read two bytes
                        if (todo == 2) n = 2; // todo = 2 => read all
                        if (todo == 3) {
                            I2Cx->CR1 = (cr1 &= ~I2C_CR1_ACK); // last byte nack
                        }
                    } else if (todo == 1 && sr1 & I2C_SR1_RXNE) { // last byte
                        n = 1;
                        I2Cx->CR2 &= ~I2C_CR2_ITBUFEN; // disable I2C_SR1_RXNE interrupts
                    }
                    while (n != 0) {
                        if (todo == 2) {
                            I2Cx->CR1 = (cr1 |= I2C_CR1_STOP); // stop after last byte
                        }
                        UINT8 data = I2Cx->DR; // read data
                        *(unit->m_dataQueue.Push()) = data; // save data
                        unit->m_bytesTransferred++;
                        unit->m_bytesToTransfer = --todo; // update todo
                        n--;
                    }
                }
                if (todo == 1) {
                    I2Cx->CR2 |= I2C_CR2_ITBUFEN; // enable I2C_SR1_RXNE interrupt
                }
            }
        }
    } else { // write transaction
        if (sr1 & I2C_SR1_SB) { // start bit
            UINT8 addr = xAction->m_address << 1; // address bits
            I2Cx->DR = addr; // send header byte with write bit;
        } else if (sr1 & I2C_SR1_ADDR || sr1 & I2C_SR1_BTF) { // tx idle
            int n = todo < 2 ? todo : 2; // write at most 2 bytes
            while (n != 0) {
                I2Cx->DR = *(unit->m_dataQueue.Pop()); // next data byte;
                unit->m_bytesTransferred++;
                unit->m_bytesToTransfer--; // no todo update!
                n--;
            }
        }
    }
    
    if (todo == 0) {
        if (!xAction->ProcessingLastUnit()) { // start next unit
            currentI2CUnit = xAction->m_xActionUnits[ xAction->m_current++ ];
            I2Cx->CR1 = I2C_CR1_PE | I2C_CR1_START | I2C_CR1_ACK; // send restart
        } else {
            xAction->Signal(I2C_HAL_XACTION::c_Status_Completed); // calls XActionStop()
        }
    }
    
    INTERRUPT_END
}
void LPC178X_I2C_Driver::ISR( void* arg )
{
    UINT8 address;
    LPC178X_I2C& I2C = LPC178X::I2C();
    
    // read status
    volatile UINT32 status;
    
    I2C_HAL_XACTION*      xAction = g_LPC178X_I2C_Driver.m_currentXAction;
    I2C_HAL_XACTION_UNIT* unit    = g_LPC178X_I2C_Driver.m_currentXActionUnit;
        
    ASSERT(xAction);
    ASSERT(unit);

    status = I2C.I2STAT & 0xFF;


    switch(status)
    {
    case 0x08: // Start Condition transmitted
    case 0x10: // Repeated Start Condition transmitted
        // Write Subordinate address and Data direction
        address  = 0xFE & (xAction->m_address << 1);
        address |= unit->IsReadXActionUnit() ? LPC178X_I2C_Driver::c_DirectionRead : LPC178X_I2C_Driver::c_DirectionWrite;
        I2C.I2DAT = address;
        // Clear STA bit
        I2C.I2CONCLR = LPC178X_I2C::STA;
        break;
    case 0x18: // Subordinate Address + W transmitted, Ack received
    case 0x28: // Data transmitted, Ack received
        // Write data
        // transaction completed
        if(unit->m_bytesToTransfer == 0)
        {
            if(xAction->ProcessingLastUnit())
            {
                xAction->Signal( I2C_HAL_XACTION::c_Status_Completed );
            }
            else
            {
                MasterXAction_Start( xAction, true );
            }
        }
        else
        {
            WriteToSubordinate( unit );
        }
        break;
    case 0x20: // Write Address not acknowledged by Subordinate
    case 0x30: // Data not acknowledged by Subordinate
    case 0x48: // Read Address not acknowledged by Subordinate
        xAction->Signal( I2C_HAL_XACTION::c_Status_Aborted );
        break;
    case 0x38: // Arbitration lost
        xAction->Signal( I2C_HAL_XACTION::c_Status_Aborted );
        break;
    case 0x40: // Subordinate Address + R transmitted, Ack received
        // if the transaction is one byte only to read, then we must send NAK immediately
        if(unit->m_bytesToTransfer == 1)
        {
            I2C.I2CONCLR = LPC178X_I2C::AA;
        }
        else
        {
            I2C.I2CONSET = LPC178X_I2C::AA;
        }
        break;
    case 0x50: // Data received, Ack Sent
    case 0x58: // Data received, NO Ack sent
        // read next byte
        ReadFromSubordinate( unit );   
        if(unit->m_bytesToTransfer == 1)
        {
            I2C.I2CONCLR = LPC178X_I2C::AA;
        }
        if(unit->m_bytesToTransfer == 0)
        {
            if(xAction->ProcessingLastUnit())
            {
                // send transaction stop
                xAction->Signal( I2C_HAL_XACTION::c_Status_Completed );                     
            }
            else
            {
                // start next
                MasterXAction_Start( xAction, true );            
            }
        }
        break;
    case 0x00: // Bus Error
        // Clear Bus error
        I2C.I2CONSET = LPC178X_I2C::STO;
        xAction->Signal( I2C_HAL_XACTION::c_Status_Aborted );
        break;
    default:
        xAction->Signal( I2C_HAL_XACTION::c_Status_Aborted );
        break;
    } // switch(status) 

    // clear the interrupt flag to start the next I2C transfer
    I2C.I2CONCLR = LPC178X_I2C::SI;
}