属于基础中的基础
必须要学会创建任务,并重点掌握任务是如何切换
后面的内容根本无从下手
1 /* flag 必须定义成全局变量才能添加到逻辑分析仪里面观察波形
2 * 在逻辑分析仪中要设置以 bit 的模式才能看到波形,不能用默认的模拟量
3 */
4 uint32_t flag1;
5 uint32_t flag2;
6
7
8 /* 软件延时,不必纠结具体的时间 */
9 void delay( uint32_t count )
10 {
11 for (; count!=0; count--);
12 }
13
14 int main(void)
15 {
16 /* 无限循环,顺序执行 */
17 for (;;) {
18 flag1 = 1;
19 delay( 100 );
20 flag1 = 0;
21 delay( 100 );
22
23 flag2 = 1;
24 delay( 100 );
25 flag2 = 0;
26 delay( 100 );
27 }
28 }
多任务系统
中,两个任务不断切换的效果图应该像下图所示那样,即两个变量的波形是完全一样的,就好像CPU在同时干两件事
一样,这才是多任务的意义。千里之行,始于本章节,不要急
任务
1 void task_entry (void *parg)
2 {
3 /* 任务主体,无限循环且不能返回 */
4 for (;;) {
5 /* 任务主体代码 */
6 }
7 }
在一个裸机系统中:
在裸机系统中,他们统统放在一个叫栈的地方
在多任务系统中:
本章我们要实现两个变量按照一定的频率轮流的翻转:
在多任务系统中,有多少个任务就需要定义多少个任务栈
/* main.c 中定义任务栈 */
1 #define TASK1_STACK_SIZE 128 (2)
2 StackType_t Task1Stack[TASK1_STACK_SIZE]; (1)
3
4 #define TASK2_STACK_SIZE 128
5 StackType_t Task2Stack[TASK2_STACK_SIZE];
0 /* portmacro.h 中的数据类型 */
1 #ifndef PORTMACRO_H
2 #define PORTMACRO_H
3
4 /* 包含标准库头文件 */
5 #include "stdint.h"
6 #include "stddef.h"
7
8
9 /* 数据类型重定义 */
10 #define portCHAR char
11 #define portFLOAT float
12 #define portDOUBLE double
13 #define portLONG long
14 #define portSHORT short
15 #define portSTACK_TYPE uint32_t
16 #define portBASE_TYPE long
17
18 typedef portSTACK_TYPE StackType_t;
19 typedef long BaseType_t;
20 typedef unsigned long UBaseType_t;
21
22
23 #endif /* PORTMACRO_H */
任务
是一个独立的函数,函数主体无限循环且不能返回0 /* main.c 中的任务函数 */
1 /* 软件延时 */
2 void delay (uint32_t count)
3 {
4 for (; count!=0; count--);
5 }
6 /* 任务1 */
7 void Task1_Entry( void *p_arg ) (1)
8 {
9 for ( ;; )
10 {
11 flag1 = 1;
12 delay( 100 );
13 flag1 = 0;
14 delay( 100 );
15 }
16 }
17
18 /* 任务2 */
19 void Task2_Entry( void *p_arg ) (2)
20 {
21 for ( ;; )
22 {
23 flag2 = 1;
24 delay( 100 );
25 flag2 = 0;
26 delay( 100 );
27 }
28 }
任务控制块
:
0 /* task.c 中定义任务控制块 */
1 typedef struct tskTaskControlBlock
2 {
3 volatile StackType_t *pxTopOfStack; /* 栈顶 */ (1)
4
5 ListItem_t xStateListItem; /* 任务节点 */ (2)
6
7 StackType_t *pxStack; /* 任务栈起始地址 */ (3)
8 /* 任务名称,字符串形式 */(4)
9 char pcTaskName[ configMAX_TASK_NAME_LEN ];
10 } tskTCB;
11 typedef tskTCB TCB_t; (5)
/* main.c 中定义任务控制块 */
1 /* 定义任务控制块 */
2 TCB_t Task1TCB;
3 TCB_t Task2TCB;
1 #if( configSUPPORT_STATIC_ALLOCATION == 1 ) (1)
2
3 TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, (2)
4 const char * const pcName, (3)
5 const uint32_t ulStackDepth, (4)
6 void * const pvParameters, (5)
7 StackType_t * const puxStackBuffer, (6)
8 TCB_t * const pxTaskBuffer ) (7)
9 {
10 TCB_t *pxNewTCB;
11 TaskHandle_t xReturn; (8)
12
13 if ( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
14 {
15 pxNewTCB = ( TCB_t * ) pxTaskBuffer;
16 pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
17
18 /* 创建新的任务 */ (9)
19 prvInitialiseNewTask( pxTaskCode, /* 任务入口 */
20 pcName, /* 任务名称,字符串形式 */
21 ulStackDepth, /* 任务栈大小,单位为字 */
22 pvParameters, /* 任务形参 */
23 &xReturn, /* 任务句柄 */
24 pxNewTCB); /* 任务栈起始地址 */
25
26 }
27 else
28 {
29 xReturn = NULL;
30 }
31
32 /* 返回任务句柄,如果任务创建成功,此时xReturn 应该指向任务控制块 */
33 return xReturn; (10)
34 }
35
36 #endif /* configSUPPORT_STATIC_ALLOCATION */
0 /* projdefs.h 中TaskFunction_t定义 */
1 #ifndef PROJDEFS_H
2 #define PROJDEFS_H
3
4 typedef void (*TaskFunction_t)( void * );
5
6 #define pdFALSE ( ( BaseType_t ) 0 )
7 #define pdTRUE ( ( BaseType_t ) 1 )
8
9 #define pdPASS ( pdTRUE )
10 #define pdFAIL ( pdFALSE )
11
12
13 #endif /* PROJDEFS_H */
0 /* task.h 中 TaskHandle_t 定义 */
1 /* 任务句柄 */
2 typedef void * TaskHandle_t;
0 /* task.c 中 prvInitialiseNewTask() 定义*/
1 static void prvInitialiseNewTask(TaskFunction_t pxTaskCode, (1)
2 const char * const pcName, (2)
3 const uint32_t ulStackDepth, (3)
4 void * const pvParameters, (4)
5 TaskHandle_t * const pxCreatedTask, (5)
6 TCB_t *pxNewTCB ) (6)
7
8 {
9 StackType_t *pxTopOfStack;
10 UBaseType_t x;
11
12 /* 获取栈顶地址 */ (7)
13 pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
14 /* 向下做8 字节对齐 */ (8)
15 pxTopOfStack = ( StackType_t * ) \
16 ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );
17
18 /* 将任务的名字存储在TCB 中 */ (9)
19 for ( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
20 {
21 pxNewTCB->pcTaskName[ x ] = pcName[ x ];
22
23 if ( pcName[ x ] == 0x00 )
24 {
25 break;
26 }
27 }
28 /* 任务名字的长度不能超过configMAX_TASK_NAME_LEN */ (10)
29 pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
30
31 /* 初始化TCB 中的xStateListItem 节点 */ (11)
32 vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
33 /* 设置xStateListItem 节点的拥有者 */ (12)
34 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
35
36
37 /* 初始化任务栈 */ (13)
38 pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack,
39 pxTaskCode,
40 pvParameters );
41
42
43 /* 让任务句柄指向任务控制块 */ (14)
44 if ( ( void * ) pxCreatedTask != NULL )
45 {
46 *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
47 }
48 }
0 /* port.c 中prvTaskExitError函数定义 */
1 #define portINITIAL_XPSR ( 0x01000000 )
2 #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
3
4 static void prvTaskExitError( void )
5 {
6 /* 函数停止在这里 */
7 for (;;);
8 }
9
10 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack,
11 TaskFunction_t pxCode,
12 void *pvParameters )
13 {
14 /* 异常发生时,自动加载到CPU 寄存器的内容 */ (1)
15 pxTopOfStack--;
16 *pxTopOfStack = portINITIAL_XPSR; (2)
17 pxTopOfStack--;
18 *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; (3)
19 pxTopOfStack--;
20 *pxTopOfStack = ( StackType_t ) prvTaskExitError; (4)
21 pxTopOfStack -= 5; /* R12, R3, R2 and R1 默认初始化为0 */
22 *pxTopOfStack = ( StackType_t ) pvParameters; (5)
23
24 /* 异常发生时,手动加载到CPU 寄存器的内容 */ (6)
25 pxTopOfStack -= 8;
26
27 /* 返回栈顶指针,此时pxTopOfStack 指向空闲栈 */
28 return pxTopOfStack; (7)
29 }
0 /* task.c 中定义就绪列表 */
1 /* 任务就绪列表 */
2 List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
1 void prvInitialiseTaskLists( void )
2 {
3 UBaseType_t uxPriority;
4
5
6 for ( uxPriority = ( UBaseType_t ) 0U;
7 uxPriority < ( UBaseType_t ) configMAX_PRIORITIES;
8 uxPriority++ )
9 {
10 vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
11 }
12 }
1 /* 初始化与任务相关的列表,如就绪列表 */
2 prvInitialiseTaskLists();
3
4 Task1_Handle = /* 任务句柄 */
5 xTaskCreateStatic( (TaskFunction_t)Task1_Entry, /* 任务入口 */
6 (char *)"Task1", /* 任务名称,字符串形式 */
7 (uint32_t)TASK1_STACK_SIZE , /* 任务栈大小,单位为字 */
8 (void *) NULL, /* 任务形参 */
9 (StackType_t *)Task1Stack, /* 任务栈起始地址 */
10 (TCB_t *)&Task1TCB ); /* 任务控制块 */
11
12 /* 将任务添加到就绪列表 */
13 vListInsertEnd( &( pxReadyTasksLists[1] ),
14 &( ((TCB_t *)(&Task1TCB))->xStateListItem ) );
15
16 Task2_Handle = /* 任务句柄 */
17 xTaskCreateStatic( (TaskFunction_t)Task2_Entry, /* 任务入口 */
18 (char *)"Task2", /* 任务名称,字符串形式 */
19 (uint32_t)TASK2_STACK_SIZE , /* 任务栈大小,单位为字 */
20 (void *) NULL, /* 任务形参 */
21 (StackType_t *)Task2Stack, /* 任务栈起始地址 */
22 (TCB_t *)&Task2TCB ); /* 任务控制块 */
23 /* 将任务添加到就绪列表 */
24 vListInsertEnd( &( pxReadyTasksLists[2] ),
25 &( ((TCB_t *)(&Task2TCB))->xStateListItem ) );
0 /* task.c 中 vTaskStartScheduler()函数 */
1 void vTaskStartScheduler( void )
2 {
3 /* 手动指定第一个运行的任务 */
4 pxCurrentTCB = &Task1TCB; (1)
5
6 /* 启动调度器 */
7 if ( xPortStartScheduler() != pdFALSE )
8 {
9 /* 调度器启动成功,则不会返回,即不会来到这里 */ (2)
10 }
11 }
0 /* port.c 中 xPortStartScheduler()函数 */
1 /*
2 * 参考资料《STM32F10xxx Cortex-M3 programming manual》4.4.7,百度搜索“PM0056”即可找到这个文档
3 * 在Cortex-M 中,内核外设SCB 中SHPR3 寄存器用于设置SysTick 和PendSV 的异常优先级
4 * System handler priority register 3 (SCB_SHPR3) SCB_SHPR3:0xE000 ED20
5 * Bits 31:24 PRI_15[7:0]: Priority of system handler 15, SysTick exception
6 * Bits 23:16 PRI_14[7:0]: Priority of system handler 14, PendSV
7 */
8 #define portNVIC_SYSPRI2_REG (*(( volatile uint32_t *) 0xe000ed20))
9
10 #define portNVIC_PENDSV_PRI (((uint32_t) configKERNEL_INTERRUPT_PRIORITY ) << 16UL)
11 #define portNVIC_SYSTICK_PRI (((uint32_t) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
12
13 BaseType_t xPortStartScheduler( void )
14 {
15 /* 配置PendSV 和 SysTick 的中断优先级为最低 */ (1)
16 portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
17 portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
18
19 /* 启动第一个任务,不再返回 */
20 prvStartFirstTask(); (2)
21
22 /* 不应该运行到这里 */
23 return 0;
24 }
1 /*
2 * 参考资料《STM32F10xxx Cortex-M3 programming manual》4.4.3,百度搜索“PM0056”即可找到这个文档
3 * 在Cortex-M 中,内核外设SCB 的地址范围为:0xE000ED00-0xE000ED3F
4 * 0xE000ED008 为SCB 外设中SCB_VTOR 这个寄存器的地址,里面存放的是向量表的起始地址,即MSP 的地址
5 */
6
7 (1)
8 __asm void prvStartFirstTask( void )
9 {
10 PRESERVE8 (2)
11
12 /* 在Cortex-M 中,0xE000ED08 是SCB_VTOR 这个寄存器的地址, (3)
13 里面存放的是向量表的起始地址,即MSP 的地址 */
14 ldr r0, =0xE000ED08 (4)
15 ldr r0, [r0] (5)
16 ldr r0, [r0] (6)
17
18 /* 设置主堆栈指针msp 的值 */
19 msr msp, r0 (7)
20
21 /* 使能全局中断 */ (8)
22 cpsie i
23 cpsie f
24 dsb
25 isb
26
27 /* 调用SVC 去启动第一个任务 */
28 svc 0 (9)
29 nop
30 nop
31 }
0 /* CPS指令用法 */
1 CPSID I ;PRIMASK=1 ;关中断
2 CPSIE I ;PRIMASK=0 ;开中断
3 CPSID F ;FAULTMASK=1 ;关异常
4 CPSIE F ;FAULTMASK=0 ;开异常
0 /* FreeRTOSConfig.h 中 修改FreeRos 中SVC、PendSV 和SysTick 中断服务函数的名称 */
1 #define xPortPendSVHandler PendSV_Handler
2 #define xPortSysTickHandler SysTick_Handler
3 #define vPortSVCHandler SVC_Handler
0 /* port.c 中vPortSVCHandler()函数真正启动第一个任务 */
1 __asm void vPortSVCHandler( void )
2 {
3 extern pxCurrentTCB; (1)
4
5 PRESERVE8
6
7 ldr r3, =pxCurrentTCB (2)
8 ldr r1, [r3] (3)
9 ldr r0, [r1] (4)
10 ldmia r0!, {r4-r11} (5)
11 msr psp, r0 (6)
12 isb
13 mov r0, #0 (7)
14 msr basepri, r0 (8)
15 orr r14, #0xd (9)
16
17 bx r14 (10)
18 }
1 /* 在task.h 中定义 */
2 #define taskYIELD() portYIELD()
3
4
5 /* 在portmacro.h 中定义 */
6 /* 中断控制状态寄存器:0xe000ed04
7 * Bit 28 PENDSVSET: PendSV 悬起位
8 */
9 #define portNVIC_INT_CTRL_REG (*(( volatile uint32_t *) 0xe000ed04))
10 #define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
11
12 #define portSY_FULL_READ_WRITE ( 15 )
13
14 #define portYIELD() \
15 { \
16 /* 触发PendSV,产生上下文切换 */ \
17 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; (1) \
18 __dsb( portSY_FULL_READ_WRITE ); \
19 __isb( portSY_FULL_READ_WRITE ); \
20 }
0 /* port.c 中真正实现任务切换函数实现 */
1 __asm void xPortPendSVHandler( void )
2 {
3 extern pxCurrentTCB; (1)
4 extern vTaskSwitchContext; (2)
5
6 PRESERVE8 (3)
7
8 mrs r0, psp (4)
9 isb
10
11 ldr r3, =pxCurrentTCB (5)
12 ldr r2, [r3] (6)
13
14 stmdb r0!, {r4-r11} (7)
15 str r0, [r2] (8)
16
17 stmdb sp!, {r3, r14} (9)
18 mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY (10)
19 msr basepri, r0 (11)
20 dsb
21 isb
22 bl vTaskSwitchContext (12)
23 mov r0, #0 (13)
24 msr basepri, r0
25 ldmia sp!, {r3, r14} (14)
26
27 ldr r1, [r3] (15)
28 ldr r0, [r1] (16)
29 ldmia r0!, {r4-r11} (17)
30 msr psp, r0 (18)
31 isb
32 bx r14 (19)
33 nop
34 }
1 void vTaskSwitchContext( void )
2 {
3 /* 两个任务轮流切换 */
4 if ( pxCurrentTCB == &Task1TCB ) (1)
5 {
6 pxCurrentTCB = &Task2TCB;
7 }
8 else (2)
9 {
10 pxCurrentTCB = &Task1TCB;
11 }
12 }
1 /**
2 ***********************************************************************
3 * @file main.c
4 * @author fire
5 * @version V1.0
6 * @date 2018-xx-xx
7 * @brief 《FreeRTOS 内核实现与应用开发实战指南》书籍例程
8 * 任务的定义与任务切换的实现
9 ***********************************************************************
10 * @attention
11 *
12 * 实验平台:野火 STM32 系列 开发板
13 *
14 * 官网 :www.embedfire.com
15 * 论坛 :http://www.firebbs.cn
16 * 淘宝 :https://fire-stm32.taobao.com
17 *
18 ***********************************************************************
19 */
20
21 /*
22 *************************************************************************
23 * 包含的头文件
24 *************************************************************************
25 */
26 #include "FreeRTOS.h"
27 #include "task.h"
28
29 /*
30 *************************************************************************
31 * 全局变量
32 *************************************************************************
33 */
34 portCHAR flag1;
35 portCHAR flag2;
36
37 extern List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
38
39
40 /*
41 *************************************************************************
42 * 任务控制块 & STACK
43 *************************************************************************
44 */
45 TaskHandle_t Task1_Handle;
46 #define TASK1_STACK_SIZE 128
47 StackType_t Task1Stack[TASK1_STACK_SIZE];
48 TCB_t Task1TCB;
49
50 TaskHandle_t Task2_Handle;
51 #define TASK2_STACK_SIZE 128
52 StackType_t Task2Stack[TASK2_STACK_SIZE];
53 TCB_t Task2TCB;
54
55
56 /*
57 *************************************************************************
58 * 函数声明
59 *************************************************************************
60 */
61 void delay (uint32_t count);
62 void Task1_Entry( void *p_arg );
63 void Task2_Entry( void *p_arg );
64
65 /*
66 ************************************************************************
67 * main 函数
68 ************************************************************************
69 */
70 /*
71 * 注意事项:1、该工程使用软件仿真,debug 需选择 Ude Simulator
72 * 2、在Target 选项卡里面把晶振Xtal(Mhz)的值改为25,默认是12,
73 * 改成25 是为了跟system_ARMCM3.c 中定义的__SYSTEM_CLOCK 相同,
74 * 确保仿真的时候时钟一致
75 */
76 int main(void)
77 {
78 /* 硬件初始化 */
79 /* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */
80
81 /* 初始化与任务相关的列表,如就绪列表 */
82 prvInitialiseTaskLists();
83
84 /* 创建任务 */
85 Task1_Handle =
86 xTaskCreateStatic( (TaskFunction_t)Task1_Entry, /* 任务入口 */
87 (char *)"Task1", /* 任务名称,字符串形式 */
88 (uint32_t)TASK1_STACK_SIZE , /* 任务栈大小,单位为字 */
89 (void *) NULL, /* 任务形参 */
90 (StackType_t *)Task1Stack, /* 任务栈起始地址 */
91 (TCB_t *)&Task1TCB ); /* 任务控制块 */
92 /* 将任务添加到就绪列表 */
93 vListInsertEnd( &( pxReadyTasksLists[1] ),
94 &( ((TCB_t *)(&Task1TCB))->xStateListItem ) );
95
96 Task2_Handle =
97 xTaskCreateStatic( (TaskFunction_t)Task2_Entry, /* 任务入口 */
98 (char *)"Task2", /* 任务名称,字符串形式 */
99 (uint32_t)TASK2_STACK_SIZE , /* 任务栈大小,单位为字 */
100 (void *) NULL, /* 任务形参 */
101 (StackType_t *)Task2Stack, /* 任务栈起始地址 */
102 (TCB_t *)&Task2TCB ); /* 任务控制块 */
103 /* 将任务添加到就绪列表 */
104 vListInsertEnd( &( pxReadyTasksLists[2] ),
105 &( ((TCB_t *)(&Task2TCB))->xStateListItem ) );
106
107 /* 启动调度器,开始多任务调度,启动成功则不返回 */
108 vTaskStartScheduler();
109
110 for (;;)
111 {
112 /* 系统启动成功不会到达这里 */
113 }
114 }
115
116 /*
117 ***********************************************************************
118 * 函数实现
119 ***********************************************************************
120 */
121 /* 软件延时 */
122 void delay (uint32_t count)
123 {
124 for (; count!=0; count--);
125 }
126 /* 任务1 */
127 void Task1_Entry( void *p_arg )
128 {
129 for ( ;; )
130 {
131 flag1 = 1;
132 delay( 100 );
133 flag1 = 0;
134 delay( 100 );
135
136 /* 任务切换,这里是手动切换 */
137 taskYIELD(); (注意)
138 }
139 }
140
141 /* 任务2 */
142 void Task2_Entry( void *p_arg )
143 {
144 for ( ;; )
145 {
146 flag2 = 1;
147 delay( 100 );
148 flag2 = 0;
149 delay( 100 );
150
151 /* 任务切换,这里是手动切换 */
152 taskYIELD(); (注意)
153 }
154 }
只是把本章的内容看完,然后再仿真看看波形是远远不够的