解决程序堵塞的优化方法(一)

      刚开始学习编写嵌入式的代码,例如在单片机上控制LED灯500ms亮灭,因为属于初学,对于MCU运行效率没有要求,所以大部分的教程都是delay_ms(500)。平时在一些基本调试中,对我们影响很小。但是我们需要了解,当程序使用大量类似delay形式的函数时,会对程序造成严重阻塞。


         以delay延时函数为例:一般写程序,都是通过while()或者for()加上条件判断,进行循环累加,如果延时函数执行过程中,有中断响应执行,但是当中断服务函数执行完毕,程序跳回后台主循环执行的时候,由于延时函数里面是条件循环,在条件没有符合要求的时候,程序将一直堵塞到延时函数,无法执行响应中断处理函数的次级函数。

    e.g. 曾经在学校项目中,在控制电机的程序中对于匀加速匀减速的部分添加了delay函数,实际设备运行起来后,别说匀加/减速,就是加速减速变化都被阻塞严重,严重影响了操作的体验。所以下面介绍一下我操作的几种解决阻塞的程序实现。

 

 

/*while形式*/
void Delay_ms(u16 nms)
{         
  while(nms--)
  {
    u32 temp;       
    SysTick->LOAD=(u32)1*fac_ms;        
    SysTick->VAL =0x00;              
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  
    do
    {temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));    
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;  
    SysTick->VAL =0X00;                 
  }
}

​
/*for 形式*/
void delay_ms(void)
​
/{
   for(u8 i =0;i<230;i++)
    for(int j =0;j<320;j++);
}

总述       

1.通过运用RTOS,线程里面调用系统提供的延时等函数,实现解决阻塞。

2.全局变量方法,定时器计数条件判断替代延时。

3.使用状态机方式,分解动作,替代延时实现。

4.使用链表式,与第三种相似。

一、 RTOS方法

      诚然现在RTOS很火,大家可以使用各种实时操作系统,利用优化后的delay函数,这个时候我们可以随便调用delay而不用担心阻塞,因为在RTOS中,在线程使用系统提供的delay函数,线程会被内核挂起,系统会自动切换到其他优先级别更高的线程运行。但是一定记住不是所有程序都适合RTOS。

    参照其他人的博文:“如果程序任务可折分性较差,折分后的各个任务之间有 N 多的同步问题和复用资源问题,此时不要用多任务操作系统,我们不能因为任务而任务,任务多并没有想象的好,多任务是用降低实时性来换取软件开发的独立性,不要被实时多任务操作系统的实时两个字骗了,这个实时只是相对于其它非实时性多任务操作系统来讲的,实时性最高的当然是你自己编写的单任务程序。”


    RTOS的延时代码做了底层的变化,和我们写的while等循环代码有很大差别。RTOS延时函数进入之后会将需要延时的线程挂起进入阻塞态,底层时钟节拍计数器进行定时,直至预约的延时时间到来,再利用任务调度器唤醒线程,线程按照RTOS的优先级执行。

1.RT_Thread 延时代码部分

                            

                           

2.U/COS III 延时代码部分

                               

                                  

 

3.FreeRTOS 延时代码部分

                                 

                                         

各大RTOS官网资料

可以按照“RTOS”关键词自行搜索,包括社区资料,源码以及开发手册的提供。下面展示国产操作系统rt_thread官网界面。

                              

正点原子与野火等资料

可以按照”正点原子“关键词自行搜索,包括正点原子的BBS以及各大视频网站都有其资料。

                                

 

                                 

一、 全局变量法

       全局变量的方法其实与时间管理的思路很像,将设置变量放置于定时器,然后进行计数器累加,再在主循环或者可以进行反复触发的函数中,通过对此变量数值的判断,从而实现等量delay函数的执行。实现方法也很简单,定义一个变量放置于定时器即可,剩下的通过变量值除以间隔时间来判断时间长短,再进行执行相应的函数。

    虽然此种方法比较简单,但是需要大家注意,通过设置全局变量法进行替代delay函数,只能放置于主循环或者多触发的程序执行部分,如果放置在初始化的部分,则没有实际用处。

代码实现部分 ->注释部分需要重点观看

/*定义一个全局变量*/
static u32 G_timer = 0;
/*定时器的时间间隔由用户自己把握,此处为了计算方便,定时器间隔设置为 1ms*/
void TIM_IRQ(void)
{
  G_timer++;
  if(G_timer % 100 == 0)/*计数器每 100ms 进入执行*/
  {
    taskfun();/*此处函数不可以执行复杂运算,只可以实现简单的IO控制,
              以及一些基本的变量赋值、加减的。
              如果程序过于复杂,会导致此处中断服务函数造成堵塞*/
  }
}
/*主程序*/
int main(int argc,char** argv)
{
  SystemInit();
  static u32 lastcnt1 =0 ,lastcnt2 =0 ;
  while(true)
  {
    if(G_timer % 100 == 0)/*此处不能使用如此函数执行,因为主循环中,
                          一般晶振频率很高,程序在主循环中执行的间隔小于
                          计数器执行的频率,也就是计数器计一个数,程序
                          可能执行多次。所以这种方法不能使用,建议使用下面的方法*/
    {
      taskfun1();
    }  
    if(G_timer % 1000 == 0)/*计数器每 1000ms或者叫1s 进入执行*/
    {
      taskfun2();/*此处可以执行复杂的运算,不需要担忧阻塞*/
       lastcnt1 = G_timer ;
    } 
    if(G_timer % 10000 == 0)/*计数器每 10000ms或者叫10s 进入执行*/
    {
      taskfun3();/*注释与taskfun2();相同*/
      lastcnt2 = G_timer ;            
    } 
  }
}

     到现在介绍了两种方法,因为篇幅有限,过长的文字不适合大家一次性阅读,我会在下一篇文章继续分享,解决堵塞的第三种及第四种方法,希望可以帮助到大家,谢谢。

 

                                                                     

                                                                                   扫码关注更多精彩

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章