   /// SPIBUS.C                                

#include "iospi.h"
#include "pic24.h"
#include "timers.h"

/// @def CARD_SS_OFF
/// @brief SDCard power off
#define CARD_SS_OFF P24_SET_BITS(P24_LATB,P24_BIT14)
/// @def CARD_SS_ON
/// @brief SDCard power on
#define CARD_SS_ON  P24_CLR_BITS(P24_LATB,P24_BIT14)
/// @def RTC_SS_OFF
/// @brief RTX power off
#define RTC_SS_OFF  P24_SET_BITS(P24_LATF,P24_BIT2)
/// @def RTC_SS_ON
/// @brief RTX power on
#define RTC_SS_ON   P24_CLR_BITS(P24_LATF,P24_BIT2)


/// @var static const spi1_configuration_t spi1_configuration[]
/// @brief SPI configurations:
///   0: spiSLOW (SCK=FCY/16, CKE=1)
///   1: spiFAST (SCK=FCY/1)
///   2: spiRTC  (SCK=FCY/16, CKE=0)
/// set by spi1con1[4:0] - 4:2 sec div 0:1 pri div
//static const spi1_configuration_t spi1_configuration[] = {   
//    // SPICON1: CKE=1, MSTEN=1, SPRE2=1,SPRE1=1,SPRE2=1,PPRE0=1
//    {0x013D, 0x0000, 0x0000 },
//   // SPICON1: CKE=1, MSTEN=1, SPRE2=1,SPRE1=1,SPRE2=1,PPRE1=1,PPRE0=1
//    {0x013F, 0x0000, 0x0000 },
//    // SPICON1: CKE=0, MSTEN=1, SPRE2=1,SPRE1=1,SPRE2=1,PPRE0=1
//    {0x003D, 0x0000, 0x0000 },
//};
//bool spi1_master_open(spi_mode_id spiUniqueConfiguration)
//{
//    if(!P24_SPI1_IS_EN())
//    {
//        P24_SPI1_SET_CON1(spi1_configuration[spiUniqueConfiguration].con1 | P24_SPI_MSTEN);
//        P24_SPI1_SET_CON2(spi1_configuration[spiUniqueConfiguration].con2);
//        P24_SPI1_SET_STAT(spi1_configuration[spiUniqueConfiguration].stat | P24_SPI_SPIEN);
//        
//        SCK1OUT_SetDigitalOutput();
//        return true;
//    }
//    return false;
//}

//PICFJ1024GA606
static const spi1_configuration_t spi1_configuration[] = {   
    // spiSLOW
    // SPICON1: SPIEN=0, CKE=1 (act->idle), CKP=1 (idle:L/act:H), 
    // DISSCK=0 DISSDI=0 DISSDO=0 ENHBUF=0 
    // MCLKEN=1 (FOSC/2) MODE16=0 MODE32=0  
    // MSTEN= 1 (Master) SMP 1 (end) 
    // SPIEN=0 SSEN=0
    // BRG=0xf (FCY/16 [62.5kHz])
    {0x0320, 0x0000, 0x0000, 0xf },
    // spiFAST
    // SPICON1: SPIEN=0, CKE=1 (act->idle), CKP=1 (idle:L/act:H), 
    // DISSCK=0 DISSDI=0 DISSDO=0 ENHBUF=0 
    // MCLKEN=1 (FOSC/2) MODE16=0 MODE32=0  
    // MSTEN= 1 (Master) SMP 1 (end) 
    // SPIEN=0 SSEN=0
    // BRG=0x0 (FCY/1 [1 MHz])
    {0x0320, 0x0000, 0x0000, 0x0 },
    // spiRTC (like Fast, but CKE=0)
    // SPICON1: SPIEN=0, CKE=0 (idle->act), CKP=1 (idle:L/act:H), 
    // DISSCK=0 DISSDI=0 DISSDO=0 ENHBUF=0 
    // MCLKEN=1 (FOSC/2) MODE16=0 MODE32=0  
    // MSTEN= 1 (Master) SMP 1 (end) 
    // SPIEN=0 SSEN=0
    // BRG=0x0 (FCY/1 [1 MHz])
    {0x0220, 0x0000, 0x0000, 0x0 },
};

bool spi1_master_open(spi_mode_id spiUniqueConfiguration)
{
 
    if(!P24_SPI1_IS_EN())
    {
        // clear rx buf
        P24_SPI1_BUF = 0;
        P24_SPI1_SET_BRGL(spi1_configuration[spiUniqueConfiguration].brgl );
        P24_SPI1_SET_CON1(spi1_configuration[spiUniqueConfiguration].con1 | P24_SPI_MSTEN);
        P24_SPI1_SET_CON2(spi1_configuration[spiUniqueConfiguration].con2);
        P24_SPI1_SET_STAT(spi1_configuration[spiUniqueConfiguration].stat );
        SPI1CON1L |= P24_SPI_SPIEN;
        
        SCK1OUT_SetDigitalOutput();
        return true;
    }
    return false;
}

inline void spi1_close(void)
{
    P24_SPI1_DIS();
}

/// __LOCAL DATA__**************************************
BYTE CardFast; /// SPI in fast mode for SDcard or not

/// ---------Mode and Speed Control-----------------------
/// Mode 1, clock = FCY/16 
void spiRTC(void) {

    // using MCC 
    // mode (0) defined in spi1_driver.c
    spi1_close();
    spi1_master_open(MODE_RTC);
}


#ifdef WITH_SPI
/// Mode 0, clock = FCY/16 (between 100 kHz and 400 kHz) 
void spiSlow(void) {
    CardFast = 0;

    // using MCC 
    // mode (0) defined in spi1_driver.c
    spi1_close();
    spi1_master_open(MODE_SLOW);
}


/// Mode 0, clock = FCY 
void spiFast(void) {
    CardFast = 1;
    // using MCC 
    // mode (0) defined in spi1_driver.c
    spi1_close();
    spi1_master_open(MODE_FAST);
}

/// -----------SLAVE SELECT/DESELECT------------
/// Deselect the RTC and release SPI bus      
void spiDeselectRTC() {
    RTC_SS_OFF;
}

/// Select the RTC set SPI mode  
void spiSelectRTC() { // 1:Successful, 0:Timeout 
    CARD_SS_OFF; //just incase it was left on
    spiRTC();
    RTC_SS_ON;
}

/// Deselect the SDcard on SPI bus       
void spiDeselectSD() {
    CARD_SS_OFF;
    spiExchange(0xFF); // Dummy clock (force MISO hi-z)
}

/// Select the card and wait for ready   
/// 1:Successful, 0:Timeout 
int spiSelectSD() {
    RTC_SS_OFF; //incase it was left on
    //start SPI in the right speed
    (CardFast) ? spiFast() : spiSlow();

    CARD_SS_ON;
    spiExchange(0xFF); // Dummy clock (force MISO enabled) 

    if (spiWaitReady())
        return 1; // OK 

    spiDeselectSD(); //was never ready so give it up

    return 0; // Timeout 
}

/// wait(.5sec) for ready signal from card 
int spiWaitReady() {
    BYTE d=0x00;
    Timer tmr;
    tkTmrSet(&tmr, 500);
    tkTmrStart(&tmr);
    do {
        d = spiExchange(0xFF);
    } while (tkTmrUpdate(&tmr) && d != 0xFF);

    return (d == 0xFF) ? 1 : 0;
}

/// --------Transmit/Receive data to/from MMC(SDcard)------------------------
/// Single byte SPI transfer 
BYTE spiExchange(BYTE dat) {

    while(P24_SPI1_TX_BUF_FULL()){
    }
    P24_SPI1_WRITE(&dat);

    while(P24_SPI1_RX_BUF_EMPTY()){
   }

   return (BYTE)P24_SPI1_BUF;
    
}

void spiXmitMulti(const BYTE* buff, UINT cnt) {
    do {
        P24_SPI1_BUF = *buff++;
        while (!P24_SPI1_RX_BUF_FULL());
        P24_SPI1_BUF;
        P24_SPI1_BUF = *buff++;
        while (!P24_SPI1_RX_BUF_FULL());
        P24_SPI1_BUF;
    } while (cnt -= 2);
}

void spiRcvrMulti(BYTE* buff, UINT cnt) {
    do {
        P24_SPI1_BUF = 0xFF;
        while (!P24_SPI1_RX_BUF_FULL());
        *buff++ = P24_SPI1_BUF;
        P24_SPI1_BUF = 0xFF;
        while (!P24_SPI1_RX_BUF_FULL());
        *buff++ = P24_SPI1_BUF;
    } while (cnt -= 2);
}

#else
#warning "WITH_SPI disabled - using SPI stubs"

void spiSlow(void)
{
    return;
}


/// @brief Set faster SPI speed
void spiFast(void)
{
    return;
}


/// @brief Turn off RTC's CS line
void spiDeselectRTC(void)
{
    return;
}


/// @brief Change modes to talk to RTC, enable its CS
void spiSelectRTC(void)
{
    return;
}


/// @brief Turn off SDcard's CS line
void spiDeselectSD(void)
{
    return;
}


/// @brief Change modes to talk to SDcard, enable its CS
int spiSelectSD(void)
{
    return -1;
}


/// @brief wait .5 sec for ready signal from SDcard
/// @return 1:received 0xFF, 0: timed out
int spiWaitReady(void)
{
    return -1;
}


/// @brief send and receive one byte over SPI
/// @param dat Byte to send
/// @return Byte received
BYTE spiExchange(BYTE dat)
{
    return 0;
}


/// @brief send several bytes, ignore received bytes
/// @param buff data to transmit
/// @param cnt number of bytes to send
void spiXmitMulti(const BYTE* buff, UINT cnt)
{
    return;
}

/// @brief receive several bytes, transmits 0xFFs
/// @param [out] buff Buffer to fill with data
/// @param cnt number of bytes to get
void spiRcvrMulti(BYTE* buff, UINT cnt)
{
    return;
}


#endif /// WITH_SPI