aademeng

该篇为“啰里啰嗦版”,另有相应的“精简版”供参考

 

“不到长城非好汉;不做OS,枉为程序员”

OS之于程序员,如同梵蒂冈之于天主教徒,那永远都是块神圣的领土。若今生不能亲历之,实乃憾事!

但是,圣域不是想进就能进的呀……

OS融合了大量的计算机的基础知识,各个知识领域之间交织紧密,初来乍到者一不小心就会绕出个死结。

我的方法是:死结就死结,不管三七二十一,直接剪断,先走下去再说,回头我们再把这个剪断的死结再接上。

 

我们知道,“多任务”一般在介绍OS的书籍中,是属于中间或者靠后的部分,又或者分散在各个章节中。而我决定上手就说它。

 

一、整体纵览:

1、硬件:

STM32F103RC

2、IDE:

MDK5

3、文件架构:

(1)标准文件:

startup_stm32f10x_hd.s:STM32官方启动文件(注意:这是针对stm32硬件配置的文件,型号要是不同,你的可能和我不一样哦)

(2)自编文件——这才是我们的重头戏哦(共5个文件:1x".asm"+2x".c"+2x".h"):

main.c:主函数和任务定义;

os_cpu_a.asm:中断与任务切换;

myos.c:硬件初始化与任务切换时的堆栈保存;

include.h和myos.h:两个头文件。

 

二、逐文解析:

1、main.c

顺着main函数这条主线,我们看到最终的OS其实就是执行了7行代码,共5个函数:

1、OSInit(); 
2、OSTaskCreate(Task1, (void*)0, (OS_STK*)&Task1Stk[TASK_STACK_SIZE-1]);
3、OSTaskCreate(Task2, (void*)0, (OS_STK*)&Task2Stk[TASK_STACK_SIZE-1]);
4、OSTaskCreate(Task3, (void*)0, (OS_STK*)&Task3Stk[TASK_STACK_SIZE-1]);
5、SysTickInit(5);
6、LedInit();
7、OSStart();

简单说说main函数功能:
当OSStart()执行之后,Task1、Task2、Task3轮流执行,即Task1()、Task2()、Task3()三个函数轮流执行:
Task1()->Task2()->Task3()->Task1()->Task2()->Task3()->Task1()->......
每个任务(或函数)执行相等的时间片,并有SysTick中断来触发PendSV中断,从而实现任务切换。
OSStart()执行之后,永不返回。

各个函数基本做了些什么,代码后面都附加了注解。
其中需要注意的地方是:OSStart()。
这个函数一旦执行了就不会返还,有点像死循环(但不是死循环哦,后来会明白的)。
仔细想想后,确实也应当如此,如果main函数return掉了的话,程序也就结束啦!
“main函数结束”就意味着CPU现在只会喝喝茶、看看报了,什么抢劫、着火它都装作没看见。

main函数就这么短,那么上面七个函数的实现在哪里呢?
这时你肯定想到"#include"了吧!
“太好了!#include只有一行!”
那我们去include.h那里看看吧。
复制代码
 1 #include "include.h"    
 2 extern OS_TCB OSTCBTbl[OS_MAX_TASKS]; // (OS Task Control Block Table)
 3 extern OS_STK TASK_IDLE_STK[TASK_STACK_SIZE]; //("TaskIdle" Stack)
 4 extern OS_TCB *OSTCBCur; // Pointer to the current running task(OS Task Control Block Current)
 5 extern OS_TCB *OSTCBNext; // Pointer to the next running task(OS Task Control Block Next)
 6 extern INT8U OSTaskNext; // Index of the next task
 7 extern INT32U TaskTickLeft; // Refer to the time ticks left for the current task
 8 extern INT32U TimeMS;       // For system time record                             
 9 extern INT32U TaskTimeSlice; // For system time record
10 
11 OS_STK Task1Stk[TASK_STACK_SIZE]; // initialize stack for task1
12 OS_STK Task2Stk[TASK_STACK_SIZE]; // initialize stack for task2
13 OS_STK Task3Stk[TASK_STACK_SIZE]; // initialize stack for task3
14 
15 void Task1(void *p_arg); // flip the led1 every 0.5s
16 void Task2(void *p_arg); // flip the led2 every 1.0s
17 void Task3(void *p_arg); // do nothing
18 
19 int main(void)
20 {
21     
22     
23     OSInit();  // OS initialization
24     OSTaskCreate(Task1, (void*)0, (OS_STK*)&Task1Stk[TASK_STACK_SIZE-1]); // create task 1
25     OSTaskCreate(Task2, (void*)0, (OS_STK*)&Task2Stk[TASK_STACK_SIZE-1]); // create task 2
26     OSTaskCreate(Task3, (void*)0, (OS_STK*)&Task3Stk[TASK_STACK_SIZE-1]); // create task 3
27     SysTickInit(5); // configure the SysTick as 5ms
28     LedInit(); // leds initialization
29     OSStart(); // start os!
30     
31     return 0; // never come here
32 }
33 
34 void Task1(void *p_arg)
35 {
36     while(1) {
37         delayMs(100); // delay 100 * 5ms = 0.5s
38         LED1TURN(); // flip the switch of led1
39     }
40 }
41 void Task2(void *p_arg)
42 {
43     while(1) {
44         delayMs(200); // delay 200 * 5ms = 1.0s
45         LED2TURN(); // flip the switch of led2 
46     }
47 }
48 
49 void Task3(void *p_arg)
50 {
51     while(1) {
52     }
53 }
复制代码

小白兔笔记:

(1)“啥是extern变量啊?”

"快去复习复习c语言教程吧。"

(2)"OS_TCB、OS_MAX_TASKS什么的都是些啥?“

"myos.h"里都有它们的定义。

(3)”\'OSTCBCur\'都是些啥怪名字?"

“针对词义复杂的变量,注意看定义那行注释,后面的括号会有对变量的简短说明,如:

extern OS_TCB *OSTCBCur; // Pointer to the current running task(OS Task Control Block Current)

 

 

 2、include.h

“我去!这不是欺骗我感情吗?main函数倒是1个#include,怎么到了这里却又来了三个!”

等等!先别灰心嘛,容我慢慢道来。

我们知道,main里的

#include "include.h"

其实就等于:

#include <stdlib.h>

#include "myos.h"

#include "stm32f10x.h"

但是我们为什么要那么费劲再弄一个"inlcude.h"文件呢?

假设我们想给这个OS再加个内存管理的功能,于是要添加几个".c"文件,而这些文件也要包含上面那几个"#include",那么我们不是要再把那几个“#include”都写一遍吗? 

不过,有了这个include.h之后,这些".c"文件只要

#include "include.h"就可以了。

多加了1个"include.h",但却清爽了n个”xxxxx.c"文件。

当然,这只是好处之一,其他的就不多说了,毕竟我们的主题是OS嘛。

复制代码
1 #ifndef INCLUDE_H
2 #define INCLUDE_H
3 
4 #include <stdlib.h>
5 #include "myos.h"
6 #include "stm32f10x.h" //stm32官方头文件
7 
8 #endif
复制代码

小白兔笔记:

(1)“#ifndef INCLUDE_H之类的东西是什么意思?”

这是为了防止多重包含头文件。

既然你问了这个问题,估计你也听不懂啥是”多重包含头文件“。

这是和编译器有关的约定,简单点说,不这么写,编译器”可能“——我说”可能“——找你茬。

(2)”stm32f10x.h“是官方根据stm32的各种硬件配置编写的头文件,可要找准着你的你自己的硬件配置使用哦!

 

3、myos.h

复制代码
 1 #ifndef MYOS_H
 2 #define MYOS_H
 3 #include "stm32f10x.h"
 4 
 5 /**********CPU DEPENDENT************/
 6 #define TASK_TIME_SLICE     5             // 5ms for every task to run every time
 7 
 8 typedef unsigned char  INT8U;             // Unsigned  8 bit quantity  
 9 typedef unsigned short INT16U;            // Unsigned 16 bit quantity 
10 typedef unsigned int   INT32U;            // Unsigned 32 bit quantity
11 
12 typedef unsigned int   OS_STK;            // Each stack entry is 32-bit wide(OS Stack)
13 
14 // assembling functions
15 void OS_ENTER_CRITICAL(void);             // Enter Critical area, that is to disable interruptions
16 void OS_EXIT_CRITICAL(void);              // Exit Critical area, that is to enable interruptions
17 void OSCtxSw(void);                       // Task Switching Function(OS Context Switch)
18 void OSStart(void);
19 
20 OS_STK* OSTaskStkInit(void (*task)(void *p_arg), // task function
21               void *p_arg,                       // (pointer of arguments)
22                 OS_STK *p_tos);                  // (pointer to the top of stack)
23 /**********CPU INDEPENDENT************/
24 
25 #define OS_MAX_TASKS    16
26 
27 #define TASK_STATE_CREATING     0
28 #define TASK_STATE_RUNNING    1
29 #define TASK_STATE_PAUSING    2
30 
31 #define TASK_STACK_SIZE     64
32               
33 #define LED1TURN() (GPIOA->ODR ^= 1<<8)  // reverse the voltage of LED1 !!!HARDWARE RELATED
34 #define LED2TURN() (GPIOD->ODR ^= 1<<2)  // reverse the voltage of LED2 !!!HARDWARE RELATED
35 
36 
37 typedef struct os_tcb {
38     OS_STK    *OSTCBStkPtr;     // (OS Task Control Block Stack Pointer)
39     INT8U     OSTCBStat;        // (OS Task Control Block Status)
40 } OS_TCB;                       // (OS Task Control Block)
41 
42 void OSInit(void);              // (OS Initialization)
43 void LedInit(void);
44 45 void OS_TaskIdle(void *p_arg);
46 void OSInitTaskIdle(void);                      // (OS Initialization of "TaskIdle")
47 void OSTaskCreate(void (*task)(void *p_arg),    // task function
48                void *p_arg,              // (pointer of arguments)
49                   OS_STK *p_tos);         // (pointer to the top of stack)
50 void OSTCBSet(OS_TCB *p_tcb, OS_STK *p_tos, INT8U task_state);
51 
52 
53 void SysTickInit(INT8U Nms);                    // (System Tick Initialization)
54 void SysTick_Handler(void);              // The interrupt function 
55 
56 INT32U GetTime(void);
57 void delayMs(volatile INT32U ms);         // The argument can\'t be too large
58 
59 #endif
复制代码

这是最后一个头文件了,也是一个简单易懂的文件。同时也是最后的平原,过了这个平原,我们可就要翻雪山啦!

(1)简单说说2个函数:

void OS_ENTER_CRITICAL(void);

void OS_EXIT_CRITICAL(void);


有一种东西叫“临界区”(CRITICAL),这些所谓“临界区”指的是一些变量所在的内存,可以直接理解成“就是些特殊变量”。
要访问这些变量必须得关掉“中断”,访问结束后再开启“中断”,开关“中断”就是这两个函数的任务了。
猜猜看哪个是开“中断”,哪个是关“中断”呢?

(2)系统时钟中断void SysTick_Handler(void)的由来:

来自官方启动文件startup_stm32f10x_hd.s。

 

小白兔笔记:

小白兔表示“感觉不会再爱了……”

 

4、os_cpu_a.asm和myos.c:

上文说过,这两个文件关系暧昧,扯开来只讲其中一个很没味道,这里先把它们都贴出来:

os_cpu_a.asm(“;”后的注释是对应"C"语言的解释):

复制代码
  1     IMPORT     OSTCBCur
  2     IMPORT    OSTCBNext
  3     
  4     EXPORT    OS_ENTER_CRITICAL
  5     EXPORT    OS_EXIT_CRITICAL
  6     EXPORT    OSStart
  7     EXPORT    PendSV_Handler
  8     EXPORT    OSCtxSw
  9     
 10 NVIC_INT_CTRL    EQU            0xE000ED04    ; Address of NVIC Interruptions Control Register
 11 NVIC_PENDSVSET   EQU            0x10000000    ; Enable PendSV
 12 NVIC_SYSPRI14    EQU         0xE000ED22  ; System priority register (priority 14).
 13 NVIC_PENDSV_PRI  EQU         0xFF        ; PendSV priority value (lowest).
 14     
 15     PRESERVE8 ; align 8
 16 
 17     AREA    |.text|, CODE, READONLY 
 18     THUMB 
 19 
 20 ;/******************OS_ENTER_CRITICAL************/
 21 OS_ENTER_CRITICAL
 22     CPSID    I    ; Enable interruptions(Change Processor States: Interrupts Disable)
 23     BX    LR    ; Return
 24 
 25 ;/******************OS_EXIT_CRITICAL************/
 26 OS_EXIT_CRITICAL
 27     CPSIE    I    ; Disable interruptions
 28     BX    LR     ; Return
 29 
 30 ;/******************OSStart************/
 31 OSStart
 32     ; disable interruptions
 33     CPSID    I                            ; OS_ENTER_CRITICAL();
 34     ; initialize PendSV
 35     ; Set the PendSV exception priority
 36     LDR     R0, =NVIC_SYSPRI14            ; R0 = NVIC_SYSPRI14;
 37     LDR     R1, =NVIC_PENDSV_PRI          ; R1 = NVIC_PENDSV_PRI;
 38     STRB    R1, [R0]                      ; *R0 = R1;
 39     
 40     ; initialize PSP as 0
 41     ; MOV    R4, #0
 42     LDR R4,  =0x0                            ; R4 = 0;
 43     MSR    PSP, R4                           ; PSP = R4;
 44     
 45     ; trigger PendSV
 46     LDR    R4, =NVIC_INT_CTRL              ; R4 = NVIC_INT_CTRL;
 47     LDR    R5, =NVIC_PENDSVSET             ; R5 = NVIC_PENDSVSET;
 48     STR    R5, [R4]                        ; *R4 = R5;
 49     
 50     ; enable interruptions
 51     CPSIE    I                            ; OS_EXIT_CRITICAL();
 52 
 53 ; should never get here
 54 ; a endless loop
 55 OSStartHang                                    
 56     B    OSStartHang
 57 
 58 ;/******************PendSV_Handler************/
 59 PendSV_Handler
 60     CPSID    I                            ; OS_ENTER_CRITICAL();
 61     ; judge if PSP is 0 which means the task is first invoked
 62     MRS     R0, PSP                            ; R0 = PSP;
 63     CBZ     R0, PendSV_Handler_NoSave          ; if(R0 == 0) goto PendSV_Handler_NoSave;
 64     
 65     ;     R12, R3, R2, R1
 66     SUB     R0, R0, #0x20            ; R0 = R0 - 0x20;
 67     
 68     ; store R4 
 69     STR     R4 , [R0]                ; *R0 = R4;
 70     ADD     R0, R0, #0x4             ; R0 = R0 + 0x4;
 71     ; store R5 
 72     STR     R5 , [R0]                ; *R0 = R5;
 73     ADD     R0, R0, #0x4             ; R0 = R0 + 0x4;
 74     ; store R6 
 75     STR     R6 , [R0]                ; *R0 = R6;
 76     ADD     R0, R0, #0x4             ; R0 = R0 + 0x4;
 77     ; store R7 
 78     STR     R7 , [R0]                ; *R0 = R7;
 79     ADD     R0, R0, #0x4             ; R0 = R0 + 0x4;
 80     ; store R8 
 81     STR     R8 , [R0]                ; *R0 = R8;
 82     ADD     R0, R0, #0x4             ; R0 = R0 + 0x4;
 83     ; store R9
 84     STR     R9, [R0]                ; *R0 = R4;
 85     ADD     R0, R0, #0x4            ; R0 = R0 + 0x4;
 86     ; store R10 
 87     STR     R10, [R0]               ; *R0 = R10;
 88     ADD     R0, R0, #0x4            ; R0 = R0 + 0x4;
 89     ; store R11 
 90     STR     R11, [R0]               ; *R0 = R11;
 91     ADD     R0, R0, #0x4            ;

分类:

技术点:

相关文章:

  • 2021-05-20
  • 2021-06-02
  • 2021-08-17
  • 2022-02-17
  • 2021-06-06
  • 2022-12-23
  • 2021-12-06
猜你喜欢
  • 2021-11-30
  • 2022-12-23
  • 2021-11-19
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-02-06
相关资源
相似解决方案