
#include "bl_I2CDriver.h"
#include <hw_prcm.h>
#include <hw_gpio.h>
#include <hw_ioc.h>
#include <hw_i2c.h>

#pragma SET_CODE_SECTION(".bl_text")

//*************************************************************************************************************
Tbl_I2CDriver::Tbl_I2CDriver(Tbl_Hw& aHw) :
    nBytesReceived(0),
    newFrameReceived(false),
    Hw(aHw)
{
    //SCL und SDA Pin festlegen
    //I2C Data und I2C Clock Pins auf input setzen
    HWREG(GPIO_BASE + GPIO_O_DOE31_0) &= ~((1 << 24) + (1 << 25));
    //Die Portfunktion (Data und Clock) setzen
    HWREG(IOC_BASE + (4*24)) = IOC_IOCFG0_PORT_ID_I2C_MSSDA + IOC_IOCFG0_PULL_CTL_DIS + IOC_IOCFG0_IE;
    HWREG(IOC_BASE + (4*25)) = IOC_IOCFG0_PORT_ID_I2C_MSSCL + IOC_IOCFG0_PULL_CTL_DIS + IOC_IOCFG0_IE;

    HWREG(PRCM_BASE + PRCM_O_I2CCLKGR) = PRCM_I2CCLKGR_AM_CLK_EN; //I2C im Power Register einschalten
    HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD; //Einstellungen des Power- Reset- Clock Managements übernehmen.

    HWREG(PRCM_BASE + PRCM_O_RESETI2C) = PRCM_RESETI2C_I2C; //I2C Modul resetten
    HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD; //Einstellungen des Power- Reset- Clock Managements übernehmen.

    for(uint32_t i = 0; i < 0xFF; i++)
        asm(" nop");

    //Master disablen. Der sollte zwar schon disabled sein, kostet aber auch nicht viel.
    HWREG(I2C0_BASE + I2C_O_MCR) = 0;
    HWREG(I2C0_BASE + I2C_O_MCTRL) = I2C_MCTRL_RUN_DIS;
    //Eventuell vohandene Interrupt Pending Bits/Flags löschen
    HWREG(I2C0_BASE + I2C_O_SICR) = I2C_SICR_STOPIC + I2C_SICR_STARTIC + I2C_SICR_DATAIC;

    HWREG(I2C0_BASE + I2C_O_MCR) = I2C_MCR_SFE_EN;  //Slave Mode enablen
    HWREG(I2C0_BASE + I2C_O_SCTL) = I2C_SCTL_DA;    //Device aktivieren

    //Die Addresse setzen. Auch der Slave Modus funktioniert nur, wenn die Adresse bei aktiviertem Modul gesetzt wird
    HWREG(I2C0_BASE + I2C_O_SOAR) = 0x2A;

    HWREG(I2C0_BASE + I2C_O_SIMR) = I2C_SIMR_STARTIM;

}

//*************************************************************************************************************
Tbl_I2CDriver::~Tbl_I2CDriver()
{
}

//*************************************************************************************************************
void Tbl_I2CDriver::Cleanup()
{
    //Hw.SignalPoint(4);
    HWREG(I2C0_BASE + I2C_O_SIMR) = 0;
    HWREG(I2C0_BASE + I2C_O_SOAR) = 0;
    //Eventuell vohandene Interrupt Pending Bits/Flags löschen
    HWREG(I2C0_BASE + I2C_O_SICR) = I2C_SICR_STOPIC + I2C_SICR_STARTIC + I2C_SICR_DATAIC;
    //Master und Slave disablen.
    HWREG(I2C0_BASE + I2C_O_MCR) = 0;
    HWREG(I2C0_BASE + I2C_O_MCTRL) = 0;
    //Hw.SignalPoint(5);

    //Die Portfunktion (Data und Clock) der Pins zurücksetzen
    HWREG(IOC_BASE + (4*24)) = IOC_IOCFG0_PULL_CTL_DIS;
    HWREG(IOC_BASE + (4*25)) = IOC_IOCFG0_PULL_CTL_DIS;
    HWREG(GPIO_BASE + GPIO_O_DOE31_0) &= ~(1 << 25);
    Hw.SignalPoint(6);
}

//*************************************************************************************************************
void Tbl_I2CDriver::checkForInterrupts()
{
    uint32_t IntReg = HWREG(I2C0_BASE + I2C_O_SMIS);

    if (IntReg != 0)
    {
        uint32_t StatusReg = HWREG(I2C0_BASE + I2C_O_SSTAT);
        //Jetzt schon alle Interrupt Bits löschen, damit die Zeit zwischen Auslesen
        //und zurücksetzen möglichst kurs ist und kein Interrupt verloren geht
        HWREG(I2C0_BASE + I2C_O_SICR) = I2C_SRIS_STOPRIS  + I2C_SRIS_STARTRIS + I2C_SRIS_DATARIS;

        if (IntReg & I2C_SMIS_STARTMIS)
        {
            //Ein Start Interrupt ist erfolgt
            nBytesReceived = 0;
            newFrameReceived = false;
            nBytesAlreadyTransmitted = 0;
            allBytesTransmitted = false;
            //Nun auch Data und Stop Interrupts einschalten
            HWREG(I2C0_BASE + I2C_O_SIMR) |= I2C_SIMR_STOPIM + I2C_SIMR_DATAIM;
        }
        else if (IntReg & I2C_SMIS_STOPMIS)
        {
            //Ein Stop Interrupt ist erfolgt
            newFrameReceived = nBytesReceived > 0;

            allBytesTransmitted = (nBytesToTransmit > 0) && (nBytesToTransmit == nBytesAlreadyTransmitted);
            if (allBytesTransmitted) nBytesToTransmit = 0;
            //Nur den Start-Interrupt enablen. Data- und Stop-Interrupts werden nicht mehr benötigt
            HWREG(I2C0_BASE + I2C_O_SIMR) = I2C_SIMR_STARTIM;
        }
        else if (IntReg & I2C_SMIS_DATAMIS)
        {
            //Ein Daten Interrupt ist erfolgt
            if (StatusReg & I2C_SSTAT_TREQ)
            {
                //Der Master möchte ein Byte lesen. Das I2C Modul hält/stretched den Clock so lange,
                //bis das Datenregister geschrieben wurde. Der Wert des Datenregisters wird dann an
                //den Master geschickt.
                if (nBytesAlreadyTransmitted >= nBytesToTransmit)
                {
                    //Es ist nichts zu senden. Also 0xFF senden
                    HWREG(I2C0_BASE + I2C_O_SDR) = 0xFF;
                }
                else
                {
                    HWREG(I2C0_BASE + I2C_O_SDR) = ToTransmitBytes[nBytesAlreadyTransmitted];
                    nBytesAlreadyTransmitted++;
                }
            }
            else if (StatusReg & I2C_SSTAT_RREQ)
            {
                //Der Master hat ein Byte geschrieben. Das I2C Modul hält/stretched den Clock so lange,
                //bis das Datenregister ausgelesen wurde
                uint8_t aByte = HWREG(I2C0_BASE + I2C_O_SDR);
                if (nBytesReceived < MaxRxBytes)
                {
                    ReceivedBytes[nBytesReceived] = aByte;
                    nBytesReceived++;
                }
            }
        }
    }
}

#pragma SET_CODE_SECTION()
