#include "STM32F0x1.h"
#include "CortexM0.h"   // NVIC_EnableIRQ
#include "gpio.h"
#include "usart.h"

extern "C" uint32_t SystemCoreClock;
static Usart * Instance = nullptr;

void Usart::irq (void) {
  volatile USART1_Type::ISR_DEF status (USART1.ISR);    // načti status přerušení
  char tdata;
  volatile char rdata;
  if (status.B.TC) {                         // od vysílače
    if (tx_ring.Read (tdata)) {               // pokud máme data
      USART1.TDR.R = (uint32_t) tdata & 0xFFu;// zapíšeme do výstupu
    } else {                                  // pokud ne
    //USART1.CR1.B.RE   = SET;                // povol prijem
      USART1.CR1.B.TCIE = RESET;              // je nutné zakázat přerušení od vysílače
    }
  }
  if (status.B.RXNE) {                       // od přijímače
    rdata = (USART1.RDR.R) & 0xFFu;           // načteme data
    (void) rdata;           // zahodime
  }
}
/// Voláno z čistého C - startup.c
extern "C" void USART1_IRQHandler (void) {
  if (Instance) Instance->irq();
};
//! [MembersConstructorExample]
Usart::Usart(const uint32_t baud) noexcept : BaseLayer(), tx_ring() {
//! [MembersConstructorExample]
  if (Instance) return;     // Chyba - jedina instance
  Instance = this;
  // 1. Clock Enable
  RCC.APB2ENR.B.USART1EN = SET;
  // 2. GPIO Alternate Config
  GpioClass txp (GpioPortA,  9, GPIO_Mode_AF);
  GpioClass rxp (GpioPortA, 10, GPIO_Mode_AF);
  txp.setAF (1);
  rxp.setAF (1);
  // 4. NVIC
  NVIC_EnableIRQ (USART1_IRQn);
  uint32_t tmp = 0;
  // 5. USART registry 8.bit bez parity
  USART1.CR1.modify([] (USART1_Type::CR1_DEF & r) -> uint32_t { // pro ilustraci, co by bylo auto
    r.B.TE      = SET;
  //r.B.RE      = SET;  // příjem je zde zbytečný
  //r.B.RXNEIE  = SET;
    return r.R;
  });
  USART1.CR2.R = 0;
  USART1.CR3.B.OVRDIS = SET;
  // Tuhle část už vezmeme přímo z knihovny, jen ty hodiny zjednodušíme na SystemCoreClock
  uint32_t apbclock = SystemCoreClock;
  uint32_t integerdivider, fractionaldivider;
  /* Determine the integer part */
  if (USART1.CR1.B.OVER8 != RESET) {
    /* Integer part computing in case Oversampling mode is 8 Samples */
    integerdivider = ((25u * apbclock) / (2u * (baud)));    
  } else  {
    /* Integer part computing in case Oversampling mode is 16 Samples */
    integerdivider = ((25u * apbclock) / (4u * (baud)));    
  }
  tmp = (integerdivider / 100u) << 4;
  /* Determine the fractional part */
  fractionaldivider = integerdivider - (100u * (tmp >> 4));
  /* Implement the fractional part in the register */
  if (USART1.CR1.B.OVER8 != RESET) {
    tmp |= ((((fractionaldivider * 8u ) + 50u) / 100u)) & ((uint8_t)0x07u);
  } else {
    tmp |= ((((fractionaldivider * 16u) + 50u) / 100u)) & ((uint8_t)0x0Fu);
  } 
  /* Write to USART BRR */
  USART1.BRR.R = (uint16_t)tmp;
  USART1.CR1.B.UE = SET;        // nakonec povolit globálně
}
//! [VirtualMethodBottom]
uint32_t Usart::Down (const char * data, const uint32_t len) {
  uint32_t res;                 // výsledek, musí žít i po ukončení smyčky
  for (res=0; res<len; res++) if (!tx_ring.Write(data[res])) break;
  USART1.CR1.B.TCIE = SET;      // po povolení přerušení okamžitě přeruší
  return res;
}
//! [VirtualMethodBottom]
void Usart::SetHalfDuplex (const bool on) const {
  USART1.CR1.B.UE = RESET;       // zakázat, jinak nelze nastavovat
  if (on)  USART1.CR3.B.HDSEL = SET;    // poloduplex on
  else     USART1.CR3.B.HDSEL = RESET;  // poloduplex off
  USART1.CR1.B.UE = SET;         // nakonec povolit globálně
}
void Usart::SetRS485 (const bool polarity) const {
  USART1.CR1.B.UE = RESET;       // zakázat, jinak nelze nastavovat
  // Nastavit pin DE (RTS)
  GpioClass de (GpioPortA,  12u, GPIO_Mode_AF);
  de.setAF (1u);
  // Nastavení driveru
  USART1.CR3.B.DEM  = SET;       // povolit DE v USARTu
  if (polarity) USART1.CR3.B.DEP = SET;
  else          USART1.CR3.B.DEP = RESET;
  // A nakonec doby vybavení (přesah) - to je hodně užitečné
//! [LambdaExampleUsage]
  USART1.CR1.modify([] (auto & r) -> auto {
    r.B.DEAT   = 1u;       // doba vybavení před start bitem - 16 ~= 1 bit, 0..31
    r.B.DEDT   = 1u;       // doba vybavení po stop bitu     - 16 ~= 1 bit, 0..31
    return r.R;
  });
//! [LambdaExampleUsage]
  USART1.CR1.B.UE = SET;
}