Embedded 如何最好地将遗留轮询嵌入式固件体系结构转换为事件驱动的体系结构?

Embedded 如何最好地将遗留轮询嵌入式固件体系结构转换为事件驱动的体系结构?,embedded,polling,freertos,firmware,event-driven,Embedded,Polling,Freertos,Firmware,Event Driven,我有一系列嵌入式产品运行典型的基于主循环的固件,有150+k行代码。通过硬件中断处理程序、计时器轮询和(想想协同例程)的组合,可以实现复杂的时序关键特性负载。实际上,protothreads轮询性能很好,“only”是模拟多线程伪并行调度(无止境循环)的语法糖。我一直在固件中添加错误修复和扩展。该领域大约有30k台设备,硬件类型和版本略有不同,约有7种 对于新产品系列成员,我只需要在新产品上集成一个基于FreeRTOS的外部项目,而所有旧产品都需要进一步的功能和改进 为了不必将整个复杂的遗留固件

我有一系列嵌入式产品运行典型的基于主循环的固件,有150+k行代码。通过硬件中断处理程序、计时器轮询和(想想协同例程)的组合,可以实现复杂的时序关键特性负载。实际上,protothreads轮询性能很好,“only”是模拟多线程伪并行调度(无止境循环)的语法糖。我一直在固件中添加错误修复和扩展。该领域大约有30k台设备,硬件类型和版本略有不同,约有7种

对于新产品系列成员,我只需要在新产品上集成一个基于FreeRTOS的外部项目,而所有旧产品都需要进一步的功能和改进

为了不必将整个复杂的遗留固件移植到FreeRTOS,而冒着破坏完美产品的风险,我计划让旧固件在FreeRTOS任务中运行。在旧产品上,固件在没有FreeRTOS的情况下仍不能运行。在FreeRTOS任务中,传统固件将消耗所有可用的处理器时间,因为其底层实现方案是基于轮询的。由于它使用prothreads(基于后台的计时器和硬件轮询)并在自由运行的处理器计数器寄存器上进行轮询,我希望能够将轮询转换为事件驱动行为

以下是两个例子:




因此,我得到的印象是,我可以将这两种编程模式(例如,通过宏魔术和FreeRTOS功能)转换为一种基于事件的底层方案

那么现在我的问题是:有人已经做过这样的事情了吗?是否有模式和/或最佳实践可遵循

[更新]

感谢您的详细回复。让我评论一些细节:我需要结合“基于多线程模拟的遗留固件(使用coo例程实现protothreads)”和一个基于FreeRTOS的项目,该项目由两个相互作用的FreeRTOS任务组成。其想法是让完整的旧固件在其自己的RTOS任务中运行,而不是在其他新任务中运行。我了解RTOS原则和模式(抢占、资源共享、阻塞操作、信号、信号量、互斥、邮箱、任务优先级等)。我计划将新旧部件的交互完全建立在这些机制的基础上。我想要的是1)如何以半自动化的方式转换传统固件(150k+LOC),以便上面介绍的忙等待/轮询方案在RTOS任务内部运行时使用新机制,或者在构建和运行当前主循环类型的固件时使用旧方法。旧代码的完全重写/完整端口不是选项。2) 更多的想法如何教导旧的固件实现,该实现用于在RTOS任务的新牢笼中保持良好的性能,而不仅仅是消耗所有可用的CPU周期(当赋予最高优先级时)或在不以最高RTOS优先级运行时产生新的大实时延迟


我想这里没有人已经做过这么特别的助教了。因此,我只需要做艰苦的工作,一个接一个地解决所有描述的问题。

您面临两大难题

  • 由于旧代码是使用protothreads(协程)实现的,因此它们之间从来没有任何异步资源争用。如果将这些任务拆分为FreeRTOS任务,则会出现抢占式调度任务切换;这些切换可能发生在protothreads不期望的地方,使数据或其他资源处于不一致的状态
  • 如果您将任何protothreads的PT_WAIT调用转换为FreeRTOS中的实际等待,那么该调用将真正阻塞。但是protothreads假设其他protothreads在被阻塞时继续
  • 因此,#1意味着不能仅仅将protothreads转换为任务,#2意味着必须将protothreads转换为任务(如果使用FreeRTOS阻塞原语,如xEventGroupWaitBits())


    最直接的方法是将所有的原线程放在一个任务中,并在该任务中继续轮询。

    您面临两个大难题

  • 因为旧代码是使用protothreads(协程)实现的,它们之间从不存在任何异步资源争用。如果将这些任务拆分为FreeRTOS任务,则会出现抢占式调度任务切换;这些切换可能发生在原线程不期望的位置,使数据或其他资源处于不一致的状态
  • 如果您在FreeRTOS中将任何一个protothreads的PT_WAIT调用转换为实际的等待,那么该调用将真正阻塞。但是protothreads假设其他protothreads在阻塞时继续
  • 因此,#1意味着不能仅仅将protothreads转换为任务,#2意味着必须将protothreads转换为任务(如果使用FreeRTOS阻塞原语,如xEventGroupWaitBits())


    最直接的方法是将所有的原线程放在一个任务中,并在该任务中继续轮询。

    在RTOS中,您创建并运行任务。如果您没有运行多个任务,则使用RTOS没有什么优势

    我不使用FreeRTOS(但已经使用),但以下内容适用于任何RTOS,并且是伪代码,而不是特定于FreeRTOS API的-许多细节(如任务优先级和堆栈分配)故意丢失

    首先,在大多数简单的RTO(包括FreeRTO)中,
    main()
    用于硬件初始化、任务创建和计划程序启动:

    int main( void )
    {
        // Necessary h/w & kernel initialisation
        initHardware() ;
        initKernel() ;
    
        // Create tasks
        createTask( task1 ) ;
        createTask( task2 ) ;
    
        // Start scheduling
        schedulerStart() ;
    
        // schedulerStart should not normally return
        return 0 ;
    }
    
    现在,让我们假设您的第一个示例是在
    task1
    中实现的。典型的RTO将同时具有计时器和延迟功能。最简单的使用方法是延迟,这适用于保证周期处理花费较少时间的情况
    // second example: wait for hardware event
    setup_hardware();
    PT_WAIT_UNTIL(hardware_ready(), pt);
    
    // hardware is ready, do something else
    
    int main( void )
    {
        // Necessary h/w & kernel initialisation
        initHardware() ;
        initKernel() ;
    
        // Create tasks
        createTask( task1 ) ;
        createTask( task2 ) ;
    
        // Start scheduling
        schedulerStart() ;
    
        // schedulerStart should not normally return
        return 0 ;
    }
    
    void task1()
    {
        // do something every 100 ms
        for(;;)
        {
            delay( 100 ) ; // assuming 1ms tick period
    
            // something
            ...
        }
    }
    
    void task1()
    {
        // do something every 100 ms
        TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
        for(;;)
        {
            timerWait() ;     
    
            // something
            ...
        }
    }
    
    createSemaphore( hardware_ready ) ;
    
    // Init hardware
    ...
    
    // Tell waiting task hardware ready
    semaphoreGive( hardware_ready ) ;
    
    void task2()
    {
        // wait for hardware ready
        semaphoreTake( hardware_ready ) ;
    
        // do something else
        for(;;)
        {
            // This loop must block is any lower-priority task
            // will run.  Equal priority tasks may run is round-robin
            // scheduling is implemented. 
            ...
        }
    }