搜索
bottom↓
回复: 250

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

  [复制链接]

出0入0汤圆

发表于 2013-5-19 14:18:22 | 显示全部楼层 |阅读模式
本帖最后由 summarize 于 2013-5-19 14:47 编辑

废话少说,先上stm8s103 IAR库工程代码压缩包。


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

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



2.共享时PC接收到的数据: PC端每1.5秒向下发送一次数据 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0xaa,0xaa为简单测试时的包尾识别码,单片机接收到数据后将其加上0x11,之后发送回给PC,下图中可看到0x03ff和0x22 0x33 0x44 0x55 0x66 0x77 0x88 0xbb两种数据。



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"则为开放给用户的接口,可定义数据类型、调度器节拍和配置是否使用消息。

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-5-26 22:40:06 | 显示全部楼层
本帖最后由 summarize 于 2013-5-26 22:57 编辑
rifjft 发表于 2013-5-23 01:07
整得不错,还有注释
有时间再弄弄寄存器版本的,这小容量的芯片用库浪费了点 ...


寄存器版本出来了.附件如下!

寄存器版本开启了TIM1_PWM(两个通道)中断和IO下降沿中断(PD4),其中IO这个中断在"iostm8s103f3.h"是没有定义的,这个头文件只定义了从0x0c之后的中断,于是我自己根据手册增加了前面0x00~0x0b中断定义.同时将寄存器版本的所有初始化代码贴出来,这些初始化代码都有详细的注释,有了它,你甚至可以不用看手册了,它必将是初学者的福音及工程师的利器.


  1. /* Includes ------------------------------------------------------------------*/
  2. #include "user.h"
  3. /* Private typedef -----------------------------------------------------------*/
  4. /* Private define ------------------------------------------------------------*/
  5. /* Private macro -------------------------------------------------------------*/
  6. /* Private variables ---------------------------------------------------------*/
  7. /* Private function prototypes -----------------------------------------------*/
  8. /* Private functions ---------------------------------------------------------*/
  9. static void TIM1_PWM_Config(void);
  10. static void TIM4_Config(void);

  11. static void GPIO_Config(void);
  12. static void UART1_Config(void);
  13. static void ADC_Config(void);
  14. static void IWDG_Config(void);
  15. static void WWDG_Config(void);
  16. static void IT_Config(void);

  17. /* Public functions ----------------------------------------------------------*/
  18. void CLK_Config(void)
  19. {
  20.   //配置内部16M时钟作为时钟源
  21.   //CLK_ICKR_HSIEN = 1;       //使能内部高速时钟(复位值)
  22.   CLK_ICKR_bit.LSIEN    = 1;  //使能内部低速时钟
  23.   CLK_CKDIVR_bit.HSIDIV = 0;  //主时钟分频 0-3对应 1 2 4 8分频
  24.   CLK_CKDIVR_bit.CPUDIV = 0;  //CPU时钟分频 0-7对应 1 2 4 8 16 32 64 128分频

  25.   //CLK_SWR = 0xE1;                 //HSI为主时钟源(复位值)
  26.   //CLK_SWCR_SWEN  = 1;             //使能自动切换时钟
  27.   //CLK_SWR = 0xD2;                 //0xD2:LSI为主时钟源(仅当LSI_EN选项位为1时)
  28.   //CLK_SWR = 0xB4;                 //0xB4:HSE为主时钟源

  29.   asm("nop");
  30.   asm("nop");
  31.   asm("nop");
  32.   asm("nop");
  33.   asm("nop");
  34.   asm("nop");
  35.   asm("nop");
  36.   asm("nop");
  37.   asm("nop");
  38.   asm("nop");
  39.   asm("nop");
  40.   asm("nop");
  41.   asm("nop");
  42.   asm("nop");
  43.   asm("nop");
  44.   asm("nop");

  45.   //外设时钟控制寄存器 CLK_PCKENR1
  46.   //        CLK_PCKENR1 = 0x00;        //复位值为0xff,即默认为开启状态
  47.   //        PCKEN10: I2C   
  48.   //        PCKEN11: SPI   
  49.   //        PCKEN12: UART1   
  50.   //        PCKEN13: UART2/3   实测此位才是UART1时钟使能(stm8s103f3)
  51.   //        PCKEN14: TIM4   
  52.   //        PCKEN15: TIM2
  53.   //        PCKEN16: TIM3
  54.   //        PCKEN17: TIM1
  55.   CLK_PCKENR1 = b10011000;    //使能TIM1,TIM4,UART1 时钟

  56.   //外设时钟控制寄存器 CLK_PCKENR2
  57.   //        CLK_PCKENR2 = 0x00;        //复位值为0xff,即默认为开启状态
  58.   //        PCKEN20: Reserved
  59.   //        PCKEN21: Reserved
  60.   //        PCKEN22: AWU
  61.   //        PCKEN23: ADC
  62.   //        PCKEN24: Reserved
  63.   //        PCKEN25: Reserved
  64.   //        PCKEN26: Reserved
  65.   //        PCKEN27: CAN
  66.   CLK_PCKENR2 = b00001000;  //使能 ADC 时钟,

  67.   //LK_CSSR_CSSEN = 1;                  //使能时钟监控功能,外间时钟出问题时切换到内部时钟源HSI/8   CLK_CSSR中的CSSD位被置位
  68.   //LK_CCOR = ;                                //时钟输出寄存器
  69. }


  70. void Peripherals_Config(void)
  71. {
  72.   GPIO_Config();

  73.   TIM1_PWM_Config();

  74.   TIM4_Config();

  75.   UART1_Config();

  76.   ADC_Config();

  77.   IWDG_Config();

  78.   WWDG_Config();

  79.   IT_Config();  //中断相关配置
  80. }

  81. static void IWDG_Config(void)
  82. {
  83.   IWDG_KR  = 0x55;        //关写保护
  84.   IWDG_PR  = 0x00;        //预分频系数4   0-6对应  4 8 16 32 64 128 256 分频
  85.   IWDG_RLR = 0xFF;        //最长超时15.90 ms
  86.   IWDG_KR  = 0xAA;        //恢复写保护,同时相关于清狗指令->用IWDG_RLR的数值刷新计数器的内容,避免了产生看门狗的复位
  87.                           //其实以上看门狗配置均为上电复位值
  88.   IWDG_KR  = 0xCC;        //启动独立看门狗
  89.   IWDG_KR  = 0xAA;        //清狗
  90. }


  91. static void WWDG_Config(void)
  92. {
  93.   WWDG_WR = 0x7f;
  94.   WWDG_CR = (uint8_t)(0x80 | WWDG_COUNTER_INIT);
  95.   WWDG_WR = (uint8_t)((uint8_t)(~0x80) & (uint8_t)(0x40 | WWDG_WINDOW_VALUE));
  96. }


  97. static void GPIO_Config(void)
  98. {
  99.   PA_ODR = b00000000;           //初始化输出低电平
  100.   PA_DDR = b11111111;           // 1=output
  101.   PA_CR1 = b11111111;           //1=上拉输入或推挽输出  0=浮空输入或开漏输出    ADC时应选浮空输入,无中断
  102.   PA_CR2 = b00000000;           //1=使能外部中断或10M输出  0=禁止外部中断或2M输出  中断边沿在EXTI_CRX中设置

  103.   PB_ODR = b00000000;           //初始化输出低电平
  104.   PB_DDR = b11111111;           // 1=output                             PB4 INPUT
  105.   PB_CR1 = b11111111;           //1=上拉输入或推挽输出  0=浮空输入或开漏输出    ADC时应选浮空输入,无中断
  106.   PB_CR2 = b00000000;           //1=使能外部中断或10M输出  0=禁止外部中断或2M输出  中断边沿在EXTI_CRX中设置

  107.   PC_ODR = b00000000;           //初始化输出电平
  108.   PC_DDR = b11111111;           // 1=output           PC4 INPUT
  109.   PC_CR1 = b11111111;           //1=上拉输入或推挽输出  0=浮空输入或开漏输出    ADC时应选浮空输入,无中断
  110.   PC_CR2 = b00000000;           //1=使能外部中断或10M输出  0=禁止外部中断或2M输出  中断边沿在EXTI_CRX中设置

  111.   //        如果硬件上没有连接RST脚,则SWIM引脚(PD1)不能配置为输出口,否则不能连上ST-LINK
  112.   PD_ODR = b00100000;           //初始化输出低电平  PD5=TX初始化输出高电平,PD6=RX配置为输入
  113.   PD_DDR = b10100011;           // 1=output 0=input PD2=AIN3 PD3=AIN4
  114.   PD_CR1 = b11111111;           //1=上拉输入或推挽输出  0=浮空输入或开漏输出    ADC时应选浮空输入,无中断
  115.   PD_CR2 = b00010000;           //1=使能外部中断或10M输出  0=禁止外部中断或2M输出  中断边沿在EXTI_CRX中设置
  116.                         //PD4作为下降沿中断测试输入引脚,配置为上拉输入
  117.        
  118.   PE_ODR = b00000000;           //初始化输出低电平
  119.   PE_DDR = b11111111;           // 1=output
  120.   PE_CR1 = b11111111;           //1=上拉输入或推挽输出  0=浮空输入或开漏输出    ADC时应选浮空输入,无中断
  121.   PE_CR2 = b00000000;           //1=使能外部中断或10M输出  0=禁止外部中断或2M输出  中断边沿在EXTI_CRX中设置
  122. }


  123. static void TIM1_PWM_Config(void)
  124. {
  125.   //须要在选项字节中配置使能对应的PWM功能管脚,同时开启对应的时钟
  126.   TIM1_PSCRH = 0x00;   //预分频器   16bit  fCK_CNT = fCK_PSC / (PSCR[15:0] + 1)
  127.   TIM1_PSCRL = 0x00;

  128.   TIM1_ARRH = (uint8_t)((800 - 1) >> 8); //自动重载值 16bit  即周期寄存器 16M/800=20K
  129.   TIM1_ARRL = (uint8_t)((800 - 1));      

  130.   //TIM1_CR1
  131. //TIM1_CR1_bit.CEN  = 1;  //CEN:允许计数,最后再开启
  132.   TIM1_CR1_bit.UDIS = 0;  //UDIS:禁止更新
  133.   TIM1_CR1_bit.URS  = 0;  //URS:更新请求源
  134.   TIM1_CR1_bit.OPM  = 0;  //0:在发生更新事件时,计数器不停止;
  135.   TIM1_CR1_bit.DIR  = 0;  //0:计数器向上计数;
  136.   TIM1_CR1_bit.CMS  = 0;  //00:边沿对齐模式。计数器依据方向位(DIR)向上或向下计数。
  137.   TIM1_CR1_bit.ARPE = 1;  //1:TIM1_ARR寄存器由预装载缓冲器缓冲。

  138.   TIM1_EGR_bit.UG   = 1;  //产生一次更新事件,更新分频系数与TIMx_CNTR

  139.   TIM1_RCR  = 0x00; //重复计数寄存器,即Reload事件发生了多少次:TIM1_RCR+1
  140.                     //这意味着在PWM模式中,(TIM1_RCR+1)对应着:
  141.                     //- 在边沿对齐模式下,PWM周期的数目;
  142.                     //- 在中心对称模式下,PWM半周期的数目;

  143.   //  通道1;
  144.   TIM1_CCMR1_bit.CC1S  = 0;   //CC1S[1:0]:捕获/比较1 选择
  145.   TIM1_CCMR1_bit.OC1FE = 0;   //OC1FE:输出比较1 快速使能
  146.   TIM1_CCMR1_bit.OC1PE = 1;   //OC1PE:输出比较1预装载使能
  147.   TIM1_CCMR1_bit.OC1M  = 7;   //110:PWM模式1,111:模式2
  148.   TIM1_CCMR1_bit.OC1CE = 0;   //0:OC1REF 不受ETRF输入(来自TIM1_TRIG引脚)的影响;  1:一旦检测到ETRF输入高电平,OC1REF=0。

  149.   TIM1_CCER1_bit.CC1E  = 1;   //1=输出使能
  150.   TIM1_CCER1_bit.CC1P  = 0;   //0=高电平有效 P
  151.   TIM1_CCER1_bit.CC1NE = 1;   //1=输出使能        --
  152.   TIM1_CCER1_bit.CC1NP = 0;   //0=高电平有效--互补输出  N

  153.   TIM1_CCR1H = 0x00;
  154.   TIM1_CCR1L = 0x00;                                   //占空比寄存器,CH1

  155.   //  通道2;
  156.   TIM1_CCMR2_bit.CC2S  = 0;   //CC1S[1:0]:捕获/比较1 选择
  157.   TIM1_CCMR2_bit.OC2FE = 0;   //OC1FE:输出比较1 快速使能
  158.   TIM1_CCMR2_bit.OC2PE = 1;   //OC1PE:输出比较1预装载使能
  159.   TIM1_CCMR2_bit.OC2M  = 7;   //110:PWM模式1,111:模式2
  160.   TIM1_CCMR2_bit.OC2CE = 0;   //0:OC1REF 不受ETRF输入(来自TIM1_TRIG引脚)的影响;  1:一旦检测到ETRF输入高电平,OC1REF=0。

  161.   TIM1_CCER1_bit.CC2E  = 1;   //1=输出使能
  162.   TIM1_CCER1_bit.CC2P  = 0;   //0=高电平有效 P   
  163.   TIM1_CCER1_bit.CC2NE = 1;   //1=输出使能        --                              //                           
  164.   TIM1_CCER1_bit.CC2NP = 0;   //0=高电平有效--互补输出  N

  165.   TIM1_CCR2H = 0x00;
  166.   TIM1_CCR2L = 0x00;                                   //占空比寄存器,CH2

  167.   //        TIM1_CR2 = ;
  168.   //        TIM1_SMCR = ;        从模式控制寄存器
  169.   //        TIM1_ETR = ;        外部触发寄存器
  170.   //TIM1_IER 中断使能寄存器
  171.   TIM1_IER_bit.UIE   = 1; //更新中断
  172.   TIM1_IER_bit.CC1IE = 0; //捕获/比较1中断
  173.   TIM1_IER_bit.CC2IE = 0; //捕获/比较2中断
  174.   TIM1_IER_bit.CC3IE = 0; //捕获/比较3中断
  175.   TIM1_IER_bit.CC4IE = 0; //捕获/比较4中断
  176.   TIM1_IER_bit.COMIE = 0; //COM中断                          
  177.   TIM1_IER_bit.TIE   = 0; //触发中断
  178.   TIM1_IER_bit.BIE   = 0; //刹车中断

  179.   //        TIM1_SR1 = ;        状态寄存器 1
  180.   //        TIM1_SR2 = ;        状态寄存器 2
  181.   //        TIM1_EGR = ;        事件产生寄存器

  182.   //刹车寄存器
  183.   TIM1_BKR_bit.MOE  = 1;  //1:如果设置了相应的使能位(TIM1_CCERX寄存器的CCIE位),则使能OC和OCN输出

  184.   TIM1_CR1_bit.CEN  = 1;  //CEN:允许计数
  185. }

  186. /**
  187.   * @brief  Configure TIM2 to generate an update interrupt each 0.5ms
  188.   * @param  None
  189.   * @retval None
  190.   */

  191. void TIM4_Config(void)
  192. {
  193.   //IM4_PSCR_bit.PSC = 7; //分频值: 2^PSC[2:0]  定时器时钟源为内部时钟(fMASTER)。   (1/16M)*128=8us
  194.   //IM4_ARR      = 124;                //自动重载值    (124+1)*8us=1ms
  195.   TIM4_PSCR_bit.PSC = 4;  //分频值: 2^PSC[2:0]         1/16M)*2^4=1us
  196.   TIM4_ARR      = 99;     //自动重载值          (99+1)us

  197.   TIM4_EGR_bit.UG   = 1;  //产生一次更新事件,更新分频系数与TIM4_CNTR
  198.   TIM4_CR1_bit.ARPE = 1;  //1:TIM4_ARR寄存器通过缓冲预装载

  199.   TIM4_SR_bit.UIF   = 0;  //TIM4 溢出中断标志
  200.   TIM4_IER_bit.UIE  = 1;  //使能TIM4 溢出中断
  201.   TIM4_CR1_bit.CEN  = 1;  //使能TIM4
  202. }




  203. /**
  204.   * @brief  UART1 Configuration for interrupt communication
  205.   * @param  None
  206.   * @retval None
  207.   */
  208. //
  209. static void UART1_Config(void)
  210. {
  211.   WORD_UNION UART1_BRR;

  212.   UART1_CR1_bit.PIEN  = 0; //校验中断使能  0:中断被禁止   1:当USART_SR中的PE为1时,产生USART中断
  213.   UART1_CR1_bit.PS    = 0; //0:偶校验。 1:奇校验
  214.   UART1_CR1_bit.PCEN  = 0; //1:奇偶校验控制被使能
  215.   UART1_CR1_bit.WAKE  = 0; //唤醒方法
  216.   UART1_CR1_bit.M     = 0; //8bit数据格式
  217.   UART1_CR1_bit.UART0 = 0; //0:UART使能  1:UART预分频器和输出禁用
  218.   UART1_CR1_bit.T8    = 0; //9bit数据格式 下的发送数据位8
  219.   UART1_CR1_bit.R8    = 0; //9bit数据格式 下的接收数据位8

  220.   UART1_CR3_bit.STOP = 0; //00:1个停止位;01:保留 10:2个停止位;11:1.5个停止位;

  221.   UART1_BRR.all = 1666;   //16M/1666=9603         

  222.   //UART1_BRR2=UART_DIV[15:12]高4位  UART_DIV[3:0]低4位  
  223.         UART1_BRR2  = UART1_BRR.byte.H & 0xf0;
  224.   UART1_BRR2 |= UART1_BRR.byte.L & 0x0f;
  225.                
  226.   //UART1_BRR1=UART_DIV[11:4]   
  227.   UART1_BRR1 = (uint8_t)(UART1_BRR.all>>4); //中间8位

  228. //UART1_CR2_bit.SBK  = 1;        //SBK: 发送断开帧
  229. //UART1_CR2_bit.RWU  = 1;        //RWU: 接收唤醒
  230.   UART1_CR2_bit.REN  = 1; //接收使能
  231.   UART1_CR2_bit.TEN  = 1; //发送使能
  232. //UART1_CR2_bit.ILIEN= 1;        //ILIEN: IDLE中断使能
  233.   UART1_CR2_bit.RIEN = 1; //接收中断使能  1:当USART_SR中的OR或者RXNE为1时,产生USART中断。
  234. //UART1_CR2_bit.TCIEN= 1;        //发送完成中断使能 1:当USART_SR中的TC为1时,产生USART中断。要发送时再使能中断(会立即产生中断)
  235. //UART1_CR2_bit.TIEN = 1;        //发送中断使能  1:当USART_SR中的TXE为1时,产生USART中断。

  236. //UART1_SR_bit.TC &= 0;          //读该位来清零
  237. //UART1_DR = 0xaa;                    //写入要发送的数据
  238. }

  239. /**
  240.   * @brief  Configure ADC  
  241.   *         
  242.   * @param  None
  243.   * @retval None
  244.   */
  245. static void ADC_Config(void)
  246. {
  247.   //        当作为模拟输入时可以关闭输入施密特触发器来降低功耗
  248.   ADC_TDRH = b00000000;       //1=ADC输入禁止施密特触发 ***********ADC时应选浮空输入,无中断
  249.   ADC_TDRL = b00011000;       //共16通道   AIN3-AIN4

  250.   ADC_CR1_bit.ADON  = 0;      //ADON:  A/D 转换开/关
  251.   ADC_CR1_bit.CONT  = 0;      // 0:单次转换模式  1:连续转换模式
  252.   ADC_CR1_bit.SPSEL = 0;      // 预分频选择位 000->111对应 2 3 4 6 8 10 12 18分频


  253. //ADC_CR2_bit.SCAN    = 1;          //1:使能扫描模式 0:不使能扫描模式
  254.   ADC_CR2_bit.ALIGN   = 1;    //1:数据右对齐。(低8字节在ADC_DRL寄存器,其余高字节位在ADC_DRH寄存器)读顺序
  255.                               //应先读低位,再读高位字节或整个一起读
  256.   ADC_CR2_bit.EXTSEL  = 0;    //外部事件触发和启动ADC   00:内部定时器1  TRG事件  01:ADC_ETR  引脚上的外部中断 其它值保留
  257.   ADC_CR2_bit.EXTTRIG = 0;    //EXTTRIG:  外触发使能位
  258.                           
  259. //ADC_CR3_bit.OVR  = 0;                    //1:数据缓存发生了数据溢出事件
  260.   ADC_CR3_bit.DBUF = 0;       //1:数据缓存功能使能,扫描模式时使用.                          

  261.   ADC_CSR_bit.CH    = 4;      // CH[3:0] : 选择转换通道    或指定从AN0到ANx扫描转换
  262. //ADC_CSR_bit.AWDIE = 0;                  //1: 使能AWD模拟看门狗中断
  263. //ADC_CSR_bit.EOCIE = 1;                  //1:使能转换结束中断
  264. //ADC_CSR_bit.AWD   = 0;                  //AWD:  模拟看门狗标志
  265.   ADC_CSR_bit.EOC   = 0;      //EOC:  转换结束                              
  266.                               
  267.   ADC_CR1_bit.ADON = 1;        //第一次置1将把ADC从低功耗模式下唤醒,之后才是启动ADC

  268. //ADC_DBxRH        缓冲寄存器
  269. //ADC_DBxRL
  270. //ADC_DRL         数据寄存器
  271. //ADC_DRH
  272. }


  273. void IT_Config(void)
  274. {
  275. //  中断配置------------------------------------------

  276. //ITC_SPR3_bit.VECT11SPR = 0;//Timer1最低优先级,
  277. //ITC_SPR4_bit.VECT15SPR = 0;//Timer3最低优先级,
  278. //ITC_SPR6_bit.VECT21SPR = 0;        //UART1/3接收满 最低优先级
  279. //ITC_SPR6_bit.VECT22SPR = 2; //adc中断为最高优先级
  280. //ITC_SPR6_bit.VECT23SPR = 0;        //Timer4最低优先级,2为最高,3为禁止优先级(复位默认值)  

  281. //EXTI_CR1:外部中断控制寄存器1  设置何种边沿触发
  282.   EXTI_CR1_bit.PAIS = 0;//[1:0] 00:下降沿和低电平触发
  283.   EXTI_CR1_bit.PBIS = 0;//[3:2] 01:仅上升沿触发
  284.   EXTI_CR1_bit.PCIS = 0;//[5:4] 10:仅下降沿触发
  285.   EXTI_CR1_bit.PDIS = 2;//[7:6] 11:上升沿和下降沿触发

  286. //EXTI_CR2;
  287.   EXTI_CR2_bit.PEIS = 0;//[1:0] GPIOE,配置同EXTI_CR1
  288.                         
  289.   EXTI_CR2_bit.TLIS = 0;//[2] TLI 0:下降沿触发 , 1:上升沿触发
  290. }
复制代码
同时更正了寄存器版本UART发送时的一个bug,i++应该是在给发送寄存器赋值之后才能执行;否则可能会出现少发送数据的情况,附件及修正部分代码如下
  1.     for (i = 0; i < u8DataSize; )//i++)                                        //应该是在UART1->DR=x 后才能执行i++
  2.     {
  3.       if(UART1_GetFlagStatus(UART1_FLAG_TXE) == SET)
  4.       {
  5.                 //UART1->DR = s_u8TxBuffer[i];               
  6.                 UART1->DR = s_u8TxBuffer[i++];                                        //在此执行i++
  7.                 //UART1_ITConfig(UART1_IT_TXE, ENABLE);
  8.                 SCHCurTaskDly(1 / SCH_SYS_TICKS_MS);                //延时发送1个字节所须要的时间                                               
  9.         }
  10.         else
  11.         {
  12.                 SCHCurTaskDly(0 / SCH_SYS_TICKS_MS);                //无延时,下次直接进来查询
  13.         }
  14.     }
复制代码
如果大家有兴趣,后续我可以出stm32版本的.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-2 23:36:56 | 显示全部楼层
本帖最后由 summarize 于 2013-6-2 23:48 编辑

今天下午和SOME PLMM爬山去了,回来心情大好,决定把STM32版本的也搞定(之前已经完成一部分).回来吃饭洗澡后就一直奋斗...直到...
STM32版本终于搞好了,本例是STM32F103C8T6,该死的看门狗在断点时还工作,导致复位(stm8s可不会这样),害我搞得这么晚才搞定.见附件!用到的资源和stm8s的一样,即UART和ADC,同时还配置好了TIM1/2/3,但只启用TIM1,TIM2/3可自行开启.后续再加入8051就停止了(如果响应好再添加PIC和NEC的,富士通和台系的就算了开发工具太麻烦不好验证).


-----------------------------------------------------------------------------
编辑原因:加入私生活^_^

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-12 21:01:15 | 显示全部楼层
本帖最后由 summarize 于 2013-6-12 21:36 编辑
yj_yulin 发表于 2013-6-5 15:07
期待8051尽快出来,不过lz要是希望推广还是得有个学习文档才好,人们只有理解了为什么要这样,有如此等等好处, ...


前天下的单(STC89C52单片机最小系统板),今天中午就到了,还挺快;
多年不用51,但使用的还是IAR环境,还算顺利,终于搞定了.附件如下



补充:本来是想省事,买了个带开关的最小系统板(因为STC的单片机ISP下载要冷重启),谁知买到手的板子的开关却是坏的,算了,赶时间不计较了,自己上网找找关于免重启的资料(以前做过,但现在已经找不到了),把免重启的代码加进去,OK,终于不用老拔线了.使用方法见下图,
重点是在 程序文件->自定义下载->选择波特率9600并填上自定义命令 0x55 0xa5 0xaa;然后点击右边的发送按键即可.




免重启核心代码:
  1.     //加入此段代码,即在收到约定命令时,重启单片机,免重新上电即可下载
  2.     if((pDataBuffer[u8DataSize-3]==0x55)
  3.        &&(pDataBuffer[u8DataSize-2]==0xa5)
  4.        &(pDataBuffer[u8DataSize-1]==0xaa))
  5.     {
  6.       SCHCurTaskDly(100 / SCH_SYS_TICKS_MS);  //delay 100ms                    
  7.       ISP_CONTR = 0x60;   //软复位进入ISP下载模式
  8.     }
复制代码
//代码在串口接收处理函数中

再啰嗦几句,之前的STM8S/32的调度节拍都是1ms,即延时时间是1ms的倍数,而51单片机比较慢,如果还是1ms则会出现异常(具体就不去研究了),实测2ms以上才OK,留点余量外加个人喜好就改为10ms了.

再者之前的STM8S/3只开串口接收中断,而51单片机的串口接收,发送中断是一起的,一开两个都开了,所以串口的发送也改为中断发送了.

还有调度器节拍的定时器0使用的是模式2(自动重载模式),使用此模式是因为我既想获得精确的时间节拍,但又不想去计算进入中断所用时间及向TH0,TL0赋值所用时间再去计算出真正TH0,TL0的初值.

下一步开始写使用说明文档.

have fun!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-7-22 01:17:39 | 显示全部楼层
本帖最后由 summarize 于 2013-7-22 01:46 编辑
soosqt 发表于 2013-6-13 09:14
支持楼主,强列希望出个PIC版本


使用说明文档已经出来了:
自制类OS调试器应用与说明

一、调度器功能说明。
本调度器是集时间触发、支持消息、支持非抢占优先级调度,借鉴了protothread思想,而使得其实现与使用类似于OS的调度器(仿ucos),所以暂时叫类OS调度器吧。
1.     时间触发:即任务可以定时执行,如每间隔一定时间执行一次,应用如定时采样、LED闪烁等,而且此间隔在任务执行过程中是可以修改的。
2.     支持消息:即任务除了定时执行方式外,还可以等待消息到了才执行,应用如串口接收处理,即等串口接收完一帧数据后,发消息通知处理任务去处理这些数据。每次只发送一个消息时,也可以当邮箱来用。
3.     支持非抢占优先级:任务创建的顺序就是优先级顺序(由高到低)。一般调度器都是所以任务顺序执行,那么所以任务执行一圈的时间有时也是比较长的,如5ms,当有一个任务的实时性要求较高,如2ms内必须响应,那么顺序式调度器就不能满足要求了,而我们的非抢占优先级调度就发挥作用了,因为它是每个任务执行完后都会重新查找下一个就绪的最高优先级任务并执行。这个任务的最快响应时间就由执行时间最长的那个任务决定,如执行时间最长的任务的时间为1.5ms,能满足要求。
4.     类OS:因为借鉴了protothread思想,使得其实现与使用类似于OS,同时在实现与风格也仿照了ucos,所以对于熟悉OS的朋友,用起来就更顺心了。而且它也可以作为学习OS的前奏。
5.     全C语言实现,移植方便,只须一个硬件定时器为其提供调度节拍的“心跳”即可。
二、调度器的移植与应用。
1.     下载调度器相关文件,并包含到自己的工程中
下载最新版本的“Schedule V1.01.rar”;网址:
http://www.amobbs.com/thread-5534907-1-1.html
解压后“Schedule”文件夹下有“sch_cfg.h”、“schedule.h”、“schedule.c”(后两个已经设置为只读属性文件);
将这三个文件复制到自己的工程目录下,在工程的main函数中包含“sch_cfg.h”,此头文件会自动包含“schedule.h”。

2.     配置”sch_cfg.h”文件
”sch_cfg.h”内有三个地方可配置,如下:
#define SCH_SYS_TICKS_MS              1      //定义调度系统时钟节拍时间(ms)
#define SCH_HW_TIM_MS        0.1   //硬件定时器中断(溢出)周期(ms)

#define SCH_CFG_Q_EN        1u  //任务内建消息使能


个为定义调度系统时钟节拍时间,单位是ms,一般配置为1~10ms即可,对于执行速度快的单片机选如stm8s、avr等配置1ms,传统的51单片机速度慢的选5ms以上比较好,实在不懂就选个10ms吧。

个为硬件定时器中断(溢出)周期,单位也是ms,调度器的时间节拍是基于硬件定时器的,所以最少要有一个硬件定时器并开启中断,还要注意硬件定时的周期必须小于等于调度器的节拍时间,这个硬件定时器的周期也是要根据单片机的执行速度和自己的应用系统要求来定,与调度节拍类似,对于执行速度快的单片机选如stm8s、avr等配置0.1ms,传统的51单片机速度慢的选0.5ms以上比较好,实在不懂就选个1ms。

个为任务内建消息使能选项,根据须要使能或禁止。如果使能,则每个任务控制块会增加如下内容:
#if SCH_CFG_Q_EN > 0u
  void         *pData;       //消息指针
  SCH_UINT8                  Size;         //消息的大小
#endif
如果是比较简单的系统,不须要用到消息传递功能,则可关闭此功能。以节省内存空间。

3.     硬件定时器中断中增加调度器节拍函数
调度器的时间节拍是基于系统硬件定时器的,因为它们之间得有“联系”才行,通过在定时器中断中增加宏“SchedTicksInc()”即可。

4.     定义任务控制块(TCB)
调度器管理的对像是任务控制块,所以要为每个任务定义自己的任务控制块。
如我们要建立一个vTestTask,则除了编写vTestTask任务函数外,还要定义它的任务控制块TestTaskTcb,定义格式为:SCH_TCB   TestTaskTcb;按此方法,我们可以编写更多的任务vTestTaskN,并定义好它的任务控制块 TestTaskNTcb。

5.     创建任务
任务编写完全并定义好它的控制块后,就可以创建任务了, 创建任务的顺序就是任务优先级的顺序,由高到低。创建任务的例子如下:
SCHTaskCreate(&TestTaskTcb, vTestTask);   //创建任务vTestTask;
SCHTaskCreate(&TestTaskNTcb, vTestTaskN); //创建任务vTestTaskN;

6.     启动调度器。
任务创建好后,即可启动调度器,调用启动调度器函数:SCHTaskSchedStart();

7.     使用例子。

以上所说的都是只针对调度器而言,对于一个完整的系统,main函数至少应该包含如下部分:
Void main()
{
    disable_interrupt();  //关全局中断
Peripherals_Config();//硬件系统初始化,必须至少包含一个硬件定时器
SCHTaskCreate(&TestTaskTcb, vTestTask);//至少创建一个任务
enable_interrupt();  //开全局中断
SCHTaskSchedStart();//启动调度器,永不返回。
}

定时执行任务例子,每500ms执行一次Fun函数。
void vTestTask (void)
{
Static unsigned char s_u8Var;//定义变量,注意任务内应定义为静态变量

  SCHTaskBegin();  //任务实体内容开始,必须有的固定语句。

         while(1)  //每个任务内都要有一个死循环。同时还要有一个挂起任务的操
//如延时或等待消息。同时注意不能使用switch语句,但可在调用函数内使用
  {
    UserFun ();       //可以是语句或函数,调用的函数内部可使用局部变量

    SCHCurTaskDly(500 / SCH_SYS_TICKS_MS);//delay500ms,根据须要调整时间
  }

  SCHTaskEnd();   //任务实体内容结束,必须有的固定语句。
}

等待消息到来再执行的例子,如等待串口接收中断发来消息:
void vUartReceiveData(void)
{
static uint8_t s_u8RxBuffer[UART_RX_BUF_SIZE];
static uint8_t s_u8RxCounter;

static uint8_t *pDataBuffer, u8DataSize;
SCHTaskBegin();

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

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

    for(s_u8RxCounter = 0; s_u8RxCounter < u8DataSize; s_u8RxCounter++)
    {
             s_u8RxBuffer[s_u8RxCounter] =(*pDataBuffer) + 0x11;
     pDataBuffer++;
    }

    for(u8DataSize = 0; u8DataSize < 255; u8DataSize++)  //借用u8DataSize

{ //检查UART_TX发送任务队列是否可用

              if (SCHTaskGetQFree(&UartTxTcb) ==SCH_Q_FREE)      
      {
       SCHTaskQpost(&UartTxTcb,
                     &s_u8RxBuffer[0],
                     s_u8RxCounter); //将接收的的数据+0x11后发回去
       break;
      }
     else
      {
       SCHCurTaskDly(1 / SCH_SYS_TICKS_MS); //delay 1ms
      }
    }
  }

SCHTaskEnd();
}

串口接收中断中接收完一帧数据后向其发送消息:
SCHTaskQpost(&UartRxTcb,//接收处理函数的TCB地址
                     &s_u8IsrRxBuffer[0,//串口接收缓冲区首地址
                     s_u8IsrRxCounter);   //接收数据大小(字节)

三、调度器任务控制块及各函数说明。
1.“schedule.h”文件下:
      ①任务控制块:struct SchTcb{#if SCH_CFG_Q_EN > 0u  void          *pData;       //消息指针  SCH_UINT8     Size;         //消息的大小#endif   SCH_DLY_TYPE      TimeCounter;  //定时计数器,时基为 "SCH_SYS_TICKS_MS"  void          (*pTask)();   //任务指针  struct SchTcb *pNextTCB;    //下一个任务控制块指针};      ②SchedTicksInc()”,此为调度器节拍计数器递增函数,在硬件定时器中调用。     ③SCHTaskBegin()”,此为固定使用函数,放在任务的开始处(变量定义后)。      ④“SCHTaskEnd()”,此为固定使用函数,放在任务的结尾处。     ⑤“SCHCurTaskPend()”, 挂起(暂停)当前任务,即任务自身。
⑥“SCHCurTaskDly(Ticks)”,当前任务延时Ticks个时间节拍。
        ⑦“SCHTaskCallSub(SubTaskName)”,任务内调用子任务,子任务格式与主任务相同。
⑧“SCHTaskQpend()”,当前任务等待消息(到来后才往下执行)。
2.“schedule.c”文件下:
①“void SCHTaskQpost(SCH_TCB   *pPostTCB,                      void      *pData,                     SCH_UINT8 Size)”,此为释放(发送)消息函数,“pPostTCB”指向要接收消息的任务的任务控制块;“pData”指向消息的首地址;“Size”为消息的大小(字节为单位)。 ②“SCH_UINT8 SCHTaskGetQFree(SCH_TCB   *pTaskTCB)”,此为查询任务消息状态,是否是自由(可用)或忙(不可用),调用SCHTaskQpend()时会将其设置为自由(可用)状态。“pTaskTCB”指向要查询的任务的任务控制块。返回值是“SCH_Q_FREE”或“SCH_Q_BUSY”,即可用或不可用。 ③“void SCHTaskCreate(SCH_TCB           *pNewTCB,                      void              (*pNewTask)(void))”,此为创建任务函数,“pNewTCB”指向要创建的任务的任务控制块,“pNewTask”为要创建任务的首地址(或叫函数名)。     ④“void SCHTaskSchedStart(void)”,此为启动调度器函数,调用后,则开始进行任务调度,每个任务结束后都会重新查找就绪的最高优先级任务并运行,此函数永不返回。    ⑤“void SCHTimeTick(void)”,此为任务节拍处理函数,每个调度节拍到来时,将任务的节拍延时计数器减1(如果其值大于0),由“SCHTaskSchedStart()”函数调用。 Stm32/8和8051的工程模板见帖子:http://www.amobbs.com/thread-5534907-1-1.html
但要注意工程模板那里的调度器并不是最新版本,下载后请自行更新为最新版本。

                              
                                                            2013-07-22


PDF文档:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-7-22 01:20:40 | 显示全部楼层
word文档复制过来格式有点不好阅读,大家还是看PDF比较舒服些。
同时如果可以,请版主帮忙更新到LZ位,方便大家。

最新调度器版本如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-4-28 20:55:23 | 显示全部楼层
summarize 发表于 2013-7-22 01:20
word文档复制过来格式有点不好阅读,大家还是看PDF比较舒服些。
同时如果可以,请版主帮忙更新到LZ位,方便 ...

鉴于C语言中建议尽量不使用"goto"语句,现将“SCHTaskSchedStart()”函数中的“goto”语句去掉,用"if else"逻辑来实现。同时将版本升级为V1.02


修改细节为将如下函数

  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. }
复制代码


改为


  1. void SCHTaskSchedStart(void)
  2. {
  3.   pCurTCB = pFirstTCB;                        //指向第一个创建的任务,之后按创建时的顺序执行下去

  4.   while (1)                                   //环形链表,可以一直循环下去
  5.   {
  6.     SCHTimeTick();                            //如果任务Tick满足条件,则将其置于可执行状态

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

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

  11.       pCurTCB = pFirstTCB;                    //每执行完一个任务,都回到起点重新查找一次可执行最高优先级任务
  12.     }
  13.     else
  14.     {
  15.             pCurTCB = pCurTCB->pNextTCB;            //指向下一个任务控制块,查找下个任务是否可执行
  16.     }
  17.   }
  18. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-7-26 09:02:20 | 显示全部楼层
ppdd 发表于 2014-6-20 21:18
我想我也说不明白的,C太弱了!

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

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

请移步:

http://www.amobbs.com/thread-5589227-1-1.html

出0入0汤圆

发表于 2013-5-19 15:57:41 | 显示全部楼层
还没下载 不过从描述上看还是不错的 支持

出0入0汤圆

 楼主| 发表于 2013-5-20 08:50:02 | 显示全部楼层
为了方便大家,接着把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();
}

出0入0汤圆

 楼主| 发表于 2013-5-22 19:59:28 | 显示全部楼层
无人问津?白花了一个多星期时间了。冏……

出0入0汤圆

发表于 2013-5-22 20:27:36 | 显示全部楼层
支持一下楼主,但现在实在没时间细细品味

出0入0汤圆

 楼主| 发表于 2013-5-22 20:45:41 | 显示全部楼层
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
复制代码
参考了不少本论坛的帖子,已经记不清具体是哪些了……

出0入0汤圆

发表于 2013-5-22 21:24:25 | 显示全部楼层
顶一下贴,楼主花了不少精力。

出0入0汤圆

发表于 2013-5-22 21:32:45 | 显示全部楼层
学习一下了啊~~

出0入17汤圆

发表于 2013-5-22 22:04:59 | 显示全部楼层
protothread用的人太少,算是曲高和寡吧

出0入0汤圆

 楼主| 发表于 2013-5-23 00:19:07 来自手机 | 显示全部楼层
hhxb 发表于 2013-5-22 22:04
protothread用的人太少,算是曲高和寡吧

何为 曲高和寡 ?

出0入0汤圆

发表于 2013-5-23 01:07:18 | 显示全部楼层
整得不错,还有注释
有时间再弄弄寄存器版本的,这小容量的芯片用库浪费了点

出0入0汤圆

 楼主| 发表于 2013-5-23 11:47:04 | 显示全部楼层
rifjft 发表于 2013-5-23 01:07
整得不错,还有注释
有时间再弄弄寄存器版本的,这小容量的芯片用库浪费了点 ...

谢谢,我会尽快出寄存器版本.

出0入0汤圆

 楼主| 发表于 2013-5-23 12:39:20 | 显示全部楼层
本帖最后由 summarize 于 2013-5-23 12:41 编辑

这里补充说明一下为什么要做支持优先级的调度:

之前手里有一个项目有多个任务,main函数简述如下

void main(void)
{
  SystemInit();

  whiile(1)
  {
    Task1();////经测试执行最长时间约0.8ms

    Task2();//经测试执行最长时间约1.2ms

    Task3();//经测试执行最长时间约0.9ms

    Task4();//经测试执行最长时间约1.5ms
  }
}

如Task1为通信数据处理任务(数据在中断中接收并放到缓冲区等待处理),要求最慢响应时间是2ms,那么如果是按照常规的顺序执行,则其它三个任务的最大执行时间(1.2+0.9+1.5)>2ms,则数据会丢失.
当时的临时解决办法是,在Task2(),Task3()后面都多调用一次Task1(),即变成下面这样:
void main(void)
{
  SystemInit();

  whiile(1)
  {
    Task1();////经测试执行最长时间约0.8ms

    Task2();//经测试执行最长时间约1.2ms
    Task1();////经测试执行最长时间约0.8ms

    Task3();//经测试执行最长时间约0.9ms
    Task1();////经测试执行最长时间约0.8ms

    Task4();//经测试执行最长时间约1.5ms
  }
}

显然,问题得到解决,但是main函数的简洁性被破坏,显示得有点杂乱了.而且只有Task1()取得了最高优先级,其它任务还是按原来那样顺序执行;所以后来就有了做一个支持优先级的非抢占式调度器,可以支持配置所有任务的优先级.

出0入17汤圆

发表于 2013-5-23 13:30:43 | 显示全部楼层
本帖最后由 hhxb 于 2013-5-23 20:39 编辑
summarize 发表于 2013-5-23 00:19
何为 曲高和寡 ?


上次看一个protothread的帖子,对宏的使用看的我脑袋都大了
楼主的代码我还没来得及看,今天仔细研读一下

出0入0汤圆

 楼主| 发表于 2013-5-23 22:15:34 | 显示全部楼层
hhxb 发表于 2013-5-23 13:30
上次看一个protothread的帖子,对宏的使用看的我脑袋都大了
楼主的代码我还没来得及看,今天仔细研读一下 ...

建议你使用SlickEdit或是其它比较专业的编辑器,它们的代码关联等功能很强大,方便阅读编辑。

给你一个我使用SlickEdit时,鼠标光标定在SCHCurTaskDly时的截图

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-5-24 11:10:26 | 显示全部楼层
summarize 发表于 2013-5-23 22:15
建议你使用SlickEdit或是其它比较专业的编辑器,它们的代码关联等功能很强大,方便阅读编辑。

给你一个 ...

//延时发送1个字节所须要的时间
这个时间怎么取?堵塞了?

出0入0汤圆

发表于 2013-5-24 11:55:38 | 显示全部楼层
留着~~还在学习STM8中~~

出0入76汤圆

发表于 2013-5-24 12:19:07 | 显示全部楼层
不错,顶一下,支持原创...

出0入0汤圆

 楼主| 发表于 2013-5-24 13:54:03 | 显示全部楼层
Hz01800475 发表于 2013-5-24 11:10
//延时发送1个字节所须要的时间
这个时间怎么取?堵塞了?

9600/8bit=1200,加上起止停止位等,算位1K频率,对应1ms,再不行可以用示波器看下发送一个字节到底用多长时间。

延时1ms的意思是过了1ms之后再进来查询一下前一字节是否发送完,如果发送完则接着发送下个字节。

出0入0汤圆

发表于 2013-5-24 14:00:36 | 显示全部楼层
summarize 发表于 2013-5-24 13:54
9600/8bit=1200,加上起止停止位等,算位1K频率,对应1ms,再不行可以用示波器看下发送一个字节到底用多 ...

发的数据比较频繁。会不会单片机。处理其它任务效率低下。

出0入0汤圆

发表于 2013-5-24 14:08:56 | 显示全部楼层
很好的思想

出0入0汤圆

发表于 2013-5-24 17:03:43 | 显示全部楼层
protothread学习提高好参考资料

出0入0汤圆

 楼主| 发表于 2013-5-24 23:26:41 | 显示全部楼层
Hz01800475 发表于 2013-5-24 14:00
发的数据比较频繁。会不会单片机。处理其它任务效率低下。

你要明白,每次如果条件符合(即上个字节已经发送完),本次所做的事情只是把要发送的数据放到UART的发送寄存器中,你发送再多的数据,也只是每间隔1ms就把发送缓冲区中的一个字节数据放在UART的发送寄存器中,这个动作应该占用的时间很少吧.而且如果其它任务更紧急,则可以将其配置为更高优先级,数据发送放在低优先级,这样UART发送最多就是因为执行了其它更高优先级任务而被"拖延"了时间,即可能是在发送完一个字节之后,间隔了2ms或更多时间才能发送下个字节.当然,如果你的UART通信不能接受这种情况发生,那你也可以将其改为中断方式发送.

出0入0汤圆

发表于 2013-5-26 22:56:21 | 显示全部楼层
好,有时间看看

出0入0汤圆

发表于 2013-5-27 09:23:17 | 显示全部楼层
顶你,再出其它MCU的版本,同时加上文档。这样就推广开了

出0入0汤圆

 楼主| 发表于 2013-5-27 09:50:59 来自手机 | 显示全部楼层
soosqt 发表于 2013-5-27 09:23
顶你,再出其它MCU的版本,同时加上文档。这样就推广开了

谢谢!其实我有一个想法,就是希望它能成为裸机程序框架的一种通用调度器,就像ucos 等OS一样,它们占据OS 地位,我们占据调度器的一片天,但是目前看来好像感冒的人不太多。也许出更多MCU版本就能改变?再次谢谢你的建议,后续我会也更多MCU版本和文档。

出0入0汤圆

发表于 2013-5-27 10:41:35 | 显示全部楼层
不错呀,寄存器的也出来了,有时间再仔细看看

其实像这种在某款芯片的调度器应用,应在标题上注明,这样关注的人可能会更多一点。

出0入0汤圆

 楼主| 发表于 2013-5-27 12:36:48 | 显示全部楼层
谢谢LS,现在已经不能更改标题了,当初起这个标题时,是想尽可能将要讲的内容表达清楚...而且字数都用完了还是觉得表达不够清楚.
同时,我选择在stm32/8论坛发帖,当然默认就是stm32/8的芯片应用了.至于以后如果增加其它单片机版本,倒是不知道放到哪里好.

出20入70汤圆

发表于 2013-5-27 12:40:08 | 显示全部楼层
标记!     

出0入4汤圆

发表于 2013-6-3 01:32:25 | 显示全部楼层
迟点消化消化

出0入0汤圆

发表于 2013-6-5 15:07:39 | 显示全部楼层
期待8051尽快出来,不过lz要是希望推广还是得有个学习文档才好,人们只有理解了为什么要这样,有如此等等好处,用着才欢喜.

出0入0汤圆

发表于 2013-6-10 19:30:35 | 显示全部楼层
是OS还是类OS?
看看。
谢楼主。

出0入0汤圆

 楼主| 发表于 2013-6-10 19:58:06 | 显示全部楼层
tiger5 发表于 2013-6-10 19:30
是OS还是类OS?
看看。
谢楼主。

名字就是调度器,当然就不会是OS了,类OS吧.全C,适用所有单片机平台.现在手头没有51单片机,准备买一块最小系统板,然后再出一版8051的.

出0入0汤圆

发表于 2013-6-11 15:09:05 | 显示全部楼层
学习一下调度器,谢谢!

出0入0汤圆

发表于 2013-6-12 21:14:50 来自手机 | 显示全部楼层
关注一下,

出0入0汤圆

发表于 2013-6-13 00:52:40 | 显示全部楼层
记号,有空的时候准备仔细看下。

出0入0汤圆

发表于 2013-6-13 09:14:46 | 显示全部楼层
支持楼主,强列希望出个PIC版本

出0入0汤圆

 楼主| 发表于 2013-6-16 11:18:59 | 显示全部楼层
soosqt 发表于 2013-6-13 09:14
支持楼主,强列希望出个PIC版本

暂时不出别的版本了,得先出文档;其实移植并不难,就是要初始化一个定时器作为调度器的节拍器,了解OS的很容易明白(当然比OS简单得多),关键是使用,所以使用说明文档更重要;

如果有了文档还是不能搞定,那里再出.

出0入0汤圆

发表于 2013-6-22 15:50:48 | 显示全部楼层
本帖最后由 majiansongok 于 2013-6-22 15:57 编辑

好东西,谢谢分享!

出0入0汤圆

发表于 2013-6-22 20:15:49 | 显示全部楼层
不知道与单纯的 protothread 比较有什么特别优势,protothread使用简单,适合几乎所有C编译器,
不过上次用C30编译器,芯片用dspic30f4013时,发现C30的中断入口带参数,导致用protothread调度时,出现无规律复位现象,后去掉了参数后才正常。

出0入0汤圆

 楼主| 发表于 2013-6-23 02:00:56 来自手机 | 显示全部楼层
tdh03z 发表于 2013-6-22 20:15
不知道与单纯的 protothread 比较有什么特别优势,protothread使用简单,适合几乎所有C编译器,
不过上次用 ...

这个是封装成类OS,支持非抢占优先级调度,消息,用过OS 的人会更喜欢。同时也可作为学习OS的前奏。同样全用C 实现,移植方便。

出0入0汤圆

发表于 2013-6-29 14:39:24 | 显示全部楼层
   mark!                           

出0入0汤圆

发表于 2013-6-30 09:49:41 | 显示全部楼层
好东西,有时间看看。

出0入0汤圆

 楼主| 发表于 2013-7-22 12:30:07 | 显示全部楼层
yj_yulin 发表于 2013-6-5 15:07
期待8051尽快出来,不过lz要是希望推广还是得有个学习文档才好,人们只有理解了为什么要这样,有如此等等好处, ...

使用说明已经出来,望指点.

出0入0汤圆

发表于 2013-7-22 15:04:19 | 显示全部楼层
mark。。。。。。。。。。。。。。。。。。。。。

出0入0汤圆

发表于 2013-7-26 15:13:51 | 显示全部楼层
summarize 发表于 2013-7-22 12:30
使用说明已经出来,望指点.

支持,lz有热情,感觉直接上github.com开项目更有助于推广,协作.

出0入0汤圆

 楼主| 发表于 2013-7-26 22:58:27 | 显示全部楼层
yj_yulin 发表于 2013-7-26 15:13
支持,lz有热情,感觉直接上github.com开项目更有助于推广,协作.

常驻本论坛,未知你说的“github.com”是个什么样的网站?

出0入0汤圆

发表于 2013-7-29 10:14:00 | 显示全部楼层
summarize 发表于 2013-7-26 22:58
常驻本论坛,未知你说的“github.com”是个什么样的网站?

开源代码项目管理, 请善用搜索.

出0入0汤圆

发表于 2013-8-28 17:33:48 | 显示全部楼层
本帖最后由 smset 于 2013-8-28 17:49 编辑

感觉你的这个调度器很不错,和我以前发的“小小调度器”核心思想上非常类似呢。


参考这个帖子:http://www.amobbs.com/thread-5508723-1-1.html

感觉你的版本很规范,虽然资源上稍微增加了一点点,不过外围辅助功能增加了不少,而且文档写得也非常不错!

赞一个!

出0入0汤圆

发表于 2013-8-28 18:33:52 来自手机 | 显示全部楼层
protothread确实是的好东西!

出0入0汤圆

 楼主| 发表于 2013-8-29 08:46:20 | 显示全部楼层
本帖最后由 summarize 于 2013-8-29 08:49 编辑
smset 发表于 2013-8-28 17:33
感觉你的这个调度器很不错,和我以前发的“小小调度器”核心思想上非常类似呢。
{: ...


是的,是参考了你的"小小调度器".

我是在看了你的调度器,然后自己学习了UCOS3,之后才有了将多种思想结合起来,写一个功能比较完善且使用方法类似OS的调度器.

多谢关注.

出0入0汤圆

 楼主| 发表于 2013-9-9 07:21:57 | 显示全部楼层
突然发现自己还可以管理帖子,故将整个过程的重要事件推荐到的LZ位,这样就不用全部看完帖子就能了解整个过程及重点。

出0入0汤圆

发表于 2013-9-9 08:15:03 | 显示全部楼层
markmarkmarkmarkmark

出0入0汤圆

发表于 2013-9-17 12:59:01 | 显示全部楼层
标记一个

出0入0汤圆

发表于 2013-9-17 13:42:39 | 显示全部楼层
本帖最后由 kinsno 于 2013-9-17 13:45 编辑
summarize 发表于 2013-9-9 07:21
突然发现自己还可以管理帖子,故将整个过程的重要事件推荐到的LZ位,这样就不用全部看完帖子就能了解整个过 ...


最新版本的升级呢? 还有吗?是否用在量产的产品上了啊?
1、应该写出你这个类OS,如果要使用,要注意一些什么点,比如延时,比如程序的写法?比如变量的定义。这些我觉得出也得出一个文档,这样避免很多人中招。

出0入0汤圆

 楼主| 发表于 2013-9-17 13:52:02 | 显示全部楼层
kinsno 发表于 2013-9-17 13:42
最新版本的升级呢? 还有吗?是否用在量产的产品上了啊?
1、应该写出你这个类OS,如果要使用,要注意一 ...

最新版本在 48楼,简约版本在公司内部推广中(目前这个版本比较完善,不少人理解接受有困难,特别是老员工).使用说明在47楼,已经说明了使用中要注意的大部分问题.具体还有哪些要注意的问题请指出,后续加入完善.

出0入0汤圆

发表于 2013-9-18 15:41:00 | 显示全部楼层
看过                     .............

出0入0汤圆

发表于 2013-9-22 14:59:03 | 显示全部楼层
summarize 发表于 2013-9-17 13:52
最新版本在 48楼,简约版本在公司内部推广中(目前这个版本比较完善,不少人理解接受有困难,特别是老员工). ...


#define SCH_CURR_LINE                (SCH_UINT8)(__LINE__+(!__LINE__))         ->这个定义是不是有问题,应该是只获取当前的行号“__LINE__”,在后面又加上一个!是什么意思呢?

没写出应该程序架构里面应该避免啥东西的?原protolthread应该是不能在程序内部使用SWITCH了。你这个呢?

出0入0汤圆

 楼主| 发表于 2013-9-22 20:46:43 | 显示全部楼层
本帖最后由 summarize 于 2013-9-22 21:10 编辑
kinsno 发表于 2013-9-22 14:59
#define SCH_CURR_LINE                (SCH_UINT8)(__LINE__+(!__LINE__))         ->这个定义是不是有问题,应该是只 ...


#define SCH_CURR_LINE                (SCH_UINT8)(__LINE__+(!__LINE__))         ->这个定义是不是有问题,应该是只获取当前的行号“__LINE__”,在后面又加上一个!是什么意思呢?
------------------------------------------------------------------------------
多谢提醒,我实际测试了一下,与  #define SCH_CURR_LINE                (SCH_UINT8)(__LINE__)  相比,加上 +(!__LINE__)) 后 ,它只能解决0x00与0x0100行不会冲突(转成8位后同时为0,编译会提示出错)。0x00行计算结果为0x01。0x0100行计算结果为0x00。但它并不能解决如0x01行与0x0101行的冲突,因为这两个行的计算结果都是0x01。所以这个算法基本没什么意义了,或者编译提示重复定义出错时,加个回车键是最简单有效的办法。


没写出应该程序架构里面应该避免啥东西的?原protolthread应该是不能在程序内部使用SWITCH了。你这个呢?
------------------------------------------------------------------------------
如果我没记错 protolthread 应该是有用switch实现跳转的版本和用goto代替switch来实现的版本。如果是switch版本,则也不能在程序内部使用switch了。如果是goto版本,则程序内部可以用switch。因为本调度器是用switch实现跳转,所以程序内部也不能用switch了。但是准确来说,应该是这个程序内因为使用了“SCHTaskBegin() ”(内含switch),所以不能再次使用switch了,但其所调用的子函数,如果没有使用SCHTaskBegin() ,则也是可以使用swithc的。这点注意事项已经在使用说明中的例子里的注释中说明了。

“while(1)  //每个任务内都要有一个死循环。同时还要有一个挂起任务的操
//如延时或等待消息。同时注意不能使用switch语句,但可在调用函数内使用”      详见47楼。

或者,以后可以出一个goto版本的,让它在程序内部也可以用swithc。

出0入0汤圆

发表于 2013-9-22 23:16:22 | 显示全部楼层
目前我还看不懂楼主的内容
不过楼主说的“而且此间隔在任务执行过程中是可以修改的。”这一条好象不太好,是不是会造成调度器时标的改变,影响计时的准确性?

出0入0汤圆

发表于 2013-9-23 08:32:34 来自手机 | 显示全部楼层
summarize 发表于 2013-9-22 20:46
#define SCH_CURR_LINE                (SCH_UINT8)(__LINE__+(!__LINE__))         ->这个定义是不是有 ...

倒不如直接修改成Short型,仅仅是多了二字节!十个任务也接20字节!要不就规定程序不要超过255行,但是这个对于有些人会很蛋疼的!

出0入0汤圆

 楼主| 发表于 2013-9-23 11:15:09 | 显示全部楼层
本帖最后由 summarize 于 2013-9-23 11:23 编辑
kinsno 发表于 2013-9-23 08:32
倒不如直接修改成Short型,仅仅是多了二字节!十个任务也接20字节!要不就规定程序不要超过255行,但是这 ...


不是不能超过255行,而是这样有可能会出现如0x00行和0x100行重复的情况,这时编译能不过,其实加个回车把0x100变成0x101就可以了。只是这样不太方便。至于改为两字节,或许以后由用户自己定义就好了。

出0入0汤圆

 楼主| 发表于 2013-9-23 11:22:16 | 显示全部楼层
zhwm3064 发表于 2013-9-22 23:16
目前我还看不懂楼主的内容
不过楼主说的“而且此间隔在任务执行过程中是可以修改的。”这一条好象不太好, ...

不会的,计时精度还是基于调度节拍时间。

出0入0汤圆

发表于 2013-11-3 17:28:21 | 显示全部楼层
这个有些情况下还是不错的选择。
楼主的使用说明也整理的不错呢。

出0入0汤圆

发表于 2013-11-3 21:30:24 | 显示全部楼层
支持楼主

出0入0汤圆

发表于 2013-11-5 11:04:16 | 显示全部楼层
虽然TCB要加个变量计数;但 SCHCurTaskDly(Ticks) 用来
将当前任务延时X个时间节拍后恢复 的方式不错。

出0入0汤圆

发表于 2013-11-5 14:07:21 | 显示全部楼层
支持



出0入0汤圆

发表于 2013-11-5 14:57:19 | 显示全部楼层
支持楼主

出0入0汤圆

发表于 2013-11-24 21:50:58 | 显示全部楼层
菜鸟学习有点困难

出0入0汤圆

发表于 2013-11-25 14:10:28 | 显示全部楼层
支持,收藏了,有时间学习下!

出0入0汤圆

发表于 2013-12-16 21:50:36 | 显示全部楼层
#define SCHTaskEnd()          ;}; SchLc=0; 这句定义的格式看不懂啊        ?麻烦大哥解释下啊

出0入0汤圆

 楼主| 发表于 2013-12-17 10:18:34 | 显示全部楼层
aming2046 发表于 2013-12-16 21:50
#define SCHTaskEnd()          ;}; SchLc=0; 这句定义的格式看不懂啊        ?麻烦大哥解释下啊
...

//看如下实例
void vLedTest(void)
{
  SCHTaskBegin();//函数开头,必须加入

  while (1)
  {
    mGreenLedToggle();        //LED取非

    SCHCurTaskDly(500 / SCH_SYS_TICKS_MS);  //delay 500ms
  }

  SCHTaskEnd();//函数结尾,必须加入
}

//将宏展开后如下:
void vLedTest(void)
{
  //SCHTaskBegin();
  static SCH_UINT8 SchLc = 0;
  switch (SchLc)
  {
  //-------------------------------------------------------
  case 0:

    while (1)
    {
      mGreenLedToggle();  //LED取非

      //SCHCurTaskDly(500 / SCH_SYS_TICKS_MS);  //delay 500ms
      {
        SchLc = SCH_CURR_LINE;//下次进来时跳转到"SCH_CURR_LINE"
        pCurTCB->TimeCounter = 500 / SCH_SYS_TICKS_MS;
      }
      return; //直接返回,下次进来时跳转到"SchLc = SCH_CURR_LINE"
    //-------------------------------------------------------
    case SCH_CURR_LINE:
    }
    //SCHTaskEnd();
    ;
  };
  SchLc = 0;  //下次进来时回到" case 0:"

}

出0入0汤圆

发表于 2013-12-17 19:37:17 | 显示全部楼层
Mark 留名~!有时间慢慢品味

出0入0汤圆

发表于 2013-12-17 20:52:57 | 显示全部楼层
顶一下。。。。。。。。。。。。

出0入0汤圆

发表于 2013-12-17 20:54:34 | 显示全部楼层
大哥我按照你的方法把他们定义的展开了,但是展开后这个函数我怎么看不懂啊
void vAdcResultSend(void)
{
  static uint8_t s_u8AdcBuffer[ADC_BUF_SIZE];
  static uint8_t i;
  static uint8_t *pDataBuffer, u8DataSize;

  //SCHTaskBegin();
  
  static SCH_UINT8 SchLc=0;
  switch(SchLc)
  {
   case 0://跳转开始,中间可插入延时,调用子函数;(适用主/子函数)

  while (1)
  {   
    //SCHCurTaskDly(1000 / SCH_SYS_TICKS_MS);   //delay 1000ms
   
    {
      SchLc=SCH_CURR_LINE;
      pCurTCB->TimeCounter=Ticks;
   
    }return ;
  case SCH_CURR_LINE:

    //SCHTaskQpend();//任务等待消息
   
    {
      SchLc=SCH_CURR_LINE;
      pCurTCB->TimeCounter=SCH_TASK_PEND;
      pCurTCB->pData=(void *)0;
    }
    return;
  case SCH_CURR_LINE:
   


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

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

    for (i = 0; i < u8DataSize; i++)
    {
      s_u8AdcBuffer[i] = *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)
    {
      //出错处理
    }   
  };
};
  SchLc=0;
  
  //SCHTaskEnd();
}

出0入0汤圆

发表于 2013-12-17 20:57:00 | 显示全部楼层
case 0:后面的while(1){}大括号里面怎么没有switch而是直接出现了两个 case SCH_CURR_LINE:

出0入0汤圆

发表于 2013-12-17 21:27:09 | 显示全部楼层
这个一定要仔细研读
谢谢分享

出0入0汤圆

 楼主| 发表于 2013-12-17 22:02:53 | 显示全部楼层
aming2046 发表于 2013-12-17 20:57
case 0:后面的while(1){}大括号里面怎么没有switch而是直接出现了两个 case SCH_CURR_LINE: ...

将"while(1){}"看作一般的语句即可,后面的"case SCH_CURR_LINE: ..." 与 最前的"case 0:"是并列关系.即:
switch(*)
{
case 0: ...

case SCH_CURR_LINE: ...

case SCH_CURR_LINE: ...
}

出0入0汤圆

发表于 2013-12-17 22:43:27 | 显示全部楼层
那你的意思是
switch(*)
{
case 0: ...

case SCH_CURR_LINE: ...

case SCH_CURR_LINE: ...
}
等价于switch(*)
{
case 0: while{case SCH_CURR_LINE: ...

case SCH_CURR_LINE: ...}


}

出0入0汤圆

 楼主| 发表于 2013-12-17 23:28:29 | 显示全部楼层
aming2046 发表于 2013-12-17 22:43
那你的意思是
switch(*)
{

是的.         

出0入0汤圆

发表于 2013-12-18 20:52:32 | 显示全部楼层
楼主能否帮我看看这段语句是什么意思
for (i = 0; i < TaskNumberSum; i++)
    {
      if ((pTCB->TimeCounter != SCH_TASK_PEND)
          && (pTCB->TimeCounter > 0))
      {
        pTCB->TimeCounter--;
      }

      pTCB = pTCB->pNextTCB;
    }

出0入0汤圆

 楼主| 发表于 2013-12-19 14:22:02 | 显示全部楼层
aming2046 发表于 2013-12-18 20:52
楼主能否帮我看看这段语句是什么意思
for (i = 0; i < TaskNumberSum; i++)
    {

    for (i = 0; i < TaskNumberSum; i++)                         //遍历所有任务的任务控制块
    {
      if ((pTCB->TimeCounter != SCH_TASK_PEND)        //该任务没有被挂起
          && (pTCB->TimeCounter > 0))                                //同时该任务控制块的计数器值大于0(后面要自减,所以必须大于0,不然会下溢)
      {
        pTCB->TimeCounter--;                                      //任务控制块的计数器自减1(为0时任务会被执行-->在别的地方判断)
      }

      pTCB = pTCB->pNextTCB;                                   //指向下一个任务的,任务控制块
    }

出0入0汤圆

发表于 2013-12-21 17:30:09 | 显示全部楼层
多谢楼主啊,对了楼主能否将你的这个程序的执行流程说明一下啊?呵呵,我的理解是:首先创建一个循环链表,然后将循环链表里面的6个成员分别指向6个任务,然后用定时器来轮询这个链表来调度任务,任务与任务之间,中断与任务之间还可以通过发送消息来判断任务是否处于可执行状态。

出0入0汤圆

 楼主| 发表于 2013-12-21 18:30:13 | 显示全部楼层
aming2046 发表于 2013-12-21 17:30
多谢楼主啊,对了楼主能否将你的这个程序的执行流程说明一下啊?呵呵,我的理解是:首先创建一个循环链表, ...

你的理解基本正确,看下任务控制块结构体:

struct SchTcb
{
#if SCH_CFG_Q_EN > 0u
  void                        *pData;       //消息指针
  SCH_UINT8                 Size;          //消息的大小
#endif

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

1.对每个任务都有一个任务控制块(SchTcb).

2.然后让这些任务控制块首尾相连构成循环链表(pNextTCB 来实现).

3.任务控制块的任务指针指向该任务(pTask 指向任务).

4.任务调度:
    A.每间隔特定时间对TimeCounter进行自减,自减条件是TimeCounter大于0且不等于0xffff(些值表示任务挂起),当TimeCounter为0进会被执行,且每执行完一个任务后就会重新从头查找一遍哪个任务满足执行条件,这样即实现了创建任务的顺序即任务的优先级顺序(最先创建的任务优先级最高),在每个任务执行后重新设置TimeCounter为某个固定值即可实现任务定时运行.

   B.任务与任务之间,中断与任务之间,可通过设置TimeCounter的值为0xffff来挂起任务,或设置TimeCounter的值为0来恢复任务.

   C.消息实现:如"B"中所述通过设置TimeCounter的不同值来实现任务的挂起或运行,再配合pData和Size来携带消息内容及数据长度.即实现了任务的消息等待(任务挂起),和任务的消息发送(恢复某个任务同时携带有数据).



出0入0汤圆

发表于 2014-1-2 17:00:50 | 显示全部楼层
summarize 发表于 2013-12-21 18:30
你的理解基本正确,看下任务控制块结构体:

struct SchTcb

话说你的GOTO版本呢?

switch你说是可以在在非任务函数(包函BEGIN的那个函数)的其它任意函数内调用,如下:
void A(void)
{
        switch()
        {

        }
}

void B(void)
{
        A();
        switch()
        {

        }
}

void C(void)
{
        B();
        switch()
        {

        }
}

最后来一个包含BEGIN的任务,如下:

void Task(void)
{
        BEGIN();
        while(1)
        {
                A();
                B();
                C();
                Delay()或wait()
        }
        END();

}

但是我总是觉得这里面如果万一出现一点差错,就是毁灭性的打击啊!虽然号称是在非任务函数内可以使用!

在上贴中有提到过,你说会出一个版本是GOTO,何时亮剑?哈哈!敬待!


出0入0汤圆

 楼主| 发表于 2014-1-23 09:34:39 | 显示全部楼层
kinsno 发表于 2014-1-2 17:00
话说你的GOTO版本呢?

switch你说是可以在在非任务函数(包函BEGIN的那个函数)的其它任意函数内调用, ...



技术人员不应该说万一来假设吧,这个是没有万一的:不能使用"switch"是因为"SCHTaskBegin();"已经使用过一次"switch",编译通不过.展开后就清楚了,而在非任务函数中没有使用过"switch",所以能使用.

protothread的GOTO版本,我也是在本坛中看到其它帖子中有提到过.我也没有去仔细了解啊,不好意思了.

最近百事缠身啊.......

出0入0汤圆

发表于 2014-1-23 09:49:57 | 显示全部楼层
summarize 发表于 2014-1-23 09:34
技术人员不应该说万一来假设吧,这个是没有万一的:不能使用"switch"是因为"SCHTaskBegin();"已经使用过一 ...


技术人员不说万一,我只是习惯性的一种怀疑自己,每一次打样,每一次产品,我都心惊胆颤,尤如初学者,哈哈,自己用的模块真是百测千测.可能你不是太理解我这种心情,有一个词叫啥来着,[强迫症].可能你会理解一些吧.
说[万一]只是一种习惯性的谨慎,每次用东西,我都会先否定,再认定; 关键我是怕[__LINE__]这个家伙,毕竟所有的东西是建立在它的基础上,它才是本调度的核心,否则一切都无源之木啊。
哈哈,看看年底,再弄一个复杂的东西,放在家里跑,有机会扛到现场的大电机旁边,希望明年可以经过我的考验,这样就可以用上来了!
PS:说话你现在回家了?票买着了?

出0入0汤圆

 楼主| 发表于 2014-1-23 10:11:17 | 显示全部楼层
kinsno 发表于 2014-1-23 09:49
技术人员不说万一,我只是习惯性的一种怀疑自己,每一次打样,每一次产品,我都心惊胆颤,尤如初学者,哈哈,自 ...


1.理解你的心情,最核心的东西是参考了"Protothread"的,而"Protothread"是UIP的核心调度方式,本坛有不少相关帖子.

2.谢谢关心,是家人回家,我不回,帮忙看店,现在想在网上找黄牛搞7号,8号的回程票,但是不知道门路和怎么操作.

出0入0汤圆

 楼主| 发表于 2014-1-23 10:35:28 | 显示全部楼层
kinsno 发表于 2014-1-2 17:00
话说你的GOTO版本呢?

switch你说是可以在在非任务函数(包函BEGIN的那个函数)的其它任意函数内调用, ...

放狗跑了一下,找到了一个帖子,里面的回复提到了GOTO实现

http://bbs.ednchina.com/BLOG_ARTICLE_1902535.HTM

但是这GOTO是用汇编来搞定的,这样移植性就不好了.

出0入0汤圆

发表于 2014-1-23 10:42:18 | 显示全部楼层
summarize 发表于 2014-1-23 10:35
放狗跑了一下,找到了一个帖子,里面的回复提到了GOTO实现

http://bbs.ednchina.com/BLOG_ARTICLE_1902535 ...

3KS,哈哈!

出0入0汤圆

发表于 2014-4-12 17:54:21 | 显示全部楼层
很好很强大的调度器!标记

出0入0汤圆

发表于 2014-4-17 10:45:23 | 显示全部楼层
好贴,默默顶起

出0入0汤圆

发表于 2014-4-17 11:03:34 | 显示全部楼层
好贴好贴,顶起!

出0入0汤圆

发表于 2014-4-17 13:19:31 | 显示全部楼层
楼主还在吗?这个东西挺有意思的,请教个问题

SCH_CURR_LINE                (SCH_UINT8)(__LINE__+(!__LINE__))       
为什么不定义成SCH_UINT16, 貌似一个文件255行很容易超过啊

出0入0汤圆

发表于 2014-4-17 14:41:11 | 显示全部楼层
   帮顶一下!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 18:24

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表