Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
}