往死里写——mainloop函数

本文深入探讨了在ECAT同步与自由运行模式下,应用程序如何进行管理和处理输出、输入事件,包括输出映射、输入映射、应用状态管理及定时器检查,确保系统的稳定性和高效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

void MainLoop(void)
{
        /* 当运行在FreeRun-Mode:  bEscIntEnabled = FALSE, bDcSyncActive = FALSE
           当运行在Synchron-Mode: bEscIntEnabled = TRUE, bDcSyncActive = FALSE
           当运行在DC-Mode:       bEscIntEnabled = FALSE, bDcSyncActive = TRUE */
        if ( (!bEscIntEnabled || !bEcatFirstOutputsReceived)     /* SM同步方式,但是没有SM事件接收 */
#if DC_SUPPORTED
          && !bDcSyncActive                                               /* DC同步方式 */
#endif
            )
        {
            /* 如果应用程序是运行在ECAT同步模式当中,函数ECAT_Application将会在ESC中断里面被调用(在mcihw.c和spihw.c) 在ECAT同步模式当中,它可以被另外得检查,如果SM事件被接受至少一次(bEcatFirstOutputReceived=1),否则,没有中断会产生和函数ECAT_Application将会在这里被调用(中断禁止,因为当执行ECAT_Application的时候,SM事件可以产生)*/
            if ( !bEscIntEnabled )
            {
                /* 应用程序正在运行在ECAT 自由运行模式,首先,我们需要检查是否有输出被接受 */
                UINT16 ALEvent = HW_GetALEventRegister();
                ALEvent = SWAPWORD(ALEvent);

                if ( ALEvent & PROCESS_OUTPUT_EVENT )
                {
                    /* 设置标志位给状态机处理 */
                    bEcatFirstOutputsReceived = TRUE;
#if !ESC_SM_WD_SUPPORTED
                    /* 重置看门狗计数器 */
                    EcatWdCounter = 0;
#endif
                    if ( bEcatOutputUpdateRunning )
                    {
                        /* 更新输出的状态量 */
                        PDO_OutputMapping();
                    }
                }
                else if ( nPdOutputSize == 0 )
                {
                    /* 如果没有输出被传输,当有输入被读,看门狗需要被重置 */
                    if ( ALEvent & PROCESS_INPUT_EVENT )
                    {
                        /* 输出被更新,设置标志位给看门狗检测 */
                        bEcatFirstOutputsReceived = TRUE;
#if !ESC_SM_WD_SUPPORTED
                        /* 重置看门狗的计数器 */
                        EcatWdCounter = 0;
#endif
                    }
                }
            }

#if AL_EVENT_ENABLED
            DISABLE_ESC_INT();
#endif
            ECAT_Application();
#if AL_EVENT_ENABLED
            ENABLE_ESC_INT();
#endif
        }

#if !ECAT_TIMER_INT
        /* 这里没有中断服务程序来处理硬件定时器,所以,检查定时器的寄存器看是否想要的周期已经过了*/
        {
            UINT32 CurTimer = HW_GetTimer();

            if(CurTimer>= ECAT_TIMER_INC_P_MS)
            {
                ECAT_CheckTimer();

                HW_ClearTimer();
            }
        }
#endif

        /* 调用EtherCAT函数 */
        ECAT_Main();

#if COE_SUPPORTED
        /* 调用低优先级的应用程序部分 */
        COE_Main();
#endif
        CheckIfEcatError();

#if CiA402_DEVICE
    if(bEcatInputUpdateRunning)
    {
        CiA402_StateMachine();//调用CIA402的处理机,来处理COE,这里没有用到
    }
#endif
}




void PDO_OutputMapping()
{

    HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize );

    APPL_OutputMapping((UINT16*) aPdOutputData);
}



/////////////////////////////////////////////////////////////////////////////////////////
/**
\param      指针指向输出处理数据

\brief    这个函数将拷贝输出从ESC内存到本地内存到硬件
*////////////////////////////////////////////////////////////////////////////////////////
void APPL_OutputMapping(Uint16* pData)
{
    Uint16 j = 0;
    Uint8 *pTmpData = (Uint8 *)pData;// 允许数据处理

    for (j = 0; j < sRxPDOassign.u16SubIndex0; j++)
    {
        switch (sRxPDOassign.aEntries[j])
        {
        /* RxPDO 2 */
        case 0x1601:
            sDOOutputs.LEDs = *pTmpData++;
            break;
        case 0x1602:
            sDO1Outputs.Count = *pTmpData++;
            sDO1Outputs.Cmd   = *pTmpData++;
            sDO1Outputs.MotorData = *pTmpData++;
            sDO1Outputs.MotorData |= (*pTmpData++ << 8);
            break;
        }
    }
}


void ECAT_Application(void)
{
#if CiA402_DEVICE
    /*轴的配置被写,在状态转换从PREOP到SAFEOP=>触发CiA402应用程序,如果设备是在SAFEOP或者OP状态
    (电机控制器的函数只有被触发,如果DC同步被激活和有效的运行模式被设置)*/
    if(bEcatInputUpdateRunning)
#endif
    {
        APPL_Application();
    }

    if ( bEcatInputUpdateRunning )
    {
        /* EtherCAT 从站是至少在SAFE-OPERATIONAL,更新输入 */
        PDO_InputMapping();
    }

}


/////////////////////////////////////////////////////////////////////////////////////////
/**
\brief    这个函数将被调用从同步的中断程序或者从mainloop当中,如果没有同步被支持
*////////////////////////////////////////////////////////////////////////////////////////
void APPL_Application(void)
{
    Uint8 LED; 					// 初始化测试数据
    static Uint8 prevState = 55;
    
    LED = sDOOutputs.LEDs;
    
    if(LED != prevState)
        set_led(LED);				//调用底层程序设置LED
    prevState = LED;
    appState = sDO1Outputs.Cmd;			// 设置应用程序的状态
    
    sDIInputs.switchs = (Uint8)Read_HVS2();
    if(appState == 0)
        appState = sDIInputs.switchs;//special mode to control app state by input switchs!
    sAI1Inputs.info1 = 0x12345678;
    sAI1Inputs.info2 = bsp_read_word(0x10);

}



void PDO_InputMapping()
{
    APPL_InputMapping((UINT16*)aPdInputData);
    HW_EscWriteIsr(((MEM_ADDR *) aPdInputData), nEscAddrInputData, nPdInputSize );
}


/////////////////////////////////////////////////////////////////////////////////////////
/**
\param      pData 指针指向输入过程数据
\brief      这个函数将会拷贝从本地内存到ESC内存到硬件
*////////////////////////////////////////////////////////////////////////////////////////
void APPL_InputMapping(Uint16* pData)
{
    Uint16 j = 0;
    Uint8 *pTmpData = (Uint8 *)pData;


   for (j = 0; j < sTxPDOassign.u16SubIndex0; j++)
   {
      switch (sTxPDOassign.aEntries[j])
      {
      /* TxPDO 1 */
      case 0x1A00:
         *pTmpData++ = sDIInputs.switchs;
         break;

      case 0x1A03: // 注意:可能是不对称的字节接近.
         *pTmpData++ = sAI1Inputs.info1 & 0xFF;
         *pTmpData++ = (sAI1Inputs.info1 & 0xFF00) >> 8;
         *pTmpData++ = (sAI1Inputs.info1 & 0xFF0000) >> 16;
         *pTmpData++ = (sAI1Inputs.info1 & 0xFF000000) >> 24;
         *pTmpData++ = sAI1Inputs.info2 & 0xFF;
         *pTmpData++ = (sAI1Inputs.info2 & 0xFF00) >> 8;
         break;
      }
   }
}


### 中断处理程序中使用延时函数的可行性分析 在嵌入式系统开发中,中断处理程序(Interrupt Service Routine, ISR)的设计至关重要。通常情况下,在ISR中直接调用延时函数并不是推荐的做法。 #### 可行性评估 1. **实时性和响应速度** - 延时函数会阻塞当前线程或任务的运行,而在中断上下文中引入这种行为可能导致其他更高优先级的事件被延迟处理。这违背了中断机制的核心目标——快速响应外部事件[^1]。 2. **资源占用** - 大多数操作系统中的`sleep()`或其他类似的延时函数实际上依赖于系统的调度器来切换任务。然而,在中断服务程序中调用这些功能可能会导致不可预测的行为,因为中断本身并不参与常规的任务调度流程[^2]。 3. **硬件特性的影响** - 对于某些特定架构下的微控制器来说,进入等待状态还可能影响外设的工作模式或者功耗管理策略,进一步增加了设计复杂度[^4]。 #### 替代方案探讨 为了克服上述局限并实现类似的功能效果,以下是几种常见的替代方法: 1. **标志位配合轮询机制** ```c volatile uint8_t delayFlag = 0; void interruptHandler(void){ // 设置标志位而非立即执行长时间操作 delayFlag = 1; } void mainLoop(void){ while(1){ if(delayFlag){ DelayMs(5); // 在主循环安全地调用延时函数 ProcessDelayedTask(); delayFlag = 0; } } } ``` 2. **利用定时器溢出触发新中断** 定义一个新的定时器用于创建精确的时间间隔到期后自动唤醒另一个较低级别的中断源完成后续动作[^2]。 3. **软定时器与消息队列结合** 使用RTOS提供的软件定时器组件发送异步通知给目标任务对象去负责实际的数据处理工作流[^3]。 ```python def soft_timer_callback(): os.message_queue.send("process_data", timeout=0) os.software_timer.start(period_ms=5, callback=soft_timer_callback) ``` 以上三种方式均能有效规避直接在中断环境中应用延时所带来的风险隐患的同时保持良好的性能表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值