STM32F1移植任务调度
什么是任务调度呢?
我在匿名代码任务管理中有提到任务调度,那么我们今天来把任务调度真正的移植出来
任务调度就是把你要做的事件分成一个一个的任务,然后确定任务执行的频率
SYSTICK滴答定时器
作为内核部分的SYSTICK定时器,是每个学单片机的人都必须会的。
因为他不会占用软件执行的时间,它的计时完全是由硬件完成的,
所以我们不需要关心它的执行时间会影响我们的程序。作为内核部分,不管是TI的tiva还是STM32都可以适用。
首先我们呢要初始化SYSTICK滴答定时器,因为默认的情况下,滴答定时器都是关闭的,因此我们需要将他开启。
SYSTICK由几个寄存器控制,分别是
1.控制集状态寄存器 2.重装载值寄存器 3.当前数值寄存器 4.校准数值寄存器(可以参考M3权威指南)
我们需要用到只有两个,一个是控制寄存器,一个重装载值寄存器设置重装载值,具体代码如下
在STM32中可以直接操作寄存器
void systick_Init(void)
{
SysTick->CTRL&=~(1<<2);
SysTick->CTRL|=1<<1;
SysTick->CTRL|=(1<<0);
SysTick->LOAD = (u32)4500;
}
在Tiva中如果找不到寄存器也可以直接操作内存地址
void systick_Init(void)
{
*((volatile u32 *)0xE000E010) &= ~(1<<2);
*((volatile u32 *)0xE000E010) |= 1<<1;
*((volatile u32 *)0xE000E010) |= 1<<0;
*((volatile u32 *)0xE000E014) = (u32)1000;
}
这段代码的含义是:
1.采用外部的时钟源 2.倒计时到0时产生异常,也就是中断 3.使能计数器 4.设置重装载值(可以随意设置)
滴答定时器是从重装载值开始倒计时,倒计时到0时,产生中断,那么我们还要写一个中断函数,
因为STM32默认的中断函数虽然是弱定义,但是默认都是死循环,如果我们不写中断服务函数,那么倒计时到0时,
会进入死循环
volatile u32 sysTickUptime = 0;
static u32 systime_ms = 0 ;
/* 没必要也可以删去 */
void sys_time(void)
{
systime_ms++;
}
/* 滴答定时器中断服务函数 */
void SysTick_Handler(void)
{
sysTickUptime++;
sys_time();
}
u32 SysTick_GetTick(void)
{
return systime_ms;
}
这里我们写了中断服务函数,我们定义一个变量sysTickUptime前面加上是指volatile是指“易变的”,
意思是提醒编译器我们的值是易变的,告诉编译器不要优化但我们计数到0的时候,中断服务函数的变量就会++,
变量相当于一个计数器,当滴答定时器开启时从0开始计数,当滴答定时器计数到0,变量+1,
这样我们就可以获得当前距离开始的时间。当我们外部调用SysTick_GetTick就可以获取到这个变量值了,
就是获取到了当前距离开始的时间。
接下来我们来看主函数
/*任务结构体*/
typedef struct
{
void(*task_run_function)(void);
u16 rate_hz;
u16 period;
u32 last_run_time;
}Task_Run_Struct;
/***************/
/*各个任务的结构体*/
/*
1:函数的指针,指向各个函数的执行
2:频率
3:周期
4:上次运行的时间
*/
static Task_Run_Struct task_run[] =
{
{Loop_1000Hz,1000, 1, 0},
{Loop_500Hz , 500, 2, 0},
{Loop_200Hz , 200, 5, 0},
{Loop_100Hz , 100, 10,0},
{Loop_50Hz , 50, 20, 0},
{Loop_1Hz , 1, 1000, 0},
};
/***************/
/*计算任务的个数*/
#define TASK_NUM (sizeof(task_run)/sizeof(Task_Run_Struct))
void Task_Run(void)
{
/*循环任务进行*/
u8 index = 0;
/*建议总任务的执行时间不要小于各个任务最短周期*/
/*如果超过时间,建议把高优先级放在低索引处*/
for(index = 0;index < TASK_NUM;index++)
{
/*获取当前的时间*/
static u32 time_now = 0;
time_now = SysTick_GetTick();
/*距离上次执行的时间已经大于或执行的周期了*/
if((time_now - task_run[index].last_run_time)>=task_run[index].period)
{
/*把这次的时间赋值给上次任务执行的时间,然后执行任务*/
task_run[index].last_run_time = time_now;
task_run[index].task_run_function();
break;
}
}
}
/*1ms执行一次*/
static void Loop_1000Hz(void)
{
}
/*2ms执行一次*/
static void Loop_500Hz(void)
{
}
/*5ms执行一次*/
static void Loop_200Hz(void)
{
}
/*10ms执行一次*/
static void Loop_100Hz(void)
{
}
/*20ms执行一次*/
static void Loop_50Hz(void)
{
}
/*1s执行一次*/
static void Loop_1Hz(void)
{
}
我自己写的一段代码注释很明了,应该能看懂
主函数
int main()
{
state = All_Init();
while(1)
{
Task_Run();
}
}
我们只需要把Task_Run放在while中一直执行就行