1. 基于时间片的轮询调度算法(仅局限单核CPU芯片):
利用定时器为每个任务函数设定执行时间间隔,保证使用频率高的函数多次被调用,提高单核芯片的资源利用率。如果只是简单地将A、B两个函数放在while中,那么在一定时间内这两个函数调用的次数是一致的,,这样就浪费了单核芯片的资源。
2. 例子:
函数A(100μs执行一次----使用频率高),
函数B(1000μs执行一次----使用频率低)。
那么在1ms 内 函数A 执行了10次,而函数B只执行1次
当然你要保证函数A在100μs内执行完毕、函数B在1000μs内执行完毕
如果超出时间的话系统会变得卡顿。有部分的函数可能没有别执行到。毕竟单核芯片只在很短时间内只做一件事。
3. PS
1、从微观上单核芯片在某一个和短时间时间段只执行一件事。从宏观上 (比如:1S):单核芯片执行多件事情;
2、任务函数最好不要出现delay等延时函数,不然整个系统变得卡顿。
4. 为了更好地理解上述描述,可以结合以下代码理解(基于STM32F103C8T6芯片编写)
- API_Schedule.c
#include "API_Schedule.h"
#include "stdlib.h"
/************任务初始化,每一个结构体都代表一个任务,添加任务和删减任务都在这里完成************/
struct TaskStruct TaskST[]=
{
// 计时 多少ms调用一次 任务运行标志位 任务函数指针
{ 0, 5000, 0, LedFlash},
{ 0, 1000, 0, UARTFC},
{ 0, 500, 0, UserTestMode},
};
/*******************************搭建时间片轮询机制代码框架********************************/
//记录任务数量
u8 TaskCount= sizeof(TaskST)/sizeof(TaskST[0]);
//放在“TIM2_IRQHandler”中断执行,用于任务计时
void OS_IT_RUN(void)
{
u8 i;
for(i=0;i<TaskCount;i++)//遍历所有循环
{
if(!TaskST[i].TaskStatus)
{
if(++TaskST[i].TaskTickNow >= TaskST[i].TaskTickMax)//计时,并判断是否到达定时时间
{
TaskST[i].TaskTickNow = 0;
TaskST[i].TaskStatus = 1;
}
}
}
}
//放在main函数中执行,自带死循环,用于执行挂起的任务
void PeachOSRun(void)
{
u8 j=0;
while(1)
{
if(TaskST[j].TaskStatus)//判断一个任务是否被挂起
{
TaskST[j].FC(); //执行该任务函数
TaskST[j].TaskStatus=0; //取消任务的挂起状态
}
if(++j>=TaskCount)//相当于不断循环遍历所有的任务
j=0;
}
}
/*************************计划表中的任务*****************************/
void LedFlash(void)
{
//printf("Led \n");//调试用,需要自己添加uart外设后再使用这段代码
}
void UARTFC(void)
{
//printf("UARTFC \n");//调试用
}
void UserTestMode(void)
{
//printf("UserTestMode \n");//调试用
}
- API_Schedule.h
#ifndef __API_SCHEDULE_H__
#define __API_SCHEDULE_H__
#include "stm32f10x.h"
//框架运行所需的函数声明
void PeachOSRun(void);
void OS_IT_RUN(void);
/*
用与存储一个任务运行的数据
*/
struct TaskStruct
{
u16 TaskTickNow;//用于计时
u16 TaskTickMax;//设置计时时间
u8 TaskStatus;//任务运行标志位
void (*FC)();//任务函数指针
};
extern struct TaskStruct TaskST[]; //声明为结构体数据,那样多个任务时方便管理
//用于示例的任务函数声明
void LedFlash(void);
void UARTFC(void);
void UserTestMode(void);
#endif
- bsp_time.c
#include "bsp_time.h"
//中断初始化
void Time2_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_Init(&NVIC_InitStruct);
}
//定时器初始化
void Time2_Init(u16 arr,u16 psr)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = arr;
TIM_TimeBaseInitStruct.TIM_Prescaler = psr;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE);
TIM_Cmd(TIM2, ENABLE);
Time2_NVIC_Init();
}
//定时器中断
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET)
{
OS_IT_RUN();
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
- bsp_time.h
#ifndef __TIME_H
#define __TIME_H
#include "stm32f10x.h"
#include "API_Schedule.h"
void Time2_Init(u16 arr,u16 psr);
#endif
- main.c
#include "stm32f10x.h"
#include "bsp_time.h"
#include "API_Schedule.h"
int main(void)
{
Time2_Init(71,1000);
PeachOSRun();
}