/*
*Vytovoril: Tomas Rehacek
*Rok:       2015
*Jako soucast diplomove prace VUT
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libopencm3/cm3/systick.h>
#include <netif/etharp.h>
#include <lwip/udp.h>

#include <hal/pin.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/spi.h>
#include <libopencm3/ethernet/phy.h>
#include <libopencm3/stm32/flash.h>
#include <hal/pin.h>

#include "eth_f417.h"
#include "modnet.h"
#include "funkce_spolecne.h"
#include "test_desky.h"
#include "funkce_Analyzator.h"

#include "micro.h"
#include "lenval.h"
#include "ports.h"

/*Prototypy funkci*/
uint16_t cteni_pameti_FPGA (void);
void odeslani_Ethernet_SPI_data_buffer (uint16_t pocet_bajtu);
void systick_inicializace(void);
/*Prototypy funkci konec*/

uint32_t citac; //citac pro systic (systemovy casovac)

struct netif netif; //struktura pouzvana pro vsechny lwIP sitova rozhrani
// PCB = Protocol Control Block - struktura pro ulozeni statusu spojeni
struct udp_pcb *udp = NULL;    //vytvoreni ukazatele na strukturu pro UDP komunikaci
struct ip_addr ipa = { IPADDR_ANY };    //vytvoreni struktury pro IP adresu s pocatecni inicializaci 0.0.0.0

extern unsigned char  _binary_Analyzator_XSVF_xsvf_start;       //zacatek sekce, kde je ulozena konfigurace pro FPGA
extern unsigned char  _binary_Analyzator_XSVF_xsvf_end;
extern unsigned char  _binary_Analyzator_XSVF_xsvf_size;

#define ODESILANI_BUF 700
uint16_t SPI_data_buffer [ODESILANI_BUF];


uint16_t cteni_pameti_FPGA (void)
{
    uint16_t pocet_dat_SPI = 0;
    bool dosazeno_dno_pameti = false;

    while(true)
        {
            ethf417_poll(&netif);  //zpracovani nezpracovanych paketu

            spi_enable(SPI3);
            pin_set(procesor_RDY,true);

            for (uint16_t i = 0; i < ODESILANI_BUF-1; i++)
            {
                if(pin_get(pamet_prazdna))
                {
                    pocet_dat_SPI = --i;
                    dosazeno_dno_pameti = true;
                    break;
                }

                SPI_data_buffer[i] = spi_read(SPI3);
                pocet_dat_SPI = i;
            }
            pin_set(procesor_RDY,false);
            spi_disable(SPI3);      //nejprve je dokoncen prenos, nez je SPI deaktivovano [Reference manual]


            wait_cyklu(1);
            if ((SPI_SR(SPI3) & SPI_SR_BSY)) //SPI prenos jeste nebyl dokoncen
            {
                while(pin_get(FPGA_prevod)) //procesor ceka na dokonceni SPI prenosu
                {
                    ethf417_poll(&netif);
                }
                while(pin_get(FPGA_prevod) != (SPI_SR(SPI3) & SPI_SR_BSY))  //kontrola stavu, kdy procesor ceka na dokonceni prenosou na SPI a pritom uz zadna data nejsou odesilana ...
                {                                                           //...pokud se tak stane, je o tom uzivatel informovan (LED2) a program dale nepokracuje z duvodu poskozeni dat
                    pin_set(LED2,true);
                }
            }


            pocet_dat_SPI++;
            if((SPI_SR(SPI3) & SPI_SR_RXNE))   //precti zbyvajici data
            {
                SPI_data_buffer[pocet_dat_SPI++] = spi_read(SPI3);
            }

            if(dosazeno_dno_pameti)
                return pocet_dat_SPI;

            odeslani_Ethernet_SPI_data_buffer (pocet_dat_SPI * 2);
        }
}

void odeslani_Ethernet_SPI_data_buffer (uint16_t pocet_bajtu)
{
    struct pbuf *p0;

    p0 = pbuf_alloc(PBUF_TRANSPORT, pocet_bajtu, PBUF_RAM); //naalokuje pamet pro paket PBUF_TRANSPORT - cela hlavicka, PBUF_RAM - zasobnik naalokovan jako jeden velky celek
    memcpy(p0->payload, SPI_data_buffer, pocet_bajtu);
    p0->len = pocet_bajtu;

    udp_sendto(udp, p0, &ipa, 55000);

    pbuf_free(p0);
}

void systick_inicializace(void)
{
    systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); // 168MHz/8
    systick_set_reload(21000); //1ms
    systick_interrupt_enable();
	systick_counter_enable();
}

void sys_tick_handler(void)     //obsluha preruseni
{
    citac++;
}


int main(void)
{
    //test_desky(); //nekonecna smycka uvnitr

    rcc_clock_setup_hse_3v3(&hse_25mhz_3v3[CLOCK_3V3_168MHZ]);  /* nastaven systémových hodin 168 MHz
                                                                  apb1 = 42 MHz
                                                                  apb2 = 84 MHz
                                                                */
    MCO2_inicializace();

    for (uint16_t i = 0; i < ODESILANI_BUF; i++)
    {
        SPI_data_buffer [i] = 0;
    }

    povoleni_hodin_GPIO_vsechny();

    LED_a_TL_inicializace();

/*Nahrani konfigurace do FPGA */
    pin_set(LED2,true);
    JTAG_inicializace();
    XSVF_predani_adresy(&_binary_Analyzator_XSVF_xsvf_start);
    xsvfExecute();
    JTAG_odpojeni();
    pin_set(LED2,false);
/*Nahrani konfigurace do FPGA konec*/


/*Inicializace rozhrani Ethernetu*/
    //MII rozhrani inicializovano ve funkci (ethf417_gpio_init())
    PHY_INTRP_RST_inicializace();
/*Inicializace rozhrani Ethernetu konec*/

	modnet_init(&netif);                //inicializace parametru pro sitove spojeni

	udp = udp_new();                    //vytvoreni nove struktury pro UDP komunikaci
	udp->so_options |= SOF_BROADCAST;   //povoleni odesilani a prijmani broadcastu
	udp_bind(udp, &ipa, 55000);
	IP4_ADDR(&ipa, 192, 168, 1, 60);  // nakopirovani IP na kterou se budou odesilat data

    rizeni_FPGA_inicializace();

    SPI3_inicializace();
    uint16_t pocet_dat_SPI = 0;

    systick_inicializace();

	while (true)
    {
        while (!pin_get(pamet_pulka))
        {
            ethf417_poll(&netif);
        }

        pocet_dat_SPI = cteni_pameti_FPGA(); //Vycitani a odesilani dat dokud neni prazdna pamet (v FPGA)
//*** Nasleduje dokonceni odeslani zkraceneho paketu po vycteni vsech dat z pameti

        if (pocet_dat_SPI == 0)     //zabraneni odesilani prazdnych paketu (pouze hlavicky)
            continue;

        odeslani_Ethernet_SPI_data_buffer (pocet_dat_SPI * 2);


        pin_toggle(LED3);

		if (citac >= 5000 )     //provedeno po 5s
        {
           pin_toggle(LED1);
           etharp_tmr();        //vycisti ARP tabulku

           citac = 0;
        }
	}

	return 0;
}
