/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2022 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /******************************************************************************* * Includes ******************************************************************************/ #include #include #include "pin_mux.h" #include "clock_config.h" #include "board.h" #include "fsl_gpio.h" #include "fsl_iomuxc.h" #include "fsl_gpt.h" #include "fsl_enet_mdio.h" #include "fsl_phyksz8081.h" #include "fsl_debug_console.h" #include "ethercattype.h" #include "nicdrv.h" #include "ethercatbase.h" #include "ethercatmain.h" #include "ethercatdc.h" #include "ethercatcoe.h" #include "ethercatfoe.h" #include "ethercatconfig.h" #include "ethercatprint.h" #include "enet/soem_enet.h" #include "soem_port.h" /******************************************************************************* * Definitions ******************************************************************************/ #ifndef PHY_AUTONEGO_TIMEOUT_COUNT #define PHY_AUTONEGO_TIMEOUT_COUNT (100000) #endif #ifndef PHY_STABILITY_DELAY_US #define PHY_STABILITY_DELAY_US (0U) #endif /*! @brief GPT timer will be used to calculate the system time and delay */ #define OSAL_TIMER_IRQ_ID GPT2_IRQn #define OSAL_TIMER GPT2 #define OSAL_TIMER_IRQHandler GPT2_IRQHandler #define OSAL_TIMER_CLK_FREQ CLOCK_GetFreq(kCLOCK_PerClk) #define NUM_1M (1000000UL) #define SOEM_PERIOD NUM_1M /* 1 second */ #define OSEM_PORT_NAME "enet0" #define ENET_RXBD_NUM (4) #define ENET_TXBD_NUM (4) #define ENET_RXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN) #define ENET_TXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN) /******************************************************************************* * Variables ******************************************************************************/ static struct enet_if_port if_port; static uint32_t timer_irq_period = 0; /* unit: microsecond*/ volatile struct timeval system_time_base = { .tv_sec = 0, .tv_usec = 0 }; /*! @brief Buffer descriptors should be in non-cacheable region and should be align to "ENET_BUFF_ALIGNMENT". */ AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t g_rxBuffDescrip[ENET_RXBD_NUM], ENET_BUFF_ALIGNMENT); AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t g_txBuffDescrip[ENET_TXBD_NUM], ENET_BUFF_ALIGNMENT); /*! @brief The data buffers can be in cacheable region or in non-cacheable region. * If use cacheable region, the alignment size should be the maximum size of "CACHE LINE SIZE" and "ENET_BUFF_ALIGNMENT" * If use non-cache region, the alignment size is the "ENET_BUFF_ALIGNMENT". */ AT_NONCACHEABLE_SECTION_ALIGN(static uint8_t g_rxDataBuff[ENET_RXBD_NUM][SDK_SIZEALIGN(ENET_RXBUFF_SIZE, ENET_BUFF_ALIGNMENT)], ENET_BUFF_ALIGNMENT); AT_NONCACHEABLE_SECTION_ALIGN(static uint8_t g_txDataBuff[ENET_TXBD_NUM][SDK_SIZEALIGN(ENET_TXBUFF_SIZE, ENET_BUFF_ALIGNMENT)], ENET_BUFF_ALIGNMENT); static enet_buffer_config_t buffConfig[] = {{ ENET_RXBD_NUM, ENET_TXBD_NUM, SDK_SIZEALIGN(ENET_RXBUFF_SIZE, ENET_BUFF_ALIGNMENT), SDK_SIZEALIGN(ENET_TXBUFF_SIZE, ENET_BUFF_ALIGNMENT), &g_rxBuffDescrip[0], &g_txBuffDescrip[0], &g_rxDataBuff[0][0], &g_txDataBuff[0][0], true, true, NULL, }}; static char IOmap[100]; /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ void irq_wake_task(void); void BOARD_InitModuleClock(void) { const clock_enet_pll_config_t config = { .enableClkOutput = true, .enableClkOutput25M = false, .loopDivider = 1, }; CLOCK_InitEnetPll(&config); } void OSAL_TIMER_IRQHandler(void) { /* Clear interrupt flag. */ GPT_ClearStatusFlags(OSAL_TIMER, kGPT_OutputCompare1Flag); system_time_base.tv_usec += timer_irq_period; if (system_time_base.tv_usec >= NUM_1M) { system_time_base.tv_sec += system_time_base.tv_usec / NUM_1M; system_time_base.tv_usec = system_time_base.tv_usec % NUM_1M; } irq_wake_task(); /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F, Cortex-M7, Cortex-M7F * Store immediate overlapping * exception return operation might vector to incorrect interrupt */ SDK_ISR_EXIT_BARRIER; } static void osal_timer_init(uint32_t usec, uint32_t priority) { uint32_t gptFreq; gpt_config_t gptConfig; assert(usec != 0); assert(priority < (1UL << __NVIC_PRIO_BITS)); timer_irq_period = usec; GPT_GetDefaultConfig(&gptConfig); GPT_Init(OSAL_TIMER, &gptConfig); gptFreq = OSAL_TIMER_CLK_FREQ; /* Divide GPT clock source frequency to 1MHz */ GPT_SetClockDivider(OSAL_TIMER, gptFreq / NUM_1M); /* Set both GPT modules to 1 second duration */ GPT_SetOutputCompareValue(OSAL_TIMER, kGPT_OutputCompare_Channel1, timer_irq_period); /* Enable GPT Output Compare1 interrupt */ GPT_EnableInterrupts(OSAL_TIMER, kGPT_OutputCompare1InterruptEnable); /* Enable at the Interrupt */ NVIC_SetPriority(OSAL_TIMER_IRQ_ID, priority); EnableIRQ(OSAL_TIMER_IRQ_ID); GPT_StartTimer(OSAL_TIMER); } void osal_gettime(struct timeval *current_time) { uint32_t usec_base; uint32_t cur_usec; uint32_t usec_again; usec_base = system_time_base.tv_usec; cur_usec = GPT_GetCurrentTimerCount(OSAL_TIMER); usec_again = system_time_base.tv_usec; if (usec_again != usec_base) { usec_base = system_time_base.tv_usec; cur_usec = GPT_GetCurrentTimerCount(OSAL_TIMER); } current_time->tv_sec = system_time_base.tv_sec; current_time->tv_usec = usec_base + cur_usec; return; } /* OSHW: register enet port to SOEM stack */ static int if_port_init(void) { memset(&if_port, 0, sizeof(if_port)); if_port.mdioHandle.ops = &enet_ops; if_port.phyHandle.ops = &phyksz8081_ops; if_port.bufferConfig = buffConfig; if_port.base = ENET; /* The miiMode should be set according to the different PHY interfaces. */ if_port.mii_mode = kENET_RmiiMode; if_port.phy_config.autoNeg = true; if_port.phy_config.phyAddr = 0x02U; if_port.srcClock_Hz = CLOCK_GetFreq(kCLOCK_IpgClk); if_port.phy_autonego_timeout_count = PHY_AUTONEGO_TIMEOUT_COUNT; if_port.phy_stability_delay_us = PHY_STABILITY_DELAY_US; return register_soem_port(OSEM_PORT_NAME, "enet", &if_port); } void irq_wake_task(void) { return; } void control_task(char *ifname) { int oloop, iloop; int expectedWKC; volatile int wkc; int old_switch0 = 0; int old_switch1 = 0; struct timeval current_time; struct timeval sleep_time; struct timeval target_time; struct timeval last_time; struct timeval diff_time; sleep_time.tv_sec = 0; sleep_time.tv_usec = 125; /* initialise SOEM, and if_port */ if (ec_init(ifname)) { PRINTF("ec_init on %s succeeded.\r\n", ifname); if (ec_config_init(FALSE) > 0) { PRINTF("%d slaves found and configured.\r\n", ec_slavecount); ec_config_map(&IOmap); ec_configdc(); PRINTF("Slaves mapped, state to SAFE_OP.\r\n"); /* wait for all slaves to reach SAFE_OP state */ ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4); oloop = ec_slave[2].Obytes; iloop = ec_slave[3].Ibytes; PRINTF("oloop = %d, iloop = %d\r\n", oloop, iloop); PRINTF("segments : %d : %d %d %d %d\r\n", ec_group[0].nsegments, ec_group[0].IOsegment[0], ec_group[0].IOsegment[1], ec_group[0].IOsegment[2], ec_group[0].IOsegment[3]); PRINTF("Request operational state for all slaves\r\n"); expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC; PRINTF("Calculated workcounter %d\r\n", expectedWKC); 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); char chk = 40; /* wait for all slaves to reach OP state */ do { ec_send_processdata(); ec_receive_processdata(EC_TIMEOUTRET); ec_statecheck(0, EC_STATE_OPERATIONAL, 50000); } while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL)); PRINTF("Operational state reached for all slaves.\r\n"); /* cyclic loop */ int is_expired; osal_gettime(&last_time); while (1) { osal_gettime(¤t_time); timeradd(¤t_time, &sleep_time, &target_time); ec_send_processdata(); wkc = ec_receive_processdata(EC_TIMEOUTRET); if (wkc >= expectedWKC) { if ((*(ec_slave[3].inputs) & 0x01) && old_switch0 == 0) { if (*(ec_slave[2].outputs) & 0x04) *(ec_slave[2].outputs) &= ~0x04; else *(ec_slave[2].outputs) |= 0x04; } if ((*(ec_slave[3].inputs) & 0x02) && old_switch1 == 0) { if (*(ec_slave[2].outputs) & 0x04) *(ec_slave[2].outputs) &= ~0x04; else *(ec_slave[2].outputs) |= 0x04; } old_switch0 = *(ec_slave[3].inputs) & 0x01; old_switch1 = *(ec_slave[3].inputs) & 0x02; if (*(ec_slave[2].outputs) & 0x02) *(ec_slave[2].outputs) &= ~0x02; else *(ec_slave[2].outputs) |= 0x02; } osal_gettime(¤t_time); timersub(¤t_time, &last_time, &diff_time); is_expired = timercmp(&target_time, ¤t_time, <=); if (diff_time.tv_usec > 130) { last_time.tv_usec = current_time.tv_usec; } last_time.tv_usec = current_time.tv_usec; last_time.tv_sec = current_time.tv_sec; if (!is_expired) { timersub(&target_time, ¤t_time, &sleep_time); osal_usleep(sleep_time.tv_usec); sleep_time.tv_usec = 125; } else { while (1) ; } } } } } /*! * @brief Main function */ int main(void) { gpio_pin_config_t gpio_config = { kGPIO_DigitalOutput, 0, kGPIO_NoIntmode }; BOARD_ConfigMPU(); BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); BOARD_InitModuleClock(); IOMUXC_EnableMode(IOMUXC_GPR, kIOMUXC_GPR_ENET1TxClkOutputDir, true); GPIO_PinInit(GPIO1, 9, &gpio_config); GPIO_PinInit(GPIO1, 10, &gpio_config); /* pull up the ENET_INT before RESET. */ GPIO_WritePinOutput(GPIO1, 10, 1); GPIO_WritePinOutput(GPIO1, 9, 0); SDK_DelayAtLeastUs(NUM_1M, CLOCK_GetFreq(kCLOCK_CpuClk)); GPIO_WritePinOutput(GPIO1, 9, 1); PRINTF("Start the soem_gpio_pulse baremetal example...\r\n"); osal_timer_init(SOEM_PERIOD, 0); if_port_init(); control_task(OSEM_PORT_NAME); return 0; }