结合时间触发+消息+protothread思想+支持优先级的非抢占调度器

2019-12-13 18:20发布

本帖最后由 summarize 于 2013-5-19 14:47 编辑

废话少说,先上stm8s103 IAR库工程代码压缩包。
Schedule-IAR-STM8S103.rar (416.35 KB, 下载次数: 538) 2013-5-19 14:15 上传 点击文件名下载附件

工程是在stm8s103f3单片机上调度通过,已经用消息实现了 UART1_TX模块的共享,即UART1_RX接收到的数据+0x11后再通过UART_TX模块发送回去,同时ADC1 通道3的转换结果也通过UART1_TX模块发送出去.见下图

1.ADC1转换结果每1秒上传一次到PC。测试式给ADC1通道3供的是5V电,所以结果是0x03ff.即1023.

共享时接收到的数据.png (61.3 KB, 下载次数: 0) 下载附件 2013-5-19 14:16 上传

3.支持非抢占式优先级调度,优先级顺序就是创建任务时的顺序,由高到底。其实现思想是,每一个任务运行结束后,都重新回到第一个创建的任务处按顺序查找某个任务是否满足运行条件,所以先创建的任务会先被“发现”其满足运行条件并运行之,核心代码如下

a.任务控制块数据结构
  1. struct SchTcb
  2. {
  3. #if SCH_CFG_Q_EN > 0u
  4.   void          *pData;       //消息指针
  5.   SCH_UINT8 Size;         //消息大小
  6. #endif

  7.   SCH_DLY_TYPE        TimeCounter;  //定时计数器,时基为 "SCH_SYS_TICKS_MS"
  8.   void          (*pTask)();   //任务指针
  9.   struct SchTcb *pNextTCB;    //下一个任务控制块指针
  10. };
复制代码b.调度核心
  1. void SCHTaskSchedStart(void)
  2. {
  3. SCHED_SART:
  4.        
  5.   pCurTCB = pFirstTCB;                        //指向第一个创建的任务,之后按创建时的顺序执行下去

  6.   while (1)                                 
  7.   {
  8.     SCHTimeTick();                            //如果任务Tick满足条件,则将其置于可执行状态

  9.     if (SCH_TASK_RUN == pCurTCB->TimeCounter) //任务处于可执行状态
  10.     {
  11.       pCurTCB->TimeCounter = SCH_TASK_PEND;   //设置为挂起状态,保证任务只执行一次

  12.       pCurTCB->pTask();                       //执行当前任务控制块指向的任务

  13.       goto SCHED_SART;                        //每执行完一个任务,都重新查找一次可执行最高优先级任务
  14.     }

  15.     pCurTCB = pCurTCB->pNextTCB;              //指向下一个任务控制块,查找下个任务是否可执行
  16.   }
  17. }
复制代码“schedule.c”和"schedule.h"已经设置为只读属性,无特殊情况不建议修改,"sch_cfg.h"则为开放给用户的接口,可定义数据类型、调度器节拍和配置是否使用消息。

本人水平有限,欢迎大家测试、指正不足。

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
101条回答
summarize
1楼-- · 2019-12-14 14:30
ppdd 发表于 2014-6-20 21:18
我想我也说不明白的,C太弱了!

第一个:就是Ke编译il有警告,同时串口不稳定,经常不能自动发命令IAP,  ...

最新版本调度器V2.0的51单片机KEIL版本出来了。

请移步:

http://www.amobbs.com/thread-5589227-1-1.html
jlhgold
2楼-- · 2019-12-14 17:44
 精彩回答 2  元偷偷看……
summarize
3楼-- · 2019-12-14 19:46
为了方便大家,接着把UART_TX模块的资源共享相关部分贴出来,分别是UART_RX_ISR,UART接收处理任务,UART发送处理任务和ADC转换结果上传PC任务.

1.UART_RX_ISR: 串口接收中断.
  1. INTERRUPT_HANDLER(UART1_RX_IRQHandler, 18)
  2. {
  3.   /* In order to detect unexpected events during development,
  4.      it is recommended to set a breakpoint on the following instruction.
  5.   */
  6.   static uint8_t s_u8IsrRxBuffer[UART_ISR_RX_BUF_SIZE];
  7.   static uint8_t s_u8IsrRxCounter;

  8.   s_u8IsrRxBuffer[s_u8IsrRxCounter] = UART1_ReceiveData8();

  9.   if (s_u8IsrRxBuffer[s_u8IsrRxCounter] == 0xaa) //for test
  10.   {
  11.     s_u8IsrRxCounter++;
  12.     if (SCHTaskGetQFree(&UartRxTcb) == SCH_Q_FREE)
  13.     {
  14.       SCHTaskQpost(&UartRxTcb,
  15.                    &s_u8IsrRxBuffer[0],
  16.                    s_u8IsrRxCounter);
  17.     }
  18.     else
  19.     {
  20.       //出错处理
  21.     }

  22.     s_u8IsrRxCounter = 0;
  23.   }
  24.   else
  25.   {
  26.     s_u8IsrRxCounter++;
  27.   }

  28.   if (s_u8IsrRxCounter > UART_RX_BUF_SIZE)
  29.   {
  30.     s_u8IsrRxCounter = 0;
  31.     //溢出出错处理
  32.   }

  33.   UART1_ClearFlag(UART1_FLAG_RXNE);
  34. }
复制代码2.UART接收处理任务: 将接收到的数据+0x11后交给UART_TX发送到PC.
  1. void vUartReceiveData(void)
  2. {
  3.   static uint8_t s_u8RxBuffer[UART_RX_BUF_SIZE];
  4.   static uint8_t s_u8RxCounter;

  5.   static uint8_t *pDataBuffer, u8DataSize;
  6.   SCHTaskBegin();

  7.   while (1)
  8.   {
  9.     SCHTaskQpend();           //任务等待接收中断发来消息

  10.     pDataBuffer = (uint8_t *)UartRxTcb.pData;
  11.     u8DataSize  = UartRxTcb.Size;

  12.     if (u8DataSize > UART_RX_BUF_SIZE)
  13.     {
  14.       u8DataSize = UART_RX_BUF_SIZE;
  15.       //出错处理
  16.     }

  17.     for (s_u8RxCounter = 0; s_u8RxCounter < u8DataSize; s_u8RxCounter++)
  18.     {
  19.       s_u8RxBuffer[s_u8RxCounter] = (*pDataBuffer) + 0x11; //copy to s_u8TxBuffer (add 0x11)
  20.       pDataBuffer++;
  21.     }

  22.     //使用如下类似方法便可与其它任务共享UART_TX资源,即任务要使用UART_TX前先检查其队列是否可用,不可用则等待
  23.     for (u8DataSize = 0; u8DataSize < 255; u8DataSize++)  //借用u8DataSize
  24.     {
  25.       if (SCHTaskGetQFree(&UartTxTcb) == SCH_Q_FREE)      //检查UART_TX发送任务队列是否可用
  26.       {
  27.         SCHTaskQpost(&UartTxTcb,
  28.                      &s_u8RxBuffer[0],
  29.                      s_u8RxCounter); //将接收的的数据+0x11后发回去
  30.         break;
  31.       }
  32.       else
  33.       {
  34.         SCHCurTaskDly(1 / SCH_SYS_TICKS_MS);  //delay 1ms
  35.       }
  36.     }

  37.     if (u8DataSize >= 100)
  38.     {
  39.       //出错处理
  40.     }
  41.   }

  42.   SCHTaskEnd();
  43. }
复制代码3.UART发送任务: 将其它任务发来的消息发送到PC.
  1. void vUartSendData(void)
  2. {
  3.   static uint8_t s_u8TxBuffer[UART_TX_BUF_SIZE];
  4.   static uint8_t i;
  5.   static uint8_t *pDataBuffer, u8DataSize;

  6.   SCHTaskBegin();

  7.   while (1)
  8.   {
  9.     SCHTaskQpend();           //任务等待消息

  10.     pDataBuffer = (uint8_t *)UartTxTcb.pData;
  11.     u8DataSize  = UartTxTcb.Size;

  12.     if (u8DataSize > UART_TX_BUF_SIZE)
  13.     {
  14.       u8DataSize = UART_TX_BUF_SIZE;
  15.       //出错处理
  16.     }

  17.     for (i = 0; i < u8DataSize; i++)
  18.     {
  19.       s_u8TxBuffer[i] = *pDataBuffer; //copy to s_u8TxBuffer
  20.       pDataBuffer++;
  21.     }
  22.    
  23.     for (i = 0; i < u8DataSize; i++)
  24.     {
  25.       if (UART1_GetFlagStatus(UART1_FLAG_TXE) == SET)
  26.       {
  27.         UART1_SendData8(s_u8TxBuffer[i]);
  28.         //        UART1_ITConfig(UART1_IT_TXE, ENABLE);
  29.       }

  30.       SCHCurTaskDly(1 / SCH_SYS_TICKS_MS);        //延时发送1个字节所须要的时间
  31.     }
  32.   }

  33.   SCHTaskEnd();
  34. }
复制代码4.ADC转换结果发送任务: 将ADC转换任务传来的相关ADC数据交给UART_TX发送到PC.

void vSendAdcResult(void)
{
  static uint8_t s_u8AdcBuffer[ADC_BUF_SIZE];
  static uint8_t i;
  static uint8_t *pDataBuffer, u8DataSize;

  SCHTaskBegin();

  while (1)
  {
    SCHCurTaskDly(1000 / SCH_SYS_TICKS_MS);   //delay 1000ms

    SCHTaskQpend();           //任务等待消息

    pDataBuffer = (uint8_t *)SendAdcResultTcb.pData;
    u8DataSize  = SendAdcResultTcb.Size;

    if (u8DataSize > ADC_BUF_SIZE)
    {
      u8DataSize = ADC_BUF_SIZE;
      //出错处理
    }

    for (i = 0; i < u8DataSize; i++)
    {
      s_u8AdcBuffer = *pDataBuffer; //copy to s_u8AdcBuffer
      pDataBuffer++;
    }

    //使用如下类似方法便可与其它任务共享UART_TX资源,即任务要使用UART_TX前先检查其队列是否可用,不可用则等待
    for (u8DataSize = 0; u8DataSize < 255; u8DataSize++)  //借用u8DataSize
    {
      if (SCHTaskGetQFree(&UartTxTcb) == SCH_Q_FREE)      //检查UART_TX发送任务队列是否可用
      {
        SCHTaskQpost(&UartTxTcb,
                     &s_u8AdcBuffer[0],
                     i);
        break;
      }
      else
      {
        SCHCurTaskDly(1 / SCH_SYS_TICKS_MS);  //delay 1ms
      }
    }

    if (u8DataSize >= 100)
    {
      //出错处理
    }
  }

  SCHTaskEnd();
}
summarize
4楼-- · 2019-12-14 20:50
无人问津?白花了一个多星期时间了。冏……
NIC
5楼-- · 2019-12-14 23:53
支持一下楼主,但现在实在没时间细细品味
summarize
6楼-- · 2019-12-15 01:38
NIC 发表于 2013-5-22 20:27
支持一下楼主,但现在实在没时间细细品味


谢谢!我倒是觉得是不是写得不够清楚明白或是组织得不好,好吧,我语文是体育老师都教的。那再把调度器所有内容贴出来:

首先是“sch_cfg.h”
  1. #ifndef __SCH_CFG_H
  2. #define __SCH_CFG_H

  3. #include "stm8s.h"

  4. //定义数据类型
  5. typedef unsigned char SCH_UINT8;
  6. typedef unsigned int  SCH_UINT16;

  7. //调度器节拍与硬件系统定时器相关定义
  8. #define SCH_SYS_TICKS_MS             1                 //定义调度系统时钟节拍时间(ms),无特殊情况不建议更改此项
  9. #define SCH_HW_TIM_MS                     0.1                 //硬件定时器中断(溢出)周期(ms),此项根据实际系统调整
  10. #define SCH_TIM_TO_TICKS_CMP        (SCH_UINT8)(SCH_SYS_TICKS_MS/SCH_HW_TIM_MS)        //硬件定时器到系统节拍计数比较值


  11. //定义可裁剪部分
  12. #define SCH_CFG_Q_EN    1u  /* 任务内建消息使能 */



  13. #endif      //__SCH_CFG_H
复制代码然后是“schedule.h”
  1. #ifndef __SCHEDULE_H
  2. #define __SCHEDULE_H

  3. #include "sch_cfg.h"


  4. //U16延时节拍不能大于65534,即任务最大延时时间65534*SCH_SYS_TICKS_MS(1ms)=65.534S
  5. #define SCH_DLY_TYPE           SCH_UINT16

  6. #define SCH_MAX_TASKS   255                               //任务数量,最大为255个。

  7. #if        SCH_MAX_TASKS <= 255
  8.   #define SCH_MAX_TASK_TYPE          SCH_UINT8                        //最大任务数<=255时定义为u8
  9. #else
  10.   #define SCH_MAX_TASK_TYPE          SCH_UINT16          //最大任务为>255则定义为u16
  11. #endif

  12. #define SCH_TASK_RUN                                        0
  13. #define SCH_TASK_PEND                                        (SCH_DLY_TYPE)0xffff

  14. #define SCH_CURR_LINE                (SCH_UINT8)(__LINE__+(!__LINE__))                                    //定义当前行号的具体实现
  15. #define SCHTaskBegin()        static SCH_UINT8 SchLc=0; switch(SchLc){ case 0://跳转开始,中间可插入延时,调用子函数;(适用主/子函数)
  16. #define SCHTaskEnd()          ;}; SchLc=0;                                                                                           //跳转结束

  17. struct SchTcb
  18. {
  19. #if SCH_CFG_Q_EN > 0u
  20.   void          *pData;       //消息指针
  21.   SCH_UINT8                 Size;         //消息的大小
  22. #endif

  23.         SCH_DLY_TYPE        TimeCounter;  //定时计数器,时基为 "SCH_SYS_TICKS_MS"
  24.   void          (*pTask)();   //任务指针
  25.   struct SchTcb *pNextTCB;    //下一个任务控制块指针
  26. };

  27. typedef struct SchTcb SCH_TCB;



  28. //-----------------------------------操作作当前任务及调用子任务------------------------------------------

  29. //挂起(暂停)当前任务,即任务自身
  30. #define SCHCurTaskPend() {SchLc=SCH_CURR_LINE;pCurTCB->TimeCounter=SCH_TASK_PEND;}return;case SCH_CURR_LINE:                                                                     

  31. //当前任务延时X个时间节拍后恢复
  32. #define SCHCurTaskDly(Ticks) {SchLc=SCH_CURR_LINE;pCurTCB->TimeCounter=Ticks;}return ;case SCH_CURR_LINE:

  33. //任务内调用子任务
  34. #define SCHTaskCallSub(SubTaskName)                                                     
  35. {                                                                                       
  36.   {SchLc=SCH_CURR_LINE;;pCurTCB->TimeCounter=SCH_TASK_RUN;}return;case SCH_CURR_LINE:   
  37.   SubTaskName();                                                                        
  38.   if (pCurTCB->TimeCounter != SCH_TASK_RUN)                                             
  39.     return ;                                                                           
  40. }

  41. //----------------------------------------消息-------------------------------------------
  42. #if SCH_CFG_Q_EN > 0u

  43. #define SCH_Q_FREE  1
  44. #define SCH_Q_BUSY  0

  45. //等待消息
  46. #define SCHTaskQpend() {SchLc=SCH_CURR_LINE;pCurTCB->TimeCounter=SCH_TASK_PEND;pCurTCB->pData=(void *)0;}return;case SCH_CURR_LINE:
  47. //释放消息
  48. extern void SCHTaskQpost(SCH_TCB   *pPostTCB,
  49.                          void      *pData,
  50.                          SCH_UINT8 Size);
  51. //获取消息队列状态
  52. extern SCH_UINT8 SCHTaskGetQFree(SCH_TCB   *pTaskTCB);
  53. #endif


  54. extern SCH_UINT8 g_u8SchedTicksCnt;
  55. extern SCH_TCB   *pCurTCB;

  56. //extern void SCHTaskPend(SCH_TCB *pTaskPendTCB); //不常用的功能,须要时可去除注释
  57. //extern void SCHTaskResume(SCH_TCB *pTaskTCB);
  58. //extern void SCHTaskDly(SCH_TCB *pTaskDlyTCB, SCH_DLY_TYPE Ticks);


  59. extern void SCHTimeTick(void);
  60. extern void SCHTaskSchedStart();

  61. extern void SCHTaskCreate( SCH_TCB      *pNewTCB,
  62.                            void         (*pNewTask)());


  63. #endif        //__SCHEDULE_H
复制代码最后是“schedule.c”

  1. #include "schedule.h"

  2. SCH_TCB *pFirstTCB, *pCurTCB;

  3. static SCH_MAX_TASK_TYPE TaskNumberSum = 0;
  4. SCH_UINT8 g_u8SchedTicksCnt = 0;

  5. /* 任务节拍处理 */
  6. void SCHTimeTick(void)
  7. {
  8.   SCH_MAX_TASK_TYPE i;

  9.   SCH_TCB *pTCB;

  10.   if (g_u8SchedTicksCnt >= SCH_TIM_TO_TICKS_CMP)
  11.   {
  12.     g_u8SchedTicksCnt -= SCH_TIM_TO_TICKS_CMP;
  13.    
  14.     pTCB = pFirstTCB;
  15.     for (i = 0; i < TaskNumberSum; i++)
  16.     {
  17.       if ((pTCB->TimeCounter != SCH_TASK_PEND)
  18.           && (pTCB->TimeCounter > 0))
  19.       {
  20.         pTCB->TimeCounter--;
  21.       }

  22.       pTCB = pTCB->pNextTCB;
  23.     }
  24.   }
  25. }


  26. /* 任务创建,调试时可在任务创建失败处放置断点 */
  27. void SCHTaskCreate(SCH_TCB           *pNewTCB,
  28.                    void              (*pNewTask)(void))
  29. {
  30.   if (TaskNumberSum == 0)
  31.   {
  32.     pFirstTCB = pNewTCB;                    //备份第一个任务控制块地址
  33.     pNewTCB->pTask = pNewTask;              //新创建的任务控制块的函数指针指向新创建的任务
  34.     pCurTCB   = pNewTCB;                    //最新任务控制块地址给当前任务控制块指针
  35.     pCurTCB->pNextTCB = pCurTCB;            //因为只有一个任务,所以指令的下一个任务控制块地址就是自己
  36.   }
  37.   else if (TaskNumberSum < SCH_MAX_TASKS)
  38.   {
  39.     pNewTCB->pTask = pNewTask;              //新创建的任务控制块的函数指针指向新创建的任务
  40.     pNewTCB->pNextTCB = pCurTCB->pNextTCB;  //当前任务控制块指向的下一个任务控制块由新建的任务控制块来指向
  41.     pCurTCB->pNextTCB = pNewTCB;            //当前任务控制块指向的下一个任务控制块更新为新建的任务
  42.     pCurTCB = pNewTCB;                      //新建的任务控制块更新为当前任务控制块
  43.   }
  44.   else
  45.   {
  46.     TaskNumberSum--;                        //任务创建失败,调试时可在此放置断点
  47.   }

  48.   TaskNumberSum++;
  49. #if SCH_CFG_Q_EN > 0u
  50.   pNewTCB->pData    = (void *)0;
  51.   pNewTCB->Size = 0;
  52. #endif
  53. }


  54. void SCHTaskSchedStart(void)
  55. {
  56. SCHED_SART:
  57.        
  58.   pCurTCB = pFirstTCB;                        //指向第一个创建的任务,之后按创建时的顺序执行下去

  59.   while (1)                              
  60.   {
  61.     SCHTimeTick();                            //如果任务Tick满足条件,则将其置于可执行状态

  62.     if (SCH_TASK_RUN == pCurTCB->TimeCounter) //任务处于可执行状态
  63.     {
  64.       pCurTCB->TimeCounter = SCH_TASK_PEND;   //设置为挂起状态,保证任务只执行一次

  65.       pCurTCB->pTask();                       //执行当前任务控制块指向的任务

  66.       goto SCHED_SART;                        //每执行完一个任务,都重新查找一次可执行最高优先级任务
  67.     }

  68.     pCurTCB = pCurTCB->pNextTCB;              //指向下一个任务控制块,查找下个任务是否可执行
  69.   }
  70. }

  71. ////----------------------------------操作指定任务,不常用---------------------------------------------------
  72. ////操作当前任务及调用子任务在"schedule.h"中
  73. //
  74. ////挂起(暂停)指定任务
  75. //void SCHTaskPend(SCH_TCB *pTaskPendTCB)
  76. //{
  77. //  pTaskPendTCB->TimeCounter = SCH_TASK_PEND;
  78. //}
  79. //
  80. ////恢复指定任务(运行)
  81. //void SCHTaskResume(SCH_TCB *pTaskResumeTCB)
  82. //{
  83. //  pTaskResumeTCB->TimeCounter = SCH_TASK_RUN;
  84. //}
  85. //
  86. ////指定任务延时X个时间节拍后恢复
  87. //void SCHTaskDly(SCH_TCB *pTaskDlyTCB, SCH_DLY_TYPE Ticks)
  88. //{
  89. //  pTaskDlyTCB->TimeCounter = Ticks;
  90. //}

  91. //---------------------------------消息-----------------------------------------
  92. //释放消息
  93. #if SCH_CFG_Q_EN > 0u

  94. void SCHTaskQpost(SCH_TCB   *pPostTCB,
  95.                   void      *pData,
  96.                   SCH_UINT8 Size)
  97. {
  98.   pPostTCB->pData = pData;
  99.   pPostTCB->Size  = Size;
  100.   pPostTCB->TimeCounter = SCH_TASK_RUN;
  101. }

  102. //查询消息列队状态,是否是自由(可用)或忙(不可用),调用SCHTaskQpend()时会将其设置为自由状态
  103. SCH_UINT8 SCHTaskGetQFree(SCH_TCB   *pTaskTCB)
  104. {
  105.   if (pTaskTCB->pData == ((void *)0))
  106.   {
  107.     return SCH_Q_FREE;
  108.   }
  109.   else
  110.   {
  111.     return SCH_Q_BUSY;
  112.   }
  113. }


  114. #endif
复制代码参考了不少本论坛的帖子,已经记不清具体是哪些了……

一周热门 更多>