AB&T
/
EasyCAT_LAB_very_simple
EasyCAT LAB - EtherCAT master very simple example
- This repository contains a very basic example for the EasyCAT LAB , a complete educational and experimental EtherCAT® system, composed of one master and two slaves.
- The EasyCAT LAB is provided as a kit by AB&T Tecnologie Informatiche, to allow everybody to have an educational EtherCAT® system up and running in a matter of minutes.
- It uses the SOEM (Simple Open EtherCAT® Master) library by rt-labs, that has been ported in the ecosystem by AB&T Tecnologie Informatiche.
- The slaves are based on the EasyCAT SHIELD and the Arduino UNO.
Note
- This example uses two LAB 2 slaves.
Note
- In this example, to keep things as simple as possible, only two bytes of data are exchanged between the slaves and the TFT display is not used .
main.cpp@2:368e7d4d8171, 11 months ago (annotated)
- Committer:
- EasyCAT
- Date:
- Wed Oct 25 15:38:52 2023 +0000
- Revision:
- 2:368e7d4d8171
- Parent:
- 1:971b4897a4c5
Bug fix
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
sulymarco | 0:bc829777f1ea | 1 | //******************************************************************************************** |
sulymarco | 0:bc829777f1ea | 2 | // * |
sulymarco | 0:bc829777f1ea | 3 | // This software is distributed as an example, "AS IS", in the hope that it could * |
sulymarco | 0:bc829777f1ea | 4 | // be useful, WITHOUT ANY WARRANTY of any kind, express or implied, included, but * |
sulymarco | 0:bc829777f1ea | 5 | // not limited, to the warranties of merchantability, fitness for a particular * |
sulymarco | 0:bc829777f1ea | 6 | // purpose, and non infringiment. In no event shall the authors be liable for any * |
sulymarco | 0:bc829777f1ea | 7 | // claim, damages or other liability, arising from, or in connection with this software. * |
sulymarco | 0:bc829777f1ea | 8 | // * |
sulymarco | 0:bc829777f1ea | 9 | //******************************************************************************************** |
sulymarco | 0:bc829777f1ea | 10 | |
sulymarco | 0:bc829777f1ea | 11 | |
sulymarco | 0:bc829777f1ea | 12 | // The AB&T EasyCAT LAB is a complete experimental EtherCAT® system, composed by |
sulymarco | 0:bc829777f1ea | 13 | // one master and two slaves. |
sulymarco | 0:bc829777f1ea | 14 | // The EasyCAT LAB software is provided free of charge and its pourpose is to allow |
sulymarco | 0:bc829777f1ea | 15 | // makers and educational institutes to experiment with the EtherCAT® protocol. |
sulymarco | 0:bc829777f1ea | 16 | // |
sulymarco | 0:bc829777f1ea | 17 | // The EasyCAT LAB is developed by "AB&T Tecnologie Informatiche" Via dell'About 2A Ivrea Italy. |
sulymarco | 0:bc829777f1ea | 18 | // www.bausano.net |
sulymarco | 0:bc829777f1ea | 19 | // www.easycatshield.com |
sulymarco | 0:bc829777f1ea | 20 | // |
sulymarco | 0:bc829777f1ea | 21 | // The EasyCAT LAB uses the SOEM library by rt:labs |
sulymarco | 0:bc829777f1ea | 22 | // https://rt-labs.com/products/soem-ethercat-master-stack/ |
sulymarco | 0:bc829777f1ea | 23 | // |
sulymarco | 0:bc829777f1ea | 24 | // EtherCAT® is a registered trademark and patented technology, licensed by Beckhoff Automation GmbH. |
sulymarco | 0:bc829777f1ea | 25 | // www.beckhoff.com |
sulymarco | 0:bc829777f1ea | 26 | // www.ethercat.org |
sulymarco | 0:bc829777f1ea | 27 | |
sulymarco | 0:bc829777f1ea | 28 | |
sulymarco | 0:bc829777f1ea | 29 | #define ETH_TXBUFNB 16 |
sulymarco | 0:bc829777f1ea | 30 | #define ETH_RXBUFNB 16 |
sulymarco | 0:bc829777f1ea | 31 | |
sulymarco | 0:bc829777f1ea | 32 | #include "mbed.h" |
sulymarco | 0:bc829777f1ea | 33 | |
sulymarco | 0:bc829777f1ea | 34 | #ifndef __align |
sulymarco | 0:bc829777f1ea | 35 | #define __align MBED_ALIGN |
sulymarco | 0:bc829777f1ea | 36 | #endif |
sulymarco | 0:bc829777f1ea | 37 | |
sulymarco | 0:bc829777f1ea | 38 | #include "config.h" |
sulymarco | 0:bc829777f1ea | 39 | #include "soem_start.h" |
sulymarco | 0:bc829777f1ea | 40 | |
sulymarco | 0:bc829777f1ea | 41 | #define CYCLE_TIME 1000 // master cycle time in uS |
sulymarco | 0:bc829777f1ea | 42 | // 1000 = 1mS |
sulymarco | 0:bc829777f1ea | 43 | |
sulymarco | 0:bc829777f1ea | 44 | |
sulymarco | 0:bc829777f1ea | 45 | #define SysMilliS() (uint32_t)Kernel::get_ms_count() |
sulymarco | 0:bc829777f1ea | 46 | |
EasyCAT | 1:971b4897a4c5 | 47 | UnbufferedSerial pc(USBTX,USBRX,115200); // set the debug serial line speed to 115200 |
sulymarco | 0:bc829777f1ea | 48 | |
sulymarco | 0:bc829777f1ea | 49 | |
sulymarco | 0:bc829777f1ea | 50 | |
sulymarco | 0:bc829777f1ea | 51 | //---- local functions --------------------------------------------------------- |
sulymarco | 0:bc829777f1ea | 52 | |
sulymarco | 0:bc829777f1ea | 53 | void Application(); |
sulymarco | 0:bc829777f1ea | 54 | void BlinkLedForever(); |
sulymarco | 0:bc829777f1ea | 55 | |
sulymarco | 0:bc829777f1ea | 56 | //------------------------------------------------------------------------------ |
sulymarco | 0:bc829777f1ea | 57 | |
sulymarco | 0:bc829777f1ea | 58 | Ticker SampleTicker; |
sulymarco | 0:bc829777f1ea | 59 | Thread thread; |
sulymarco | 0:bc829777f1ea | 60 | |
sulymarco | 0:bc829777f1ea | 61 | DigitalOut Test_1(D1); // debug test points |
sulymarco | 0:bc829777f1ea | 62 | DigitalOut Test_2(D2); // |
sulymarco | 0:bc829777f1ea | 63 | DigitalOut Test_3(D3); // |
sulymarco | 0:bc829777f1ea | 64 | DigitalOut Test_4(D4); // |
sulymarco | 0:bc829777f1ea | 65 | |
sulymarco | 0:bc829777f1ea | 66 | DigitalOut LED_RED(LED3); |
sulymarco | 0:bc829777f1ea | 67 | |
sulymarco | 0:bc829777f1ea | 68 | |
sulymarco | 0:bc829777f1ea | 69 | //------------------------------------------------------------------------------ |
sulymarco | 0:bc829777f1ea | 70 | |
sulymarco | 0:bc829777f1ea | 71 | int ExpectWorkCounter; |
sulymarco | 0:bc829777f1ea | 72 | int WorkCounter; |
sulymarco | 0:bc829777f1ea | 73 | int WorkCounterSafe; |
sulymarco | 0:bc829777f1ea | 74 | bool NetworkError; |
sulymarco | 0:bc829777f1ea | 75 | bool NetworkErrorSafe; |
sulymarco | 0:bc829777f1ea | 76 | |
sulymarco | 0:bc829777f1ea | 77 | #define DATA_EXCHANGE_FLAG (1UL << 0) |
sulymarco | 0:bc829777f1ea | 78 | #define APPLICATION_FLAG (1UL << 1) |
sulymarco | 0:bc829777f1ea | 79 | |
sulymarco | 0:bc829777f1ea | 80 | EventFlags event_flags; |
sulymarco | 0:bc829777f1ea | 81 | |
sulymarco | 0:bc829777f1ea | 82 | Mutex IO_data; |
sulymarco | 0:bc829777f1ea | 83 | |
sulymarco | 0:bc829777f1ea | 84 | |
sulymarco | 0:bc829777f1ea | 85 | //---- data exchange thread ---------------------------------------------------- |
sulymarco | 0:bc829777f1ea | 86 | |
sulymarco | 0:bc829777f1ea | 87 | void ExchangeMaster() |
sulymarco | 0:bc829777f1ea | 88 | { |
sulymarco | 0:bc829777f1ea | 89 | while (true) |
sulymarco | 0:bc829777f1ea | 90 | { |
sulymarco | 0:bc829777f1ea | 91 | event_flags.wait_any(DATA_EXCHANGE_FLAG); // the thread waits for the synchronization flag |
sulymarco | 0:bc829777f1ea | 92 | |
sulymarco | 0:bc829777f1ea | 93 | //Test_1 = 1; |
sulymarco | 0:bc829777f1ea | 94 | |
sulymarco | 0:bc829777f1ea | 95 | IO_data.lock(); // Ethercat data exchange |
sulymarco | 0:bc829777f1ea | 96 | ec_send_processdata(); // |
sulymarco | 0:bc829777f1ea | 97 | WorkCounter = ec_receive_processdata(EC_TIMEOUTRET); |
sulymarco | 0:bc829777f1ea | 98 | |
sulymarco | 0:bc829777f1ea | 99 | if (WorkCounter != ExpectWorkCounter) |
sulymarco | 0:bc829777f1ea | 100 | NetworkError = true; |
sulymarco | 0:bc829777f1ea | 101 | else |
sulymarco | 0:bc829777f1ea | 102 | NetworkError = false; |
sulymarco | 0:bc829777f1ea | 103 | |
sulymarco | 0:bc829777f1ea | 104 | IO_data.unlock(); // |
sulymarco | 0:bc829777f1ea | 105 | event_flags.set(APPLICATION_FLAG); // synchronize the application |
sulymarco | 0:bc829777f1ea | 106 | |
sulymarco | 0:bc829777f1ea | 107 | //Test_1 = 0; |
sulymarco | 0:bc829777f1ea | 108 | } |
sulymarco | 0:bc829777f1ea | 109 | } |
sulymarco | 0:bc829777f1ea | 110 | |
sulymarco | 0:bc829777f1ea | 111 | |
sulymarco | 0:bc829777f1ea | 112 | //----- thicker generated sample time ------------------------------------------ |
sulymarco | 0:bc829777f1ea | 113 | |
sulymarco | 0:bc829777f1ea | 114 | void SampleIsr() // set the event that starts |
sulymarco | 0:bc829777f1ea | 115 | { // the data exchange |
sulymarco | 0:bc829777f1ea | 116 | event_flags.set(DATA_EXCHANGE_FLAG); // |
sulymarco | 0:bc829777f1ea | 117 | } // |
sulymarco | 0:bc829777f1ea | 118 | |
sulymarco | 0:bc829777f1ea | 119 | |
sulymarco | 0:bc829777f1ea | 120 | //****** initialization ******************************************************** |
sulymarco | 0:bc829777f1ea | 121 | |
sulymarco | 0:bc829777f1ea | 122 | int main() |
sulymarco | 0:bc829777f1ea | 123 | { |
sulymarco | 0:bc829777f1ea | 124 | int i; |
sulymarco | 0:bc829777f1ea | 125 | |
sulymarco | 0:bc829777f1ea | 126 | printf("Start \n"); |
sulymarco | 0:bc829777f1ea | 127 | |
sulymarco | 0:bc829777f1ea | 128 | Test_1 = 0; |
sulymarco | 0:bc829777f1ea | 129 | Test_2 = 0; |
sulymarco | 0:bc829777f1ea | 130 | Test_3 = 0; |
sulymarco | 0:bc829777f1ea | 131 | Test_4 = 0; |
sulymarco | 0:bc829777f1ea | 132 | |
sulymarco | 0:bc829777f1ea | 133 | NetworkError = false; |
sulymarco | 0:bc829777f1ea | 134 | |
sulymarco | 0:bc829777f1ea | 135 | if (ec_init(NULL)) // init SOEM |
sulymarco | 0:bc829777f1ea | 136 | { |
sulymarco | 0:bc829777f1ea | 137 | printf("ec_init succeeded.\n"); |
sulymarco | 0:bc829777f1ea | 138 | printf("Scanning the network\n"); |
sulymarco | 0:bc829777f1ea | 139 | |
sulymarco | 0:bc829777f1ea | 140 | if (network_scanning()) |
sulymarco | 0:bc829777f1ea | 141 | { |
sulymarco | 0:bc829777f1ea | 142 | if (network_configuration()) // check network configuration |
sulymarco | 0:bc829777f1ea | 143 | { |
sulymarco | 0:bc829777f1ea | 144 | ec_config_map(&IOmap); // map the I/O |
sulymarco | 0:bc829777f1ea | 145 | MapLocalStructures(); |
sulymarco | 0:bc829777f1ea | 146 | |
sulymarco | 0:bc829777f1ea | 147 | printf("\nSlaves mapped, state to SAFE_OP.\n"); |
sulymarco | 0:bc829777f1ea | 148 | // wait for all slaves to reach SAFE_OP state |
sulymarco | 0:bc829777f1ea | 149 | ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE); |
sulymarco | 0:bc829777f1ea | 150 | |
sulymarco | 0:bc829777f1ea | 151 | printf("Request operational state for all slaves\n"); |
sulymarco | 0:bc829777f1ea | 152 | ec_slave[0].state = EC_STATE_OPERATIONAL; |
sulymarco | 0:bc829777f1ea | 153 | |
sulymarco | 0:bc829777f1ea | 154 | ec_send_processdata(); // send one valid process data to make outputs in slaves happy |
sulymarco | 0:bc829777f1ea | 155 | ExpectWorkCounter = ec_receive_processdata(EC_TIMEOUTRET); |
sulymarco | 0:bc829777f1ea | 156 | |
sulymarco | 0:bc829777f1ea | 157 | ec_writestate(0); // request OP state for all slaves |
sulymarco | 0:bc829777f1ea | 158 | |
sulymarco | 0:bc829777f1ea | 159 | // wait for all slaves to reach OP state |
sulymarco | 0:bc829777f1ea | 160 | ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE); |
sulymarco | 0:bc829777f1ea | 161 | if (ec_slave[0].state == EC_STATE_OPERATIONAL ) |
sulymarco | 0:bc829777f1ea | 162 | { |
sulymarco | 0:bc829777f1ea | 163 | printf("Operational state reached for all slaves.\n"); |
sulymarco | 0:bc829777f1ea | 164 | } |
sulymarco | 0:bc829777f1ea | 165 | else |
sulymarco | 0:bc829777f1ea | 166 | { |
sulymarco | 0:bc829777f1ea | 167 | printf("Not all slaves reached operational state.\n"); |
sulymarco | 0:bc829777f1ea | 168 | ec_readstate(); |
sulymarco | 0:bc829777f1ea | 169 | for(i = 1; i<=ec_slavecount ; i++) |
sulymarco | 0:bc829777f1ea | 170 | { |
sulymarco | 0:bc829777f1ea | 171 | if(ec_slave[i].state != EC_STATE_OPERATIONAL) |
sulymarco | 0:bc829777f1ea | 172 | { |
sulymarco | 0:bc829777f1ea | 173 | printf("Slave %d State=0x%04x StatusCode=0x%04x\n", |
sulymarco | 0:bc829777f1ea | 174 | i, ec_slave[i].state, ec_slave[i].ALstatuscode); |
sulymarco | 0:bc829777f1ea | 175 | } |
sulymarco | 0:bc829777f1ea | 176 | } |
sulymarco | 0:bc829777f1ea | 177 | |
sulymarco | 0:bc829777f1ea | 178 | printf("Not all slaves reached operational state!\n"); |
sulymarco | 0:bc829777f1ea | 179 | void BlinkLedForever(); |
sulymarco | 0:bc829777f1ea | 180 | } |
sulymarco | 0:bc829777f1ea | 181 | |
sulymarco | 0:bc829777f1ea | 182 | |
sulymarco | 0:bc829777f1ea | 183 | thread.start(ExchangeMaster); |
sulymarco | 0:bc829777f1ea | 184 | thread.set_priority(osPriorityRealtime); |
sulymarco | 0:bc829777f1ea | 185 | |
sulymarco | 0:bc829777f1ea | 186 | SampleTicker.attach_us(&SampleIsr, CYCLE_TIME); |
sulymarco | 0:bc829777f1ea | 187 | |
sulymarco | 0:bc829777f1ea | 188 | Application(); |
sulymarco | 0:bc829777f1ea | 189 | } |
sulymarco | 0:bc829777f1ea | 190 | |
sulymarco | 0:bc829777f1ea | 191 | else |
sulymarco | 0:bc829777f1ea | 192 | { |
sulymarco | 0:bc829777f1ea | 193 | printf("Mismatch of network units!\n"); |
sulymarco | 0:bc829777f1ea | 194 | BlinkLedForever(); |
sulymarco | 0:bc829777f1ea | 195 | } |
sulymarco | 0:bc829777f1ea | 196 | } |
sulymarco | 0:bc829777f1ea | 197 | |
sulymarco | 0:bc829777f1ea | 198 | else |
sulymarco | 0:bc829777f1ea | 199 | { |
sulymarco | 0:bc829777f1ea | 200 | printf("No slaves found!\n"); |
sulymarco | 0:bc829777f1ea | 201 | BlinkLedForever(); |
sulymarco | 0:bc829777f1ea | 202 | } |
sulymarco | 0:bc829777f1ea | 203 | } |
sulymarco | 0:bc829777f1ea | 204 | else |
sulymarco | 0:bc829777f1ea | 205 | { |
sulymarco | 0:bc829777f1ea | 206 | printf("Ethernet interface init failed!"); |
sulymarco | 0:bc829777f1ea | 207 | BlinkLedForever(); |
sulymarco | 0:bc829777f1ea | 208 | } |
sulymarco | 0:bc829777f1ea | 209 | } |
sulymarco | 0:bc829777f1ea | 210 | |
sulymarco | 0:bc829777f1ea | 211 | |
sulymarco | 0:bc829777f1ea | 212 | //****** user master application ********************************************** |
sulymarco | 0:bc829777f1ea | 213 | |
sulymarco | 0:bc829777f1ea | 214 | void Application() |
sulymarco | 0:bc829777f1ea | 215 | { |
sulymarco | 0:bc829777f1ea | 216 | |
sulymarco | 0:bc829777f1ea | 217 | while(1) |
sulymarco | 0:bc829777f1ea | 218 | { |
sulymarco | 0:bc829777f1ea | 219 | event_flags.wait_any(APPLICATION_FLAG); // the application waits for the synchronization flag |
sulymarco | 0:bc829777f1ea | 220 | |
sulymarco | 0:bc829777f1ea | 221 | //Test_2 = 1; |
sulymarco | 0:bc829777f1ea | 222 | |
sulymarco | 0:bc829777f1ea | 223 | IO_data.lock(); // copy the Ethercat data to a safe buffer |
sulymarco | 0:bc829777f1ea | 224 | memcpy(&IOmapSafe[0], &IOmap[0], IO_MAP_SIZE); // |
sulymarco | 0:bc829777f1ea | 225 | // |
sulymarco | 0:bc829777f1ea | 226 | if (NetworkError) // |
sulymarco | 0:bc829777f1ea | 227 | { // |
sulymarco | 0:bc829777f1ea | 228 | NetworkErrorSafe = NetworkError; // |
sulymarco | 0:bc829777f1ea | 229 | WorkCounterSafe = WorkCounter; // |
sulymarco | 0:bc829777f1ea | 230 | } // |
sulymarco | 0:bc829777f1ea | 231 | IO_data.unlock(); // |
sulymarco | 0:bc829777f1ea | 232 | |
sulymarco | 0:bc829777f1ea | 233 | if (NetworkErrorSafe) |
sulymarco | 0:bc829777f1ea | 234 | { |
sulymarco | 0:bc829777f1ea | 235 | printf("Network error!\n"); |
sulymarco | 0:bc829777f1ea | 236 | |
sulymarco | 0:bc829777f1ea | 237 | if(WorkCounterSafe >= 0) |
sulymarco | 0:bc829777f1ea | 238 | { |
sulymarco | 0:bc829777f1ea | 239 | printf("Expected working counter %d\n", ExpectWorkCounter); |
sulymarco | 0:bc829777f1ea | 240 | printf("Actual working counter %d\n", WorkCounterSafe); |
sulymarco | 0:bc829777f1ea | 241 | } |
sulymarco | 0:bc829777f1ea | 242 | else |
sulymarco | 0:bc829777f1ea | 243 | { |
sulymarco | 0:bc829777f1ea | 244 | printf("Timeout\n"); |
sulymarco | 0:bc829777f1ea | 245 | } |
sulymarco | 0:bc829777f1ea | 246 | |
sulymarco | 0:bc829777f1ea | 247 | printf("Please fix the error and press the reset button \n"); |
sulymarco | 0:bc829777f1ea | 248 | |
sulymarco | 0:bc829777f1ea | 249 | SampleTicker.detach(); // stop the sample interrupt |
sulymarco | 0:bc829777f1ea | 250 | BlinkLedForever(); // and loop for ever |
sulymarco | 0:bc829777f1ea | 251 | } |
sulymarco | 0:bc829777f1ea | 252 | |
sulymarco | 0:bc829777f1ea | 253 | |
sulymarco | 0:bc829777f1ea | 254 | //----- slaves data management ----------- |
sulymarco | 0:bc829777f1ea | 255 | |
sulymarco | 0:bc829777f1ea | 256 | |
EasyCAT | 2:368e7d4d8171 | 257 | out_LAB_1->Segments = in_LAB_2->Buttons; // send to the slave LAB_2_1 the buttons status |
sulymarco | 0:bc829777f1ea | 258 | // from the slave LAB_2_2 |
sulymarco | 0:bc829777f1ea | 259 | |
sulymarco | 0:bc829777f1ea | 260 | |
EasyCAT | 2:368e7d4d8171 | 261 | out_LAB_2->Segments = in_LAB_1->Buttons; // send to the slave LAB_2_2 the buttons status |
sulymarco | 0:bc829777f1ea | 262 | // from the slave LAB_2_1 |
sulymarco | 0:bc829777f1ea | 263 | |
sulymarco | 0:bc829777f1ea | 264 | |
sulymarco | 0:bc829777f1ea | 265 | //---------------------------------------- |
sulymarco | 0:bc829777f1ea | 266 | |
sulymarco | 0:bc829777f1ea | 267 | |
sulymarco | 0:bc829777f1ea | 268 | |
sulymarco | 0:bc829777f1ea | 269 | IO_data.lock(); // copy the IO data from the safe area |
sulymarco | 0:bc829777f1ea | 270 | memcpy(&IOmap[0], &IOmapSafe[0], IO_MAP_SIZE); // to the EtherCAT buffer |
sulymarco | 0:bc829777f1ea | 271 | IO_data.unlock(); // |
sulymarco | 0:bc829777f1ea | 272 | |
sulymarco | 0:bc829777f1ea | 273 | //Test_2 = 0; |
sulymarco | 0:bc829777f1ea | 274 | } |
sulymarco | 0:bc829777f1ea | 275 | } |
sulymarco | 0:bc829777f1ea | 276 | |
sulymarco | 0:bc829777f1ea | 277 | //****************************************************************************** |
sulymarco | 0:bc829777f1ea | 278 | |
sulymarco | 0:bc829777f1ea | 279 | |
sulymarco | 0:bc829777f1ea | 280 | void BlinkLedForever() // blink the red led forever to signal |
sulymarco | 0:bc829777f1ea | 281 | // an unrecoverable error condition |
sulymarco | 0:bc829777f1ea | 282 | |
sulymarco | 0:bc829777f1ea | 283 | // fix the error and press the reset button |
sulymarco | 0:bc829777f1ea | 284 | { |
sulymarco | 0:bc829777f1ea | 285 | while(1) |
sulymarco | 0:bc829777f1ea | 286 | { |
sulymarco | 0:bc829777f1ea | 287 | LED_RED = !LED_RED; |
EasyCAT | 1:971b4897a4c5 | 288 | ThisThread::sleep_for(100ms); |
sulymarco | 0:bc829777f1ea | 289 | } |
sulymarco | 0:bc829777f1ea | 290 | } |
sulymarco | 0:bc829777f1ea | 291 | |
sulymarco | 0:bc829777f1ea | 292 |