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.
Diff: EasyCAT.cpp
- Revision:
- 0:7816b38c99cc
- Child:
- 2:e0fc1b098ce8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EasyCAT.cpp Tue Sep 12 17:09:43 2017 +0000 @@ -0,0 +1,584 @@ +//******************************************************************************************** +// * +// 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" + + +DigitalOut * SpiCs; +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); // + + // macro for the SPI transfer +inline static void SPI_TransferTx (unsigned char Data) +{ // + Spi.write(Data); // +}; // + // +inline static void SPI_TransferTxLast (unsigned char Data) +{ // + Spi.write(Data); // +}; // + // +inline static unsigned char SPI_TransferRx (unsigned char Data) +{ // + return Spi.write(Data); // +}; // + + + +//---- 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 +{ + 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; + unsigned char i; + + SCS_Low_macro // SPI chip select enable + + SPI_TransferTx(COMM_SPI_READ); // SPI read command + SPI_TransferTx(Addr.Byte[1]); // address of the register + SPI_TransferTxLast(Addr.Byte[0]); // to read, MsByte first + + for (i=0; i<Len; i++) // read the requested number of bytes + { // LsByte first + Result.Byte[i] = SPI_TransferRx(DUMMY_BYTE); // + } // + + 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 + + SPI_TransferTx(COMM_SPI_WRITE); // SPI write command + SPI_TransferTx(Addr.Byte[1]); // address of the register + SPI_TransferTx(Addr.Byte[0]); // to write MsByte first + + SPI_TransferTx(Data.Byte[0]); // data to write + SPI_TransferTx(Data.Byte[1]); // LsByte first + SPI_TransferTx(Data.Byte[2]); // + SPI_TransferTxLast(Data.Byte[3]); // + + 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; + unsigned char i; + + #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 + + SPI_TransferTx(COMM_SPI_READ); // SPI read command + SPI_TransferTx(0x00); // address of the read + SPI_TransferTxLast(0x00); // fifo MsByte first + + for (i=0; i< FST_BYTE_NUM_ROUND_OUT; i++) // transfer the data + { // + BufferOut.Byte[i] = SPI_TransferRx(DUMMY_BYTE); // + } // + + 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 + + SPI_TransferTx(COMM_SPI_READ); // SPI read command + SPI_TransferTx(0x00); // address of the read + SPI_TransferTxLast(0x00); // fifo MsByte first + + for (i=0; i< (SEC_BYTE_NUM_ROUND_OUT); i++) // transfer loop for the remaining + { // bytes + BufferOut.Byte[i+64] = SPI_TransferRx(DUMMY_BYTE); // we transfer the second part of + } // the buffer, so offset by 64 + + 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; + unsigned char i; + + + + #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 + + SPI_TransferTx(COMM_SPI_WRITE); // SPI write command + SPI_TransferTx(0x00); // address of the write fifo + SPI_TransferTx(0x20); // MsByte first + + for (i=0; i< (FST_BYTE_NUM_ROUND_IN - 1 ); i++) // transfer the data + { // + SPI_TransferTx (BufferIn.Byte[i]); // + } // + // + SPI_TransferTxLast (BufferIn.Byte[i]); // one last byte + + 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 + + SPI_TransferTx(COMM_SPI_WRITE); // SPI write command + SPI_TransferTx(0x00); // address of the write fifo + SPI_TransferTx(0x20); // MsByte first + + for (i=0; i< (SEC_BYTE_NUM_ROUND_IN - 1); i++) // transfer loop for the remaining + { // bytes + SPI_TransferTx (BufferIn.Byte[i+64]); // we transfer the second part of + } // the buffer, so offset by 64 + // + SPI_TransferTxLast (BufferIn.Byte[i+64]); // one last byte + + SCS_High_macro // disable SPI chip select + #endif +}