矣,沙发是我dee
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);
}
}
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
----->生成汇编文件后可以看到,进入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 标志;
#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;
}
}
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)
// {
// }
// }
}
};
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
}
}
}
[修改于 9年8个月前 - 2015/06/05 14:26:47]
引用 acmilan:C#写习惯了
C++中public一个就够了,想全部public的话也可以直接把class改成struct。
引用 acmilan:Win32下中断是可以接管的。IDT hook或者hook KiTrapXX
模拟中断返回法
在dos中也可以实现,在win32下由于不能直接挂接中断,timer调用堆栈又过长,所以不大可行
win32下可以用这种方法实现异函数返回,但是要把编译器优化关掉,不然函数搞成内联或不使用ebp就爽了
引用 phpskycn:怎么做?
Win32下中断是可以接管的。IDT hook或者hook KiTrapXX
引用 acmilan:R0下直接改IDT或者KiTrapXX头部。
怎么做?
引用 phpskycn:还是R3下没法做→_→
R0下直接改IDT或者KiTrapXX头部。
自己的逻辑中注意同步问题即可
引用 acmilan:……R3下当然不可能了
还是R3下没法做→_→
引用 acmilan:我以为我的办法是歪门邪道呢,原来还有名字呀,还有什么嵌入式OS是这种办法吗,UCOS那样的调度觉得不好玩
模拟中断返回法
在dos中也可以实现,在win32下由于不能直接挂接中断,timer调用堆栈又过长,所以不大可行
win32下可以用这种方法实现异函数返回,但是要把编译器优化关掉,不然函数搞成内联或不使用ebp就爽了
引用 yanli12321:期待大作
大赞。。。。。。。表示准备用一个暑假拿下嵌入式linux呵呵SOC FPGA了,敬请期待~~~
引用 hackerboygn:我倒是没看过ucos的源码,觉得实时性应该差不多吧,都没啥实时性,想要有实时性都可以依靠中断
ucos是基于抢占式的调度算法,我认为作为实时系统这是优势
引用 超级玛丽:中断给予每个中断相同的CPU时间,如果需要优先级 也可以设置,或者分配不同的权重
钻研精神不错!不过你这个就没有任务优先级可言了,每个任务从头到尾来一遍再从头到尾往复,如果任务中有时序要求严格的代码就必须关中断调度,ucos的方式就好一些,其实ucos也可以不用delay直接死循环的,高优先级的可以抢占的优先级的任务
引用 amo:就是那个用setjmp/longjmp写出来的库?
如果打算用于具体产品上,建议研究一下protothread,实际就是几个宏定义,任何C语言编译器都直接支持……没有任何一行汇编代码,一个“线程”最低只需额外2字节RAM,完全没有RTOS的各种临界区、同步问题
而且可以直接用在各种RTOS、...
引用 金星凌日:不是什么库,是把switch-case的头尾巧妙的用宏封装一下,你搜索一下关键词,看看代码就知道了
就是那个用setjmp/longjmp写出来的库?
引用 amo:这个库好像比较适合51单片机。。。
不是什么库,是把switch-case的头尾巧妙的用宏封装一下,你搜索一下关键词,看看代码就知道了
引用 amo:说是不可重入的函数 不支持局部变量
不是什么库,是把switch-case的头尾巧妙的用宏封装一下,你搜索一下关键词,看看代码就知道了
引用 amo:是这个啊。不过这个用到了静态变量,因而不可重入。我记得还有个改进版,直接在宏里写malloc,可以重入,支持局部变量,只不过写法不太正常。
不是什么库,是把switch-case的头尾巧妙的用宏封装一下,你搜索一下关键词,看看代码就知道了
引用 金星凌日:支持的,因为这个是“协程”,每个任务都是独占CPU的,只有它主动释放CPU,别的任务才可能得到执行。
是这个啊。不过这个用到了静态变量,因而不可重入。我记得还有个改进版,直接在宏里写malloc,可以重入,支持局部变量,只不过写法不太正常。
引用 金星凌日:C还是不要随便用malloc,建议抽时间读读uip和lwip这两个TCP/IP协议栈,这两个都是亚当的大作,亚当是一个大牛级别的程序员,值得学习……
是这个啊。不过这个用到了静态变量,因而不可重入。我记得还有个改进版,直接在宏里写malloc,可以重入,支持局部变量,只不过写法不太正常。
引用 amo:我知道这是非抢占式的。但这样的函数显然不能递归。而且,如果我没记错的话,这个协程在出现以下情况时会出错:函数A循环调用函数B和C,B和C都调用了D,而D中使用了这种协程。
支持的,因为这个是“协程”,每个任务都是独占CPU的,只有它主动释放CPU,别的任务才可能得到执行。
既然CPU是自己管理的,那么什么时候使用自动变量什么时候使用静态变量,当然也是可以根据自己意愿来控制的。
1-可以使用自动变量的情况:
…...
引用 金星凌日:麻烦再请评价一下我写的调度器,我只测试过大概是可行的,写了复杂点的程序都是没问题,你觉得有什么缺陷或者会有什么缺陷
我知道这是非抢占式的。但这样的函数显然不能递归。而且,如果我没记错的话,这个协程在出现以下情况时会出错:函数A循环调用函数B和C,B和C都调用了D,而D中使用了这种协程。
引用 amo:麻烦再请评价一下我写的调度器,我只测试过大概是可行的,写了复杂点的程序都是没问题,你觉得有什么缺陷或者会有什么缺陷
C还是不要随便用malloc,建议抽时间读读uip和lwip这两个TCP/IP协议栈,这两个都是亚当的大作,亚当是一个大牛级别的程序员,值得学习……
他的做法就是先设计合理的数据结构,自己管理数据
引用 张静茹:没见你在Task变量里边保存R4-R11的数值[s:30]
麻烦再请评价一下我写的调度器,我只测试过大概是可行的,写了复杂点的程序都是没问题,你觉得有什么缺陷或者会有什么缺陷
引用 acmilan:确实没存,因为stm32 进入中断之后 自动压栈的寄存器里不包括R4-R11
没见你在Task变量里边保存R4-R11的数值
而且并没有保存所有的栈帧,因此如果局部变量不对等的话就爽了
引用 张静茹:c语言程序可能不会使用这几个寄存器,但是汇编语言不一定。另外最好在主函数里设定一个栈帧保存标志点,在这个标志点以下的栈帧都进行保存,这样即使有局部变量不对等也不会出错。
确实没存,因为stm32 进入中断之后 自动压栈的寄存器里不包括R4-R11
引用 acmilan:可能是,我用内联汇编 操作寄存器,等调试的时候 看的汇编代码操作的就成别的寄存器了,具体什么规律忘记了,
c语言程序可能不会使用这几个寄存器,但是汇编语言不一定。另外最好在主函数里设定一个栈帧保存标志点,在这个标志点以下的栈帧都进行保存,这样即使有局部变量不对等也不会出错。
引用 张静茹:如果不想保存栈帧,也可以为各个任务分配各自独立的栈(就像电脑操作系统那样)。主任务是当前栈,副任务可能是一个空栈。
可能是,我用内联汇编 操作寄存器,等调试的时候 看的汇编代码操作的就成别的寄存器了,具体什么规律忘记了,
栈没考虑过,可能真的会覆盖
引用 金星凌日:你这是数据结构没设计好……产生这种问题的场景如多个线程都需要共用一个串口、socket、文件等唯一性的资源,商业软件都会通过队列来解决这种冲突(而不是每个地方都要层层调用直达最底层)。
我知道这是非抢占式的。但这样的函数显然不能递归。而且,如果我没记错的话,这个协程在出现以下情况时会出错:函数A循环调用函数B和C,B和C都调用了D,而D中使用了这种协程。
引用 张静茹:用了ARM之后就再也没碰过汇编了,所以这个就没法多说了[s::lol]
麻烦再请评价一下我写的调度器,我只测试过大概是可行的,写了复杂点的程序都是没问题,你觉得有什么缺陷或者会有什么缺陷
200字以内,仅用于支线交流,主线讨论请采用回复功能。