• 回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

STM32之多任务裸机

       在嵌入式开发中,特别是工业控制中,外设比较多,比如串口通讯这块,有GPS模块,wifi模块,RS485通讯,232通讯。还有十几路的输入采样,8路输出控制,按键事件,串口屏数据实时显示事件等多种事件。大部分都会采用实时操作系统,但是实时操作系统对RAM和ROM都有要求的,所以我们看看在单片机裸奔时如何高效的实现多任务处理。

        其实作为嵌入式工程师来说,大家大部分时间都一直忙碌于公司的项目,没有时间做自己的东西,其实有空时多回头看看自己多年来做的项目,可以把以前的积累沉淀一下。怎么做才能使整个单片机系统的框架更加简洁方便可靠。

总结出单片机大致应用程序的架构有三种:

1. 简单的前后台顺序执行程序,这类写法是大多数人使用的方法,不需用思考程序的具体架构,直接通过执行顺序编写应用程序即可。

2. 时间片轮询法,此方法是介于顺序执行与操作系统之间的一种方法。

3. 操作系统,此法应该是应用程序编写的最高境界。

       如果你能转换一下思路,不再把业务逻辑中各个模块的关系看成基于因果(顺序),而是基于时间,模块间如果需要确定次序可以采用标志位进行同步。那么恭喜你,你已经有了采用实时系统的思想,可以尝试使用RT-thread等操作系统来完成你的项目了。但是,使用操作系统有几个问题,第一是当单片机资源有限的时候,使用操作系统恐怕不太合适;第二是学习操作系统本身有一定的难度,至少你需要花费一定的时间;第三如果你的项目复杂度没有那么高,使用操作系统有点大材小用。
全部回复(7)
正序查看
倒序查看
2021-04-13 15:27

主要分享时间片轮询法

看看时间轮询如何实现单片机逻辑程序的高效执行基于定时器触发,调度效率高,最大化减少无效的代码运行时间将定时器与状态机和伪线程语法融合到一个框架,任务函数可以有两种写法。具体看下怎么写

#ifndef huihui
        #define huihui
        #define MAXTASKS 6   //事件任务的数量,根据需要而定
        extern  volatile unsigned char timers[MAXTASKS];
        #define _SS static unsigned char _lc=0; switch(_lc){default: 
        #define _EE ;}; _lc=0; return 255;
        #define WaitX(tickets)  do {_lc=(__LINE__+((__LINE__%256)==0))%256; return tickets ;} while(0); case (__LINE__+((__LINE__%256)==0))%256: 

        #define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); }  while(0); 
        #define RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} }   //前面的任务优先保证执行

        #define CallSub(SubTaskName) do {unsigned char currdt; _lc=(__LINE__+((__LINE__%256)==0))%256; return 0; case (__LINE__+((__LINE__%256)==0))%256:  currdt=SubTaskName(); if(currdt!=255) return currdt;} while(0);
        #define InitTasks() {unsigned char i; for(i=MAXTASKS;i>0 ;i--) timers[i-1]=0; }
        #define UpdateTimers() {unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}}

        #define SEM unsigned int 
        //初始化信号量
        #define InitSem(sem) sem=0;
        //等待信号量
        #define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0);
        //等待信号量或定时器溢出, 定时器tickets 最大为0xFFFE
        #define WaitSemX(sem,tickets)  do { sem=tickets+1; WaitX(0); if(sem>1){ sem--;  return 1;} } while(0);
        //发送信号量
        #define SendSem(sem)  do {sem=0;} while(0);
#endif//终止

0
回复
2021-04-13 15:33
@lihui710884923
主要分享时间片轮询法看看时间轮询如何实现单片机逻辑程序的高效执行基于定时器触发,调度效率高,最大化减少无效的代码运行时间将定时器与状态机和伪线程语法融合到一个框架,任务函数可以有两种写法。具体看下怎么写#ifndefhuihui      #definehuihui      #defineMAXTASKS6  //事件任务的数量,根据需要而定      extern  volatileunsignedchartimers[MAXTASKS];      #define_SSstaticunsignedchar_lc=0;switch(_lc){default:      #define_EE;};_lc=0;return255;      #defineWaitX(tickets)  do{_lc=(__LINE__+((__LINE__%256)==0))%256;returntickets;}while(0);case(__LINE__+((__LINE__%256)==0))%256:      #defineRunTask(TaskName,TaskID)do{if(timers[TaskID]==0)timers[TaskID]=TaskName();}  while(0);      #defineRunTaskA(TaskName,TaskID){if(timers[TaskID]==0){timers[TaskID]=TaskName();continue;}}  //前面的任务优先保证执行      #defineCallSub(SubTaskName)do{unsignedcharcurrdt;_lc=(__LINE__+((__LINE__%256)==0))%256;return0;case(__LINE__+((__LINE__%256)==0))%256:  currdt=SubTaskName();if(currdt!=255)returncurrdt;}while(0);      #defineInitTasks(){unsignedchari;for(i=MAXTASKS;i>0;i--)timers[i-1]=0;}      #defineUpdateTimers(){unsignedchari;for(i=MAXTASKS;i>0;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255))timers[i-1]--;}}      #defineSEMunsignedint      //初始化信号量      #defineInitSem(sem)sem=0;      //等待信号量      #defineWaitSem(sem)do{sem=1;WaitX(0);if(sem>0)return1;}while(0);      //等待信号量或定时器溢出,定时器tickets最大为0xFFFE      #defineWaitSemX(sem,tickets)  do{sem=tickets+1;WaitX(0);if(sem>1){sem--;  return1;}}while(0);      //发送信号量      #defineSendSem(sem)  do{sem=0;}while(0);#endif//终止

我们这次是5个事件

1.电压采集

2.处理串口屏的数据1

3.处理串口屏的数据2

4.向上位机发送数据

5.AD采集数据上传串口屏

unsigned char task0(){
_SS
  while(1)
  {
         
   WaitX(100);
          FXJ_DYCJ(); /*电压采集*/
  }
_EE
}

unsigned char  task1(){
_SS
  while(1){
   
   WaitX(10);
         Usart2_PeiFangDeal();
  }
_EE
}

unsigned char  task2(){
_SS
  while(1){
   WaitX(100); 
   Usart1_ReceiveDeal();          
  }
_EE
}

unsigned char  task3(){
_SS
  while(1){
   WaitX(100); 
   
   USART2_Send();/*向上位机发送数据*/ 
  }
_EE
}
unsigned char  task4(){
_SS
  while(1){
   WaitX(200); 
    USART1_SendADC();/*向屏上发送采集的电压信号*/
  }
_EE
}
定时这块都是采用滴答定时器中断,1ms为时标void SysTick_Handler(void)
{
     UpdateTimers();
}
每1ms对定时时间执行减一操作
{unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}}
主函数只需要调用事件就可以,可以设置任务函数的优先级执行
int main(void)
{
                BSP_Init();
            InitTasks(); //初始化任务,给TIME清零
                
                while(1)
                {
                   RunTask(task0,0);
           RunTaskA(task1,1);              
           RunTaskA(task2,2); 
           RunTaskA(task3,3); 
           RunTaskA(task4,4);                         
                }
                        
 }

0
回复
iszjt
LV.5
4
2021-07-14 09:55

感谢楼主分享!!!

请问STM32F103可以下载程序但连灯都点不了是为什么?

0
回复
鲁珀特
LV.4
5
2021-07-19 22:38

个人感觉基于STM32的操作系统用起来不是那么方便,还是时间片轮询的方式更能接近底层。

1
回复
airwill
LV.2
6
2021-07-27 14:30

STM32之多任务裸机,  本文内容和题目好像切合度不高。

怎么做才能使整个单片机系统的框架更加简洁方便可靠。这一点值得赞赏,做完一个项目回头想一想,怎样才能更好,是学习进步的好方法。

多任务和裸机是个矛盾的说法。因为任务一词已经再 OS 里严格定义。那就不妨理解成需要处理的多个事务吧

0
回复
2021-07-30 15:53
@airwill
STM32之多任务裸机, 本文内容和题目好像切合度不高。怎么做才能使整个单片机系统的框架更加简洁方便可靠。这一点值得赞赏,做完一个项目回头想一想,怎样才能更好,是学习进步的好方法。多任务和裸机是个矛盾的说法。因为任务一词已经再OS里严格定义。那就不妨理解成需要处理的多个事务吧

看自己理解了,没啥,就是多个事件,裸机跑

0
回复
2021-08-05 18:49
@lihui710884923
主要分享时间片轮询法看看时间轮询如何实现单片机逻辑程序的高效执行基于定时器触发,调度效率高,最大化减少无效的代码运行时间将定时器与状态机和伪线程语法融合到一个框架,任务函数可以有两种写法。具体看下怎么写#ifndefhuihui      #definehuihui      #defineMAXTASKS6  //事件任务的数量,根据需要而定      extern  volatileunsignedchartimers[MAXTASKS];      #define_SSstaticunsignedchar_lc=0;switch(_lc){default:      #define_EE;};_lc=0;return255;      #defineWaitX(tickets)  do{_lc=(__LINE__+((__LINE__%256)==0))%256;returntickets;}while(0);case(__LINE__+((__LINE__%256)==0))%256:      #defineRunTask(TaskName,TaskID)do{if(timers[TaskID]==0)timers[TaskID]=TaskName();}  while(0);      #defineRunTaskA(TaskName,TaskID){if(timers[TaskID]==0){timers[TaskID]=TaskName();continue;}}  //前面的任务优先保证执行      #defineCallSub(SubTaskName)do{unsignedcharcurrdt;_lc=(__LINE__+((__LINE__%256)==0))%256;return0;case(__LINE__+((__LINE__%256)==0))%256:  currdt=SubTaskName();if(currdt!=255)returncurrdt;}while(0);      #defineInitTasks(){unsignedchari;for(i=MAXTASKS;i>0;i--)timers[i-1]=0;}      #defineUpdateTimers(){unsignedchari;for(i=MAXTASKS;i>0;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255))timers[i-1]--;}}      #defineSEMunsignedint      //初始化信号量      #defineInitSem(sem)sem=0;      //等待信号量      #defineWaitSem(sem)do{sem=1;WaitX(0);if(sem>0)return1;}while(0);      //等待信号量或定时器溢出,定时器tickets最大为0xFFFE      #defineWaitSemX(sem,tickets)  do{sem=tickets+1;WaitX(0);if(sem>1){sem--;  return1;}}while(0);      //发送信号量      #defineSendSem(sem)  do{sem=0;}while(0);#endif//终止

楼主写代码还是换行好看,好分析,避免出错不易查找。

0
回复