这个简单的基于 ARM Cortex-M SysTick 的任务调度程序将无法工作。我应该自己管理抢占吗?
This simple ARM Cortex-M SysTick based task scheduler wont work. Should I manage preemption myself?
所以,我正在做一个基于 ARM Cortex M3 的非常简单的时间触发模式。
这个想法 is:when SysTick 得到服务,任务数组索引在 systick 处递增,指向任务的函数指针也是如此。 PendSV 处理程序被调用,并调用任务。我正在使用 Atmel ICE JTAG 对其进行调试。
发生的事情是它停留在第一个任务上,甚至没有增加计数器。它不会去任何地方。
代码模式:
#include <asf.h> // atmel software framework. cmsis and board support package.
#define NTASKS 3
typedef void (*TaskFunction)(void);
void task1(void);
void task2(void);
void task3(void);
TaskFunction run = NULL;
uint32_t count1 = 0; //counter for task1
uint32_t count2 = 0; // 2
uint32_t count3 = 0; // 3
TaskFunction tasks[NTASKS] = {task1, task2, task3};
volatile uint8_t tasknum = 0;
void task1(void)
{
while(1)
{
count1++;
}
}
void task2(void)
{
while(1)
{
count2++;
}
}
void task3(void)
{
while(1)
{
count3++;
}
}
void SysTick_Handler(void)
{
tasknum = (tasknum == NTASKS-1) ? 0 : tasknum+1;
run = tasks[tasknum];
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
void PendSV_Handler(void)
{
run();
}
int main(void)
{
sysclk_init();
board_init();
pmc_enable_all_periph_clk();
SysTick_Config(1000);
while(1);
}
恐怕这种设计模式存在根本性缺陷。
在第一个 SysTick 事件中,task1()
将从 PendSV 处理程序 中调用 ,因此不会 return。进一步的 SysTick 事件将中断 PendSV 处理程序并再次设置 PendSV 位,但除非 运行ning 任务结束并且 PendSV 处理程序被允许 return 它不可能再次被调用。
好消息是 M3 上的适当上下文切换只需要少量的汇编语言——大概 10 行。您还需要进行一些设置,让用户模式代码使用进程堆栈指针等,并且您需要为每个任务设置一个堆栈,但这并不是全部。
如果您真的想在 SysTick 到达时取消 运行ning 任务并启动另一个,它们可以共享同一个堆栈;但如果这是进程堆栈,那么它的堆栈指针可以从 PendSV 中重置而不影响来自处理程序模式的 return 会容易得多。您还需要进行一些堆栈检查以说服 PendSV 'return' 开始您想要 运行.
的下一个任务
所以,我正在做一个基于 ARM Cortex M3 的非常简单的时间触发模式。 这个想法 is:when SysTick 得到服务,任务数组索引在 systick 处递增,指向任务的函数指针也是如此。 PendSV 处理程序被调用,并调用任务。我正在使用 Atmel ICE JTAG 对其进行调试。 发生的事情是它停留在第一个任务上,甚至没有增加计数器。它不会去任何地方。 代码模式:
#include <asf.h> // atmel software framework. cmsis and board support package.
#define NTASKS 3
typedef void (*TaskFunction)(void);
void task1(void);
void task2(void);
void task3(void);
TaskFunction run = NULL;
uint32_t count1 = 0; //counter for task1
uint32_t count2 = 0; // 2
uint32_t count3 = 0; // 3
TaskFunction tasks[NTASKS] = {task1, task2, task3};
volatile uint8_t tasknum = 0;
void task1(void)
{
while(1)
{
count1++;
}
}
void task2(void)
{
while(1)
{
count2++;
}
}
void task3(void)
{
while(1)
{
count3++;
}
}
void SysTick_Handler(void)
{
tasknum = (tasknum == NTASKS-1) ? 0 : tasknum+1;
run = tasks[tasknum];
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
void PendSV_Handler(void)
{
run();
}
int main(void)
{
sysclk_init();
board_init();
pmc_enable_all_periph_clk();
SysTick_Config(1000);
while(1);
}
恐怕这种设计模式存在根本性缺陷。
在第一个 SysTick 事件中,task1()
将从 PendSV 处理程序 中调用 ,因此不会 return。进一步的 SysTick 事件将中断 PendSV 处理程序并再次设置 PendSV 位,但除非 运行ning 任务结束并且 PendSV 处理程序被允许 return 它不可能再次被调用。
好消息是 M3 上的适当上下文切换只需要少量的汇编语言——大概 10 行。您还需要进行一些设置,让用户模式代码使用进程堆栈指针等,并且您需要为每个任务设置一个堆栈,但这并不是全部。
如果您真的想在 SysTick 到达时取消 运行ning 任务并启动另一个,它们可以共享同一个堆栈;但如果这是进程堆栈,那么它的堆栈指针可以从 PendSV 中重置而不影响来自处理程序模式的 return 会容易得多。您还需要进行一些堆栈检查以说服 PendSV 'return' 开始您想要 运行.
的下一个任务