#include "system.h"
#include "spiclass.h"
typedef __SIZE_TYPE__ size_t;

enum SPICLK : uint32_t {
  FPCLK_2 = 0u, // 72 MHz
  FPCLK_4,      // 36 MHz
  FPCLK_8,      // 18 MHz
  FPCLK_16,     // 9  MHz
  FPCLK_32,     // 4.5    MHz
  FPCLK_64,     // 2.25   MHz
  FPCLK_128,    // 1.125  MHz
  FPCLK_256,    // 0.5625 MHz
};

static SpiClass * pSpiInstance = nullptr;
extern "C" {
//[[gnu::interrupt]] extern void DMA1_Channel2_IRQHandler();
  [[gnu::interrupt]] extern void DMA1_Channel3_IRQHandler();
  extern void * memcpy (void * dest, const void * src, size_t n);
};
void DMA1_Channel3_IRQHandler() {   // transmit channel
  if (pSpiInstance) pSpiInstance->drq();
}
static constexpr unsigned FM = 3u; // 50 MHz
static void InitPins () noexcept {
  // PA4 - NSS, PA5 - SCK, PA6 - MISO, PA7 - MOSI
  GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> uint32_t {
  /*r.B.MODE4 = FM;
    r.B.CNF4  = 2u; // alt push - pull
    r.B.MODE6 = 0u; // input mode
    r.B.CNF6  = 1u; // floating */
    r.B.MODE5 = FM;
    r.B.CNF5  = 2u; // alt push - pull
    r.B.MODE7 = FM;
    r.B.CNF7  = 2u; // alt push - pull
    return r.R;
  });
  // AFIO - default
}
void SpiClass::drq() {
  if (!driver) return;
  DMA1_Type::INTFR_DEF state (DMA1.INTFR);
  if (state.B.GIF3  != RESET) {   // Zřejmě zbytečné, ale pokud používám víc DMA
    DMA1.INTFCR.B.CGIF3  = SET;   // kanálů, pak to tak má být.
  } else return;                  // Událost nevznikla pro kanál 1.
/*if (state.B.HTIF3) {
    DMA1.INTFCR.B.CHTIF3 = SET; // clear half
  } */
  if (state.B.TCIF3) {
    DMA1.INTFCR.B.CTCIF3 = SET; // clear complete
    driver->Send (ptrh, LEDS_LEN);
  }
}

SpiClass::SpiClass(OneWay<uint8_t> & base) noexcept : driver (& base), ptrl (buffer), ptrh (buffer + PADDING) {
  pSpiInstance = this;
  for (unsigned n=0u; n<FULL_LEN; n++) buffer[n] = 0u;
  Color * ptr = reinterpret_cast<Color*>(ptrh);
  const OneColor oz(0x00);
  for (unsigned n=0; n<NUMLEDS; n++) {
    Color & c = ptr [n];
    c.b = oz; c.g = oz; c.r = oz;
  }
}
void SpiClass::Init() {
  RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> uint32_t {
    r.B.SPI1EN = SET;
    r.B.IOPAEN = SET;
    r.B.AFIOEN = SET;
    return r.R;
  });
  RCC.AHBPCENR.B.DMA1EN = SET;
  InitPins();
  // Configure the peripheral data register address
  DMA1.PADDR3.R = reinterpret_cast<size_t> (& SPI1.DATAR);
  // Configure the memory address
  DMA1.MADDR3.R = reinterpret_cast<size_t> (buffer);
  // Configure the number of DMA tranfer to be performs on DMA channel 3
  DMA1.CNTR3 .R = FULL_LEN;
  // Configure increment, size, interrupts and circular mode
  DMA1.CFGR3.modify([] (DMA1_Type::CFGR3_DEF & r) -> uint32_t {
    r.B.PL      = 3u;       // highest priority
    r.B.DIR     = SET;      // memory -> periferal
    r.B.MINC    = SET;      // memory increment
    r.B.MSIZE   = 0u;       // 8-bit
    r.B.PSIZE   = 0u;       // 8-bit
  //r.B.HTIE    = SET;      // INT Enable HALF
    r.B.TCIE    = SET;      // INT Enable FULL
    r.B.CIRC    = SET;      // Circular MODE
  // Enable DMA Channel 1
    r.B.EN      = SET;
    return r.R;
  }); 
  SPI1.CTLR1.modify([](SPI1_Type::CTLR1_DEF & r) -> uint32_t {
    r.B.CPHA     = RESET;
    r.B.CPOL     = RESET;
    r.B.MSTR     = SET;
    r.B.DFF      = RESET;     // 8 bit
    r.B.SSM      = SET;
    r.B.SSI      = SET;
    r.B.LSBFIRST = SET;
    /* 2.25 MHz  - 1bit = 444 ns
     * 1 LED => 9 x 8 x 0.444 = 32 us DMA celkem (10 + 4) x 32 = 0.448 ms
     * */
    r.B.BR       = FPCLK_64;
    return r.R;
  });
  SPI1.CTLR2.modify([](SPI1_Type::CTLR2_DEF & r) -> uint32_t {
    r.B.SSOE     = SET;
  //r.B.RXNEIE   = SET;
  //r.B.TXEIE    = SET;
    r.B.TXDMAEN  = SET;
    return r.R;
  });
  NVIC.EnableIRQ(DMA1_Channel3_IRQn);
  SPI1.CTLR1.B.SPE = SET;
}