/***************************************************************************//**
 *   @file   main.c
 *   @brief  Initialisation et séquencement des tâches de l'application
 *   @author Kévin MARCHAND(Kevin.marchand@intelligence-electronique.com)
********************************************************************************
 * Copyright 2014(c) PLCD, Inc.
 *
 * Programme principal.
 * Initialisation et séquencement des tâches de l'application.
 *
********************************************************************************
 *   SVN Revision: 1
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
/* Device header file */
#if defined(__XC16__)
    #include <xc.h>
#elif defined(__C30__)
    #if defined(__dsPIC33E__)
    	#include <p33Exxxx.h>
    #elif defined(__dsPIC33F__)
    	#include <p33Fxxxx.h>
    #endif
#endif

/******************************************************************************/
/************************* Define Declarations ********************************/
/******************************************************************************/
#define THIS_IS_STACK_APPLICATION

// Include all headers for any enabled TCPIP Stack functions
#include "Include/TCPIPStack/TCPIP.h"

#include "MODBUSTCPServer.h"

#include <stdint.h>         // Includes uint16_t definition
#include <stdbool.h>        // Includes true/false definition
#include "system.h"         // Systeme oscillateur configuration
#include "user.h"           // Fonctions et paramètres de l'application
#include "AD7190.h"         // Header file of AD7190 Driver (Analogique Gauge de pesée).
#include "ADComm.h"         // Header file of AD7190 SPI Communication Driver.
#include "UARTsComm.h"      // Header file of UART Communication Driver.
#include "uart.h"           // Header for UART module library functions
#include "PPS.h"            // Header file for Peripheral Pin Select (PPS) to allow Function Remapping.
#include "MemFlash.h"       // Fichier Entête pour l'inclusion des fonctions de lecture/ecriture de la Flash
#include "ADC10bitsDrv.h"   // Initialisation des périphériques de l'ADC du PIC
#include "SPIFRAM.h"        // Fichier Entête pour l'inclusion des fonctions de lecture/ecriture de la FRAM

#include "B40/abcc.h"
#include "B40/abcc_sys_adapt.h"
#include "B40/ad_obj.h"
#include "B40/user_adi.h"
#include "B40/user_setup.h"

#include "usb/usb.h"
#include "usb/usb_device.h"
#include "usb/usb_device_cdc.h"

#include "ECAN/ECAN1Drv.h"
#include "ECAN/ECAN1Node.h"

#include "Process.h"

typedef enum ABCCPowerStateType{
   ABCCPOWER_RESET,
   ABCCPOWER_STARTDRIVER,
   ABCCPOWER_RESETRELEASE,
   ABCCPOWER_WAITCOM,
   ABCCPOWER_RUNNING,
   ABCCPOWER_RESETREQ,
}ABCCPowerStateType;

/******************************************************************************/
/* Declaration externe MODBUS                                                 */
/******************************************************************************/
extern WORD INPUT_REG[INPUT_REG_SIZE];
//extern WORD_VAL1 COIL;
//extern void MODBUSTCPServer(void);
BOOL8    fResetABCC = FALSE;
ABCCPowerStateType ABCCPowerState = ABCCPOWER_RESET;
BOOL8    ABCC_StateMachineEnabled = TRUE;

/******************************************************************************/
/************************ Variables Declarations ******************************/
/******************************************************************************/
unsigned long address;
//! Declare TCPIP AppConfig structure and some other supporting stack variables
APP_CONFIG AppConfig;
//! Checksum of the ROM defaults for AppConfig
static unsigned short wOriginalAppConfigChecksum;                               
int __C30_UART = 2;                                                             // Use UART2 instead of UART1 for stdout (printf functions).

// Private prototype functions
void InitTCPIPAppConfig(void);
void InitializeBoard(void);
void ProcessIO(void);
void DisplayIPValue(IP_ADDR IPVal);
void ADInit(void);
void ABCCMainStateMachine(unsigned int delayMS);

/***************************************************************************//**
 * @brief Programme principal
 * @param none.
 * @return none.
*******************************************************************************/
int16_t main(void)
{
    UINT8 bSw1;

    static DWORD tABCC = 0;
    static DWORD t = 0;
    static DWORD dwLastIP = 0;

    // Initialize application specific hardware IO ports and peripherals
    InitializeBoard();    
    // Configure the oscillator for the device
    ConfigureOscillator();
    InitApp();
    // Configuration des UARTs
    UARTs_Init();
    // Configuration USB en mode CDC (Port COM virtuel)
    USBDeviceInit();
    USBDeviceAttach();

    // Configuration ECAN
    ECAN1DMAConfig(NUM_OF_RX_BUFFERS);
    ECAN1ClockConfig();
    ECAN1InterruptConfig();
    ECAN1RxFiltersConfig();
    ECAN1TxRxBuffersConfig();
    ECAN1SetOPMode();

    // Initialize TCPIP stack-related hardware components that may be
    // required by the UART configuration routines
    TickInit();
    #if defined(STACK_USE_MPFS2)
    MPFSInit();
    #endif
 
    // Initialize TCPIP Stack and application related NV variables into AppConfig.
    InitTCPIPAppConfig();

    // Initialize TCPIP core stack layers (MAC, ARP, TCP, UDP) and application modules (HTTP, SNMP, etc.)
    StackInit();  
    #if defined(STACK_USE_UART2TCP_BRIDGE)
    UART2TCPBridgeInit();
    #endif
    // Initialisation de l'AD7190 pour la pesé
    ADSPI_Init();
    ADInit();
  
    LED0_IO = 1;
    
    // Initialisation pour la lecture analogique AN1 et AN2
    InitAdc1();         // Initialisation du module ADC1 du microcontrolleur
    InitDma0();			// Initialisation du contrôleur DMA comme buffer de données de l'ADC
    InitTmr3();			// Initialisation du Timer pour générer l'événement d'échantillonnage (donne la fréquence d'échantillonnage)

    LED1_IO = 1;

    // Définit le numéro de l'esclave pour le PROFIBUS
    LecFRAM_Param();
    US_SetSwitch1(PAR.CommNumEsclave[4]);

    if (PAR.CommNumEsclave[4] == 0)
        ABCC_StateMachineEnabled = FALSE;
    else
        ABCC_StateMachineEnabled = TRUE;
    // This infinite loop will continuously execute all stack-related tasks,
    // as well as your own application's functions.

    LED0_IO = 0;
    
    ModifieVitesseComm(0);
    ModifieVitesseComm(1);
    ModifieVitesseComm(2);
    ModifieVitesseComm(3);
    
    while(1)
    {
        ProcessIO();
        process_plcd();
        
        // Fait clignoté la LED0 toutes les secondes, permet de détecter un plantage du CPU.
        if(TickGet() - t >= TICK_SECOND)
        {
            t = TickGet();
//            LED0_IO ^= 1;
            // transmitNextECANmsg = 1;             // Pour test envoie ECAN
        }

        // This task performs normal TCPIP stack task including checking
        // for incoming packet, type of packet and calling
        // appropriate stack entity to process it.
        StackTask();

        // This TCPIP tasks invokes each of the core stack application tasks
        StackApplications();
 
        #if defined(STACK_USE_ZEROCONF_MDNS_SD)
        mDNSProcess();
        // Use this function to exercise service update function
        // HTTPUpdateRecord();
        #endif
    
        #if defined(STACK_USE_MODBUS_TCP_SERVER)
//            MODBUSTCPServer();
    	#endif

        #if defined(STACK_USE_BERKELEY_API)
            //BerkeleyTCPClient();
            BerkeleyTCPServer();
            //BerkeleyUDPClient();
        #endif

        // If the local IP address has changed (ex: due to DHCP lease change)
        // write the new IP address to UART and Announce service
        if(dwLastIP != AppConfig.MyIPAddr.Val)
        {
            dwLastIP = AppConfig.MyIPAddr.Val;
            #if defined(STACK_USE_UART)
                putrsUART((ROM char*)"\r\nNew IP Address: ");
            #endif
            DisplayIPValue(AppConfig.MyIPAddr);
            #if defined(STACK_USE_UART)
                putrsUART((ROM char*)"\r\n");
            #endif
            #if defined(STACK_USE_ANNOUNCE)
                AnnounceIP();
            #endif
            #if defined(STACK_USE_ZEROCONF_MDNS_SD)
                mDNSFillHostRecord();
            #endif
        }
        // Si le module B40 est présent et le délai ABCC_DELAYMS écoulé
        if(ABCC_StateMachineEnabled && (GetCounterMillis() - tABCC >= ABCC_DELAYMS)){
            ABCCMainStateMachine(ABCC_DELAYMS);
            tABCC = GetCounterMillis();
        }

        #if defined(USB_POLLING)
            USBDeviceTasks();
        #endif
        if( USBGetDeviceState() < CONFIGURED_STATE || USBIsDeviceSuspended()== true)
        {
            continue;
        }
           //Application CDC specific tasks
        DeviceCDCTasks();
    }
}

/***************************************************************************//**
 * @brief Machine d'état principale du module B40 PROFIBUS DPV1
 * @param delayMS - Temps écoulé depuis le dernier appel.
 * @return none.
*******************************************************************************/
void ABCCMainStateMachine(unsigned int delayMS)
{
    switch( ABCCPowerState )
    {
      case ABCCPOWER_RESET:
         ABCC_SYS_HWReset();
         ABCCPowerState = ABCCPOWER_STARTDRIVER;
         break;
      case ABCCPOWER_STARTDRIVER:
         if( ABCC_StartDriver( ABP_OP_MODE_SPI, FALSE ) == ABCC_OK )
         {
            ABCCPowerState = ABCCPOWER_RESETRELEASE;
         }else
             ABCC_StateMachineEnabled = FALSE;
         break;
      case ABCCPOWER_RESETRELEASE:
            ABCC_HWReleaseReset();
            ABCCPowerState = ABCCPOWER_WAITCOM;
         break;
      case ABCCPOWER_WAITCOM:
         ABCC_RunTimerSystem( delayMS );
         if( ABCC_isReadyForCommunication() )
         {
            AD_NewWrPd();
            ABCCPowerState = ABCCPOWER_RUNNING;
         }
         break;
      case ABCCPOWER_RUNNING:
         if( fResetABCC )
         {
            fResetABCC = FALSE;
            ABCCPowerState = ABCCPOWER_RESETREQ;
            break;
         }
         ABCC_RunTimerSystem( delayMS );                                        //Call driver timer system.
         ABCC_RunDriver();                                                      // This function runs the driver and must be called cyclically.
         UpdateB40ADI();
         break;
      case ABCCPOWER_RESETREQ:
         ABCC_ShutdownDriver();
         ABCCPowerState = ABCCPOWER_RESET;
         break;
      default:
         break;
    };   
}


/***************************************************************************//**
 * @brief Writes an IP address to UART as available
 * @param IPVal - Adresse IP.
 * @return none.
*******************************************************************************/
void DisplayIPValue(IP_ADDR IPVal)
{
    static BYTE IPDigit[4];
    BYTE i;
    for(i = 0; i < sizeof(IP_ADDR); i++)
    {
        uitoa((WORD)IPVal.v[i], IPDigit);
        #if defined(STACK_USE_UART)
            putsUART((char *) IPDigit);
        #endif
        if(i == sizeof(IP_ADDR)-1)
            break;
        #if defined(STACK_USE_UART)
            while(BusyUART());
            WriteUART('.');
        #endif
    }
}

void GestionReponseModbus(char numUart)
{
int             tailleReponse;
int             indice;
unsigned int    crc;

switch (numUart)
{
    case 1 : memcpy(&MODBUS_RX[6],&TrameU1.Data[0],280); break;
    case 2 : memcpy(&MODBUS_RX[6],&TrameU2.Data[0],280); break;
    case 3 : memcpy(&MODBUS_RX[6],&TrameU3.Data[0],280); break;
    case 4 : memcpy(&MODBUS_RX[6],&TrameU4.Data[0],280); break;
}

tailleReponse = ModbusUartServer(numUart);

if (tailleReponse > 0)                                                          // Si il y a une reponse on l'envoie
{
    crc = CRC_ChecksumWord(tailleReponse-6, &MODBUS_TX[6]);
    MODBUS_TX[tailleReponse+1] = crc/256;
    MODBUS_TX[tailleReponse] = crc%256;

    switch (numUart)
    {
        case 3 : UART3TXEN_IO = 1; Delay10us(1); break;
        case 4 : UART4TXEN_IO = 1; Delay10us(1); break;
    }

    indice = 6;
    while(indice < tailleReponse+2)
    {
        switch (numUart)
        {
            case 1 :    while(U1STAbits.UTXBF);                                 /* wait if the buffer is full */
                        U1TXREG = MODBUS_TX[indice]; break;                     /* transfer data word to TX reg */
            case 2 :    while(U2STAbits.UTXBF);                                 /* wait if the buffer is full */
                        U2TXREG = MODBUS_TX[indice]; break;                     /* transfer data word to TX reg */
            case 3 :    while(U3STAbits.UTXBF);                                 /* wait if the buffer is full */
                        U3TXREG = MODBUS_TX[indice];                            /* transfer data word to TX reg */
                        while(BusyUART3()); break;                              // Attente de la fin de la transmission
            case 4 :    while(U4STAbits.UTXBF);                                 /* wait if the buffer is full */
                        U4TXREG = MODBUS_TX[indice];                            /* transfer data word to TX reg */
                        while(BusyUART4()); break;                              // Attente de la fin de la transmission
        }
        indice++;
    }
    switch (numUart)
    {
        case 3 : Delay10us(1); UART3TXEN_IO = 0; break;
        case 4 : Delay10us(1); UART4TXEN_IO = 0; break;
    }
}
}

/***************************************************************************//**
 * @brief Fonction appelée en boucle par le main() pour contrôler les opérations.
 *
 * @param none.
 *
 * @return none.
*******************************************************************************/
void ProcessIO(void)
{
char            reponse[BUFFER_SIZE] = {0};
int             data[4];

// Gestion des défauts de trames
if (TrameU1.Compteur > 0) TrameU1.Compteur--;
else TrameU1.buffIdx = 0;

if (TrameU2.Compteur > 0) TrameU2.Compteur--;
else TrameU2.buffIdx = 0;

if (TrameU3.Compteur > 0) TrameU3.Compteur--;
else TrameU3.buffIdx = 0;

if (TrameU4.Compteur > 0) TrameU4.Compteur--;
else TrameU4.buffIdx = 0;

if (TrameU1.Status == 1)                                                        // COM_RS232
{
    if (PAR.CommType[COM_RS232] != COMM_IE)
    {
        GestionReponseModbus(1);
    }
    else
    {
        ProccessCmd(&TrameU1.Data[0],TrameU1.Length, &reponse[0]);
        if (strlen(reponse) > 0)                                                // Si il y a une reponse on l'envoie
            putsUART1 ((unsigned int *)reponse);
    }
    TrameU1.Status = 0;
}

PAR.CommType[COM_UART2] = COMM_PC;
if (TrameU2.Status == 1)
{
    if (PAR.CommType[COM_UART2] != COMM_IE)
    {
        GestionReponseModbus(2);
    }
    else
    {
        ProccessCmd(&TrameU2.Data[0],TrameU2.Length, &reponse[0]);
        if (strlen(reponse) > 0)                                                // Si il y a une reponse on l'envoie
        {
            putsUART2((unsigned int *)reponse);
            while(BusyUART2());
            DelayMs(10);
        }
    }
    TrameU2.Status = 0;
}

if (TrameU3.Status == 1)                                                        // COM_RS485_1
{
    if (PAR.CommType[COM_RS485_1] != COMM_IE)                                   // Communication Modbus
    {
        GestionReponseModbus(3);
    }
    else                                                                        // Communication Intelligence Electronique
    {
        ProccessCmd(&TrameU3.Data[0],TrameU3.Length, &reponse[0]);
        if (strlen(reponse) > 0)                                                // Si il y a une reponse on l'envoie
        {
            putsUART3((unsigned int *)reponse);
            while(BusyUART3());
            DelayMs(10);
        }
    }
    TrameU3.Status = 0;
}

if (TrameU4.Status == 1)                                                        // COM_RS485_2
{
    if (PAR.CommType[COM_RS485_2] != COMM_IE)                                   // Communication Modbus
    {
        GestionReponseModbus(4);
    }
    else                                                                        // Communication Intelligence Electronique
    {
        ProccessCmd(&TrameU4.Data[0],TrameU4.Length, &reponse[0]);
        if (strlen(reponse) > 0)                                                // Si il y a une reponse on l'envoie
        {
            putsUART4((unsigned int *)reponse);
            while(BusyUART4());
            DelayMs(10);
        }
    }
    TrameU4.Status = 0;


//    ProccessCmd(&TrameU4.Data[0],TrameU4.Length, &reponse[0]);
//    if (strlen(reponse) > 0)                                                  // Si il y a une reponse on l'envoie
//        SendStrUART4(reponse);
//    TrameU4.Status = 0;
}

// Exemple de transmission ECAN
if (transmitNextECANmsg == 1)
{
    data[0] = rand();
    data[1] = rand();
    data[2] = rand();
//    data[3] = CRC_ChecksumWord((unsigned int *)data, 6,0);
    ECANCreateEIDPacket(data,TXSID1,TXEID1,ecanTxMsgBuffer);
    transmitNextECANmsg = 0;
    ECAN1SendPacket();
}

// Exemple de reception ECAN
if (receivedECANmsg== 1)
{
/* Check integrity of received packet. The
 * rxBufferIndex keeps track of where the latest
 * message is stored.
 * You can always check the 8th word of the message
 * to see which filter let it through.*/
    receivedECANmsg = 0;
    rxECANIndex = rxECANBufferIndex;

        //if(ecanRxMsgBuffer[rxECANIndex][0] == ':'){
            ProccessCmd("INITMILLIS",10,&reponse[0]);
            /*if(strlen(reponse) > 0){// Si il y a une reponse on l'envoie
                ECANCreateEIDPacket(&reponse[0],TXSID1,TXEID1,ecanTxMsgBuffer);
                ECAN1SendPacket();
                //putsUSB(reponse,strlen(reponse));
            }*/
        //}
        //crcval= CRC_ChecksumWord((unsigned int *)&(ecanRxMsgBuffer[rxECANIndex][3]),6,0);
        //if(crcval != ecanRxMsgBuffer[rxECANIndex][6])
        //{
            /* If the CRC is invalid then stop the ECAN module,
             * switch on LED10 and wait */
            //C1CTRL1bits.REQOP = 1;
            //while(1);
        //}

    }
 //   return;
    // Performs a single conversion.
   //result = AD7190_SingleConversion();
        //ST7579_String(3, 10, "1Rd:");
        //ST7579_HexNumber(3, 70, result, 3);
  
        // Continuous reads.
//        result = AD7190_ContinuousReadAvg(10);
        //ST7579_String(4, 10, "AVG:");
        //ST7579_HexNumber(4, 70, result, 3);


 //       #if defined(STACK_USE_UART)
 //           sprintf(strRes,"%06ld\r\n\0",result);
 //           putsUART2(strRes);
 //       #endif

  /*  INPUT_REG[1] = (WORD)(result);
    INPUT_REG[2] = (WORD)(result >> 16);
    INPUT_REG[1] = (WORD)((INPUT_REG[1] & 0xFF) << 8 | (INPUT_REG[1] & 0xFF00) >> 8);
    INPUT_REG[2] = (WORD)((INPUT_REG[2] & 0xFF) << 8 | (INPUT_REG[2] & 0xFF00) >> 8);*/


    INPUT_REG[3] = IN0_IO;
    INPUT_REG[4] = IN1_IO;
    INPUT_REG[5] = IN2_IO;
    INPUT_REG[6] = IN3_IO;
    INPUT_REG[7] = IN4_IO;
    INPUT_REG[8] = IN5_IO;
    INPUT_REG[9] = IN6_IO;
    INPUT_REG[10] = IN7_IO;
}
// Initialise l'adresse MAC par défaut
static ROM BYTE SerializedMACAddress[6] = {MY_DEFAULT_MAC_BYTE1, MY_DEFAULT_MAC_BYTE2, MY_DEFAULT_MAC_BYTE3, MY_DEFAULT_MAC_BYTE4, MY_DEFAULT_MAC_BYTE5, MY_DEFAULT_MAC_BYTE6};
/***************************************************************************//**
 * @brief Write/Read non-volatile config TCPIP variables (AppConfig).
 * @pre MPFSInit() is already called.
 * @param none.
 *
 * @return none.
*******************************************************************************/
void InitTCPIPAppConfig(void)
{
#if defined(EEPROM_CS_TRIS)
    unsigned char vNeedToSaveDefaults = 0;
#endif
    
    while(1)
    {
        // Start out zeroing all AppConfig bytes to ensure all fields are deterministic for checksum generation
        memset((void*)&AppConfig, 0x00, sizeof(AppConfig));
        
        AppConfig.Flags.bIsDHCPEnabled = TRUE;
        AppConfig.Flags.bInConfigMode = TRUE;

        // Initialisation de l'adresse MAC
        memcpypgm2ram((void*)&AppConfig.MyMACAddr, (ROM void*)SerializedMACAddress, sizeof(AppConfig.MyMACAddr));
        #if defined(EEPROM_CS_TRIS) && defined(USE_EEPROM_25AAXXE48)
            XEEReadArray(0x00FA, (BYTE*)&AppConfig.MyMACAddr, 6);
        #endif

        AppConfig.MyIPAddr.Val = MY_DEFAULT_IP_ADDR_BYTE1 | MY_DEFAULT_IP_ADDR_BYTE2<<8ul | MY_DEFAULT_IP_ADDR_BYTE3<<16ul | MY_DEFAULT_IP_ADDR_BYTE4<<24ul;
        AppConfig.DefaultIPAddr.Val = AppConfig.MyIPAddr.Val;
        AppConfig.MyMask.Val = MY_DEFAULT_MASK_BYTE1 | MY_DEFAULT_MASK_BYTE2<<8ul | MY_DEFAULT_MASK_BYTE3<<16ul | MY_DEFAULT_MASK_BYTE4<<24ul;
        AppConfig.DefaultMask.Val = AppConfig.MyMask.Val;
        AppConfig.MyGateway.Val = MY_DEFAULT_GATE_BYTE1 | MY_DEFAULT_GATE_BYTE2<<8ul | MY_DEFAULT_GATE_BYTE3<<16ul | MY_DEFAULT_GATE_BYTE4<<24ul;
        AppConfig.PrimaryDNSServer.Val = MY_DEFAULT_PRIMARY_DNS_BYTE1 | MY_DEFAULT_PRIMARY_DNS_BYTE2<<8ul  | MY_DEFAULT_PRIMARY_DNS_BYTE3<<16ul  | MY_DEFAULT_PRIMARY_DNS_BYTE4<<24ul;
        AppConfig.SecondaryDNSServer.Val = MY_DEFAULT_SECONDARY_DNS_BYTE1 | MY_DEFAULT_SECONDARY_DNS_BYTE2<<8ul  | MY_DEFAULT_SECONDARY_DNS_BYTE3<<16ul  | MY_DEFAULT_SECONDARY_DNS_BYTE4<<24ul;
        
        // Load the default NetBIOS Host Name
        memcpypgm2ram(AppConfig.NetBIOSName, (ROM void*)MY_DEFAULT_HOST_NAME, 16);
        FormatNetBIOSName(AppConfig.NetBIOSName);  

        // Compute the checksum of the AppConfig defaults as loaded from ROM
        wOriginalAppConfigChecksum = CalcIPChecksum((BYTE*)&AppConfig, sizeof(AppConfig));

        #if defined(EEPROM_CS_TRIS)
        {
            NVM_VALIDATION_STRUCT NVMValidationStruct;
            // Check to see if we have a flag set indicating that we need to 
            // save the ROM default AppConfig values.
            if(vNeedToSaveDefaults)
                SaveAppConfig(&AppConfig);        
            // Read the NVMValidation record and AppConfig struct out of EEPROM/Flash
            #if defined(EEPROM_CS_TRIS)
            {
                XEEReadArray(0x0000, (BYTE*)&NVMValidationStruct, sizeof(NVMValidationStruct));
                XEEReadArray(sizeof(NVMValidationStruct), (BYTE*)&AppConfig, sizeof(AppConfig));                
            }            
            #endif
    
            // Check EEPROM validitity.  If it isn't valid, set a flag so 
            // that we will save the ROM default values on the next loop 
            // iteration.
            if((NVMValidationStruct.wConfigurationLength != sizeof(AppConfig)) ||
               (NVMValidationStruct.wOriginalChecksum != wOriginalAppConfigChecksum) ||
               (NVMValidationStruct.wCurrentChecksum != CalcIPChecksum((BYTE*)&AppConfig, sizeof(AppConfig))))
            {
                // Check to ensure that the vNeedToSaveDefaults flag is zero, 
                // indicating that this is the first iteration through the do 
                // loop.  If we have already saved the defaults once and the 
                // EEPROM still doesn't pass the validity check, then it 
                // means we aren't successfully reading or writing to the 
                // EEPROM.  This means you have a hardware error and/or 
                // SPI configuration error.
                if(vNeedToSaveDefaults)
                {
                    while(1);
                }
                
                // Set flag and restart loop to load ROM defaults and save them
                vNeedToSaveDefaults = 1;
                continue;
            }
            
            // If we get down here, it means the EEPROM/Flash has valid contents 
            // and either matches the ROM defaults or previously matched and 
            // was run-time reconfigured by the user.  In this case, we shall 
            // use the contents loaded from EEPROM/Flash.
            break;
        }
        #endif
        break;
    }
}
/***************************************************************************//**
 * @brief Initialisation des pattes du microcontrôleur.
 * @details Définie la direction des ports (0=sortie, 1=entrée) et l'état par
 * défaut, affecte les fonctions des ports (numérique ou analogique) et
 * éffectue le mappage des pattes sur les périphériques utilisés (UART, SPI...)
 * @see HardwareProfile.h
 *
 * @return none.
*******************************************************************************/
void InitializeBoard(void)
{
    // LEDs
    LED0_TRIS = 0;
    LED1_TRIS = 0;

    // BUTTON
    BUTTON0_TRIS = 1;
    BUTTON1_TRIS = 1;

    // Initialisation de tous les ports en digital
    ANSELA = 0x0000;
    ANSELB = 0x0000;
    ANSELC = 0x0000;
    ANSELD = 0x0000;
    ANSELE = 0x0000;
    ANSELG = 0x0000;
 
    // Initialisation des entrées sous interruption
    iPPSInput(IN_FN_PPS_INT1,31);		//Assing INT1 to pin RPI31
    iPPSInput(IN_FN_PPS_INT2,30);		//Assing INT2 to pin RPI30
    iPPSInput(IN_FN_PPS_INT4,73);		//Assing INT4 to pin RPI73
    INT1_TRIS = 1;
    INT2_TRIS = 1;
    B40_IRQ_TRIS = 1;

    iPPSInput(IN_FN_PPS_INT3,86);		//Assing INT3 to pin RPI86

    // Initialisation des entrées
    IN0_TRIS = 1; IN1_TRIS = 1; IN2_TRIS = 1; IN3_TRIS = 1; IN4_TRIS = 1; IN5_TRIS = 1; IN6_TRIS = 1; IN7_TRIS = 1;
    C1_TRIS = 1; C2_TRIS = 1;
    
    // Initialisation des entrées analogiques
    ANSELEbits.ANSE8 = 1;
    ANSELEbits.ANSE9 = 1;
    AN20_TRIS = 1;
    AN21_TRIS = 1;

    // Initialisation des sorties
    OUT0_TRIS = 0;OUT1_TRIS = 0;OUT2_TRIS = 0;OUT3_TRIS = 0;OUT4_TRIS = 0;OUT5_TRIS = 0;OUT6_TRIS = 0;OUT7_TRIS = 0;
    OUT0_IO = 0; OUT1_IO = 0; OUT2_IO = 0; OUT3_IO = 0; OUT4_IO = 0; OUT5_IO = 0; OUT6_IO = 0; OUT7_IO = 0;
    B40_RESET_IO = 0;
    B40_RESET_TRIS = 0;

    // Initialisations des UART
    iPPSInput(IN_FN_PPS_U1RX,IN_PIN_PPS_RP100);		//Assing U2RX to pin RP100
    iPPSOutput(OUT_PIN_PPS_RP101,OUT_FN_PPS_U1TX);       //Assing U2TX to pin RP101
    UART1TX_TRIS = 0;
    UART1RX_TRIS = 1;
    UART1SD_TRIS = 0;
    UART1SD_IO = 1;
    iPPSInput(IN_FN_PPS_U2RX,IN_PIN_PPS_RP104);		//Assing U2RX to pin RP104
    iPPSOutput(OUT_PIN_PPS_RP98,OUT_FN_PPS_U2TX);       //Assing U2TX to pin RP98
    UART2TX_TRIS = 0;
    UART2RX_TRIS = 1;
    iPPSInput(IN_FN_PPS_U3RX,76);                       //Assing U3RX to pin RPI76
    iPPSOutput(OUT_PIN_PPS_RP67,OUT_FN_PPS_U3TX);       //Assing U3TX to pin RP67
    UART3TX_TRIS = 0;
    UART3RX_TRIS = 1;
    UART3TXEN_TRIS = 0;
    UART3TXEN_IO = 0;
    iPPSInput(IN_FN_PPS_U4RX,IN_PIN_PPS_RP69);		//Assing U4RX to pin RP69
    iPPSOutput(OUT_PIN_PPS_RP68,OUT_FN_PPS_U4TX);       //Assing U4TX to pin RP68
    UART4TX_TRIS = 0;
    UART4RX_TRIS = 1;
    UART4TXEN_TRIS = 0;
    UART4TXEN_IO = 0;

    // Initialisation de ECAN
    iPPSOutput(OUT_PIN_PPS_RP112,OUT_FN_PPS_C1TX);       //Assing ECANTX to pin RP112
    iPPSInput(IN_FN_PPS_C1RX,22);                       //Assing ECANRX to pin RPI22
    ECANTX_TRIS = 0;
    ECANRX_TRIS = 1;
    
    // Initialisation des SPI
    // Initialisation SPI AD7190
    iPPSOutput(OUT_PIN_PPS_RP87,OUT_FN_PPS_SCK1);       //Assing SCK1 to pin RP87
    iPPSOutput(OUT_PIN_PPS_RP85,OUT_FN_PPS_SDO1);       //Assing SDO1 to pin RP85
    iPPSInput(IN_FN_PPS_SDI1,86);                       //Assing SDI1 to pin RPI86
    AD_SCLK_TRIS = 0;
    AD_DOUT_TRIS = 0;
    AD_DIN_TRIS = 1;
    AD_CS_TRIS = 0;
//    AD_SYNC_TRIS = 0;
    // Permet de mettre l'AD7195 sous interuption patte RDY/
    iPPSInput(IN_FN_PPS_INT3,86);                       //Assing INT3 to pin RPI86

    // SPI3 - module B40
    iPPSOutput(OUT_PIN_PPS_RP96,OUT_FN_PPS_SCK3);       //Assing SCK3 to pin RP96
    iPPSOutput(OUT_PIN_PPS_RP71,OUT_FN_PPS_SDO3);       //Assing SDO3 to pin RP71
    iPPSInput(IN_FN_PPS_SDI3,75);                       //Assing SDI3 to pin RPI75
    B40_CS_IO = 1;
    B40_CS_TRIS = 0;
    B40_SCLK_TRIS = 0;
    B40_MOSI_TRIS = 0;
    B40_MISO_TRIS = 1;
    B40_MD_TRIS = 1;

    // Initialisation SPI FLASH
//    iPPSOutput(OUT_PIN_PPS_RP82,OUT_FN_PPS_SCK3);       //Assing SCK3 to pin RP82
//    iPPSOutput(OUT_PIN_PPS_RP84,OUT_FN_PPS_SDO3);       //Assing SDO3 to pin RP84
//    iPPSInput(IN_FN_PPS_SDI3,83);                       //Assing SDI3 to pin RPI83
    FLASH_SCK_TRIS = 0;
    FLASH_SDO_TRIS = 0;
    FLASH_SDI_TRIS = 1;
    FLASH_CS_TRIS = 0;


    // Initialisation SPI EEPROM
    iPPSOutput(OUT_PIN_PPS_RP126,OUT_FN_PPS_SCK4);      //Assing SCK4 to pin RP126
    iPPSOutput(OUT_PIN_PPS_RP80,OUT_FN_PPS_SDO4);       //Assing SDO4 to pin RP80
    iPPSInput(IN_FN_PPS_SDI4,81);                       //Assing SDI4 to pin RPI81
    EEPROM_CS_IO = 1;
    EEPROM_CS_TRIS = 0;
    FRAM_CS_IO = 1;
    FRAM_CS_TRIS = 0;
    XEEInit();
    FRAMInit();

    ENC_CS_IO = 1;
    ENC_CS_TRIS = 0;

}

#if defined(EEPROM_CS_TRIS)
/***************************************************************************//**
 * @brief Write the validation struct and current AppConfig contents to EEPROM/Flash
 * @details Ensure adequate space has been reserved in non-volatile storage to
 * store the entire AppConfig structure.  If you get stuck in this while(1)
 * trap, it means you have a design time misconfiguration in TCPIPConfig.h.
 * You must increase MPFS_RESERVE_BLOCK to allocate more space.
 * @see HardwareProfile.h
 *
 * @return none.
*******************************************************************************/
void SaveAppConfig(const APP_CONFIG *ptrAppConfig)
{
    NVM_VALIDATION_STRUCT NVMValidationStruct;

    #if defined(STACK_USE_MPFS2)
        if(sizeof(NVMValidationStruct) + sizeof(AppConfig) > MPFS_RESERVE_BLOCK)
            while(1);
    #endif

    // Get proper values for the validation structure indicating that we can use 
    // these EEPROM/Flash contents on future boot ups
    NVMValidationStruct.wOriginalChecksum = wOriginalAppConfigChecksum;
    NVMValidationStruct.wCurrentChecksum = CalcIPChecksum((BYTE*)ptrAppConfig, sizeof(APP_CONFIG));
    NVMValidationStruct.wConfigurationLength = sizeof(APP_CONFIG);

    // Write the validation struct and current AppConfig contents to EEPROM/Flash
    #if defined(EEPROM_CS_TRIS)
        XEEBeginWrite(0x0000);
        XEEWriteArray((BYTE*)&NVMValidationStruct, sizeof(NVMValidationStruct));
        XEEWriteArray((BYTE*)ptrAppConfig, sizeof(APP_CONFIG));
    #endif
}
#endif

/***************************************************************************//**
 * @brief Initialisation du composant AD7195 pour l'acquisition analogique.
 * @details Vérifie la communication avec l'AD7195 et initialise le mode de
 * fonctionnement par défaut du composant.
 *
 * @return none.
*******************************************************************************/
void ADInit(void)
{
    if(AD7190_Init())
    {
        putsUART2("AD7190 OK");
        while(BusyUART2());
    }else{
        putsUART2("AD7190 Err");
        while(BusyUART2());
    }    
    // Selects unipolar operation and ADC's input range to +-Vref/1.
    AD7190_RangeSetup(1, AD7190_CONF_GAIN_128);

    // Calibrates channel AIN3(+) - AIN4(-).
    AD7190_Calibrate(AD7190_MODE_CAL_INT_ZERO, AD7190_CH_AIN3P_AIN4M);
    // Calibrates channel AIN3(+) - AIN4(-).
    AD7190_Calibrate(AD7190_MODE_CAL_INT_FULL, AD7190_CH_AIN3P_AIN4M);
}


/***************************************************************************//**
 * @brief Indique sur la LED1 le status USB
 * @param none.
 * @return none.
*******************************************************************************/
void LEDUpdateUSBStatus(void)
{
    static uint16_t ledCount = 0;

    if(USBIsDeviceSuspended() == true){
        mLED1_Off();
        return;
    }
    switch(USBGetDeviceState())
    {
        case CONFIGURED_STATE:
            /* We are configured.  Blink fast.
             * On for 75ms, off for 75ms, then reset/repeat. */
            if(ledCount == 1)
            {
                mLED1_On();
            }
            else if(ledCount == 75)
            {
                mLED1_Off();
            }
            else if(ledCount > 150)
            {
                ledCount = 0;
            }
            break;

        default:
            /* We aren't configured yet, but we aren't suspended so let's blink with
             * a slow pulse. On for 50ms, then off for 950ms, then reset/repeat. */
            if(ledCount == 1)
            {
                mLED1_On();
            }
            else if(ledCount == 50)
            {
                mLED1_Off();
            }
            else if(ledCount > 950)
            {
                ledCount = 0;
            }
            break;
    }

    /* Increment the millisecond counter. */
    ledCount++;
}

bool USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, uint16_t size)
{
    switch( (int) event )
    {
        case EVENT_TRANSFER:
            break;

        case EVENT_SOF:
            /* We are using the SOF as a timer to time the LED indicator.  Call
             * the LED update function here. */
            LEDUpdateUSBStatus();
            break;

        case EVENT_SUSPEND:
            /* Update the LED status for the suspend event. */
            LEDUpdateUSBStatus();
            break;

        case EVENT_RESUME:
            /* Update the LED status for the resume event. */
            LEDUpdateUSBStatus();
            break;

        case EVENT_CONFIGURED:
            /* When the device is configured, we can (re)initialize the
             * demo code. */
            DeviceCDCInitialize();
            break;

        case EVENT_SET_DESCRIPTOR:
            break;

        case EVENT_EP0_REQUEST:
            /* We have received a non-standard USB request.  The HID driver
             * needs to check to see if the request was for it. */
            USBCheckCDCRequest();
            break;

        case EVENT_BUS_ERROR:
            break;

        case EVENT_TRANSFER_TERMINATED:
            break;

        default:
            break;
    }
    return true;
}