#include "BM_SPI_MASTER.h"

/*************************************************
Description:         SPI初始化函數
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::begin(void)
{
  _spiSck  = 13;
  _spiMosi = 11;
  _spiMiso = 12;

  pinMode (_spiSck, OUTPUT);
  pinMode (_spiMosi, OUTPUT);
  pinMode (_spiMiso, INPUT);

  _spiTransferMode = SPI_MODE0;
  _spiDataMode = MSBFIRST;
  _spiTransferClock = SPI_CLOCK_DIV2;
  _begin = 1;
  _end = 0;
  switch (_spiTransferClock)
  {
    case SPI_CLOCK_DIV2: _timer = 1; break;
    case SPI_CLOCK_DIV4: _timer = 2; break;
    case SPI_CLOCK_DIV8: _timer = 4; break;
    case SPI_CLOCK_DIV16: _timer = 8; break;
    case SPI_CLOCK_DIV32: _timer = 16; break;
    case SPI_CLOCK_DIV64: _timer = 32; break;
    case SPI_CLOCK_DIV128: _timer = 64; break;
    default: break;
  }
}

/*************************************************
Description:         SPI模式1傳輸函數
Input:               @param data 要發送的數據
Output:              無
Return:              read_data   讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SW_SPI::transferMode1(uint8_t data)
{
  uint8_t i = 0;
  unsigned read_data = 0;
  digitalWrite(_spiSck, LOW);


  if (_spiDataMode == 1)
  {
    for (i = 0; i < 8; i++)
    {
      read_data = read_data << 1;

      digitalWrite(_spiSck, HIGH);        //第一个边沿

      if (digitalRead(_spiMiso) == 1)
        read_data = read_data + 1;

      delay();
      if ((data & 0x80) == 0x80)          //第二个边沿
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);

      digitalWrite(_spiSck, LOW);

      data = data << 1;
    }
  }
  else if (_spiDataMode == 0)
  {
    for (i = 0; i < 8; i++)
    {
      read_data = read_data >> 1;

      digitalWrite(_spiSck, HIGH);//第一个边沿

      if (digitalRead(_spiMiso == 1))
        read_data = read_data | 0x80;


      delay();
      if ((data & 0x01) == 0x01) //第二个边沿
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);
      digitalWrite(_spiSck, LOW);


      data = data >> 1;
    }
  }
  return read_data;
}

/*************************************************
Description:         SPI模式0傳輸函數
Input:               @param data 要發送的數據
Output:              無
Return:              read_data   讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SW_SPI::transferMode0(uint8_t data)
{
  uint8_t i = 0;
  unsigned read_data = 0;
  digitalWrite(_spiSck, LOW);
  if (_spiDataMode == 1)
  {
    for (i = 0; i < 8; i++)
    {
      if ((data & 0x80) == 0x80)
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);

      data = data << 1;
      digitalWrite(_spiSck, HIGH);//第一个边沿
      delay();
      delayMicroseconds(5);
      read_data = read_data << 1;
      if (digitalRead(_spiMiso) == 1)
        read_data = read_data + 1;
      digitalWrite(_spiSck, LOW);//第二个边沿
      delayMicroseconds(5);

    }
  }
  else if (_spiDataMode == 0)
  {
    for (i = 0; i < 8; i++)
    {
      if ((data & 0x01) == 0x01)
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);

      data = data >> 1;
      digitalWrite(_spiSck, HIGH);
      delay();
      digitalWrite(_spiSck, LOW);

      read_data = read_data >> 1;
      if (digitalRead(_spiMiso) == 1)
        read_data = read_data | 0x80;
    }
  }
  return read_data;
}

/*************************************************
Description:         SPI模式3傳輸函數
Input:               @param data 要發送的數據
Output:              無
Return:              read_data   讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SW_SPI::transferMode3(uint8_t data)
{
  uint8_t i = 0;
  unsigned read_data = 0;
  digitalWrite(_spiSck, HIGH);
  if (_spiDataMode == 1)
  {
    for (i = 0; i < 8; i++)
    {
      read_data = read_data << 1;

      digitalWrite(_spiSck, LOW);//第一个边沿

      if (digitalRead(_spiMiso) == 1)
        read_data = read_data + 1;

      delay();
      if ((data & 0x80) == 0x80)
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);
      digitalWrite(_spiSck, HIGH);//第二个边沿
      data = data << 1;
    }
  }
  else if (_spiDataMode == 0)
  {
    for (i = 0; i < 8; i++)
    {
      read_data = read_data >> 1;

      digitalWrite(_spiSck, LOW);//第一个边沿

      if (digitalRead(_spiMiso) == 1)
        read_data = read_data | 0x80;

      delay();
      if ((data & 0x01) == 0x01)
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);
      digitalWrite(_spiSck, HIGH);//第二个边沿
      data = data >> 1;
    }
  }
  return read_data;
}

/*************************************************
Description:         SPI模式2傳輸函數
Input:               @param data 要發送的數據
Output:              無
Return:              read_data   讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SW_SPI::transferMode2(uint8_t data)
{
  uint8_t i = 0;
  unsigned read_data = 0;
  digitalWrite(_spiSck, HIGH);
  if (_spiDataMode == 1)
  {
    for (i = 0; i < 8; i++)
    {
      if ((data & 0x80) == 0x80)
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);

      data = data << 1;
      digitalWrite(_spiSck, LOW);//第一个边沿
      delay();
      digitalWrite(_spiSck, HIGH);//第二个边沿

      read_data = read_data << 1;
      if (digitalRead(_spiMiso) == 1)
        read_data = read_data + 1;
    }
  }
  else if (_spiDataMode == 0)
  {
    for (i = 0; i < 8; i++)
    {
      if ((data & 0x01) == 0x01)
        digitalWrite(_spiMosi, HIGH);
      else
        digitalWrite(_spiMosi, LOW);

      data = data >> 1;
      digitalWrite(_spiSck, LOW);//第一个边沿
      delay();
      digitalWrite(_spiSck, HIGH);//第二个边沿

      read_data = read_data >> 1;
      if (digitalRead(_spiMiso) == 1)
        read_data = read_data | 0x80;
    }
  }
  return read_data;
}

/*************************************************
Description:         SPI傳輸函數
Input:               @param data 要發送的數據
Output:              無
Return:              讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SW_SPI::transfer(uint8_t data)
{
  if (_end != 1)
    switch (_spiTransferMode)
    {
      case SPI_MODE0: return transferMode0(data); break;
      case SPI_MODE1: return transferMode1(data); break;
      case SPI_MODE2: return transferMode2(data); break;
      case SPI_MODE3: return transferMode3(data); break;
      default: break;
    }
}

/*************************************************
Description:         停止SPI通信
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::end()
{
  _end = 1;
}

/*************************************************
Description:         設置SPI數據傳輸模式
Input:               @param data_mode 傳輸數據格式
                      MSBFIRST 
                      LSBFIRST 
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::setBitOrder(uint8_t data_mode)
{
  _spiDataMode = data_mode;
}

/*************************************************
Description:         設置SPI傳輸速率
Input:               @param data_clock 傳輸速率
                       SPI_CLOCK_DIV4 0x00
                       SPI_CLOCK_DIV16 0x01
                       SPI_CLOCK_DIV64 0x02
                       SPI_CLOCK_DIV128 0x03
                       SPI_CLOCK_DIV2 0x04
                       SPI_CLOCK_DIV8 0x05
                       SPI_CLOCK_DIV32 0x06
Output:              無 
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::setClockDivider(uint8_t transfer_clock)
{
  _spiTransferClock = transfer_clock;
  switch (_spiTransferClock)
  {
    case SPI_CLOCK_DIV2: _timer = 1; break;
    case SPI_CLOCK_DIV4: _timer = 2; break;
    case SPI_CLOCK_DIV8: _timer = 4; break;
    case SPI_CLOCK_DIV16: _timer = 8; break;
    case SPI_CLOCK_DIV32: _timer = 16; break;
    case SPI_CLOCK_DIV64: _timer = 32; break;
    case SPI_CLOCK_DIV128: _timer = 64; break;
    default: break;
  }
}

/*************************************************
Description:         設置SPI傳輸模式
Input:               @param transfer_mode 傳輸模式
                       SPI_MODE0 0x00
                       SPI_MODE1 0x04
                       SPI_MODE2 0x08
                       SPI_MODE3 0x0c
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::setDataMode(uint8_t transfer_mode)
{
  _spiTransferMode = transfer_mode;
}

/*************************************************
Description:         設置SPI sck、mosi、miso對應Arduio引腳
Input:               @param sck sck線對應的引腳
                     @param mosi mosi對應的引腳
                     @param miso miso對應的引腳
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::changeSPIPin(uint8_t sck, uint8_t mosi, uint8_t miso)
{
  _spiSck  = sck;
  _spiMosi = mosi;
  _spiMiso = miso;

  pinMode (_spiSck, OUTPUT);
  pinMode (_spiMosi, OUTPUT);
  pinMode (_spiMiso, INPUT);
}

/*************************************************
Description:         延時_timer個時鐘週期
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::delay()
{
  uint8_t i = 0;
  for (i = 0; i < _timer; i++)
    __asm__("nop\n\t");
}

/*************************************************
Description:         SPI發送16位數據
Input:               @param data 要發送的數據
Output:              無
Return:              uint16_t 讀取的數據
Others:              其它說明
*************************************************/
uint16_t BM_SW_SPI::transfer16(uint16_t data)
{
  uint16_t TX_L = data & 0xff;
  uint16_t TX_H = data >> 8;

  uint16_t RX_H = 0;
  uint16_t RX_L = 0;
  if (_spiDataMode == LSBFIRST)
  {
    RX_L = transfer(TX_L);
    RX_H = transfer(TX_H);
    return ((RX_H << 8) + RX_L);
  }
  else if (_spiDataMode == MSBFIRST)
  {
    RX_H = transfer(TX_H);
    RX_L = transfer(TX_L);
    return ((RX_H << 8) + RX_L);
  }
  return 0;
}

/*************************************************
Description:         從buf數據中發送count個字節的數據
Input:               @param buf 發送的數據數組
                     @param count 發送的字節數
Output:              無 
Return:              無
Others:              其它說明
*************************************************/
void BM_SW_SPI::transfer(uint8_t *buf, uint8_t count)
{
  uint8_t i = 0;
  for (i = 0; i < count; i++)
  {
    transfer(buf[i]);
  }
}

/*************************************************
Description:         BM_SPI類的構造函數，使用該構造函數創建對象SPI將使用軟件模擬SPI。
Input:               @param cspin cs片選引腳
                     @param sckpin  sck時鐘線引腳
                     @param misopin miso主機接收引腳
                     @param mosipin mosi主機發送引腳
                     @param clock SPI時鐘設置
                     @param bitOrder SPI數據傳輸模式
                     @param dataMode SPI傳輸模式
Output:              無
Return:              無
Others:              其它說明
*************************************************/
BM_SPI_MASTER::BM_SPI_MASTER(uint8_t cspin, uint8_t sckpin, uint8_t misopin, uint8_t mosipin, uint8_t clock , uint8_t bitOrder, uint8_t dataMode )
{
  _the_BM_SPI = &_BM_SW_SPI;
  _the_SPIClass = NULL;
  _spiTransferMode = dataMode;
  _spiDataMode = bitOrder;
  _spiTransferClock = clock;
  _spiSck = sckpin;
  _spiMosi = mosipin;
  _spiMiso = misopin;

  _spiCs = cspin;

}

/*************************************************
Description:         BM_SPI類的構造函數，使用該構造函數創建對象SPI將使用硬件SPI。
Input:               @param cspin cs片選引腳
                     @param clock SPI時鐘設置
                     @param bitOrder SPI數據傳輸模式
                     @param dataMode SPI傳輸模式
Output:              無
Return:              無
Others:              其它說明
*************************************************/
BM_SPI_MASTER::BM_SPI_MASTER(uint8_t cspin, uint32_t clock , uint8_t bitOrder , uint8_t dataMode, SPIClass *theSPI) {

  _the_SPIClass = theSPI;
  _the_BM_SPI = NULL;
  _spiTransferMode = dataMode;
  _spiDataMode = bitOrder;
  _spiTransferClock = clock;

  _spiCs = cspin;
}

/*************************************************
Description:         BM_SPI類的構造函數，使用該構造函數創建對象SPI將使用硬件SPI。
Input:               無 
Output:              無
Return:              無
Others:              其它說明
*************************************************/
BM_SPI_MASTER::BM_SPI_MASTER()
{
   _the_SPIClass = &SPI;
  _the_BM_SPI = NULL;
  _spiTransferMode = SPI_MODE0;
  _spiDataMode = MSBFIRST;
  _spiTransferClock = SPI_CLOCK_DIV2;

  _spiCs = 10;
}
/*************************************************
Description:         初始化函數，設置SPI的相關引腳和參數。
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::begin(void)
{
  if (_the_BM_SPI != NULL)
  {
    _the_BM_SPI->begin();
    _the_BM_SPI->setBitOrder(_spiDataMode);
    _the_BM_SPI->setClockDivider(_spiTransferClock);
    _the_BM_SPI->setDataMode(_spiTransferMode);
    _the_BM_SPI->changeSPIPin(_spiSck, _spiMosi, _spiMiso);

    pinMode (_spiCs, OUTPUT);
    digitalWrite(_spiCs, HIGH);
  }
  else if (_the_SPIClass != NULL)
  {
    _the_SPIClass->begin();

    _the_SPIClass->setBitOrder(_spiDataMode);
    _the_SPIClass->setClockDivider(_spiTransferClock);
    _the_SPIClass->setDataMode(_spiTransferMode);

    pinMode (_spiCs, OUTPUT);
    digitalWrite(_spiCs, HIGH);
  }
  _begin = 1;
}

/*************************************************
Description:         SPI默認相關參數設置
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::beginTransaction()
{
  SPISettings settings(4000000, MSBFIRST, SPI_MODE0);
  if (_the_SPIClass != NULL)
    _the_SPIClass->beginTransaction(settings);
}

/*************************************************
Description:         SPI相關參數設置
Input:               @param seetings 設置參數類
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::beginTransaction(SPISettings settings)
{
  if (_the_SPIClass != NULL)
    _the_SPIClass->beginTransaction(settings);
}
void BM_SPI_MASTER::beginTransaction(uint32_t clock,uint8_t bitOrder,uint8_t dataMode)
{
  if (_the_BM_SPI != NULL)
  {
    _the_BM_SPI->setBitOrder(_spiDataMode);
    _the_BM_SPI->setClockDivider(_spiTransferClock);
    _the_BM_SPI->setDataMode(_spiTransferMode);
  }
}

/*************************************************
Description:         停止與SPI接口通信
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::endTransaction()
{
   if (_the_SPIClass != NULL)
    _the_SPIClass->endTransaction();
}

void BM_SPI_MASTER::attachInterrupt()
{
  if (_the_SPIClass != NULL)
    _the_SPIClass->attachInterrupt();
}
void BM_SPI_MASTER::detachInterrupt()
{
  if (_the_SPIClass != NULL)
    _the_SPIClass->detachInterrupt();
}
void BM_SPI_MASTER::usingInterrupt(uint8_t interruptNumber)
{
  if (_the_SPIClass != NULL)
    _the_SPIClass->usingInterrupt( interruptNumber);
}
void BM_SPI_MASTER::notUsingInterrupt(uint8_t interruptNumber)
{
  if (_the_SPIClass != NULL)
    _the_SPIClass->notUsingInterrupt(interruptNumber);
}
/*************************************************
Description:         SPI發送/接收一個字節數據
Input:               @param data 發送的數據
Output:              無
Return:              uint8_t 讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SPI_MASTER::transfer(uint8_t data)
{
  if (_the_BM_SPI != NULL)
  {
    return _the_BM_SPI->transfer(data);
  }
  else if (_the_SPIClass != NULL)
  {
    return _the_SPIClass->transfer(data);
  }
}

/*************************************************
Description:         SPI發送16位數據
Input:               @param data 要發送的數據
Output:              無
Return:              uint16_t 讀取的數據
Others:              其它說明
*************************************************/
uint16_t BM_SPI_MASTER::transfer16(uint16_t data)
{
  if (_the_BM_SPI != NULL)
  {
    return _the_BM_SPI->transfer16(data);
  }
  else if (_the_SPIClass != NULL)
  {
    return  _the_SPIClass->transfer16(data);
  }
}

/*************************************************
Description:         從buf數據中發送count個字節的數據
Input:               @param buf 發送的數據數組
                     @param count 發送的字節數
Output:              無 
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::transfer(uint8_t *buf, uint8_t count)
{
  uint8_t i = 0;
  if (_the_BM_SPI != NULL)
  {
    for(i = 0;i < count;i++)
      _the_BM_SPI->transfer(buf[i]);

  }
  else if (_the_SPIClass != NULL)
  {
    for(i = 0;i < count;i++)
      _the_SPIClass->transfer(buf[i]);

  }
}

/*************************************************
Description:         設置SPI數據傳輸模式
Input:               @param data_mode 傳輸數據格式
                      MSBFIRST 
                      LSBFIRST 
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::setBitOrder(uint8_t data_mode)
{
  if (_the_BM_SPI != NULL)
  {
    _spiDataMode = data_mode;
    _the_BM_SPI->setBitOrder(_spiDataMode);
  }
  else if (_the_SPIClass != NULL)
  {
    _spiDataMode = data_mode;
    _the_SPIClass->setBitOrder(_spiDataMode);
  }
}

/*************************************************
Description:         設置SPI傳輸速率
Input:               @param data_clock 傳輸速率
                       SPI_CLOCK_DIV4 0x00
                       SPI_CLOCK_DIV16 0x01
                       SPI_CLOCK_DIV64 0x02
                       SPI_CLOCK_DIV128 0x03
                       SPI_CLOCK_DIV2 0x04
                       SPI_CLOCK_DIV8 0x05
                       SPI_CLOCK_DIV32 0x06
Output:              無 
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::setClockDivider(uint8_t transfer_clock)
{
  if (_the_BM_SPI != NULL)
  {
    _spiTransferClock = transfer_clock;
    _the_BM_SPI->setClockDivider(_spiTransferClock);
  }
  else if (_the_SPIClass != NULL)
  {
    _spiTransferClock = transfer_clock;
    _the_SPIClass->setClockDivider(_spiTransferClock);
  }
}

/*************************************************
Description:         設置SPI傳輸模式
Input:               @param transfer_mode 傳輸模式
                       SPI_MODE0 0x00
                       SPI_MODE1 0x04
                       SPI_MODE2 0x08
                       SPI_MODE3 0x0c
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::setDataMode(uint8_t transfer_mode)
{
  if (_the_BM_SPI != NULL)
  {
    _spiTransferMode = transfer_mode;
    _the_BM_SPI->setDataMode(_spiTransferMode);
  }
  else if (_the_SPIClass != NULL)
  {
    _spiTransferMode = transfer_mode;
    _the_SPIClass->setDataMode(_spiTransferMode);
  }
}


/*************************************************
Description:         停止SPI通信
Input:               無
Output:              無
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::end()
{
  _begin = 0;
  if (_the_BM_SPI != NULL)
  {
    _the_BM_SPI->end();
  }
  else if (_the_SPIClass != NULL)
  {
    _the_SPIClass->end();
  }
}

/*************************************************
Description:         從buffer數據中發送len個字節的數據
Input:               @param buf 發送的數據數組
                     @param len 發送的字節數
Output:              無 
Return:              無
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::writeBytes(uint8_t* buffer, uint8_t len)
{ 
  if(_begin == 1)
  {
    uint8_t i = 0;
    digitalWrite(_spiCs, LOW);

    for (i = 0; i < len; i++)
    {
      transfer(buffer[i]);
    }

    digitalWrite(_spiCs, HIGH);
    delayMicroseconds(25);
  }
}

/*************************************************
Description:         讀取len個字節數據到buffer數組中
Input:               @param buffer 讀取的數據數組
                     @param len 讀取的字節數
Output:              對輸出結果的說明
Return:              函數返回值的說明
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::readBytes(uint8_t* buffer, uint8_t len,uint8_t sendvalue)
{
  if(_begin == 1)
  {
    uint8_t i = 0;
    digitalWrite(_spiCs, LOW);

    for (i = 0; i < len; i++)
    {
      delayMicroseconds(25);
      buffer[i] = transfer(sendvalue);
    }
    digitalWrite(_spiCs, HIGH);
    delayMicroseconds(25);
  }
}

/*************************************************
Description:         SPI發送/接收一個字節數據
Input:               @param data 發送的數據
Output:              無
Return:              uint8_t 讀取的數據
Others:              其它說明
*************************************************/
void BM_SPI_MASTER::writeByte(uint8_t data)
{
  if(_begin == 1)
  {
    digitalWrite(_spiCs, LOW);

    transfer(data);

    digitalWrite(_spiCs, HIGH);
    delayMicroseconds(25);
  }
}

/*************************************************
Description:         讀取一個字節數據
Input:               無
Output:              無
Return:              uint8_t data 讀取的數據
Others:              其它說明
*************************************************/
uint8_t BM_SPI_MASTER::readByte(uint8_t sendvalue)
{
  if(_begin == 1)
  {
    uint8_t data = 0;
    digitalWrite(_spiCs, LOW);

    data = transfer(sendvalue);

    digitalWrite(_spiCs, HIGH);
    delayMicroseconds(25);
    return data;
  }
  return 0;
}
