本帖最后由 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.任务控制块数据结构
- 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; //下一个任务控制块指针
- };
复制代码b.调度核心
- void SCHTaskSchedStart(void)
- {
- SCHED_SART:
-
- pCurTCB = pFirstTCB; //指向第一个创建的任务,之后按创建时的顺序执行下去
- while (1)
- {
- SCHTimeTick(); //如果任务Tick满足条件,则将其置于可执行状态
- if (SCH_TASK_RUN == pCurTCB->TimeCounter) //任务处于可执行状态
- {
- pCurTCB->TimeCounter = SCH_TASK_PEND; //设置为挂起状态,保证任务只执行一次
- pCurTCB->pTask(); //执行当前任务控制块指向的任务
- goto SCHED_SART; //每执行完一个任务,都重新查找一次可执行最高优先级任务
- }
- pCurTCB = pCurTCB->pNextTCB; //指向下一个任务控制块,查找下个任务是否可执行
- }
- }
复制代码“schedule.c”和"schedule.h"已经设置为只读属性,无特殊情况不建议修改,"sch_cfg.h"则为开放给用户的接口,可定义数据类型、调度器节拍和配置是否使用消息。
本人水平有限,欢迎大家测试、指正不足。
寄存器版本出来了.附件如下!
寄存器版本开启了TIM1_PWM(两个通道)中断和IO下降沿中断(PD4),其中IO这个中断在"iostm8s103f3.h"是没有定义的,这个头文件只定义了从0x0c之后的中断,于是我自己根据手册增加了前面0x00~0x0b中断定义.同时将寄存器版本的所有初始化代码贴出来,这些初始化代码都有详细的注释,有了它,你甚至可以不用看手册了,它必将是初学者的福音及工程师的利器.
- /* Includes ------------------------------------------------------------------*/
- #include "user.h"
- /* Private typedef -----------------------------------------------------------*/
- /* Private define ------------------------------------------------------------*/
- /* Private macro -------------------------------------------------------------*/
- /* Private variables ---------------------------------------------------------*/
- /* Private function prototypes -----------------------------------------------*/
- /* Private functions ---------------------------------------------------------*/
- static void TIM1_PWM_Config(void);
- static void TIM4_Config(void);
- static void GPIO_Config(void);
- static void UART1_Config(void);
- static void ADC_Config(void);
- static void IWDG_Config(void);
- static void WWDG_Config(void);
- static void IT_Config(void);
- /* Public functions ----------------------------------------------------------*/
- void CLK_Config(void)
- {
- //配置内部16M时钟作为时钟源
- //CLK_ICKR_HSIEN = 1; //使能内部高速时钟(复位值)
- CLK_ICKR_bit.LSIEN = 1; //使能内部低速时钟
- CLK_CKDIVR_bit.HSIDIV = 0; //主时钟分频 0-3对应 1 2 4 8分频
- CLK_CKDIVR_bit.CPUDIV = 0; //CPU时钟分频 0-7对应 1 2 4 8 16 32 64 128分频
- //CLK_SWR = 0xE1; //HSI为主时钟源(复位值)
- //CLK_SWCR_SWEN = 1; //使能自动切换时钟
- //CLK_SWR = 0xD2; //0xD2:LSI为主时钟源(仅当LSI_EN选项位为1时)
- //CLK_SWR = 0xB4; //0xB4:HSE为主时钟源
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- //外设时钟控制寄存器 CLK_PCKENR1
- // CLK_PCKENR1 = 0x00; //复位值为0xff,即默认为开启状态
- // PCKEN10: I2C
- // PCKEN11: SPI
- // PCKEN12: UART1
- // PCKEN13: UART2/3 实测此位才是UART1时钟使能(stm8s103f3)
- // PCKEN14: TIM4
- // PCKEN15: TIM2
- // PCKEN16: TIM3
- // PCKEN17: TIM1
- CLK_PCKENR1 = b10011000; //使能TIM1,TIM4,UART1 时钟
- //外设时钟控制寄存器 CLK_PCKENR2
- // CLK_PCKENR2 = 0x00; //复位值为0xff,即默认为开启状态
- // PCKEN20: Reserved
- // PCKEN21: Reserved
- // PCKEN22: AWU
- // PCKEN23: ADC
- // PCKEN24: Reserved
- // PCKEN25: Reserved
- // PCKEN26: Reserved
- // PCKEN27: CAN
- CLK_PCKENR2 = b00001000; //使能 ADC 时钟,
- //LK_CSSR_CSSEN = 1; //使能时钟监控功能,外间时钟出问题时切换到内部时钟源HSI/8 CLK_CSSR中的CSSD位被置位
- //LK_CCOR = ; //时钟输出寄存器
- }
- void Peripherals_Config(void)
- {
- GPIO_Config();
- TIM1_PWM_Config();
- TIM4_Config();
- UART1_Config();
- ADC_Config();
- IWDG_Config();
- WWDG_Config();
- IT_Config(); //中断相关配置
- }
- static void IWDG_Config(void)
- {
- IWDG_KR = 0x55; //关写保护
- IWDG_PR = 0x00; //预分频系数4 0-6对应 4 8 16 32 64 128 256 分频
- IWDG_RLR = 0xFF; //最长超时15.90 ms
- IWDG_KR = 0xAA; //恢复写保护,同时相关于清狗指令->用IWDG_RLR的数值刷新计数器的内容,避免了产生看门狗的复位
- //其实以上看门狗配置均为上电复位值
- IWDG_KR = 0xCC; //启动独立看门狗
- IWDG_KR = 0xAA; //清狗
- }
- static void WWDG_Config(void)
- {
- WWDG_WR = 0x7f;
- WWDG_CR = (uint8_t)(0x80 | WWDG_COUNTER_INIT);
- WWDG_WR = (uint8_t)((uint8_t)(~0x80) & (uint8_t)(0x40 | WWDG_WINDOW_VALUE));
- }
- static void GPIO_Config(void)
- {
- PA_ODR = b00000000; //初始化输出低电平
- PA_DDR = b11111111; // 1=output
- PA_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PA_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- PB_ODR = b00000000; //初始化输出低电平
- PB_DDR = b11111111; // 1=output PB4 INPUT
- PB_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PB_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- PC_ODR = b00000000; //初始化输出电平
- PC_DDR = b11111111; // 1=output PC4 INPUT
- PC_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PC_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- // 如果硬件上没有连接RST脚,则SWIM引脚(PD1)不能配置为输出口,否则不能连上ST-LINK
- PD_ODR = b00100000; //初始化输出低电平 PD5=TX初始化输出高电平,PD6=RX配置为输入
- PD_DDR = b10100011; // 1=output 0=input PD2=AIN3 PD3=AIN4
- PD_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PD_CR2 = b00010000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- //PD4作为下降沿中断测试输入引脚,配置为上拉输入
-
- PE_ODR = b00000000; //初始化输出低电平
- PE_DDR = b11111111; // 1=output
- PE_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PE_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- }
- static void TIM1_PWM_Config(void)
- {
- //须要在选项字节中配置使能对应的PWM功能管脚,同时开启对应的时钟
- TIM1_PSCRH = 0x00; //预分频器 16bit fCK_CNT = fCK_PSC / (PSCR[15:0] + 1)
- TIM1_PSCRL = 0x00;
- TIM1_ARRH = (uint8_t)((800 - 1) >> 8); //自动重载值 16bit 即周期寄存器 16M/800=20K
- TIM1_ARRL = (uint8_t)((800 - 1));
- //TIM1_CR1
- //TIM1_CR1_bit.CEN = 1; //CEN:允许计数,最后再开启
- TIM1_CR1_bit.UDIS = 0; //UDIS:禁止更新
- TIM1_CR1_bit.URS = 0; //URS:更新请求源
- TIM1_CR1_bit.OPM = 0; //0:在发生更新事件时,计数器不停止;
- TIM1_CR1_bit.DIR = 0; //0:计数器向上计数;
- TIM1_CR1_bit.CMS = 0; //00:边沿对齐模式。计数器依据方向位(DIR)向上或向下计数。
- TIM1_CR1_bit.ARPE = 1; //1:TIM1_ARR寄存器由预装载缓冲器缓冲。
- TIM1_EGR_bit.UG = 1; //产生一次更新事件,更新分频系数与TIMx_CNTR
- TIM1_RCR = 0x00; //重复计数寄存器,即Reload事件发生了多少次:TIM1_RCR+1
- //这意味着在PWM模式中,(TIM1_RCR+1)对应着:
- //- 在边沿对齐模式下,PWM周期的数目;
- //- 在中心对称模式下,PWM半周期的数目;
- // 通道1;
- TIM1_CCMR1_bit.CC1S = 0; //CC1S[1:0]:捕获/比较1 选择
- TIM1_CCMR1_bit.OC1FE = 0; //OC1FE:输出比较1 快速使能
- TIM1_CCMR1_bit.OC1PE = 1; //OC1PE:输出比较1预装载使能
- TIM1_CCMR1_bit.OC1M = 7; //110:PWM模式1,111:模式2
- TIM1_CCMR1_bit.OC1CE = 0; //0:OC1REF 不受ETRF输入(来自TIM1_TRIG引脚)的影响; 1:一旦检测到ETRF输入高电平,OC1REF=0。
- TIM1_CCER1_bit.CC1E = 1; //1=输出使能
- TIM1_CCER1_bit.CC1P = 0; //0=高电平有效 P
- TIM1_CCER1_bit.CC1NE = 1; //1=输出使能 --
- TIM1_CCER1_bit.CC1NP = 0; //0=高电平有效--互补输出 N
- TIM1_CCR1H = 0x00;
- TIM1_CCR1L = 0x00; //占空比寄存器,CH1
- // 通道2;
- TIM1_CCMR2_bit.CC2S = 0; //CC1S[1:0]:捕获/比较1 选择
- TIM1_CCMR2_bit.OC2FE = 0; //OC1FE:输出比较1 快速使能
- TIM1_CCMR2_bit.OC2PE = 1; //OC1PE:输出比较1预装载使能
- TIM1_CCMR2_bit.OC2M = 7; //110:PWM模式1,111:模式2
- TIM1_CCMR2_bit.OC2CE = 0; //0:OC1REF 不受ETRF输入(来自TIM1_TRIG引脚)的影响; 1:一旦检测到ETRF输入高电平,OC1REF=0。
- TIM1_CCER1_bit.CC2E = 1; //1=输出使能
- TIM1_CCER1_bit.CC2P = 0; //0=高电平有效 P
- TIM1_CCER1_bit.CC2NE = 1; //1=输出使能 -- //
- TIM1_CCER1_bit.CC2NP = 0; //0=高电平有效--互补输出 N
- TIM1_CCR2H = 0x00;
- TIM1_CCR2L = 0x00; //占空比寄存器,CH2
- // TIM1_CR2 = ;
- // TIM1_SMCR = ; 从模式控制寄存器
- // TIM1_ETR = ; 外部触发寄存器
- //TIM1_IER 中断使能寄存器
- TIM1_IER_bit.UIE = 1; //更新中断
- TIM1_IER_bit.CC1IE = 0; //捕获/比较1中断
- TIM1_IER_bit.CC2IE = 0; //捕获/比较2中断
- TIM1_IER_bit.CC3IE = 0; //捕获/比较3中断
- TIM1_IER_bit.CC4IE = 0; //捕获/比较4中断
- TIM1_IER_bit.COMIE = 0; //COM中断
- TIM1_IER_bit.TIE = 0; //触发中断
- TIM1_IER_bit.BIE = 0; //刹车中断
- // TIM1_SR1 = ; 状态寄存器 1
- // TIM1_SR2 = ; 状态寄存器 2
- // TIM1_EGR = ; 事件产生寄存器
- //刹车寄存器
- TIM1_BKR_bit.MOE = 1; //1:如果设置了相应的使能位(TIM1_CCERX寄存器的CCIE位),则使能OC和OCN输出
- TIM1_CR1_bit.CEN = 1; //CEN:允许计数
- }
- /**
- * @brief Configure TIM2 to generate an update interrupt each 0.5ms
- * @param None
- * @retval None
- */
- void TIM4_Config(void)
- {
- //IM4_PSCR_bit.PSC = 7; //分频值: 2^PSC[2:0] 定时器时钟源为内部时钟(fMASTER)。 (1/16M)*128=8us
- //IM4_ARR = 124; //自动重载值 (124+1)*8us=1ms
- TIM4_PSCR_bit.PSC = 4; //分频值: 2^PSC[2:0] 1/16M)*2^4=1us
- TIM4_ARR = 99; //自动重载值 (99+1)us
- TIM4_EGR_bit.UG = 1; //产生一次更新事件,更新分频系数与TIM4_CNTR
- TIM4_CR1_bit.ARPE = 1; //1:TIM4_ARR寄存器通过缓冲预装载
- TIM4_SR_bit.UIF = 0; //TIM4 溢出中断标志
- TIM4_IER_bit.UIE = 1; //使能TIM4 溢出中断
- TIM4_CR1_bit.CEN = 1; //使能TIM4
- }
- /**
- * @brief UART1 Configuration for interrupt communication
- * @param None
- * @retval None
- */
- //
- static void UART1_Config(void)
- {
- WORD_UNION UART1_BRR;
- UART1_CR1_bit.PIEN = 0; //校验中断使能 0:中断被禁止 1:当USART_SR中的PE为1时,产生USART中断
- UART1_CR1_bit.PS = 0; //0:偶校验。 1:奇校验
- UART1_CR1_bit.PCEN = 0; //1:奇偶校验控制被使能
- UART1_CR1_bit.WAKE = 0; //唤醒方法
- UART1_CR1_bit.M = 0; //8bit数据格式
- UART1_CR1_bit.UART0 = 0; //0:UART使能 1:UART预分频器和输出禁用
- UART1_CR1_bit.T8 = 0; //9bit数据格式 下的发送数据位8
- UART1_CR1_bit.R8 = 0; //9bit数据格式 下的接收数据位8
- UART1_CR3_bit.STOP = 0; //00:1个停止位;01:保留 10:2个停止位;11:1.5个停止位;
- UART1_BRR.all = 1666; //16M/1666=9603
- //UART1_BRR2=UART_DIV[15:12]高4位 UART_DIV[3:0]低4位
- UART1_BRR2 = UART1_BRR.byte.H & 0xf0;
- UART1_BRR2 |= UART1_BRR.byte.L & 0x0f;
-
- //UART1_BRR1=UART_DIV[11:4]
- UART1_BRR1 = (uint8_t)(UART1_BRR.all>>4); //中间8位
- //UART1_CR2_bit.SBK = 1; //SBK: 发送断开帧
- //UART1_CR2_bit.RWU = 1; //RWU: 接收唤醒
- UART1_CR2_bit.REN = 1; //接收使能
- UART1_CR2_bit.TEN = 1; //发送使能
- //UART1_CR2_bit.ILIEN= 1; //ILIEN: IDLE中断使能
- UART1_CR2_bit.RIEN = 1; //接收中断使能 1:当USART_SR中的OR或者RXNE为1时,产生USART中断。
- //UART1_CR2_bit.TCIEN= 1; //发送完成中断使能 1:当USART_SR中的TC为1时,产生USART中断。要发送时再使能中断(会立即产生中断)
- //UART1_CR2_bit.TIEN = 1; //发送中断使能 1:当USART_SR中的TXE为1时,产生USART中断。
- //UART1_SR_bit.TC &= 0; //读该位来清零
- //UART1_DR = 0xaa; //写入要发送的数据
- }
- /**
- * @brief Configure ADC
- *
- * @param None
- * @retval None
- */
- static void ADC_Config(void)
- {
- // 当作为模拟输入时可以关闭输入施密特触发器来降低功耗
- ADC_TDRH = b00000000; //1=ADC输入禁止施密特触发 ***********ADC时应选浮空输入,无中断
- ADC_TDRL = b00011000; //共16通道 AIN3-AIN4
- ADC_CR1_bit.ADON = 0; //ADON: A/D 转换开/关
- ADC_CR1_bit.CONT = 0; // 0:单次转换模式 1:连续转换模式
- ADC_CR1_bit.SPSEL = 0; // 预分频选择位 000->111对应 2 3 4 6 8 10 12 18分频
- //ADC_CR2_bit.SCAN = 1; //1:使能扫描模式 0:不使能扫描模式
- ADC_CR2_bit.ALIGN = 1; //1:数据右对齐。(低8字节在ADC_DRL寄存器,其余高字节位在ADC_DRH寄存器)读顺序
- //应先读低位,再读高位字节或整个一起读
- ADC_CR2_bit.EXTSEL = 0; //外部事件触发和启动ADC 00:内部定时器1 TRG事件 01:ADC_ETR 引脚上的外部中断 其它值保留
- ADC_CR2_bit.EXTTRIG = 0; //EXTTRIG: 外触发使能位
-
- //ADC_CR3_bit.OVR = 0; //1:数据缓存发生了数据溢出事件
- ADC_CR3_bit.DBUF = 0; //1:数据缓存功能使能,扫描模式时使用.
- ADC_CSR_bit.CH = 4; // CH[3:0] : 选择转换通道 或指定从AN0到ANx扫描转换
- //ADC_CSR_bit.AWDIE = 0; //1: 使能AWD模拟看门狗中断
- //ADC_CSR_bit.EOCIE = 1; //1:使能转换结束中断
- //ADC_CSR_bit.AWD = 0; //AWD: 模拟看门狗标志
- ADC_CSR_bit.EOC = 0; //EOC: 转换结束
-
- ADC_CR1_bit.ADON = 1; //第一次置1将把ADC从低功耗模式下唤醒,之后才是启动ADC
- //ADC_DBxRH 缓冲寄存器
- //ADC_DBxRL
- //ADC_DRL 数据寄存器
- //ADC_DRH
- }
- void IT_Config(void)
- {
- // 中断配置------------------------------------------
- //ITC_SPR3_bit.VECT11SPR = 0;//Timer1最低优先级,
- //ITC_SPR4_bit.VECT15SPR = 0;//Timer3最低优先级,
- //ITC_SPR6_bit.VECT21SPR = 0; //UART1/3接收满 最低优先级
- //ITC_SPR6_bit.VECT22SPR = 2; //adc中断为最高优先级
- //ITC_SPR6_bit.VECT23SPR = 0; //Timer4最低优先级,2为最高,3为禁止优先级(复位默认值)
- //EXTI_CR1:外部中断控制寄存器1 设置何种边沿触发
- EXTI_CR1_bit.PAIS = 0;//[1:0] 00:下降沿和低电平触发
- EXTI_CR1_bit.PBIS = 0;//[3:2] 01:仅上升沿触发
- EXTI_CR1_bit.PCIS = 0;//[5:4] 10:仅下降沿触发
- EXTI_CR1_bit.PDIS = 2;//[7:6] 11:上升沿和下降沿触发
- //EXTI_CR2;
- EXTI_CR2_bit.PEIS = 0;//[1:0] GPIOE,配置同EXTI_CR1
-
- EXTI_CR2_bit.TLIS = 0;//[2] TLI 0:下降沿触发 , 1:上升沿触发
- }
复制代码同时更正了寄存器版本UART发送时的一个bug,i++应该是在给发送寄存器赋值之后才能执行;否则可能会出现少发送数据的情况,附件及修正部分代码如下- for (i = 0; i < u8DataSize; )//i++) //应该是在UART1->DR=x 后才能执行i++
- {
- if(UART1_GetFlagStatus(UART1_FLAG_TXE) == SET)
- {
- //UART1->DR = s_u8TxBuffer[i];
- UART1->DR = s_u8TxBuffer[i++]; //在此执行i++
- //UART1_ITConfig(UART1_IT_TXE, ENABLE);
- SCHCurTaskDly(1 / SCH_SYS_TICKS_MS); //延时发送1个字节所须要的时间
- }
- else
- {
- SCHCurTaskDly(0 / SCH_SYS_TICKS_MS); //无延时,下次直接进来查询
- }
- }
复制代码如果大家有兴趣,后续我可以出stm32版本的.今天下午和SOME PLMM爬山去了,回来心情大好,决定把STM32版本的也搞定(之前已经完成一部分).回来吃饭洗澡后就一直奋斗...直到...
STM32版本终于搞好了,本例是STM32F103C8T6,该死的看门狗在断点时还工作,导致复位(stm8s可不会这样),害我搞得这么晚才搞定.见附件!用到的资源和stm8s的一样,即UART和ADC,同时还配置好了TIM1/2/3,但只启用TIM1,TIM2/3可自行开启.后续再加入8051就停止了(如果响应好再添加PIC和NEC的,富士通和台系的就算了开发工具太麻烦不好验证).
-----------------------------------------------------------------------------
编辑原因:加入私生活^_^
使用说明文档已经出来了:
自制类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文档:
同时如果可以,请版主帮忙更新到LZ位,方便大家。
最新调度器版本如下:
鉴于C语言中建议尽量不使用"goto"语句,现将“SCHTaskSchedStart()”函数中的“goto”语句去掉,用"if else"逻辑来实现。同时将版本升级为V1.02
修改细节为将如下函数
- void SCHTaskSchedStart(void)
- {
- SCHED_SART:
-
- pCurTCB = pFirstTCB; //指向第一个创建的任务,之后按创建时的顺序执行下去
- while (1) //环形链表,可以一直循环下去
- {
- SCHTimeTick(); //如果任务Tick满足条件,则将其置于可执行状态
- if (SCH_TASK_RUN == pCurTCB->TimeCounter) //任务处于可执行状态
- {
- pCurTCB->TimeCounter = SCH_TASK_PEND; //设置为挂起状态,保证任务只执行一次
- pCurTCB->pTask(); //执行当前任务控制块指向的任务
- goto SCHED_SART; //每执行完一个任务,都重新查找一次可执行最高优先级任务
- }
- pCurTCB = pCurTCB->pNextTCB; //指向下一个任务控制块,查找下个任务是否可执行
- }
- }
复制代码改为
- void SCHTaskSchedStart(void)
- {
- pCurTCB = pFirstTCB; //指向第一个创建的任务,之后按创建时的顺序执行下去
- while (1) //环形链表,可以一直循环下去
- {
- SCHTimeTick(); //如果任务Tick满足条件,则将其置于可执行状态
- if (SCH_TASK_RUN == pCurTCB->TimeCounter) //任务处于可执行状态
- {
- pCurTCB->TimeCounter = SCH_TASK_PEND; //设置为挂起状态,保证任务只执行一次
- pCurTCB->pTask(); //执行当前任务控制块指向的任务
- pCurTCB = pFirstTCB; //每执行完一个任务,都回到起点重新查找一次可执行最高优先级任务
- }
- else
- {
- pCurTCB = pCurTCB->pNextTCB; //指向下一个任务控制块,查找下个任务是否可执行
- }
- }
- }
复制代码一周热门 更多>