基于STM32裸机的简单任务调度程序

通常在STM32上面进行裸机开发时,有的需要进行延时操作,常用的办法是使用systick计时并使CPU空转。当延时时间达到毫秒级的时候,这种方式会大幅度导致程序运行效率下降。本文采用一种仿照OSAL的任务队列的方法,当一个任务延时的时候,不耽误其他任务的运行。

通常裸机程序没有内存管理,文章中的程序没有使用malloc函数。

一、任务的数据类型与数据结构

通常来说,一个任务就是一个函数,在设定好的时间调用该函数。函数的无返回值。任务之间可能需要传递消息,可以通过任务函数的参数传递。以下是一个任务函数的模板:

void Func1(uint32_t para) {
    uint8_t a = (uint8_t)para;
    uint8_t b = (uint8_t)(para >> 8);
    uint8_t c = (uint8_t)(para >> 16);
    uint8_t d = (uint8_t)(para >> 24);
    // Todo ……
}

这样可以一次传递4个u8类型的消息。

如果传递的消息比较多,类型比较复杂,可以采用下列形式:

void Func1(void* para) {
    func1msg* msg = (func1msg*)para;
    // Todo ……
}

其中func1msg的定义为

typedef struct func1msg {
    uint8_t a,b;
    uint16_t c;
    uint32_t d;
    char* e;
}

可以定义一个func1msg类型的全局变量,然后在消息间传递变量的指针,实现消息传递。

所有可能执行的任务函数放在一个数组中,数组的类型如下:

typedef void (*pTaskFn)(void* para);

假设程序中有三个任务函数,分别为func1, func2, func3,则任务函数数组定义如下:

pTaskFn taskFnArr[] = {Func1, Func2, Func3};

任务函数数组长度为:

uint8_t taskNumber = sizeof(taskFnArr) / sizeof(pTaskFn);

实际上一个任务不仅仅包括任务函数,还有任务函数所需的参数(消息),任务的执行时间,任务是否被激活等信息,任务的数据结构如下:

typedef enum {
    TASK_DISABLE = 0,
    TASK_ENABLE
} TASK_STATUS;

typedef struct {
    TASK_STATUS taskEn;
    void* parameter;
    uint32_t delayMs;
    pTaskFn func;
} TASK;

任务队列的数组定义如下:

TASK taskArr[sizeof(taskFnArr) / sizeof(pTaskFn)] = {0};

二、任务的初始化

任务的初始化包含两部分:系统时钟的初始化和任务队列的初始化。

系统时钟采用systick,使用以下函数初始化systick:

void SystickInit(void) {
    SysTick_Config(SystemCoreClock / 1000);
}

 实现了每隔1ms中断一次的功能。

程序中需要利用systick中断实现计时功能,代码如下:

uint32_t uwTick = 0;

void IncTick(void) {
	uwTick++;
}

uint32_t  GetTick(void) {
	return uwTick;
}

在systick中断函数中调用IncTick函数:

void SysTick_Handler(void)
{
    IncTick();
}

即可实现系统时钟的初始化。

任务队列的初始化如下:

void TaskInit(void) {
    int i = 0;
    for(i=0;i<taskNumber;i+) {
        taskArr[i].func        = taskFnArr[i];
        taskArr[i].parameter   = 0;
        taskArr[i].delayMs     = 0;
        taskArr[i].taskEn      = TASK_DISABLE;
    }
}

三、任务的运行与激活

任务的运行函数如下:

uint8_t taskIndex = 0;

void TaskRun(void) {
	for(;;) {
		if(taskArr[taskIndex].taskEn == TASK_ENABLE && taskArr[taskIndex].delayMs <= GetTick()) {
			taskArr[taskIndex].taskEn = TASK_DISABLE;
			taskArr[taskIndex].func(taskArr[taskIndex].parameter);
		}
		if(taskIndex < (taskNumber-1)) {
			++taskIndex;
		} else {
			taskIndex = 0;
		}
	}
}

函数循环的遍历每个任务,当任务处于激活状态,且到达定时的时间,则先将激活标志位置为DISABLE,然后执行该任务中的函数。之所以需要先改变激活标志位,是因为在任务中可能会再次激活该任务。

任务的激活函数如下:

void SetTask(pTaskFn taskFn, uint32_t delayMs, void* para) {
	int i=0;
	for(i=0;i<taskNumber;i++) {
		if(taskArr[i].func == taskFn)
			break;
	}
	if(i >= TASK_NUMBER) return;
    taskArr[i].delayMs     = GetTick() + delayMs;
    taskArr[i].taskEn      = TASK_ENABLE;
    taskArr[i].parameter   = para;
}

遍历任务队列,通过设定的任务的函数指针找到目标任务,并将激活标准设定为ENABLE,定时时间设定为当前时间+延时时间,并保存参数指针,即可实现任务的定时激活。

四、完整代码

task.c

#include "task.h"
#include "systick.h"
#include "func.h"

pTaskFn taskFnArr[] = {Func1, Func2, Func3};
uint8_t taskNumber = sizeof(taskFnArr) / sizeof(pTaskFn);
TASK taskArr[sizeof(taskFnArr) / sizeof(pTaskFn)] = {0};
uint8_t taskIndex = 0;

void TaskInit(void) {
    for(int i=0;i<taskNumber ;++i) {
        taskArr[i].func        = taskFnArr[i];
        taskArr[i].parameter   = 0;
        taskArr[i].delayMs     = 0;
        taskArr[i].taskEn      = TASK_DISABLE;
    }
}

void TaskRun(void) {
	for(;;) {
		if(taskArr[taskIndex].taskEn == TASK_ENABLE && taskArr[taskIndex].delayMs <= GetTick()) {
			taskArr[taskIndex].taskEn = TASK_DISABLE;
			taskArr[taskIndex].func(taskArr[taskIndex].parameter);
		}
		if(taskIndex < (taskNumber-1)) {
			++taskIndex;
		} else {
			taskIndex = 0;
		}
	}
}

void SetTask(pTaskFn taskFn, uint32_t delayMs, void* para) {
	int i=0;
	for(i=0;i<taskNumber;i++) {
		if(taskArr[i].func == taskFn)
			break;
	}
	if(i >= TASK_NUMBER) return;
    taskArr[i].delayMs     = GetTick() + delayMs;
    taskArr[i].taskEn      = TASK_ENABLE;
    taskArr[i].parameter   = para;
}

task.h

#ifndef __TASK_H
#define __TASK_H

#include "main.h"

typedef void (*pTaskFn)(void* para);

typedef enum {
    TASK_DISABLE = 0,
    TASK_ENABLE
} TASK_STATUS;

typedef struct {
    TASK_STATUS taskEn;
    void* parameter;
    uint32_t delayMs;
    pTaskFn func;
} TASK;

void TaskInit(void);
void TaskRun(void);
void SetTask(pTaskFn taskFn, uint32_t delayMs, void* para);

#endif

systick.c

#include "systick.h"

uint32_t uwTick = 0;

void SystickInit(void) {
    SysTick_Config(SystemCoreClock / 1000);
}

void IncTick(void) {
	uwTick++;
}

uint32_t  GetTick(void) {
	return uwTick;
}

systick.h

#ifndef __SYSTICK_H
#define __SYSTICK_H

#include "main.h"

void SystickInit(void);
void IncTick(void);
uint32_t GetTick(void);

#endif

stm32f10x_it.c

#include "stm32f10x_it.h"
#include "systick.h"

void SysTick_Handler(void)
{
    IncTick();
}

func.c

#include "func.h"
#include "task.h"

func1msg f1msg;
func2msg f2msg;
func3msg f3msg;

void Func1(void* para) {
    func1msg* msg = (func1msg*)para;
    // TODO…
    SetTask(Func2, 10, &f2msg);
}

void Func2(void* para) {
    func2msg* msg = (func2msg*)para;
    // TODO…
    SetTask(Func3, 10, &f3msg);
}

void func3(void* para) {
    func3msg* msg = (func3msg*)para;
    // TODO…
    SetTask(Func1, 10, &f1msg);
}

func.h 

#ifndef __FUNC_H
#define __FUNC_H

#include "main.h"

typedef struct {
    //data
} func1msg;

typedef struct {
    //data
} func2msg;

typedef struct {
    //data
} func3msg;

extern func1msg f1msg;
extern func2msg f2msg;
extern func3msg f3msg;

void Func1(void* para);
void Func2(void* para);
void Func3(void* para);

#endif

main.c

#include "main.h"
#include "task.h"
#include "systick.h"
#include "func.h"

int main() {
    //TODO……
    SystickInit();
    TaskInit();
    SetTask(Func1, 20, &f1msg);
    TaskRun();
}

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值