/*◄■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ NMRA_DCC.c ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■► */

// <editor-fold defaultstate="collapsed" desc="Title">
/*────────────────────────────────────────────────────────────────────────────*/
/*                        © John William Darsow                               */
/* Created on Tuesday, October 27, 2015                                       */
/* Current Version Date: (see File Date)                                      */
/*                                                                            */
/* This source file is subject of the GNU general public license 2,           */
/* that is available at the world-wide-web at                                 */
/* http://www.gnu.org/licenses/gpl.txt                                        */
/*                                                                            */
/*────────────────────────────────────────────────────────────────────────────*/
/* Original files for Arduino Systems by:                                     */
// file:      NmraDcc.cpp
// author:    Alex Shepherd
// webpage:   http://opendcc.org/
// history:   2011-06-26 Initial Version copied in from OpenDCC
/*                                                                            */
/* ReWriten for Microchip MCU's by John W Darsow                              */
/*────────────────────────────────────────────────────────────────────────────*/
/*                                                                            */
/* purpose:   Provide a simplified interface to decode NMRA DCC packets       */
/*			  and build DCC Mobile and Stationary Decoders                       */
/*────────────────────────────────────────────────────────────────────────────*/
/*  Compiled using:     MPLAB X IDE v3.05                                     */
/*                      XC8 v1.33                                             */
/*                                                                            */
/* Modifications,UpDates and ToDo:                                            */
/*                                                                            */
/*────────────────────────────────────────────────────────────────────────────*/
/* NMRA                                                                           */
/*────────────────────────────────────────────────────────────────────────────*/
/* DCC Receive Routine                                                        */
/*          uses two interrupts: a rising edge in DCC polarity triggers INTx  */
/*          in INTx handler, Timer0 CompareB with a delay of 80us is started. */
/*          On Timer0 CompareB Match the level of DCC is evaluated and        */
/*          parsed.                                                           */
/*                                                                            */
/*                           |<-----116us----->|                              */
/*                                                                            */
/*           DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________             */
/*                           ^-INTx                                           */
/*                           |----87us--->|                                   */
/*                                        ^Timer-INT: reads zero              */
/*                                                                            */
/*           DCC 0: _________XXXXXXXXXXXXXXXXXX__________________             */
/*                           ^-INTx                                           */
/*                           |----------->|                                   */
/*                                        ^Timer-INT: reads one               */
/*                                                                            */
/*────────────────────────────────────────────────────────────────────────────*/
// </editor-fold>

/*═════════════════════════════════ INCLUDEs ═════════════════════════════════*/
#include <xc.h>
#include <stdio.h>
#include <string.h>
#include "EEPROM_24xx00-16.h"
#include "NMRA_DCC.h"

/*═══════════════════════════ VARIABLE DEFINITIONS ═══════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Variable Definitions">

DccProcState_t DccProcState;                     // Structure for link
DccMsg_t       DccRx;                            // Packet Reception structure to build packet from signal input
DccMsg_t       DccPacket ;                       // Packet data buffer
CV29_t         CV29;                             // CV29 as a structure

uInt8          FactoryDefaultCVIndex = 0;        // Array index for Factory Default CV array
CVPair_t       FactoryDefaultCVs[] =             // addr,value pairs array for resetting CVs to default
{
  {CV_ACCESSORY_DECODER_ADDRESS_LSB, 1},                   // NMRA default address LSB
  {CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},                   // NMRA default address MSB
  {CV_VERSION_ID,                    PROD_ID_DIY},         // NMRA Manufacturer defined version info 
  {CV_MANUFACTURER_ID,               MAN_ID_DIY},          // Values assigned by NMRA
  {CV_29_CONFIG,                     INIT_CV29_VALUE}      // NMRA Configuration Data #1
  /* The following CVs are user defined */
};
uInt16         sizeFactoryDefaultCVs = (sizeof(FactoryDefaultCVs) / sizeof(CVPair_t)) - 1; // Size of Array

/* Flags */
//Bit ;
// </editor-fold>// Variable Definitions

/*═══════════════════════════ FUNCTION  DEFINITIONS ══════════════════════════*/
// <editor-fold defaultstate="collapsed" desc="Functions Definitions">

/*────────────────────────────── Process Packet ──────────────────────────────*/
uInt8 Dcc_ProcessPacket(void)
{                                 // Copy, Process packet into structure and return Packet type
   uInt8  errorByte = 0;          // Error Detection byte
   uInt8  index;                  // array index
   uInt16 BoardAddressH;          // Hi part of Address from the Packet Byte0
   uInt16 BoardAddressL;          // Lo part of Address from the Packet Byte1
   uInt8  ExtAddrBits;
   
// <editor-fold defaultstate="collapsed" desc="Check Packet">
      
      di();                            // We need to do this with interrupts disabled
      
      uInt8 nBytes;  // counter
      DccPacket.Size = 0;                   // Clear DccRx if it's a 0
      for (nBytes = 0; nBytes < MAX_DCC_MESSAGE_LEN; nBytes++)  //
         DccPacket.Data[nBytes] = 0;             // clear packet buffer  

      ei();                            // enable interrupts
      
      for (index = 0; index < DccPacket.Size - 1; index++) // Except the last error byte
         errorByte ^= DccPacket.Data[index];               // Build Error Detection Data Byte

      if (errorByte != DccPacket.Data[DccPacket.Size - 1u])    // Compare error bytes
      {                                                              // Packet REEOR
      DccProcState.PacketType = ERROR_PCKT;                          //◄ 0 Report packet error
      return (unsigned) DccProcState.PacketType;
      }
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="Broadcast Packets">
   if ((DccPacket.Data[0] == 0b11111111) &&           //◄ 1 Idle Packet For All Decoders
       (DccPacket.Data[1] == 0b00000000))
   {                                        
      DccProcState.PacketType = IDLE_PCKT;            // Report Idle
      return (unsigned) DccProcState.PacketType;
   }
   
   if ((DccPacket.Data[0] == 0b00000000) &&           //◄ 2 Reset Packet For All Decoders
       (DccPacket.Data[1] == 0b00000000))
   {                                       
      DccProcState.PacketType = RESET_PCKT;           // , clear from All Stop condition
      DccProcState.StateFlags.Bits.HardReset = SOFT;  // soft Reset, clear from All Stop condition
      return (unsigned) DccProcState.PacketType;
   }
   
   if ((DccPacket.Data[0] == 0b00000000) &&           //◄ 3 Broadcast Stop Packets For All MF? Decoders
      ((DccPacket.Data[1] | ALL_STOP_MASK) == ALL_STOP_MASK)) 
   {                                       
      DccProcState.PacketType = STOP_PCKT;            // set All Stop aspect to signals
      return (unsigned) DccProcState.PacketType;
   }
// </editor-fold>

// <editor-fold defaultstate="collapsed" desc="Basic and Extended Accessory Packets">
   if ((DccPacket.Data[0] >= 128) && (DccPacket.Data[0] <= 191))
   {//■■►♦ Basic or Extended Accessory Decoders with 9 and 11-bit addresses
      if ((DccPacket.Data[1]  & 0b10000000) && !CV29.AccDecoder.Extended)
      {//■►Basic 9 bit address (Byte2 bit7 = 1)
         BoardAddressL = (DccPacket.Data[0]  & 0b00111111u);                        // Make room for 3 bits, Bits 3-8
         BoardAddressH = ((unsigned) ~DccPacket.Data[1] & 0b01110000u) << 2;        // bits 4-6 of Byte2 to bits 0-2 
         /* "By convention these bits (bits 4-6) are in ones complement." */
         DccProcState.Address = BoardAddressH | BoardAddressL;                      // 9 bit Address
         DccProcState.AccyPosition  = (DccPacket.Data[1] & 0b00001000u) >> 3;       // get Accessory Position
         DccProcState.OutputControl = (DccPacket.Data[1] & 0b00000111u);            // get Accessory Output Control

         if (DccProcState.Address == DCC_ThisAddr() | DccProcState.Address == 504)
         {//▼ We Have a Match for ThisDecoder or Broadcast Command for Basic Accessory Decoders
            if (DccPacket.Size == 3) 
            {//► Basic Decoder Control
               /*Since most devices are paired, the convention is that bit "0" of the second byte is used to distinguish
                 between which of a pair of outputs the accessory decoder is activating or  deactivating. Bits 1 and 2
                 of byte two are used to indicate which of 4 pairs of outputs the packet is controlling.*/
               DccProcState.PacketType = BASIC_CTRL_PCKT;                           //◄ 4 Basic Decoder Control
            }
            else
            {//► Basic Decoder CV Access (in bytes 3,4&5)
               DccProcState.CVinstType = (DccPacket.Data[1] & 0b00001100u) >> 2;    // get CV instruction type
               DccProcState.CVaddr     = (DccPacket.Data[2] & 0b00000011u) << 8;    // get CV MSb
               DccProcState.CVaddr     = DccProcState.CVaddr | DccPacket.Data[3];   // get CV LSb and combine
               DccProcState.CVvalue    = DccPacket.Data[4];                         // get CV Data
               DccProcState.PacketType = BASIC_CV_PCKT;                             //◄ 5 Basic Decoder CV Access
            }
         }
      }
      else 
      {//■► Extended 11 bit address (Byte2 bit7 = 0)
         BoardAddressL = (DccPacket.Data[0] & 0b00111111u) << 2;     // LSbits, Bits 5-0 make room for extra LSbits
         /* ones complement? NMRA don't say */
         ExtAddrBits   = (DccPacket.Data[1] & 0b00000110u) >> 1;     // Get Extended Address bits 1-2 of Byte2
         /* NMRA doesn't specify where bits 1&2 of Byte2 go, I went with LSb */
         BoardAddressL = BoardAddressL | ExtAddrBits;                // Combine LSbits & Extended Address
         BoardAddressH = (DccPacket.Data[1] & 0b01110000u) << 4;     // bits 4-6 to bits 8-10
         DccProcState.Address       = BoardAddressH | BoardAddressL; // 11 bit Address

         if (DccProcState.Address == DCC_ThisAddr() | DccProcState.Address == 2019)
         {//▼ We Have a Match for ThisDecoder or Broadcast for Extended Accessory Decoders
            if (DccPacket.Size == 4)
            {//► Extended Decoder Control 
               DccProcState.OutputControl = (DccPacket.Data[2] & 0b00011111u);      // get Accessory Output Control
               /*The Extended Accessory Decoder Control Packet is included for the purpose of transmitting aspect control
                 to signal decoders or data bytes to more complex accessory decoders. Each signal head can display one
                 aspect at a time.
                * XXXXX is for a single head. A value of 00000 for XXXXX indicates the absolute stop aspect. All other
                  aspects represented by the values for XXXXX are determined by the signaling system used and the
                  prototype being modeled.*/
               DccProcState.PacketType = EXTND_CTRL_PCKT;                           //◄ 6 Extended Decoder Control
            }
            else
            {//► Extended Decoder CV Access (in bytes 3,4&5)
               DccProcState.CVinstType  = (DccPacket.Data[1] & 0b00001100u) >> 2;   // get CV instruction type
               DccProcState.CVaddr = (DccPacket.Data[2] & 0b00000011u) << 8;        // get CV MSb
               DccProcState.CVaddr = DccProcState.CVaddr | DccPacket.Data[3];       // get CV LSb and combine
               DccProcState.CVvalue = DccPacket.Data[4];                            // get CV Data
               DccProcState.PacketType = EXTND_CV_PCKT;                             //◄ 7 Extended Decoder CV Access
            }
         }
      }
      return (unsigned) DccProcState.PacketType;
   }
// </editor-fold>

      return 99; // this should never happen
}

/*──────────────────────── Get This decoders Address ─────────────────────────*/
uInt16 DCC_ThisAddr(void)
{
   return (unsigned) (DCC_readCV(CV_ACCESSORY_DECODER_ADDRESS_MSB) << 8)
           | DCC_readCV(CV_ACCESSORY_DECODER_ADDRESS_LSB);
}

/*────────────────────────────────── Read CV ─────────────────────────────────*/
uInt8 DCC_readCV(uInt16 CVaddr)
{
   uInt8 CVvalue;

   #if	EEPROM_SIZE > 0
      CVvalue = eeprom_read(CVaddr);      // get CV from int EEPROM
   #else
      CVvalue = xeeprom_rd_byte(CVaddr);  // get CV from ext I²C EEPROM
   #endif
  return CVvalue ;
}

/*───────────────────────────────── Write CV ─────────────────────────────────*/
uInt8 DCC_writeCV(uInt16 CVaddr, uInt8 CVvalue)
{
   uInt8 CVvalueOld;

   //+ Check for Read-Only
   
   #if EEPROM_SIZE > 0
      CVvalueOld = eeprom_read(CVaddr);               // Read CV from int EEPROM
   #else
      CVvalueOld = xeeprom_rd_byte(CVaddr);           // Read CV from ext I²C EEPROM
   #endif

   if (CVvalueOld != CVvalue)                         // If different
   {
      #if	EEPROM_SIZE > 0
         eeprom_write(CVaddr, CVvalue);               // write CV to int EEPROM
      #else
         xeeprom_wr_byte(CVaddr, CVvalue);            // write CV to ext I²C EEPROM
      #endif
   }

   #if EEPROM_SIZE > 0
      CVvalue = eeprom_read(CVaddr);                  // ReRead CV from int EEPROM
   #else
      CVvalue = xeeprom_rd_byte(CVaddr);              // ReRead CV from ext I²C EEPROM
   #endif

   return CVvalue;                                    // and Return it
}

/*────────────────────────────── Acknowledge CV ──────────────────────────────*/
void ackCV(void)
{
// For Service Mode
// This function is called by the NmraDcc library when a DCC ACK needs to be sent
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read 
//const int DccAckPin = 3 ;
  
//  digitalWrite( DccAckPin, HIGH );         // Blink light
//  delay( 6 );  
//  digitalWrite( DccAckPin, LOW );
}

/*──────────────────────────── Check CV Validity ─────────────────────────────*/
uInt8 validCV(uInt16 CVaddr, uInt8 Writable)
{  // Does ThisDecoder have such a CV & is it writable
//   if ((CVaddr == CV_MANUFACTURER_ID) && Writable)
//      notifyCVResetFactoryDefault();                  // Report Reset

//   if (NotifyCV_f) notifyCVValid(CVaddr, Writable);     // Report ??

   uInt8 Valid = 1;                                   // set True

   if (CVaddr > 1024) // MAXCV
      Valid = 0;                                      // CVaddr Out of Range

   if (Writable && ((CVaddr == CV_VERSION_ID) || (CVaddr == CV_MANUFACTURER_ID)))
      Valid = 0;                                      // CVaddr Read Only

   return Valid;
}

/*──────────────────────────── Initialize Decoder ────────────────────────────*/
void Dcc_Initialize() {

   // Clear all the static member variables
   memset(&DccRxPacket, 0, sizeof(DccMsg_t));       // Clear with 0
   memset(&DccPacket, 0, sizeof(DccMsg_t));         // Clear with 0


   CV29.byte = INIT_CV29_VALUE;                  // Set CV29 Var
   DCC_writeCV(CV_29_CONFIG, INIT_CV29_VALUE);   // Set CV29  in EEPROM
   DccProcState.StateFlags.Bits.AccyDcdrType = CV29.AccDecoder.AccDecoder;
   DccProcState.StateFlags.Bits.AddrMethod = CV29.AccDecoder.Output;
   
   // Clear Dcc Proc State
//   clearDccProcState();
//   DccProcState.PageRegister = 1;  // Set the Page Register to it's default of 1 only on the first Reset 
//   DccProcState.DuplicateCount = 0;// Clear the LastMsg buffer and DuplicateCount in preparation for possible CV programming
//   memset(&DccProcState.LastMsg, 0, sizeof(DccMsg_t));

}

/*───────────────────────────── Clear DCC State ──────────────────────────────*/
void clearDccProcState(void)
{
  DccProcState.DuplicateCount = 0;// Clear the LastMsg buffer and DuplicateCount in preparation for possible CV programming
  memset(&DccProcState.LastMsg, 0, sizeof(DccMsg_t));
}

/*─────────────────────────────── Reset Memory ───────────────────────────────*/
void Dcc_ResetMemory(void)   // Clear All EEPROM & set Factory Default CVs
{
   uInt16 CVaddr = -1;       // The do will start at 0
   
   do {
      CVaddr++;
      DCC_writeCV(CVaddr, 0);    // Set all to 0
   } while(CVaddr < MAXCV); 
   for (uInt8 i = 0; i <= sizeFactoryDefaultCVs; i++)      // 3bytes per CVpair
      DCC_writeCV(FactoryDefaultCVs[i].CV, FactoryDefaultCVs[i].Value);
//   notifyDccReset(1);        // Report Hard Reset
}

/*===========================  DCC Debug Routines  ===========================*/
#ifdef DCC_DEBUG
/*─────────────────────────────────── ¿?¿? ───────────────────────────────────*/
uInt8 NmraDcc_getIntCount(void)
{
  return DccProcState.IntCount;
}

/*─────────────────────────────────── ¿?¿? ───────────────────────────────────*/
uInt8 NmraDcc_getTickCount(void)
{
  return DccProcState.TickCount;
}

/*─────────────────────────────────── ¿?¿? ───────────────────────────────────*/
uInt8 NmraDcc_getState(void)
{
  return DccRx.State;
}

/*─────────────────────────────────── ¿?¿? ───────────────────────────────────*/
uInt8 NmraDcc_getBitCount(void)
{
  return DccRx.BitCount;
}
#endif


// </editor-fold>// Functions Definitions
