工程结构
时间片调度相关的代码位于SCHEDULER目录下的Scheduler和Task文件中,匿名飞控原本的时钟由传感器输出的1ms脉冲实现的外部中断提供,为使时间片调度算法可以用于一般的工程,此处改为使用定时器TIM7的1ms定时器中断,位于HAEDWARE目录下的timer文件(原因:f407的TIM7无法用于PWM输出,使用定时器TIM7可减少移植的冲突,如有需要,可改为其他定时器)
具体代码
1.Task.c用于存放用户任务函数,线程中的用户任务的执行不应占用太长时间
#include "Task.h"
#include "usart.h"
#include "delay.h"
void task1()
{
printf("任务1执行\r\n");
}
void task2()
{
printf("任务2执行\r\n");
}
void task3()
{
printf("任务3执行\r\n");
}
2.Scheduler.c,存放有调度器初始化函数,调度器运行函数,用户任务在此文件中进行注册(如task2()和task1()函数)
#include "Scheduler.h"
#include "Task.h"
static void Loop_1000Hz(void)
{
}
static void Loop_500Hz(void)
{
}
static void Loop_200Hz(void)
{
}
static void Loop_100Hz(void)
{
}
static void Loop_50Hz(void)
{
}
static void Loop_20Hz(void)
{
task2();
}
static void Loop_2Hz(void)
{
task1();
}
static sched_task_t sched_tasks[] =
{
{Loop_1000Hz, 1000, 0, 0},
{Loop_500Hz, 500, 0, 0},
{Loop_200Hz, 200, 0, 0},
{Loop_100Hz, 100, 0, 0},
{Loop_50Hz, 50, 0, 0},
{Loop_20Hz, 20, 0, 0},
{Loop_2Hz, 2, 0, 0},
};
#define TASK_NUM (sizeof(sched_tasks) / sizeof(sched_task_t))
void Scheduler_Setup(void)
{
uint8_t index = 0;
for (index = 0; index < TASK_NUM; index++)
{
sched_tasks[index].interval_ticks = 1000 / sched_tasks[index].rate_hz;
if (sched_tasks[index].interval_ticks < 1)
{
sched_tasks[index].interval_ticks = 1;
}
}
}
void Scheduler_Run(void)
{
uint8_t index = 0;
for (index = 0; index < TASK_NUM; index++)
{
uint32_t tnow = GetTimeMs();
if (tnow - sched_tasks[index].last_run >= sched_tasks[index].interval_ticks)
{
sched_tasks[index].last_run = tnow;
sched_tasks[index].task_func();
}
}
}
相关说明匿名的注释已经说的很清楚了,这里做简单补充和移植说明。Scheduler_Setup函数是调度器初始化函数,需在主函数的while(1)前调用一次。Scheduler_Run函数是调度器运行函数,需放到main函数的while(1)中循环调用,主要内容为获取系统当前时间,用当前时间减去上一次执行的时间,若大于等于该线程的执行周期,则执行对应的线程,线程中的用户任务的执行不应占用太长时间,否则会阻塞线程。匿名飞控的调度器是软实时调度,而非FreeRTOS式的硬实时调度。(硬实时是系统执行一个程序,只要规定的时间用完,那么就一定跳出去,不管执行完还是没有执行完。软实时没有硬性的时间限制,它可以推迟一段时间来完成自己的任务。)Scheduler_Run中调用的GetTimeMs()函数位于timer.c文件中,如下。 3.timer.c,存放定时器初始化、定时器中断和GetTimeMs()函数
#include "timer.h"
#include "usart.h"
static uint64_t TimeMs = 0;
void TIM7_IRQHandler(void)
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
{
TimeMs++;
TIM_ClearITPendingBit(TIM7, TIM_IT_Update );
}
}
void TIM7_Int_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE );
TIM_Cmd(TIM7,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
uint32_t GetTimeMs(void)
{
return TimeMs;
}
4.main.c,软硬件初始化,调用Scheduler_Run()开启调度器
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "timer.h"
#include "Scheduler.h"
int main(void)
{
uart_init(115200);
delay_init(168);
TIM7_Int_Init(10-1,8400-1);
Scheduler_Setup();
while(1){
Scheduler_Run();
}
}
如有需要,这里提供完整工程可供参考。
https:
|