Yang Zhang / EasyCAT_lib

EasyCAT.cpp

Committer:
daid
Date:
2019-03-28
Revision:
2:e0fc1b098ce8
Parent:
0:7816b38c99cc
Child:
3:7d8b74ba7225

File content as of revision 2:e0fc1b098ce8:

//********************************************************************************************
//                                                                                           *
// AB&T Tecnologie Informatiche - Ivrea Italy                                                *
// http://www.bausano.net                                                                    *
// https://www.ethercat.org/en/products/791FFAA126AD43859920EA64384AD4FD.htm                 *
//                                                                                           *  
//********************************************************************************************    
//                                                                                           *
// This software is distributed as an example, in the hope that it could be useful,          *
// WITHOUT ANY WARRANTY, even the implied warranty of FITNESS FOR A PARTICULAR PURPOSE       *
//                                                                                           *
//******************************************************************************************** 


//----- EasyCAT library for mbed boards -----------------------------------------------------
//----- Derived from the AB&T EasyCAT Arduino shield library V 1.5 170912--------------------------

//----- Tested with the STM32 NUCLEO-F767ZI board -------------------------------------------



#include "mbed.h"
#include "EasyCAT.h"                         


static DigitalOut * SpiCs;								 	
static SPI Spi(D11, D12, D13);                          // declare MOSI MISO SCK 

#define SCS_Low_macro  (*SpiCs = 0);			// macro for set/reset SPI chip select
#define SCS_High_macro (*SpiCs = 1);			//

inline static void SPI_TransferTxBuffer(const void* buffer, int buffer_size)
{
    Spi.write(static_cast<const char*>(buffer), buffer_size, NULL, 0);
}

inline static void SPI_TransferRxBuffer(void* buffer, int buffer_size)
{
    Spi.write(NULL, 0, static_cast<char*>(buffer), buffer_size);
}
  
//---- constructors --------------------------------------------------------------------------------

EasyCAT::EasyCAT()                              //------- default constructor ---------------------- 
{                                               // 
  Sync_ = ASYNC;                                // if no synchronization mode is declared
                                                // ASYNC is the default
                                                //
  SCS_ = (PinName)D9;                           // if no chip select is declared                 
  SpiCs = new DigitalOut(SCS_, 1);              // pin D9 is the default  
}                                               //


EasyCAT::EasyCAT(PinName SCS) 					//------- SPI_CHIP_SELECT options -----------------
                                                //
                                                // for EasyCAT board REV_A we can choose between:
                                                // D8, D9, D10
                                                //  
                                                // for EasyCAT board REV_B we have three additional options:
                                                // A5, D6, D7 
{                                       
  Sync_ = ASYNC;                                // if no synchronization mode is declared
                                                // ASYNC is the default

  SCS_ = SCS;                                   //  initialize chip select  
  SpiCs = new DigitalOut(SCS_, 1);				//
}                                               // 


EasyCAT::EasyCAT(SyncMode Sync)                 //-------Synchronization options ---------------------- 
                                                //   
                                                // we can choose between:
                                                // ASYNC   = free running i.e. no synchronization
                                                //           between master and slave (default)   
                                                //
                                                // DC_SYNC = interrupt is generated from the
                                                //           Distributed clock unit
                                                //
                                                // SM_SYNC = interrupt is generated from the
                                                //           Syncronizatiom manager 2 
{                                               //
  Sync_ = Sync;                                 //                                           
                                                //                                        
  SCS_ = (PinName)D9;                           // default chip select is pin 9 
  SpiCs = new DigitalOut(SCS_, 1);              //
}                                               //  


                                                //-- Synchronization and chip select options -----  
EasyCAT::EasyCAT(PinName SCS, SyncMode Sync) 	//
												//				
{                                               //
  Sync_ = Sync;                                 //  
                                                //   
  SCS_ = SCS;                                   //  initialize chip select                                                 
  SpiCs = new DigitalOut(SCS_, 1);              // 
}                                               //  


  
//---- EasyCAT board initialization ---------------------------------------------------------------


bool EasyCAT::Init()
{
  #define Tout 1000
  
  ULONG TempLong;
  unsigned short i;
   
  Spi.frequency(14000000);						          // SPI speed 14MHz  
  Spi.format(8,0);	  							          // 8 bit mode 0
  
  wait_ms(100);
 
  SPIWriteRegisterDirect (RESET_CTL, DIGITAL_RST);        // LAN9252 reset 
   
  i = 0;                                                  // reset timeout 
  do                                                      // wait for reset to complete
  {                                                       //
    i++;                                                  //
    TempLong.Long = SPIReadRegisterDirect (RESET_CTL, 4); //
  }while (((TempLong.Byte[0] & 0x01) != 0x00) && (i != Tout));    
                                                          //                                                       
  if (i == Tout)                                          // time out expired      
  {                                                       //   
    return false;                                         // initialization failed  
  }                                                         
  
  i = 0;                                                  // reset timeout  
  do                                                      // check the Byte Order Test Register
  {                                                       //
    i++;                                                  //      
    TempLong.Long = SPIReadRegisterDirect (BYTE_TEST, 4); //
  }while ((TempLong.Long != 0x87654321) && (i != Tout));  //    
                                                          //                                                            
  if (i == Tout)                                          // time out expired      
  {                                                       // 
    return false;                                         // initialization failed  
  }            
  
  i = 0;                                                  // reset timeout  
  do                                                      // check the Ready flag
  {                                                       //
    i++;                                                  //    
    TempLong.Long = SPIReadRegisterDirect (HW_CFG, 4);    //
  }while (((TempLong.Byte[3] & READY) == 0) && (i != Tout));//
                                                          //
  if (i == Tout)                                          // time out expired      
  {                                                       //
    return false;                                         // initialization failed  
  }            
  

  
#ifdef BYTE_NUM
  printf ("STANDARD MODE\n"); 
#else
  printf ("CUSTOM MODE\n"); 
#endif

  printf ("%u Byte Out\n",TOT_BYTE_NUM_OUT);  
  printf ("%u Byte In\n",TOT_BYTE_NUM_IN);      

  printf ("Sync = ");                                                              
                                                            
                                                          
  if ((Sync_ == DC_SYNC) || (Sync_ == SM_SYNC))           //--- if requested, enable --------   
  {                                                       //--- interrupt generation -------- 
  
    if (Sync_ == DC_SYNC)
    {                                                     // enable interrupt from SYNC 0
      SPIWriteRegisterIndirect (0x00000004, AL_EVENT_MASK, 4);  
                                                          // in AL event mask register, and disable 
                                                          // other interrupt sources    
      printf("DC_SYNC\n");                                                      
    }                                                       
                                                                                                         
    else
    {                                                     // enable interrupt from SM 0 event
      SPIWriteRegisterIndirect (0x00000100, AL_EVENT_MASK, 4);  
                                                          // in AL event mask register, and disable 
                                                          // other interrupt sources 
      printf("SM_SYNC\n");    
    }   
                                                         
    SPIWriteRegisterDirect (IRQ_CFG, 0x00000111);         // set LAN9252 interrupt pin driver  
                                                          // as push-pull active high
                                                          // (On the EasyCAT shield board the IRQ pin
                                                          // is inverted by a mosfet, so Arduino                                                        
                                                          // receives an active low signal)
                                                                        
    SPIWriteRegisterDirect (INT_EN, 0x00000001);          // enable LAN9252 interrupt      
  } 

  else
  {
    printf("ASYNC\n");
  }
  

  TempLong.Long = SPIReadRegisterDirect (ID_REV, 4);      // read the chip identification 
  printf ("Detected chip ");                     		  // and revision, and print it
  printf ("%x ", TempLong.Word[1]);                       // out on the serial line
  printf (" Rev ");                                       //    
  printf ("%u \n", TempLong.Word[0]);                     //    
  
  
 /* 
  printf ("%u \n", TOT_BYTE_NUM_OUT); 
  printf ("%u \n", BYTE_NUM_OUT);                   
  printf ("%u \n", BYTE_NUM_ROUND_OUT);                
  printf ("%u \n", LONG_NUM_OUT);                  
     
  printf ("%u \n", SEC_BYTE_NUM_OUT);                    
  printf ("%u \n", SEC_BYTE_NUM_ROUND_OUT);                       
  printf ("%u \n\n", SEC_LONG_NUM_OUT);                   

  printf ("%u \n", TOT_BYTE_NUM_IN); 
  printf ("%u \n", BYTE_NUM_IN);                       
  printf ("%u \n", BYTE_NUM_ROUND_IN);                   
  printf ("%u \n", LONG_NUM_IN);                    
     
  printf ("%u \n", SEC_BYTE_NUM_IN);                    
  printf ("%u \n", SEC_BYTE_NUM_ROUND_IN);                         
  printf ("%u \n", SEC_LONG_NUM_IN);                         
*/
  
  
//--------------------------------------------------------------------------------------------  
  
  
  return true;                                            // initalization completed        
};  




//---- EtherCAT task ------------------------------------------------------------------------------

unsigned char EasyCAT::MainTask()                           // must be called cyclically by the application

{
  bool WatchDog = true;
  bool Operational = false; 
  unsigned char i;
  ULONG TempLong; 
  unsigned char Status;  
 
  
  TempLong.Long = SPIReadRegisterIndirect (WDOG_STATUS, 1); // read watchdog status
  if ((TempLong.Byte[0] & 0x01) == 0x01)                    //
    WatchDog = false;                                       // set/reset the corrisponding flag
  else                                                      //
    WatchDog = true;                                        //

    
  TempLong.Long = SPIReadRegisterIndirect (AL_STATUS, 1);   // read the EtherCAT State Machine status
  Status = TempLong.Byte[0] & 0x0F;                         //
  if (Status == ESM_OP)                                     // to see if we are in operational state
    Operational = true;                                     //
  else                                                      // set/reset the corrisponding flag
    Operational = false;                                    //    


                                                            //--- process data transfert ----------
                                                            //                                                        
  if (WatchDog | !Operational)                              // if watchdog is active or we are 
  {                                                         // not in operational state, reset 
    for (i=0; i < TOT_BYTE_NUM_OUT ; i++)                   // the output buffer
    BufferOut.Byte[i] = 0;                                  //

/*                                                          // debug
    if (!Operational)                                       //
      printf("Not operational\n");                    		//
    if (WatchDog)                                           //    
      printf("WatchDog\n");                           		//  
*/                                                          //
  }
  
  else                                                      
  {                                                         
    SPIReadProcRamFifo();                                   // otherwise transfer process data from 
  }                                                         // the EtherCAT core to the output buffer  
                 
  SPIWriteProcRamFifo();                                    // we always transfer process data from
                                                            // the input buffer to the EtherCAT core  

  if (WatchDog)                                             // return the status of the State Machine      
  {                                                         // and of the watchdog
    Status |= 0x80;                                         //
  }                                                         //
  return Status;                                            //   
  
}


    
//---- read a directly addressable registers  -----------------------------------------------------

unsigned long EasyCAT::SPIReadRegisterDirect (unsigned short Address, unsigned char Len)

                                                   // Address = register to read
                                                   // Len = number of bytes to read (1,2,3,4)
                                                   //
                                                   // a long is returned but only the requested bytes
                                                   // are meaningful, starting from LsByte                                                 
{
  ULONG Result; 
  UWORD Addr;
  Addr.Word = Address; 

  SCS_Low_macro                                             // SPI chip select enable

  char buffer[3] = {COMM_SPI_READ, Addr.Byte[1], Addr.Byte[0]};
  SPI_TransferTxBuffer(buffer, 3);
  SPI_TransferRxBuffer(Result.Byte, Len);
  
  SCS_High_macro                                            // SPI chip select disable 
 
  return Result.Long;                                       // return the result
}




//---- write a directly addressable registers  ----------------------------------------------------

void EasyCAT::SPIWriteRegisterDirect (unsigned short Address, unsigned long DataOut)

                                                   // Address = register to write
                                                   // DataOut = data to write
{ 
  ULONG Data; 
  UWORD Addr;
  Addr.Word = Address;
  Data.Long = DataOut;    

  SCS_Low_macro                                             // SPI chip select enable  
  
  char buffer[7] = {COMM_SPI_WRITE, Addr.Byte[1], Addr.Byte[0],
                    Data.Byte[0], Data.Byte[1], Data.Byte[2], Data.Byte[3]};
  SPI_TransferTxBuffer(buffer, 7);
 
  SCS_High_macro                                            // SPI chip select enable   
}


//---- read an undirectly addressable registers  --------------------------------------------------

unsigned long EasyCAT::SPIReadRegisterIndirect (unsigned short Address, unsigned char Len)

                                                   // Address = register to read
                                                   // Len = number of bytes to read (1,2,3,4)
                                                   //
                                                   // a long is returned but only the requested bytes
                                                   // are meaningful, starting from LsByte                                                  
{
  ULONG TempLong;
  UWORD Addr;
  Addr.Word = Address;
                                                            // compose the command
                                                            //
  TempLong.Byte[0] = Addr.Byte[0];                          // address of the register
  TempLong.Byte[1] = Addr.Byte[1];                          // to read, LsByte first
  TempLong.Byte[2] = Len;                                   // number of bytes to read
  TempLong.Byte[3] = ESC_READ;                              // ESC read 

  SPIWriteRegisterDirect (ECAT_CSR_CMD, TempLong.Long);     // write the command

  do
  {                                                         // wait for command execution
    TempLong.Long = SPIReadRegisterDirect(ECAT_CSR_CMD,4);  //
  }                                                         //
  while(TempLong.Byte[3] & ECAT_CSR_BUSY);                  //
                                                             
                                                              
  TempLong.Long = SPIReadRegisterDirect(ECAT_CSR_DATA,Len); // read the requested register
  return TempLong.Long;                                     //
}


//---- write an undirectly addressable registers  -------------------------------------------------

void  EasyCAT::SPIWriteRegisterIndirect (unsigned long DataOut, unsigned short Address, unsigned char Len)

                                                   // Address = register to write
                                                   // DataOut = data to write                                                    
{
  ULONG TempLong;
  UWORD Addr;
  Addr.Word = Address;


  SPIWriteRegisterDirect (ECAT_CSR_DATA, DataOut);            // write the data

                                                              // compose the command
                                                              //                                
  TempLong.Byte[0] = Addr.Byte[0];                            // address of the register  
  TempLong.Byte[1] = Addr.Byte[1];                            // to write, LsByte first
  TempLong.Byte[2] = Len;                                     // number of bytes to write
  TempLong.Byte[3] = ESC_WRITE;                               // ESC write

  SPIWriteRegisterDirect (ECAT_CSR_CMD, TempLong.Long);       // write the command

  do                                                          // wait for command execution
  {                                                           //
    TempLong.Long = SPIReadRegisterDirect (ECAT_CSR_CMD, 4);  //  
  }                                                           //  
  while (TempLong.Byte[3] & ECAT_CSR_BUSY);                   //
}


//---- read from process ram fifo ----------------------------------------------------------------


void EasyCAT::SPIReadProcRamFifo()    // read BYTE_NUM bytes from the output process ram, through the fifo
                                      //        
                                      // these are the bytes received from the EtherCAT master and
                                      // that will be use by our application to write the outputs
{
  ULONG TempLong;
   
  #if TOT_BYTE_NUM_OUT > 0

    SPIWriteRegisterDirect (ECAT_PRAM_RD_CMD, PRAM_ABORT);        // abort any possible pending transfer

  
    SPIWriteRegisterDirect (ECAT_PRAM_RD_ADDR_LEN, (0x00001000 | (((uint32_t)TOT_BYTE_NUM_OUT) << 16)));   
                                                                  // the high word is the num of bytes
                                                                  // to read 0xTOT_BYTE_NUM_OUT----
                                                                  // the low word is the output process        
                                                                  // ram offset 0x----1000 

    SPIWriteRegisterDirect (ECAT_PRAM_RD_CMD, 0x80000000);        // start command        
 
 
                                                //------- one round is enough if we have ----
                                                //------- to transfer up to 64 bytes --------
   
    do                                                            // wait for the data to be       
    {                                                             // transferred from the output  
      TempLong.Long = SPIReadRegisterDirect (ECAT_PRAM_RD_CMD,2); // process ram to the read fifo       
    }                                                             //    
    while (TempLong.Byte[1] != FST_LONG_NUM_OUT);                 // 
  
    SCS_Low_macro                                                 // enable SPI chip select 
  
    char buffer[3] = {COMM_SPI_READ, 0, 0x00};
    SPI_TransferTxBuffer(buffer, 3);
    SPI_TransferRxBuffer(BufferOut.Byte, FST_BYTE_NUM_ROUND_OUT);
    
    SCS_High_macro                                                // disable SPI chip select    
  #endif  

  
  #if SEC_BYTE_NUM_OUT > 0                    //-- if we have to transfer more then 64 bytes --
                                              //-- we must do another round -------------------
                                              //-- to transfer the remainig bytes -------------


    do                                                          // wait for the data to be       
    {                                                           // transferred from the output  
      TempLong.Long = SPIReadRegisterDirect(ECAT_PRAM_RD_CMD,2);// process ram to the read fifo 
    }                                                           //    
    while (TempLong.Byte[1] != SEC_LONG_NUM_OUT);               //  

    SCS_Low_macro                                               // enable SPI chip select   
    
    char buffer[3] = {COMM_SPI_READ, 0, 0x00};
    SPI_TransferTxBuffer(buffer, 3);
    SPI_TransferRxBuffer(&BufferOut.Byte[64], SEC_BYTE_NUM_ROUND_OUT);
    
    SCS_High_macro                                              // SPI chip select disable  
  #endif    
}


//---- write to the process ram fifo --------------------------------------------------------------

void EasyCAT::SPIWriteProcRamFifo()    // write BYTE_NUM bytes to the input process ram, through the fifo
                                       //    
                                       // these are the bytes that we have read from the inputs of our                   
                                       // application and that will be sent to the EtherCAT master
{
  ULONG TempLong;
  
  
  
  #if TOT_BYTE_NUM_IN > 0  
  
    SPIWriteRegisterDirect (ECAT_PRAM_WR_CMD, PRAM_ABORT);        // abort any possible pending transfer
  
 
    SPIWriteRegisterDirect (ECAT_PRAM_WR_ADDR_LEN, (0x00001200 | (((uint32_t)TOT_BYTE_NUM_IN) << 16)));   
                                                                  // the high word is the num of bytes
                                                                  // to write 0xTOT_BYTE_NUM_IN----
                                                                  // the low word is the input process        
                                                                  // ram offset  0x----1200
                                                                                               
    SPIWriteRegisterDirect (ECAT_PRAM_WR_CMD, 0x80000000);        // start command  
  
  
                                                //------- one round is enough if we have ----
                                                //------- to transfer up to 64 bytes --------
    
    do                                                            // check that the fifo has      
    {                                                             // enough free space 
      TempLong.Long = SPIReadRegisterDirect (ECAT_PRAM_WR_CMD,2); //  
    }                                                             //  
    while (TempLong.Byte[1] < FST_LONG_NUM_IN);                   //
  
    SCS_Low_macro                                                 // enable SPI chip select
  
    char buffer[3] = {COMM_SPI_WRITE, 0x00, 0x20};
    SPI_TransferTxBuffer(buffer, 3);
    SPI_TransferTxBuffer(BufferIn.Byte, FST_BYTE_NUM_ROUND_IN);
  
    SCS_High_macro                                                // disable SPI chip select           
  #endif        

  
  #if SEC_BYTE_NUM_IN > 0                     //-- if we have to transfer more then 64 bytes --
                                              //-- we must do another round -------------------
                                              //-- to transfer the remainig bytes -------------

    do                                                          // check that the fifo has     
    {                                                           // enough free space       
      TempLong.Long = SPIReadRegisterDirect(ECAT_PRAM_WR_CMD,2);// 
    }                                                           //  
    while (TempLong.Byte[1] < (SEC_LONG_NUM_IN));               //
                             
    SCS_Low_macro                                               // enable SPI chip select
    
    char buffer[3] = {COMM_SPI_WRITE, 0x00, 0x20};
    SPI_TransferTxBuffer(buffer, 3);
    SPI_TransferTxBuffer(&BufferIn.Byte[64], SEC_BYTE_NUM_ROUND_IN);

    SCS_High_macro                                              // disable SPI chip select    
  #endif       
}