#ifndef ABT_EasyCAT_H #define ABT_EasyCAT_H #include <Arduino.h> #include <SPI.h> //---- AB&T EasyCAT library V_1.4 ----------------------------------------------------------------- // // AB&T Tecnologie Informatiche - Ivrea Italy // http://www.bausano.net // https://www.ethercat.org/en/products/791FFAA126AD43859920EA64384AD4FD.htm // // This library has been tested with Arduino IDE 1.6.8 // https://www.arduino.cc //****** configuration parameters ***************************************************************** #define SPI_fast_transfer // enable fast SPI transfert (default) #define SPI_fast_SS // enable fast SPI chip select management (default) //************************************************************************************************* //---- LAN9252 registers -------------------------------------------------------------------------- //---- access to EtherCAT registers ------------------- #define ECAT_CSR_DATA 0x0300 // EtherCAT CSR Interface Data Register #define ECAT_CSR_CMD 0x0304 // EtherCAT CSR Interface Command Register //---- access to EtherCAT process RAM ----------------- #define ECAT_PRAM_RD_ADDR_LEN 0x0308 // EtherCAT Process RAM Read Address and Length Register #define ECAT_PRAM_RD_CMD 0x030C // EtherCAT Process RAM Read Command Register #define ECAT_PRAM_WR_ADDR_LEN 0x0310 // EtherCAT Process RAM Write Address and Length Register #define ECAT_PRAM_WR_CMD 0x0314 // EtherCAT Process RAM Write Command Register #define ECAT_PRAM_RD_DATA 0x0000 // EtherCAT Process RAM Read Data FIFO #define ECAT_PRAM_WR_DATA 0x0020 // EtherCAT Process RAM Write Data FIFO //---- EtherCAT registers ----------------------------- #define AL_STATUS 0x0130 // AL status #define WDOG_STATUS 0x0440 // watch dog status //---- LAN9252 registers ------------------------------ #define HW_CFG 0x0074 // hardware configuration register #define BYTE_TEST 0x0064 // byte order test register #define RESET_CTL 0x01F8 // reset register #define ID_REV 0x0050 // chip ID and revision //---- LAN9252 flags ------------------------------------------------------------------------------ #define ECAT_CSR_BUSY 0x80 #define PRAM_READ_BUSY 0x80 #define PRAM_READ_AVAIL 0x01 #define PRAM_WRITE_AVAIL 0x01 #define READY 0x08000000 #define DIGITAL_RST 0x00000001 #define ETHERCAT_RST 0x00000040 //---- EtherCAT flags ----------------------------------------------------------------------------- // EtherCAT state machine #define ESM_INIT 0x01 // init #define ESM_PREOP 0x02 // pre-operational #define ESM_BOOT 0x03 // bootstrap #define ESM_SAFEOP 0x04 // safe-operational #define ESM_OP 0x08 // operational //--- ESC commands -------------------------------------------------------------------------------- #define ESC_WRITE 0x80 #define ESC_READ 0xC0 //---- SPI ---------------------------------------------------------------------------------------- #define COMM_SPI_READ 0x03 #define COMM_SPI_WRITE 0x02 #define DUMMY_BYTE 0xFF #if defined(ARDUINO_ARCH_AVR) #define SpiSpeed 8000000 #elif defined (ARDUINO_ARCH_SAM) #define SpiSpeed 14000000 #elif defined (ARDUINO_ARCH_SAMD) #define SpiSpeed 12000000 #else #define SpiSpeed 8000000 #endif //---- typedef ------------------------------------------------------------------------------------ typedef union { unsigned short Word; unsigned char Byte[2]; } UWORD; typedef union { unsigned long Long; unsigned short Word[2]; unsigned char Byte[4]; } ULONG; typedef union { unsigned char Byte [32]; unsigned long Long [8]; } PROCBUFFER; //------------------------------------------------------------------------------------------------- class EasyCAT { public: EasyCAT(unsigned char SPI_CHIP_SELECT); // constructor EasyCAT(); // default constructor unsigned char MainTask(); // EtherCAT main task // must be called cyclically by the application bool Init(); // EasyCAT board initialization PROCBUFFER BufferOut; // output process data buffer PROCBUFFER BufferIn; // input process data buffer private: void SPIWriteRegisterDirect (unsigned short Address, unsigned long DataOut); unsigned long SPIReadRegisterDirect (unsigned short Address, unsigned char Len); void SPIWriteRegisterIndirect (unsigned long DataOut, unsigned short Address); unsigned long SPIReadRegisterIndirect (unsigned short Address, unsigned char Len); void SPIReadProcRamFifo(); void SPIWriteProcRamFifo(); unsigned char SCS; #if defined(ARDUINO_ARCH_SAM) Pio* pPort_SCS; uint32_t Bit_SCS; #endif #if defined(ARDUINO_ARCH_SAMD) EPortType Port_SCS; uint32_t Bit_SCS; #endif #if defined(ARDUINO_ARCH_AVR) unsigned char Mask_SCS; unsigned char Port_SCS; volatile uint8_t* pPort_SCS; #endif //----- fast SPI chip select management ---------------------------------------------------------- #if defined SPI_fast_SS #if defined(ARDUINO_ARCH_AVR) // -- AVR architecture (Uno - Mega) --------- #define SCS_Low_macro *pPort_SCS &= ~(Mask_SCS); #define SCS_High_macro *pPort_SCS |= (Mask_SCS); //#elif defined(ARDUINO_ARCH_SAMD) //--- SAMD architecture (Zero) -------------- // #define SCS_Low_macro PORT->Group[Port_SCS].OUTCLR.reg = (1<<Bit_SCS); // #define SCS_High_macro PORT->Group[Port_SCS].OUTSET.reg = (1<<Bit_SCS); #elif defined(ARDUINO_ARCH_SAM) //---- SAM architecture (Due) --------------- #define SCS_Low_macro pPort_SCS->PIO_CODR = Bit_SCS; #define SCS_High_macro pPort_SCS->PIO_SODR = Bit_SCS; #else //-- standard management for others architectures -- #define SCS_Low_macro digitalWrite(SCS, LOW); #define SCS_High_macro digitalWrite(SCS, HIGH); #endif //----- standard SPI chip select management ------------------------------------------------------ #else #define SCS_Low_macro digitalWrite(SCS, LOW); #define SCS_High_macro digitalWrite(SCS, HIGH); #endif //------------------------------------------------------------------------------------------------- //----- fast SPI transfert ------------------------------------------------------------------------ #if defined SPI_fast_transfer #if defined(ARDUINO_ARCH_AVR) // -- AVR architecture (Uno - Mega) --------- inline static void SPI_TransferTx(unsigned char Data) { \ SPDR = Data; \ asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; \ }; inline static void SPI_TransferTxLast(unsigned char Data) { \ SPDR = Data; \ asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; \ }; inline static unsigned char SPI_TransferRx(unsigned char Data) { \ SPDR = Data; \ asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; \ return SPDR; }; #elif defined(ARDUINO_ARCH_SAMD) //--- SAMD architecture (Zero) -------------- inline static void SPI_TransferTx (unsigned char Data){ \ while(SERCOM4->SPI.INTFLAG.bit.DRE == 0){}; \ SERCOM4->SPI.DATA.bit.DATA = Data;}; inline static void SPI_TransferTxLast(unsigned char Data){ \ while(SERCOM4->SPI.INTFLAG.bit.DRE == 0){}; \ SERCOM4->SPI.DATA.bit.DATA = Data; \ while(SERCOM4->SPI.INTFLAG.bit.TXC == 0){};}; inline static unsigned char SPI_TransferRx (unsigned char Data){ \ unsigned char Dummy = SERCOM4->SPI.DATA.bit.DATA; \ while(SERCOM4->SPI.INTFLAG.bit.DRE == 0){}; \ SERCOM4->SPI.DATA.bit.DATA = Data; \ while(SERCOM4->SPI.INTFLAG.bit.RXC == 0){}; \ return SERCOM4->SPI.DATA.bit.DATA;}; \ #elif defined(ARDUINO_ARCH_SAM) //---- SAM architecture (Due) --------------- // TODO! currently standard transfert is used inline static void SPI_TransferTx (unsigned char Data) {SPI.transfer(Data); }; inline static void SPI_TransferTxLast (unsigned char Data) {SPI.transfer(Data); }; inline static unsigned char SPI_TransferRx (unsigned char Data) {return SPI.transfer(Data); }; #else //-- standard transfert for others architectures inline static void SPI_TransferTx (unsigned char Data) {SPI.transfer(Data); }; inline static void SPI_TransferTxLast (unsigned char Data) {SPI.transfer(Data); }; inline static unsigned char SPI_TransferRx (unsigned char Data) {return SPI.transfer(Data); }; #endif //---- standard SPI transfert --------------------------------------------------------------------- #else inline static void SPI_TransferTx (unsigned char Data) {SPI.transfer(Data); }; inline static void SPI_TransferTxLast (unsigned char Data) {SPI.transfer(Data); }; inline static unsigned char SPI_TransferRx (unsigned char Data) {return SPI.transfer(Data); }; #endif //---------------------------------------------------------------------------------------- }; #endif
#include "EasyCAT.h" #include <SPI.h> //---- AB&T EasyCAT library V_1.4 ----------------------------------------------------------------- // // AB&T Tecnologie Informatiche - Ivrea Italy // http://www.bausano.net // https://www.ethercat.org/en/products/791FFAA126AD43859920EA64384AD4FD.htm // // This library has been tested with Arduino IDE 1.6.8 // https://www.arduino.cc //--- V_1.4 --- // // The MainTask function now return the state of the // Ethercat State machine and of the Wachdog // // Now the SPI chip select is initialized High inside the constructor //--- V_1.3 --- // Replaced delay(100) in Init() function with a wait loop // // //--- V_1.2 --- // SPI chip select is configured by the application, setting a constructor parameter. // If no chip select is declared in the constructor, pin 9 will be used as default. // Code cleaning. // Comments in english. // TODO: fast SPI transfert for DUE board // // //--- V_1.1 --- // First official release. // SPI chip select is configured editing the library ".h" file. //---- constructor -------------------------------------------------------------------------------- EasyCAT::EasyCAT(unsigned char SPI_CHIP_SELECT) //------- SPI_CHIP_SELECT options ----------------- // // for EasyCAT board REV_A we can choose between: // 8, 9, 10 // // for EasyCAT board REV_B we have three additional options: // A5, 6, 7 { SCS = (unsigned char)SPI_CHIP_SELECT; // initialize chip select digitalWrite (SCS, HIGH); // } EasyCAT::EasyCAT() //------- default constructor ---------------------- { // SCS = 9; // if no chip select is declared, default is pin 9 digitalWrite (SCS, HIGH); // initialize chip select } // //---- EasyCAT board initialization --------------------------------------------------------------- bool EasyCAT::Init() { ULONG TempLong; SPI.begin(); #if defined(ARDUINO_ARCH_SAMD) // calculate the microcontroller port and Bit_SCS = g_APinDescription[SCS].ulPin; // pin for fast SPI chip select management Port_SCS = g_APinDescription[SCS].ulPort; // #endif // // #if defined(ARDUINO_ARCH_SAM) // Bit_SCS = g_APinDescription[SCS].ulPin; // pPort_SCS = g_APinDescription[SCS].pPort; // #endif // // #if defined(ARDUINO_ARCH_AVR) // Mask_SCS = (digitalPinToBitMask(SCS)); // Port_SCS = digitalPinToPort(SCS); // pPort_SCS = portOutputRegister(Port_SCS); // #endif // digitalWrite(SCS, HIGH); pinMode(SCS, OUTPUT); SPI.beginTransaction(SPISettings(SpiSpeed, MSBFIRST, SPI_MODE0)); // set SPI parameters SPIWriteRegisterDirect (RESET_CTL, (DIGITAL_RST & ETHERCAT_RST)); // LAN9252 reset //delay (100); // wait 100mS for (unsigned int i=0; i<50000; i++) { digitalWrite(SCS, HIGH); } TempLong.Long = SPIReadRegisterDirect (BYTE_TEST, 4); // read test register if (TempLong.Long == 0x87654321) // if the test register is ok { // check also the READY flag TempLong.Long = SPIReadRegisterDirect (HW_CFG, 4); // if (TempLong.Long & READY) // { // SPI.endTransaction(); // if both are ok return true; // initalization completed } } SPI.endTransaction(); // SPI.end(); // otherwise // return false; // initialization failed }; //---- EtherCAT task ------------------------------------------------------------------------------ unsigned char EasyCAT::MainTask() // must be called cyclically by the application { bool WatchDog = 0; bool Operational = 0; unsigned char i; ULONG TempLong; unsigned char Status; // set SPI parameters SPI.beginTransaction(SPISettings(SpiSpeed, MSBFIRST, SPI_MODE0)); TempLong.Long = SPIReadRegisterIndirect (WDOG_STATUS, 1); // read watchdog status if ((TempLong.Byte[0] & 0x01) == 0x01) // WatchDog = 0; // set/reset the corrisponding flag else // WatchDog = 1; // 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 = 1; // else // set/reset the corrisponding flag Operational = 0; // //--- process data transfert ---------- // if (WatchDog | !Operational) // if watchdog is active or we are { // not in operational state, reset for (i=0; i<4; i++) // the output buffer BufferOut.Long[i] = 0; // /* // debug if (!Operational) // Serial.println("Not operational"); // if (WatchDog) // Serial.println("WatchDog"); // */ // } 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 SPI.endTransaction(); // 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) // 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] = 4; // we write always 4 bytes 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 32 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; SPIWriteRegisterDirect (ECAT_PRAM_RD_ADDR_LEN, 0x00201000); // we always read 32 bytes 0x0020---- // output process ram offset 0x----1000 SPIWriteRegisterDirect (ECAT_PRAM_RD_CMD, 0x80000000); // start command do // wait for data to be transferred { // from the output process ram TempLong.Long = SPIReadRegisterDirect (ECAT_PRAM_RD_CMD,4); // to the read fifo } // while (!(TempLong.Byte[0] & PRAM_READ_AVAIL) || (TempLong.Byte[1] != 8)); SCS_Low_macro // SPI chip select enable 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<32; i++) // 32 bytes read loop { // BufferOut.Byte[i] = SPI_TransferRx(DUMMY_BYTE); // } // SCS_High_macro // SPI chip select disable } //---- write to the process ram fifo -------------------------------------------------------------- void EasyCAT::SPIWriteProcRamFifo() // write 32 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; SPIWriteRegisterDirect (ECAT_PRAM_WR_ADDR_LEN, 0x00201200); // we always write 32 bytes 0x0020---- // input process ram offset 0x----1200 SPIWriteRegisterDirect (ECAT_PRAM_WR_CMD, 0x80000000); // start command do // check fifo has available space { // for data to be written TempLong.Long = SPIReadRegisterDirect (ECAT_PRAM_WR_CMD,4); // } // while (!(TempLong.Byte[0] & PRAM_WRITE_AVAIL) || (TempLong.Byte[1] < 8) ); 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<31; i++) // 32 bytes write loop { // SPI_TransferTx (BufferIn.Byte[i]); // } // // SPI_TransferTxLast (BufferIn.Byte[31]); // SCS_High_macro // disable SPI chip select }
//---- AB&T EasyCAT shield application example ------------------------------------------------------------------ // // // AB&T Tecnologie Informatiche - Ivrea Italy // http://www.bausano.net // https://www.ethercat.org/en/products/791FFAA126AD43859920EA64384AD4FD.htm // #include "EasyCAT.h" // EasyCAT library to interface the LAN9252 #include <SPI.h> // SPI library EasyCAT EASYCAT; // EasyCAT istantiation // The constructor allow us to choose the pin used for the EasyCAT SPI chip select // Without any parameter pin 9 will be used // for EasyCAT board REV_A we can choose between: // 8, 9, 10 // // for EasyCAT board REV_B we can choose between: // 8, 9, 10, A5, 6, 7 // example: //EasyCAT EASYCAT(8); // pin 8 will be used as SPI chip select // The chip select chosen by the firmware must match the setting on the board // On board REV_A the chip select is set soldering // a 0 ohm resistor in the appropriate position // On board REV_B the chip select is set // througt a bank of jumpers //---- pins declaration ------------------------------------------------------------------------------ const int Ana0 = A0; // analog input 0 const int Ana1 = A1; // analog input 1 const int BitOut0 = A2; // digital output bit 0 const int BitOut1 = A3; // digital output bit 1 const int BitOut2 = A4; // digital output bit 2 const int BitOut3 = A5; // digital output bit 3 const int BitIn0 = 3; // digital input bit 0 const int BitIn1 = 5; // digital input bit 1 const int BitIn2 = 6; // digital input bit 2 const int BitIn3 = 7; // digital input bit 3 //---- global variables --------------------------------------------------------------------------- UWORD ContaUp; // used for sawthoot test generation UWORD ContaDown; // unsigned long Millis = 0; unsigned long PreviousSaw = 0; unsigned long PreviousCycle = 0; //---- setup --------------------------------------------------------------------------------------- void setup() { Serial.begin(9600); // serial line initialization //(used only for debug) Serial.print ("\nEasyCAT - Generic EtherCAT slave\n"); // print the banner pinMode(BitOut0, OUTPUT); // digital output pins setting pinMode(BitOut1, OUTPUT); // pinMode(BitOut2, OUTPUT); // pinMode(BitOut3, OUTPUT); // pinMode(BitIn0, INPUT_PULLUP); // digital input pins setting pinMode(BitIn1, INPUT_PULLUP); // pinMode(BitIn2, INPUT_PULLUP); // pinMode(BitIn3, INPUT_PULLUP); // ContaUp.Word = 0x0000; // ContaDown.Word = 0x0000; // //---- initialize the EasyCAT board ----- if (EASYCAT.Init() == true) // initialization { // succesfully completed Serial.print ("initialized"); // } // else // initialization failed { // the EasyCAT board was not recognized Serial.print ("initialization failed"); // // The most common reason is that the SPI // chip select choosen on the board doesn't // match the one choosen by the firmware pinMode(13, OUTPUT); // stay in loop for ever // with the Arduino led blinking while(1) // { // digitalWrite (13, LOW); // delay(500); // digitalWrite (13, HIGH); // delay(500); // } // } } //---- main loop ---------------------------------------------------------------------------------------- void loop() // In the main loop we must call ciclically the { // EasyCAT task and our application // // This allows the bidirectional exachange of the data // between the EtherCAT master and our application // // The EasyCAT cycle and the Master cycle are asynchronous // delay(2); // This delay allows us to set the EasyCAT cycle time // according to the needs of our application // // For user interface applications a cycle time of 100mS, // or even more, is appropriate, but, for data processing // applications, a faster cycle time may be required // // In this case we can also completely eliminate this // delay in order to obtain the fastest possible response // Instead we can also use millis() to set the cycle time // // example: //Millis = millis(); // //if (Millis - PreviousCycle >= 50) // each 50 mS { // // // PreviousCycle = Millis; // EASYCAT.MainTask(); // execute the EasyCAT task Application(); // execute the user application } } //---- user application ------------------------------------------------------------------------------ void Application () { UWORD Analog0; UWORD Analog1; // --- analog inputs management --- Analog0.Word = analogRead(Ana0); // read analog input 0 Analog0.Word >>= 2; // normalize it on 8 bits EASYCAT.BufferIn.Byte[0] = Analog0.Byte[0]; // and put the result into // input Byte 0 Analog1.Word = analogRead(Ana1); // read analog input 1 Analog1.Word >>= 2; // normalize it on 8 bits EASYCAT.BufferIn.Byte[1] = Analog1.Byte[0]; // and put the result into // input Byte 1 // --- four output bits management ---- // if (EASYCAT.BufferOut.Byte[0] & (1<<0)) // the four output bits are mapped to the digitalWrite (BitOut0, HIGH); // lower nibble of output Byte 0 else // digitalWrite (BitOut0, LOW); // we read each bit and write it // to the corrisponding pin if (EASYCAT.BufferOut.Byte[0] & (1<<1)) // digitalWrite (BitOut1, HIGH); // else // digitalWrite (BitOut1, LOW); // // if (EASYCAT.BufferOut.Byte[0] & (1<<2)) // digitalWrite (BitOut2, HIGH); // else // digitalWrite (BitOut2, LOW); // // if (EASYCAT.BufferOut.Byte[0] & (1<<3)) // digitalWrite (BitOut3, HIGH); // else // digitalWrite (BitOut3, LOW); // //--- four input bits management --- // if (digitalRead(BitIn0)) // the four input pins are mapped to the EASYCAT.BufferIn.Byte[6] |= (1<<0); // lower nibble of input Byte 6 else // EASYCAT.BufferIn.Byte[6] &= ~(1<<0); // we read each pin and write it // to the corresponding bit if (digitalRead(BitIn1)) // EASYCAT.BufferIn.Byte[6] |= (1<<1); // else // EASYCAT.BufferIn.Byte[6] &= ~(1<<1); // // if (digitalRead(BitIn2)) // EASYCAT.BufferIn.Byte[6] |= (1<<2); // else // EASYCAT.BufferIn.Byte[6] &= ~(1<<2); // // if (digitalRead(BitIn3)) // EASYCAT.BufferIn.Byte[6] |= (1<<3); // else // EASYCAT.BufferIn.Byte[6] &= ~(1<<3); // // --- test sawtooth generation --- // Millis = millis(); // each 100 mS if (Millis - PreviousSaw >= 100) // { // PreviousSaw = Millis; // // ContaUp.Word++; // we increment the variable ContaUp ContaDown.Word--; // and decrement ContaDown } // // we use these variables to create sawtooth, // with different slopes and periods, for // test pourpose, in input Bytes 2,3,4,5,30,31 EASYCAT.BufferIn.Byte[2] = ContaUp.Byte[0]; // slow rising slope EASYCAT.BufferIn.Byte[3] = ContaUp.Byte[1]; // extremly slow rising slope EASYCAT.BufferIn.Byte[4] = ContaDown.Byte[0]; // slow falling slope EASYCAT.BufferIn.Byte[5] = ContaDown.Byte[1]; // extremly slow falling slope EASYCAT.BufferIn.Byte[30] = ContaUp.Byte[0] << 2; // medium speed rising slope EASYCAT.BufferIn.Byte[31] = ContaDown.Byte[0] << 2; // medium speed falling slope }
//---- AB&T EasyCAT shield simple HMI application example ------------------------------------------------------------------ // // // AB&T Tecnologie Informatiche - Ivrea Italy // http://www.bausano.net/en/ // https://www.ethercat.org/en/products/791FFAA126AD43859920EA64384AD4FD.htm // // This application demonstrates a simple EtherCAT HMI (human machine interface) built by: // // an EasycatShield http://www.bausano.net/en/hardware/ethercat-e-arduino/easycat.html // an Arduino UNO https://www.arduino.cc/en/Main/ArduinoBoardUno // an Adafruit 2.8" TFT capacitive touch shield for Arduino https://www.adafruit.com/product/1947 // Many thanks to the Arduino Team and to Adafruit for their cool products :-) //-------------------------------------------------------------------------------------------------------------------- #include "EasyCAT.h" // EasyCAT library (we need version 1.4 or higher) #include <Adafruit_GFX.h> // Core graphics library #include <SPI.h> // this is needed for display #include <Adafruit_ILI9341.h> // #include <Wire.h> // this is needed for FT6206 #include <Adafruit_FT6206.h> // #include <Fonts/FreeMonoBold24pt7b.h> #include <Fonts/FreeMonoBold12pt7b.h> Adafruit_FT6206 ctp = Adafruit_FT6206(); // The FT6206 uses hardware I2C (SCL/SDA) #define TFT_CS 10 // The display also uses hardware SPI, plus #9 & #10 #define TFT_DC 9 // Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); // #define BLACK 0x0000 // assign human-readable names to some common 16-bit color values #define BLUE 0x001F // #define RED 0xF800 // #define GREEN 0x07E0 // #define CYAN 0x07FF // #define MAGENTA 0xF81F // #define YELLOW 0xFFE0 // #define WHITE 0xFFFF // // #define ORANGE 0xFC00 // EasyCAT EASYCAT(8); // Instantiate the EasyCAT library choosing pin 8 // as SPI chip select not to interfere with the LCD // // !!! Remember to set pin 8 also on the EasyCAT board, using the jumper, // if you have rev B, or the 0 ohm resistor, if you have rev A !!! //---- global variables --------------------------------------------------------------------------- unsigned long Millis, PreviousMillis; int Analog_X =0, Analog_X_prev = 1; int Analog_Y =0, Analog_Y_prev = 1; bool FirstRound = true; bool Blink = true; bool Running = false; unsigned char N_Cycles = 0; unsigned char Conta; unsigned char EcatState; unsigned char LedStatus = 0; unsigned char TouchStatus_prev = 0; //---- setup --------------------------------------------------------------------------------------- void setup() { Serial.begin(9600); // Initialize the debug serial line Serial.print ("\nEasyCAT HMI\n"); // Print the banner Serial.print ("\nAB&T www.bausano.net\n"); // Serial.print ("\nMade in ITALY\n"); // tft.begin(); // Initialize the LCD if (! ctp.begin(40)) // Initialize the touch screen { // pass in 'sensitivity' coefficient Serial.println("Couldn't start FT6206 touchscreen controller"); while (1); } Serial.println("Capacitive touchscreen started"); tft.fillScreen(BLACK); // Clear the LCD tft.setRotation(1); // Set the orientation to horizontal tft.setFont(&FreeMonoBold24pt7b); // Set large font tft.setTextSize(1); // Set text size tft.setTextColor(GREEN); // Print the first part of the banner on the LCD tft.setCursor(105, 60); // tft.print ("AB&T"); // tft.setFont(&FreeMonoBold12pt7b); // Set small font tft.setCursor(60, 130); // Print the second part of the banner on the LCD tft.print ("www.bausano.net"); // tft.setCursor(70, 190); // tft.print ("Made in ITALY"); // delay (3000); // Wait a while tft.fillScreen(BLACK); // clear the screen EASYCAT.Init(); // Initialize the EasyCAT board if (EASYCAT.Init() == true) //---- Initialization succeded ----------- { // Serial.print ("inizialized"); // // tft.setFont(&FreeMonoBold24pt7b); // Print the "OK" banner on the LCD // tft.setTextColor(ORANGE); // tft.setCursor(60, 50); // tft.print ("EasyCAT"); // // tft.setFont(&FreeMonoBold12pt7b); // // tft.setCursor(78, 110); // tft.print ("inizialized!"); // PrintSmile (160, 180, GREEN); // } else //---- Initialization failed ------------- { // Serial.print ("inizialization failed"); // // tft.setFont(&FreeMonoBold24pt7b); // Print the "KO" banner on the LCD // tft.setTextColor(ORANGE); // The EasyCAT board is not recognized tft.setCursor(63, 50); // The most common reason is that the SPI tft.print ("EasyCAT"); // chip select choosen on the board doesn't // match the one choosen by the firmware tft.setFont(&FreeMonoBold12pt7b); // // tft.setCursor(78, 110); // tft.print ("init failed!"); // PrintSad (160, 180, GREEN); // pinMode(13, OUTPUT); // while(1) // Stay in loop for ever, blinking the Arduino led { // digitalWrite (13, LOW); // delay(500); // digitalWrite (13, HIGH); // delay(500); // } // } delay (3000); // Wait a while tft.fillScreen(BLACK); // Clear the screen // ---- Print on the LCD the fixed texts and drawing of the HMI --- tft.setTextColor(YELLOW); // tft.setFont(&FreeMonoBold24pt7b); // tft.setCursor(85, 30); // Analog input "X" tft.print("X="); // tft.drawRect(60, 40, 257, 15, YELLOW); // tft.setCursor(85, 110); // Analog input "Y" tft.print("Y="); // tft.drawRect(60, 123, 257, 15, YELLOW); // tft.setFont(&FreeMonoBold12pt7b); // Leds // tft.setCursor(8, 235); // tft.print("led1 led2 led3 led4"); // // tft.drawCircle(35, 185, 18, GREEN); // tft.drawCircle(120, 185, 18, GREEN); // tft.drawCircle(205, 185, 18, GREEN); // tft.drawCircle(290, 185, 18, GREEN); // } //---- main loop ---------------------------------------------------------------------------------------- void loop() // In the main loop we must call ciclically the { // EasyCAT task and our application // // This allows the bidirectional exachange of the data // between the EtherCAT master and our application // // The EasyCAT cycle and the Master cycle are asynchronous // Millis = millis(); // For this application we choose a cycle time of 150 mS if (Millis - PreviousMillis >= 150) // that is suitable for an HMI { // PreviousMillis = Millis; // EcatState = EASYCAT.MainTask(); // EasyCAT task // (It is mandatory to use EasyCAT library version 1.4 or higher) Application(); // User application } } //---- user application ---------------------------------------------------------------------------------------------------------- void Application () { // -------------------------------- output data management --------------------------------------------- // We read the analog values from the EtherCAT output buffer, // i.e. from the data that come from the Master, // and visualize them on the LCD Analog_X = EASYCAT.BufferOut.Byte[0] | (EASYCAT.BufferOut.Byte[1] << 8); // Analog_X is mapped to output Bytes 0 and 1 Analog_Y = EASYCAT.BufferOut.Byte[2] | (EASYCAT.BufferOut.Byte[3] << 8); // Analog_Y is mapped to output Bytes 2 and 3 Analog_X = Analog_X >> 3; // normalize the values on 12 bit i.e. from 0 t0 4095 Analog_Y = Analog_Y >> 3; // if ((Analog_X != Analog_X_prev) || (FirstRound == true)) // If the Analog_X is changed from the previous reading, or this { // is the first time we pass here, refresh the visualization // PrintValueRight (Analog_X, Analog_X_prev, 305, 30, ORANGE); // Print the value on the LCD, in numerical form, right justified PrintValueBar (Analog_X, Analog_X_prev, 61, 41, ORANGE); // Print the value on the LCD, as a bar // Analog_X_prev = Analog_X; // Remember the value for the next cycle } if ((Analog_Y != Analog_Y_prev) || (FirstRound == true)) // The same for Analog_Y ... { // PrintValueRight (Analog_Y, Analog_Y_prev, 305, 110, ORANGE); // PrintValueBar (Analog_Y, Analog_Y_prev, 61, 124, ORANGE); // // Analog_Y_prev = Analog_Y; // } FirstRound = false; // Remember that we already passed here // -------------------------------- input data management ---------------------------------------------- // We read the data from the touch, process them, and write // the result to the EtherCAT input buffer, i.e. to the data // that will be sent to the Master. ProcessTouch(&LedStatus); // Read and process the data from the touch // and visualize the led status on the LCD EASYCAT.BufferIn.Byte[0] = LedStatus; // The status of the leds is mapped to Byte input 0 EASYCAT.BufferIn.Byte[31] = Conta++; // We also increase Conta every cycle and put it in // input Byte 31, just for test pourpose // ------------------------------------- HALT/RUN visualization --------------------------------------- if (EcatState == 0x08) // If the EasyCAT is in Operational State { // if (!Running) // and the communication is running { // PrintRun(); // we print a green "RUN" on the LCD Running = true; // } // } else // Otherwise we print a blinking red "HALT" { // Running = false; // // if (N_Cycles++ > 4) // { // N_Cycles = 0; // if (Blink) // { // PrintHalt(); // Blink = false; // } // else // { // tft.fillRect(27, 5, 15, 79, BLACK); // Blink = true; // } // } } } //----- print a value between 0 and 4095, right justified ------------------------------------------------------------ void PrintValueRight (unsigned int Value, unsigned int Value_prev, unsigned int x, unsigned int y, unsigned int Color) { #define Width 32 // space between characters, in pixel unsigned char i; unsigned char Length, Length_prev; unsigned char N_Clear, N_Print; char AsciiString [8]; char AsciiString_prev [8]; // We use the current and the previous value // to clear and redraw only the needed characters, // to avoid flickering itoa (Value, AsciiString, 10 ); // Convert the current value and the previous value itoa (Value_prev, AsciiString_prev, 10 ); // into ASCII strings Length = strlen(AsciiString); // Calculate the strings length Length_prev = strlen(AsciiString_prev); // if (Length != Length_prev) // If the string length is changed { // we clear and redraw everything N_Clear = 5; // N_Print = Length; // } // else // Else we compute how many characters { // to clear and redraw for (i=0; i<Length; i++) // { // if(AsciiString[i] != AsciiString_prev[i]) // break; // } // N_Clear = Length - i; // N_Print = N_Clear; // } // tft.setFont(&FreeMonoBold24pt7b); // Set large font tft.fillRect(x-(N_Clear*Width), y-30, N_Clear*Width, 32, BLACK); // Clear the area x -= Width; // Set the X for the first character on the right for (i=0; i<N_Print; i++) // Print the string, from right to left { // tft.drawChar(x, y, AsciiString[Length-1-i], Color, 0, 1); // x -= Width; // } // } //----- print a value between 0 and 4095, as a bar ------------------------------------------------------------ void PrintValueBar (unsigned int Value, unsigned int Value_prev, unsigned int x, unsigned int y, unsigned int Color) { #define Tickness 13 // Bar tickness, in pixel unsigned char Value_8, Value_8_prev; // We use the current and the previous value // to clear and redraw only the needed pixel, // to avoid flickering Value_8 = Value >> 4; // We normalize the current and the previous Value_8_prev = Value_prev >> 4; // value to 8 bits, i.e. from 0 to 255 Serial.println(Value_8); Serial.println(Value_8_prev); if (Value_8 > Value_8_prev) { tft.fillRect(x + Value_8_prev, y, (Value_8 - Value_8_prev), Tickness, Color); } if (Value_8 < Value_8_prev) { tft.fillRect(x + Value_8, y, (Value_8_prev - Value_8), Tickness, BLACK); } Serial.println(); } //--------- print "Smile" emotion ------------------------------------------------------------------------- void PrintSmile(unsigned int x, unsigned int y, unsigned int Color) { tft.drawCircle(x, y-6, 21, Color); tft.drawCircle(x, y-6, 20, Color); tft.drawCircle(x, y-6, 19, Color); tft.fillRect(x-30, y-30, 60, 38, BLACK); tft.drawCircle(x, y, 30, Color); tft.drawCircle(x, y, 29, Color); tft.drawCircle(x, y, 28, Color); tft.fillCircle(x - 10, y- 8, 3, Color); tft.fillCircle(x + 10, y- 8, 3, Color); } //--------- print "Sad" emotion-------------------------------------------------------------------------------- void PrintSad(unsigned int x, unsigned int y, unsigned int Color) { tft.drawCircle(x, y+29, 21, Color); tft.drawCircle(x, y+29, 20, Color); tft.drawCircle(x, y+29, 19, Color); tft.fillRect(x-30, y+16, 60, 35, BLACK); tft.drawCircle(x, y, 30, Color); tft.drawCircle(x, y, 29, Color); tft.drawCircle(x, y, 28, Color); tft.fillCircle(x - 10, y- 8, 3, Color); tft.fillCircle(x + 10, y- 8, 3, Color); } //--------- print a vertical green "RUN" ---------------------------------------------------------------------- void PrintRun (void) { tft.fillRect(27, 5, 15, 79, BLACK); tft.setFont(&FreeMonoBold12pt7b); tft.setTextColor(GREEN); tft.setCursor(27, 18); tft.print("R"); tft.setCursor(27, 39); tft.print("U"); tft.setCursor(27, 61); tft.print("N"); } //--------- print a vertical red "HALT" ---------------------------------------------------------------------- void PrintHalt (void) { tft.fillRect(27, 5, 15, 79, BLACK); tft.setFont(&FreeMonoBold12pt7b); tft.setTextColor(RED); tft.setCursor(27, 18); tft.print("H"); tft.setCursor(28,39); tft.print("A"); tft.setCursor(28, 61); tft.print("L"); tft.setCursor(28, 83); tft.print("T"); } //--- process data from the Touch and draw led status on the LCD ------------------------------------------- void ProcessTouch(unsigned char* LedStatus) { unsigned char LedToggle; unsigned char TouchStatus = 0x00; if (ctp.touched()) // If the touch has been tapped, we process the data { TS_Point p = ctp.getPoint(); // Retrive the tapped point // Serial.print("X = "); Serial.print(p.x); // Print out raw data from the touch controller // Serial.print("\tY = "); Serial.println(p.y); // for debug pourpose if (p.x > (160)) // If the tapped point belong the led area at the // bottom of the screen, find out wich led is and // set the corrisponding flag in TouchStatus { if (p.y > (260)) // led 1 { // TouchStatus |= 0x01; // // Serial.println("1"); // } // if ((p.y < (225)) && (p.y > 170)) // led 2 { // TouchStatus |= 0x02; // // Serial.println("2"); // } // if ((p.y < (140)) && (p.y > 85)) // led 3 { // TouchStatus |= 0x04; // // Serial.println("3"); // } // if (p.y < (60)) // led 4 { // TouchStatus |= 0x08; // // Serial.println("4"); // } // LedToggle = ~TouchStatus_prev & TouchStatus; // If there is a rising edge in some bit of TouchStatus *LedStatus = *LedStatus ^ LedToggle; // we toggle the corrisponding bit in LedStatus DrawLed(*LedStatus); // Draw the leds on the LCD } } TouchStatus_prev = TouchStatus; // Remember if one led has been tapped } //---- draw the leds on the LCD ----------------------------------------------------------------------------------------- void DrawLed (unsigned char LedStatus) { if (LedStatus & 0x01) // Draw the leds according to LedStatus tft.fillCircle(35, 185, 16, GREEN); // else // tft.fillCircle(35, 185, 16, BLACK); // // if (LedStatus & 0x02) // tft.fillCircle(120, 185, 16, GREEN); // else // tft.fillCircle(120, 185, 16, BLACK); // // if (LedStatus & 0x04) // tft.fillCircle(205, 185, 16, GREEN); // else // tft.fillCircle(205, 185, 16, BLACK); // // if (LedStatus & 0x08) // tft.fillCircle(290, 185, 16, GREEN); // else // tft.fillCircle(290, 185, 16, BLACK); // }