/** \file * \brief Example code for Simple Open EtherCAT master * * This is a minimal example running rt-kernel TTOS. Check * tutorial files in documentations for som steps to get your * system running. * (c)Andreas Karlsson 2012 */ #include #include "ethercat.h" #include "string.h" #include #include #include #define pPORTFIO_SET ((vuint16_t *)PORTFIO_SET) #define pPORTFIO_CLEAR ((vuint16_t *)PORTFIO_CLEAR) #define pPORTFIO_DIR ((vuint16_t *)PORTFIO_DIR) #define EK1100_1 1 #define EL4001_1 2 #define EL3061_1 3 #define EL1008_1 4 #define EL1008_2 5 #define EL2622_1 6 #define EL2622_2 7 #define EL2622_3 8 #define EL2622_4 9 #define NUMBER_OF_SLAVES 9 typedef struct { uint8 in1; uint8 in2; uint8 in3; uint8 in4; uint8 in5; uint8 in6; uint8 in7; uint8 in8; } in_EL1008_t; typedef struct { uint8 out1; uint8 out2; } out_EL2622_t; typedef struct { int16 out1; } out_EL4001_t; typedef struct { int32 in1; } in_EL3061_t; out_EL4001_t slave_EL4001_1; in_EL3061_t slave_EL3061_1; in_EL1008_t slave_EL1008_1; in_EL1008_t slave_EL1008_2; out_EL2622_t slave_EL2622_1; out_EL2622_t slave_EL2622_2; out_EL2622_t slave_EL2622_3; uint32 network_configuration(void) { /* Do we got expected number of slaves from config */ if (ec_slavecount < NUMBER_OF_SLAVES) return 0; /* Verify slave by slave that it is correct*/ if (strcmp(ec_slave[EK1100_1].name,"EK1100")) return 0; else if (strcmp(ec_slave[EL4001_1].name,"EL4001")) return 0; else if (strcmp(ec_slave[EL3061_1].name,"EL3061")) return 0; else if (strcmp(ec_slave[EL1008_1].name,"EL1008")) return 0; else if (strcmp(ec_slave[EL1008_2].name,"EL1008")) return 0; else if (strcmp(ec_slave[EL2622_1].name,"EL2622")) return 0; else if (strcmp(ec_slave[EL2622_2].name,"EL2622")) return 0; else if (strcmp(ec_slave[EL2622_3].name,"EL2622")) return 0; else if (strcmp(ec_slave[EL2622_4].name,"EL2622")) return 0; return 1; } int32 get_input_int32(uint16 slave_no,uint8 module_index) { int32 return_value; uint8 *data_ptr; /* Get the IO map pointer from the ec_slave struct */ data_ptr = ec_slave[slave_no].inputs; /* Move pointer to correct module index */ data_ptr += module_index * 4; /* Read value byte by byte since all targets can't handle misaligned * addresses */ return_value = *data_ptr++; return_value += (*data_ptr++ << 8); return_value += (*data_ptr++ << 16); return_value += (*data_ptr++ << 24); return return_value; } void set_input_int32 (uint16 slave_no, uint8 module_index, int32 value) { uint8 *data_ptr; /* Get the IO map pointer from the ec_slave struct */ data_ptr = ec_slave[slave_no].inputs; /* Move pointer to correct module index */ data_ptr += module_index * 4; /* Read value byte by byte since all targets can handle misaligned * addresses */ *data_ptr++ = (value >> 0) & 0xFF; *data_ptr++ = (value >> 8) & 0xFF; *data_ptr++ = (value >> 16) & 0xFF; *data_ptr++ = (value >> 24) & 0xFF; } uint8 get_input_bit (uint16 slave_no,uint8 module_index) { /* Get the the startbit position in slaves IO byte */ uint8 startbit = ec_slave[slave_no].Istartbit; /* Mask bit and return boolean 0 or 1 */ if (*ec_slave[slave_no].inputs & BIT (module_index - 1 + startbit)) return 1; else return 0; } int16 get_output_int16(uint16 slave_no,uint8 module_index) { int16 return_value; uint8 *data_ptr; /* Get the IO map pointer from the ec_slave struct */ data_ptr = ec_slave[slave_no].outputs; /* Move pointer to correct module index */ data_ptr += module_index * 2; /* Read value byte by byte since all targets can handle misaligned * addresses */ return_value = *data_ptr++; return_value += (*data_ptr++ << 8); return return_value; } void set_output_int16 (uint16 slave_no, uint8 module_index, int16 value) { uint8 *data_ptr; /* Get the IO map pointer from the ec_slave struct */ data_ptr = ec_slave[slave_no].outputs; /* Move pointer to correct module index */ data_ptr += module_index * 2; /* Read value byte by byte since all targets can handle misaligned * addresses */ *data_ptr++ = (value >> 0) & 0xFF; *data_ptr++ = (value >> 8) & 0xFF; } uint8 get_output_bit (uint16 slave_no,uint8 module_index) { /* Get the the startbit position in slaves IO byte */ uint8 startbit = ec_slave[slave_no].Ostartbit; /* Mask bit and return boolean 0 or 1 */ if (*ec_slave[slave_no].outputs & BIT (module_index - 1 + startbit)) return 1; else return 0; } void set_output_bit (uint16 slave_no, uint8 module_index, uint8 value) { /* Get the the startbit position in slaves IO byte */ uint8 startbit = ec_slave[slave_no].Ostartbit; /* Set or Clear bit */ if (value == 0) *ec_slave[slave_no].outputs &= ~(1 << (module_index - 1 + startbit)); else *ec_slave[slave_no].outputs |= (1 << (module_index - 1 + startbit)); } extern tt_sched_t * tt_sched[]; char IOmap[128]; int dorun = 0; uint8_t load1s, load5s, load10s; uint32_t error_counter = 0; void tt_error (uint32_t task_ix); void tt_error (uint32_t task_ix) { error_counter++; } static void my_cyclic_callback (void * arg) { while (1) { stats_get_load (&load1s, &load5s, &load10s); rprintp ("Processor load was %d:%d:%d (1s:5s:10s) with TT errors: %d\n", load1s, load5s, load10s,error_counter); task_delay(20000); } } void read_io (void * arg) { /* Function connected to cyclic TTOS task * The function is executed cyclic according * sceduel specified in schedule.tt */ *pPORTFIO_SET = BIT (6); /* Send and receive processdata * Note that you need som synchronization * case you modifey IO in local application */ ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); *pPORTFIO_CLEAR = BIT (6); } void simpletest(void *arg) { char *ifname = arg; int cnt, i, j; *pPORTFIO_DIR |= BIT (6); rprintp("Starting simple test\n"); /* initialise SOEM */ if (ec_init(ifname)) { rprintp("ec_init succeeded.\n"); /* find and auto-config slaves */ if ( ec_config_init(FALSE) > 0 ) { rprintp("%d slaves found and configured.\n",ec_slavecount); /* Check network setup */ if (network_configuration()) { /* Run IO mapping */ ec_config_map(&IOmap); rprintp("Slaves mapped, state to SAFE_OP.\n"); /* wait for all slaves to reach SAFE_OP state */ ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE); /* Print som information on the mapped network */ for( cnt = 1 ; cnt <= ec_slavecount ; cnt++) { rprintp("\nSlave:%d\n Name:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d[ns]\n Has DC: %d\n", cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits, ec_slave[cnt].state, ec_slave[cnt].pdelay, ec_slave[cnt].hasdc); rprintp(" Configured address: %x\n", ec_slave[cnt].configadr); rprintp(" Outputs address: %x\n", ec_slave[cnt].outputs); rprintp(" Inputs address: %x\n", ec_slave[cnt].inputs); for(j = 0 ; j < ec_slave[cnt].FMMUunused ; j++) { rprintp(" FMMU%1d Ls:%x Ll:%4d Lsb:%d Leb:%d Ps:%x Psb:%d Ty:%x Act:%x\n", j, (int)ec_slave[cnt].FMMU[j].LogStart, ec_slave[cnt].FMMU[j].LogLength, ec_slave[cnt].FMMU[j].LogStartbit, ec_slave[cnt].FMMU[j].LogEndbit, ec_slave[cnt].FMMU[j].PhysStart, ec_slave[cnt].FMMU[j].PhysStartBit, ec_slave[cnt].FMMU[j].FMMUtype, ec_slave[cnt].FMMU[j].FMMUactive); } rprintp(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n", ec_slave[cnt].FMMU0func, ec_slave[cnt].FMMU1func, ec_slave[cnt].FMMU2func, ec_slave[cnt].FMMU3func); } rprintp("Request operational state for all slaves\n"); ec_slave[0].state = EC_STATE_OPERATIONAL; /* send one valid process data to make outputs in slaves happy*/ ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); /* request OP state for all slaves */ ec_writestate(0); /* wait for all slaves to reach OP state */ ec_statecheck(0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE); if (ec_slave[0].state == EC_STATE_OPERATIONAL ) { rprintp("Operational state reached for all slaves.\n"); } else { rprintp("Not all slaves reached operational state.\n"); ec_readstate(); for(i = 1; i<=ec_slavecount ; i++) { if(ec_slave[i].state != EC_STATE_OPERATIONAL) { rprintp("Slave %d State=0x%04x StatusCode=0x%04x\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode); } } } /* Simple blinking lamps BOX demo */ uint8 digout = 0; slave_EL4001_1.out1 = (int16)0x3FFF; set_output_int16(EL4001_1,0,slave_EL4001_1.out1); task_spawn ("t_StatsPrint", my_cyclic_callback, 20, 1024, (void *)NULL); tt_start_wait (tt_sched[0]); while(1) { dorun = 0; slave_EL1008_1.in1 = get_input_bit(EL1008_1,1); // Start button slave_EL1008_1.in2 = get_input_bit(EL1008_1,2); // Turnkey RIGHT slave_EL1008_1.in3 = get_input_bit(EL1008_1,3); // Turnkey LEFT /* (Turnkey MIDDLE + Start button) OR Turnkey RIGHT OR Turnkey LEFT Turnkey LEFT: Light positions bottom to top. Loop, slow operation. Turnkey MIDDLE: Press start button to light positions bottom to top. No loop, fast operation. Turnkey RIGHT: Light positions bottom to top. Loop, fast operation. */ if (slave_EL1008_1.in1 || slave_EL1008_1.in2 || slave_EL1008_1.in3) { digout = 0; /* *ec_slave[6].outputs = digout; */ /* set_output_bit(slave_name #,index as 1 output on module , value */ set_output_bit(EL2622_1,1,(digout & BIT (0))); /* Start button */ set_output_bit(EL2622_1,2,(digout & BIT (1))); /* Turnkey RIGHT */ set_output_bit(EL2622_2,1,(digout & BIT (2))); /* Turnkey LEFT */ set_output_bit(EL2622_2,2,(digout & BIT (3))); set_output_bit(EL2622_3,1,(digout & BIT (4))); set_output_bit(EL2622_3,2,(digout & BIT (5))); while(dorun < 95) { dorun++; if (slave_EL1008_1.in3) task_delay(tick_from_ms(20)); else task_delay(tick_from_ms(5)); digout = (uint8) (digout | BIT((dorun / 16) & 0xFF)); set_output_bit(EL2622_1,1,(digout & BIT (0))); /* LED1 */ set_output_bit(EL2622_1,2,(digout & BIT (1))); /* LED2 */ set_output_bit(EL2622_2,1,(digout & BIT (2))); /* LED3 */ set_output_bit(EL2622_2,2,(digout & BIT (3))); /* LED4 */ set_output_bit(EL2622_3,1,(digout & BIT (4))); /* LED5 */ set_output_bit(EL2622_3,2,(digout & BIT (5))); /* LED6 */ slave_EL1008_1.in1 = get_input_bit(EL1008_1,2); /* Turnkey RIGHT */ slave_EL1008_1.in2 = get_input_bit(EL1008_1,3); /* Turnkey LEFT */ slave_EL3061_1.in1 = get_input_int32(EL3061_1,0); /* Read AI */ } } task_delay(tick_from_ms(2)); } } else { rprintp("Mismatch of network units!\n"); } } else { rprintp("No slaves found!\n"); } rprintp("End simple test, close socket\n"); /* stop SOEM, close socket */ ec_close(); } else { rprintp("ec_init failed"); } rprintp("End program\n"); } static void test_osal_timer_timeout_us (const uint32 timeout_us) { osal_timert timer; ASSERT (timeout_us > 4000); osal_timer_start (&timer, timeout_us); ASSERT (osal_timer_is_expired (&timer) == false); osal_usleep (timeout_us - 2000); ASSERT (osal_timer_is_expired (&timer) == false); osal_usleep (4000); ASSERT (osal_timer_is_expired (&timer)); } static void test_osal_timer (void) { test_osal_timer_timeout_us (10*1000); /* 10ms */ test_osal_timer_timeout_us (100*1000); /* 100ms */ test_osal_timer_timeout_us (1000*1000); /* 1s */ test_osal_timer_timeout_us (2000*1000); /* 2s */ } #define USECS_PER_SEC 1000000 #define USECS_PER_TICK (USECS_PER_SEC / CFG_TICKS_PER_SECOND) #ifndef ABS #define ABS(x) ((x) < 0 ? -(x) : (x)) #endif static int32 time_difference_us (const ec_timet stop, const ec_timet start) { int32 difference_us; ASSERT (stop.sec >= start.sec); if (stop.sec == start.sec) { ASSERT (stop.usec >= start.usec); } difference_us = (stop.sec - start.sec) * USECS_PER_SEC; difference_us += ((int32)stop.usec - (int32)start.usec); ASSERT (difference_us >= 0); return difference_us; } /** * Test osal_current_time() by using it for measuring how long an osal_usleep() * takes, in specified number of microseconds. */ static void test_osal_current_time_for_delay_us (const int32 sleep_time_us) { ec_timet start; ec_timet stop; int32 measurement_us; int32 deviation_us; const int32 usleep_accuracy_us = USECS_PER_TICK; boolean is_deviation_within_tolerance; start = osal_current_time (); osal_usleep (sleep_time_us); stop = osal_current_time (); measurement_us = time_difference_us (stop, start); deviation_us = ABS (measurement_us - sleep_time_us); is_deviation_within_tolerance = deviation_us <= usleep_accuracy_us; ASSERT (is_deviation_within_tolerance); } static void test_osal_current_time (void) { test_osal_current_time_for_delay_us (0); test_osal_current_time_for_delay_us (1); test_osal_current_time_for_delay_us (500); test_osal_current_time_for_delay_us (999); test_osal_current_time_for_delay_us (USECS_PER_TICK); test_osal_current_time_for_delay_us (USECS_PER_TICK-1); test_osal_current_time_for_delay_us (USECS_PER_TICK+1); test_osal_current_time_for_delay_us (2 * 1000 * 1000); /* 2s */ } #include static void test_oshw_htons (void) { uint16 network; uint16 host; host = 0x1234; network = oshw_htons (host); ASSERT (network == htons (host)); } static void test_oshw_ntohs (void) { uint16 host; uint16 network; network = 0x1234; host = oshw_ntohs (network); ASSERT (host == ntohs (network)); } int main (void) { test_oshw_htons (); test_oshw_ntohs (); test_osal_timer (); test_osal_current_time (); rprintp("SOEM (Simple Open EtherCAT Master)\nSimple test\n"); task_spawn ("simpletest", simpletest, 9, 8192, NULL); return (0); }