#include "system.h"
#include "usartclass.h"
static UsartClass * pInstance  = nullptr;
static constexpr unsigned HCLK = 48'000'000u;
extern "C" void USART1_IRQHandler (void) __attribute__((interrupt));
void USART1_IRQHandler (void) {
  if (pInstance) pInstance->irq();
};

UsartClass::UsartClass(const unsigned int _baud) noexcept : BaseLayer (), tx_ring () {
  pInstance = this;
  // 1. Clock Enable
  RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
    r.B.USART1EN = SET;
    r.B.IOPDEN   = SET;
    return r.R;
  });
  // 2. GPIO Alternate Config - default TX/PD5, RX/PD6
  GPIOD.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
    r.B.MODE5 = 1u; 
    r.B.CNF5  = 2u;  // or 3u for open drain
    r.B.MODE6 = 0u;
    r.B.CNF6  = 1u;  // floating input
    return r.R;
  });
  // 4. NVIC
  NVIC.EnableIRQ (USART1_IRQn);
  // 5. USART registry 8.bit bez parity
  USART1.CTLR1.modify([] (USART1_Type::CTLR1_DEF & r) -> auto {
    r.B.RE      = SET;
    r.B.TE      = SET;
    r.B.RXNEIE  = SET;
    return r.R;
  });
  USART1.CTLR2.R = 0;
  //USART1.CTLR3.B.OVRDIS = SET;
  const uint32_t tmp = HCLK / _baud;
  USART1.BRR.R = tmp;
  USART1.CTLR1.B.UE = SET;        // nakonec povolit globálně
}
void UsartClass::irq () {
  volatile USART1_Type::STATR_DEF status (USART1.STATR);   // načti status přerušení
  char rdata, tdata;
  
  if (status.B.TXE) {                         // od vysílače
    if (tx_ring.Read (tdata)) {               // pokud máme data
      USART1.DATAR.B.DR = (uint8_t) tdata;    // zapíšeme do výstupu
    } else {                                  // pokud ne
      // Předpoklad je half-duplex i.e. RS485, jinak jen zakázat TXEIE
      rdata = (USART1.DATAR.B.DR);            // dummy read
      USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto {
        r.B.RE    = SET;                      // povol prijem
        r.B.TXEIE = RESET;                    // je nutné zakázat přerušení od vysílače
        return r.R;
      });
    }
  }
  if (status.B.RXNE) {                        // od přijímače
    rdata = (USART1.DATAR.B.DR);              // načteme data
    Up (&rdata, 1u);                          // a pošleme dál
  }
}
uint32_t UsartClass::Down(const char * data, const uint32_t len) {
  unsigned n = 0u;
  for (n=0u; n<len; n++) {
    if (!tx_ring.Write(data[n])) break;
  }
  USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto {
    r.B.RE    = RESET;
    r.B.TXEIE = SET;    // po povolení přerušení okamžitě přeruší
    return r.R;
  });
  return n;
}