EasyCAT shield library - It allows you to make an EtherCAT slave

Dependents:   TestEasyCAT_SM_sync TestEasyCAT_LoopBack TestEasyCAT_DC_sync TestEasyCAT

The EasyCAT Shield and /static/img/mbed.gif boards

/media/uploads/EasyCAT/easycat_onnucleo.jpg

  • The EasyCAT Shield is an Arduino shield, designed and manufactured in Italy by AB&T Tecnologie Informatiche, that allow us to build a custom EtherCAT® slave in an easy way.
  • The EasyCAT Shield uses the 3x2 SPI connector to communicate with the microcontroller. This connector is standard on all the Arduino boards but some Arduino compatible boards don’t provide it. In this case, the SPI signal are always present on pins 13,12,and 11. Some examples of this boards are the STM32 Nucleo and the NXP LPCXpresso, part of the Mbed ecosystem.
  • To address this issue in the EasyCAT Shield revision “C” there are three solder jumpers, on the bottom side of the board, that allow us to connect the SPI signals, SCK,MISO and MOSI, also on pins 13, 12 and 11.

/media/uploads/EasyCAT/spi_selection_jumpered.jpg

  • For your convenience the EasyCAT Shield can be ordered with the three solder jumpers already bridged and with the 3x2 connector not installed on the board. To request this option select EasyCAT spi_on_13_12_11 in the webshop.

Import libraryEasyCAT_lib

EasyCAT shield library - It allows you to make an EtherCAT slave

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       
}