概述
首先介绍几个寄存器,这几个寄存器只能在汇编或内联汇编时才能访问,C语言是访问不到的
R0-R12,都可作为临时变量存储,跟C语言的变量差不多,不过汇编中的存储变量是用寄存器,而且不用声明,全局可见,不分全局和局部,而且是32位的
比如想计算1+1,结果放在r0中
mov r0,0x01
add r0,0x01
图片来自互联网,互联网来自<<Cortex-M3权威指南>>
R13(MSP/PSP)堆栈寄存器,汇编指令PUSH,POP会影响R13的值,
PUSH {R0} //R0的值将被压入栈内 R13-4
POP{R1} //将SP所指向的内存赋值给R1 SP+4
R14 :是连接寄存器(LR),用于在调用子程序时存储返回地址,应该是不能用mov mrs msr访问的,只能用pop push保存R15 是程序计数器(PC),每条指令前都对应一个地址,把这个地址赋值给R15,程序就立即跳过去执行地址对应的程序xRPS特殊功能寄存器组 详细内容请参考<<Cortex-M3权威指南>>
之前用过UCOS,可是他每个死循环任务下面必须有一个延时函数,才可以把调度权限交还给系统,自然这个延时函数上面也不可以有死循环,
不然将永远卡在死循环上,别的任务也就不能再被调用了
<code class="lang-cpp">int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
SysTick_Configuration();
OSInit();
OSTaskCreate( TaskStart, //task pointer
(void *)0, //parameter
(OS_STK *)&TASK_START_STK[START_STK_SIZE-1], //task stack top pointer
START_TASK_Prio ); //task priority
OSStart();
return 0;
}
//开始任务
void TaskStart(void * pdata)
{
pdata = pdata;
OS_ENTER_CRITICAL();
OSTaskCreate(TaskLed, (void * )0, (OS_STK *)&TASK_LED_STK[LED_STK_SIZE-1], LED_TASK_Prio);
OSTaskCreate(TaskLed1, (void * )0, (OS_STK *)&TASK_LED1_STK[LED1_STK_SIZE-1], LED1_TASK_Prio);
OSTaskSuspend(START_TASK_Prio); //suspend but not delete
OS_EXIT_CRITICAL();
}
//任务1
//控制DS0的亮灭.
void TaskLed(void *pdata)
{
while(1)
{
LED0 = !LED0;
OSTimeDlyHMSM(0,0,1,100);
}
}
//任务2
//控制DS1的亮灭.
void TaskLed1(void *pdata)
{
while(1)
{
LED1 = !LED1;
OSTimeDlyHMSM(0,0,1,300);
}
}</code>
正文开始:
我写的这个调度系统是用stm32 定时器中断调度任务的,任务中不需要ucos那种延时函数,定时器每隔一段时间中断当前任务,保存现场,恢复第二个任务的现场,这时PC寄存器被改为第二个任务上次中断前执行的位置然后退出中断,,从任务二上次中断前的位置继续执行调度原理:
参考<<Cortex-M3权威指南>>中 第九章 中断的具体行为
当 C M 3开始响应一个中断时内核会自动 把 8个寄存器(R0-R3,R12,LR,PC,xPSR)的值压入栈,最关键的是PC,他直接决定了中断退出以后开始执行的位置,R0-R3,R12则保存了一些中间变量,保证了恢复现场以后程序正确执行
程序流程: 标志:↓↓↓
进入main() ----> 初始化GPIO,时钟,定时器,开中断 ---->进入任务0 ---->定时器中断时间到 ---->开始进入中断 ---->系统自动把8个寄存器(R0-R3,R12,LR,PC,xPSR)的值压入栈 ---->进入中断函数TIM3_IRQHandler(此时SP堆栈指针正指向R0,R0+4后,指向R1)
<code class="lang-cpp">TIM3_IRQHandler PROC
PUSH {r4,lr}
MRS r4,MSP
MOV r0,r4
ADD r0,r0,#8
MOV r4,r0
LDR r0,|L0.640|
STR r4,[r0,#0]
BL IRQHandler
POP {r4,pc}
ENDP</code>----->生成汇编文件后可以看到,进入TIM3_IRQHandler函数先把R4和LR压栈(这是编译器自动做的),MSP堆栈指针 - 8 ----->MRS r4,MSP 保存栈指针----->指针+8(对前面R4和LR的补偿)对准 8个被自动压栈的寄存器的R0----->保存指针到全局变量Address_BASE----->bl IRQHandler 调用任务调度程序----->清除中断待处理位----->根据之前保存的栈地址,加载8个寄存器保存当前任务现场----->调用任务二的地址----->task->Start_Next(); 更新任务标志----->退出中断 并恢复被修改了返回地址的8个寄存器----->执行任务1----->定时器中断时间到 ---->进入中断----->保存任务1现场----->恢复任务0 ----->退出中断----->goto标志;
<code class="lang-cpp">#include "../h/main.h"
extern Task *task;
int main(void)
{
task = new Task(); //创建任务管理对象 这是一个C++类对象
Init();//初始化 时钟 串口 GPIO
Timerx_Init(20,7199);//初始化TIM3,开中断,每2ms进一次中断
TIM3->CNT = 0x01;//意义不明 不要也行
__asm //內联汇编
{
bl Task0 //跳转到任务1
}
// while (true);
// delete task;
}
int temp = 0;
void Task0(void) //任务1
{
while (true)
{
LED0 = !LED0;
}
}
void Task1(void) //任务2
{
while (true)
{
LED1 = !LED1;
}
}</code>
<code class="lang-cpp">class Task //任务管理类
{
public:Task(void) //构造函数,初始化时会自动调用
{
Reg_Buff[0][6]=((unsigned int)&Task0) ; //初始化任务0的指针
Reg_Buff[1][6]=((unsigned int)&Task1) ; //初始化任务1的指针
Reg_Buff[0][6]=Reg_Buff[1][7]=0x61000000 ; //初始化xRSP
Current = 0;
Next = 1;
}
public: static const unsigned char Count = Task_Count;
public: unsigned char Current; //当前任务
public: unsigned char Next ; //下一个任务
public: volatile unsigned int Reg_Buff[Task_Count][8];
public: void Start_Next() //更新至下一个任务
{
(Current + 1 < Count) ? Current++ : Current = 0;
(Next + 1 < Count) ? Next++ : Next = 0;
// if (Next != 0 && (Next - Current) != 1)
// {
// while (true)
// {
// }
// }
}
};</code><code class="lang-cpp">Task *task ;
unsigned int Address_BASE = 0;
void IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx的中断待处理位:TIM 中断源
__asm
{
ldr r5, [Address_BASE]
str r5, [&task->Reg_Buff[task->Current][0]]//R0
ldr r5, [Address_BASE , 0x4]
str r5, [&task->Reg_Buff[task->Current][1]]//R1
ldr r5, [Address_BASE , 0x8]
str r5, [&task->Reg_Buff[task->Current][2]]//R2
ldr r5, [Address_BASE , 0xc]
str r5, [&task->Reg_Buff[task->Current][3]]//R3
ldr r5, [Address_BASE , 0x10]
str r5, [&task->Reg_Buff[task->Current][4]]//R12
// ldr r5, [Address_BASE , 0x14]
// str r5, [&task->Reg_Buff[task->Current][5]]//R13 LR
ldr r5, [Address_BASE , 0x18]
str r5, [&task->Reg_Buff[task->Current][6]]//R14 PC
ldr r5, [Address_BASE , 0x1c]
str r5, [&task->Reg_Buff[task->Current][7]]//R15 xRSP
/*↑↑↑保存当然运行中的任务现场↑↑↑*/
ldr r5, [&task->Reg_Buff[task->Next][0]]//R0
str r5, [Address_BASE]
ldr r5, [&task->Reg_Buff[task->Next][1]]//R1
str r5, [Address_BASE, 0x4]
ldr r5, [&task->Reg_Buff[task->Next][2]]//R2
str r5, [Address_BASE, 0x8]
ldr r5, [&task->Reg_Buff[task->Next][3]]//R3
str r5, [Address_BASE, 0xc]
ldr r5, [&task->Reg_Buff[task->Next][4]]//R12
str r5, [Address_BASE, 0x10]
// ldr r5, [&task->Reg_Buff[task->Next][5]]//R13 LR
// str r5, [Address_BASE, 0x14]
ldr r5, [&task->Reg_Buff[task->Next][6]]//R14 PC
//orr r5, 0x01
str r5, [Address_BASE, 0x18]
ldr r5, [&task->Reg_Buff[task->Next][7]]//R15 xRSP
str r5, [Address_BASE, 0x1c]
/*↑↑↑恢复上一个任务的现场↑↑↑*/
}
task->Start_Next(); //下一个任务
}
}
extern "C"
{
void TIM3_IRQHandler(void) //TIM3中断 中断中不能有太多东西,否则进中断时压栈太多 MSP不容易计算
{
__ASM
{
mrs r4, msp
add r4, 0x08
str r4, [&Address_BASE]
bl IRQHandler
}
}
}</code>
最后
以上就是自信寒风为你收集整理的c语言实现stm32多线程,自己动手写STM32多任务调度器的全部内容,希望文章能够帮你解决c语言实现stm32多线程,自己动手写STM32多任务调度器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复