以前工作中用过arm7,没有MMU,也没有用任何OS.现在回忆当时的代码结构,我觉得可以叫无限循环的有限状态机.arm7不跑OS,就相当于单片机,单片机跑的肯定是无限死循环.有限状态机是因为整个代码要处理很多外部的事情,那就是大的循环里面来套小循环,以轮询的方式来检查外界的变化,然后系统作出变化,系统在有限的状态中切换.
OS的一个标识就是支持多任务的并发.比方说linux,我们看起来是多个进程在同时运行,实际上还是cpu运行一下这个进程,再运行一下其他进程.这个就涉及到上下文切换以及进程调度的算法.当然我这里说的是单核的情况,如果是SMP的话,可能有些区别.暂时也没研究linux下的进程调度,但是通过自己写个最简单的任务调度可以理解任务调度是个什么回事.其实说到底任务调度就是那么回事.据说linux的最初版本,就是两个任务在不停的打印AB...
硬件平台是s3c2440:
利用RTC的Tick中断,在中断处理中进行任务的调度,调度算法采用最简单的轮循.
其中核心就是中断的写法,PCB的建立和任务调度中任务堆栈的保存和恢复:
调度的代码参考《ARM System Developer's Guide》一书.
给出scheduler.S的代码:
kernelScheduler: /* @ --------------------------------------------------- @ Round Robin Scheduler @ --------------------------------------------------- */ CurrentTask: ldr r3,=PCB_CurrentTask ldr r0,[r3] ldr r1,=PCB_Table ldr r1,[r1,r0,LSL#2] ldr r2,=PCB_PtrCurrentTask str r1,[r2] /* @ ** PCB_PtrCurrentTask - updated with the new address */ NextTask: add r0,r0,#1 cmp r0,#3 moveq r0,#0 str r0,[r3] ldr r1,=PCB_Table ldr r1,[r1,r0,LSL#2] ldr r0,=PCB_PtrNextTask str r1,[r0] /* @ ** PCB_PtrCurrentTask = current PCB @ ** PCB_PtrNextTask = next PCB @ ** PCB_CurrentTask = new TASK_ID @ ------------------------------------------------------ @ Context Switch @ ------------------------------------------------------ */ handler_contextswitch: ldmfd sp!,{r0-r12,r14} ldr r13,=PCB_PtrCurrentTask ldr r13,[r13] sub r13,r13,#60 stmia r13,{r0-r14}^ mrs r0, SPSR stmdb r13,{r0,r14} ldr r13,=PCB_PtrNextTask ldr r13,[r13] sub r13,r13,#60 ldmdb r13,{r0,r14} msr spsr_cxsf, r0 ldmia r13,{r0-r14}^ ldr r13,=PCB_TopOfIRQStack ldr r13,[r13] movs pc,r14 .end
一共写了3个静态任务,第一个任务做一个简单的算术运算,第二个任务是一个流水灯,而第三个任务是利用蜂鸣器来产生旋律.
仅贴出第三个任务的代码:
.text .global EntryTask3 Feq_Table: .word 20 .word 40 .word 60 .word 80 .word 100 .word 120 .word 140 .word 160 .word 180 .word 200 .word 220 .word 240 CurrentFeq: .word 0x0 task3_delay: ldr r3,=0xffffff task3_delay1: sub r3,r3,#1 cmp r3,#0x0 bne task3_delay1 mov pc,lr EntryTask3: loopfeq: ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (stop<<0) str r2, [r1] ldr r2, =GPBCON ldr r1,[r2] //ldr r1, =0x15400 bic r1,r1,#0x3 orr r1,r1,#0x2 str r1,[r2] ldr r1,=TCFG0 ldr r2,=(Prescaler0<<0) str r2, [r1] ldr r1,=TCFG1 ldr r2,=(DMA_MODE<<20) | (MUX0<<0) str r2, [r1] ldr r3,=CurrentFeq ldr r0,[r3] ldr r4,=Feq_Table ldr r4,[r4,r0,LSL#2] @用r4存放table中的值 add r0,r0,#1 cmp r0,#12 moveq r0,#0 str r0,[r3] ldr r1,=TCNTB0 //ldr r4,=100 str r4, [r1] mov r4,r4,LSR #2 ldr r1,=TCMPB0 //ldr r4,=25 str r4, [r1] ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (man_update<<1) | (start<<0) str r2, [r1] ldr r1,=TCON ldr r2,=(DZ_eable<<4) | (auto_reload<<3) | (inverter<<2) | (clear_man_update<<1) | (start<<0) str r2, [r1] bl task3_delay b loopfeq第三个任务就是每隔一段时间就有新的频率产生.
整个编译出来不到2K,直接在4K的sram中跑.看到的就是led在流水,蜂鸣器在各种频率.忽然想到了“落霞与孤鹜齐飞”.有了任务调度,看起来还是很美的.
下一步,利用MMU的MPU功能,改进这个任务调度,并且任务是单独编译的.总大小应该不会超过4K,这样我就暂时不用看Nandflash的datasheet了.不过迟早是要看的.......