你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【连载】从单片机到操作系统④——FreeRTOS创建任务&开启...

[复制链接]
xiaojie0513 发布时间:2018-5-26 21:52
本帖最后由 xiaojie0513 于 2018-5-29 13:42 编辑
9 m3 c9 H$ U* J) H5 i) ^' c; e% \; |/ Q7 [' r3 \5 F
创客的兄弟姐妹们大家好,我是杰杰。又到了更新的时候了。
听首歌缓解一下心情。

+ d" W' L4 G* q9 c) J, c
开始今天的内容之前,先补充一下上篇文章【连载】从单片机到操作系统③——走进FreeRTOS的一点点遗漏的知识点。
1BaseType_t xTaskCreate(       TaskFunction_t pvTaskCode,; ~, x1 i1 r8 m) G$ w. H0 ]. j
2                              const char * const pcName,# Q. o, L2 B  ^" X' v
3                              uint16_t usStackDepth,
% d. |: e) x9 h: p# G' P 4                              void *pvParameters,- }% L" C. v% v+ S- ]; N, w# y" v
5                              UBaseType_t uxPriority,
( u* y2 I  j) ` 6                              TaskHandle_t *pvCreatedTask1 j2 Q0 M) J' M
7                          )
;
& b/ r6 `9 p: p/ P 8创建任务中的堆栈大小问题,在task.h中有这样子的描述:* T: D) L4 K1 s( ^' ~* k  _1 L7 \' m
9/**" r% J" v5 E. p0 B' O+ Z3 M& ?* ~, F
10* @param usStackDepth The size of the task stack specified as the number of variables the stack * can hold - not the number of bytes.  For example, if the stack is 16 bits wide and  , f$ Z5 b$ B7 J1 O6 Y! T
11* usStackDepth is defined as 100, 200 byteswill be allocated for stack storage.
6 G! D7 Q4 U8 p9 J  K12*/
代码可左右滑动
1 T8 L) _: P6 V; Z
  当任务创建时,内核会分为每个任务分配属于任务自己的唯一堆栈。usStackDepth 值用于告诉内核为它应该分配多大的栈空间。
这个值指定的是栈空间可以保存多少个字(word) ,而不是多少个字节(byte)
文档也有说明,如果是16位宽度的话,假如usStackDepth = 100;那么就是200个字节(byte)。
当然,我用的是stm32,32位宽度的, usStackDepth=100;那么就是400个字节(byte)。

, Y1 c+ F; Z; s+ q& R$ D9 d2 S
  好啦,补充完毕。下面正式开始我们今天的主题。

, C* J, T( [4 k  p2 n! j$ B
  我自己学的是应用层的东西,很多底层的东西我也不懂,水平有限,出错了还请多多包涵。
  其实我自己写文章的时候也去跟着火哥的书看着底层的东西啦,但是本身自己也是不懂,不敢乱写。所以,这个《从单片机到操作系统》系列的文章,我会讲一点底层,更多的是应用层,主要是用的方面。
  ?9 {# C7 i6 w; U3 r
按照一般的写代码的习惯,在main函数里面各类初始化完毕了,并且创建任务成功了,那么,可以开启任务调度了。
1int main(void)" h7 w# f4 F! J7 I8 w
2
{) g. D8 U2 A2 A- }- E3 @( f1 s/ {
3    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4   
& V- M# C& ^0 J! B9 i% B3 w) { 4    Delay_Init();                       //延时函数初始化     
) ^- d: a5 w& D5 A: J# [ 5    Uart_Init(115200);                  //初始化串口
) [% Z) G# a" @ 6    LED_Init();                     //初始化LED9 U4 {# _# h, g) ?0 p' A# d
7    KEY_Init();
2 W4 v* Z) z- _' C& A 8    //创建开始任务
6 g4 Z" f, Q+ u3 C 9    xTaskCreate((TaskFunction_t )start_task,            //任务函数
3 b3 J# v) y. L( R0 T1 f10                (const char*    )"start_task",          //任务名称
4 M* F+ G7 ?6 J9 S5 x& r11                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
5 J" p, a% R5 Z12                (void*          )NULL,                  //传递给任务函数的参数( \* L% a- K9 b. t
13                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
3 H% Z/ S  e) }& F: N/ \14                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
2 ~8 u# J8 T( t2 p  ^15    vTaskStartScheduler();          //开启任务调度" _. B  s% F" r  o& {
16}
6 V) P5 D. Q3 I. T. A7 _" E

$ ]' A! z' X$ p0 a) k  [
  来大概看看分析一下创建任务的过程,虽然说会用就行,但是也是要知道了解一下的。
注意:下面说的创建任务均为xTaskCreate(动态创建而非静态创建。
1pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
: k; |/ R* g- x2 z. O  c 2/*lint !e961 MISRA exception as the casts are only redundant for some ports. */  s. ~7 J) g- E8 q! q  B
3            if( pxStack != NULL )
5 a3 f* s' {* k 4            {
! _8 P1 ^  J0 m$ H 5                /* Allocate space for the TCB. */1 x8 _1 P# P: ^
6                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );5 I  ~7 b4 |) Q# B9 ~
7                /*lint !e961 MISRA exception as the casts are only redundant for some paths. */5 L' a/ s& y0 I1 h  r; c. t
8                if( pxNewTCB != NULL )
$ h1 z: O; `2 r 9                {) n5 s. R2 b+ w4 D  M6 |
10                    /* Store the stack location in the TCB. *// V4 S) [. s2 n
11                    pxNewTCB->pxStack = pxStack;
4 e! |( k* }9 `12                }% |7 c5 l2 c" m
13                else
8 A  A' l- d& C! U+ x14                {# G' L+ Z6 P7 g4 b
15                    /* The stack cannot be used as the TCB was not created.  Free: q! i+ N' I! g  n; y
16                    it again. */
: B# {( H9 ]* q' c2 O3 d
17                    vPortFree( pxStack );) F- K- p& H8 P/ z2 l7 X  x
18                }
7 P( i1 b/ c- R* `! m) D9 [19            }# j7 z2 o$ t$ [2 k5 V6 t# I3 M
20            else
7 _; m" E5 }4 Z21            {! _' x+ k+ Z& y, a) t3 A* z) Z# U
22                pxNewTCB = NULL;
3 _! n. L7 a6 u* E23            }: r5 Z. F! ~. ~( S
24        }
  首先是利用pvPortMalloc给任务的堆栈分配空间,if( pxStack != NULL )如果内存申请成功就接着给任务控制块申请内存。pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );同样是使用pvPortMalloc();如果任务控制块内存申请失败则释放 之前已经申请成功的任务堆栈的内存vPortFree( pxStack );
  然后就初始化任务相关的东西,并且将新初始化的任务控制块添加到列表中prvAddNewTaskToReadyList( pxNewTCB );
  最后返回任务的状态,如果是成功了就是pdPASS,假如失败了就是返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;

: F. t1 l) _- x7 P- Q 1prvInitialiseNewTask(     pxTaskCode,
# {! l. k( V! Y% D 2                          pcName, : G& e; O! i; W
3                         ( uint32_t ) usStackDepth,
+ I6 \4 O- g5 S7 r& m 4                          pvParameters,
2 P6 [& `5 a) i6 J 5                          uxPriority,
9 H- m3 t8 _9 [0 ~ 6                         pxCreatedTask,4 Y8 r* L7 m' s) j; W
7                          pxNewTCB,
" }5 P6 S5 L* p" G 8                         NULL );, E# B% O6 L  E( q* d' @
9            prvAddNewTaskToReadyList( pxNewTCB );9 L$ |* L4 f7 {9 ^+ o
10            xReturn = pdPASS;# Q3 n6 J$ H* c4 v
11        }( O& s, d7 x: }: o. ]: k4 ]1 Q5 ?
12        else
2 O1 u6 f/ L& t! U+ w+ o8 s13        {
5 |8 K) V4 m' p14            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
# T8 i& S$ M0 L) T& n( k( w7 A15        }
1 u5 T+ Z! V2 ?" H0 c$ m16        return xReturn;$ b7 ?: v9 C- t+ i5 ~
17    }
3 p; ]5 |8 E0 g9 h* {- A: G18// 相关宏定义4 |, X8 k0 v+ T
19#define pdPASS            ( pdTRUE )6 s* A2 D. \$ M0 u4 ~1 x$ x
20#define pdTRUE            ( ( BaseType_t ) 1 )5 {; `4 [' h: v# I6 o* s% ]
21/* FreeRTOS error definitions. */, G& c$ s; @( i1 \) `7 ^+ V. v
22#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY    ( -1 )
5 S/ s" }/ |6 S7 _, ~
) E1 [8 ]) y. |
  具体的static void prvInitialiseNewTask(()实现请参考FreeRTOS的tasks.c文件的767行代码。具体的static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )实现请参考FreeRTOS的tasks.c文件的963行代码。

& T; }  d' w" E" t3 p7 T2 U
  因为这些是tasks.c中的静态的函数,仅供xTaskCreate创建任务内部调用的,我们无需理会这些函数的实现过程,当然如果需要请自行了解。
' e) ]* B1 g5 D5 y: B8 Y
创建完任务就开启任务调度了:
1vTaskStartScheduler();          //开启任务调度  s" r2 N# ~/ }# ?* X& q

6 f: z. G' J5 o! [8 x  P0 g# c
在任务调度里面,会创建一个空闲任务(我们将的都是动态创建任务,静态创建其实一样的)
1xReturn = xTaskCreate(    prvIdleTask,  S; J& h- Q$ G6 a# y
2                          "IDLE", configMINIMAL_STACK_SIZE," K: i0 z+ r$ v4 h" l2 J2 g
3                          ( void * ) NULL,* G9 V4 z/ |5 e. D# t% Z
4                          ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),4 K' {- y5 I2 W) v  n4 i+ F, D
5                          &xIdleTaskHandle );
' e% g5 g: ?, p& X# r. e 6/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
+ N2 l+ R8 K% V2 y+ H' _) L0 m 7    }& ]+ }" F; q$ {6 a' v* k+ R9 j4 P
8相关宏定义:5 {3 u: {" u" \; x8 V* T' }/ c
9#define tskIDLE_PRIORITY            ( ( UBaseType_t ) 0U )
# \1 o& R/ r/ v. O, b) u10#ifndef portPRIVILEGE_BIT( W+ ?) a* B! ?8 e5 l( I! _
11    #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 )
# v7 y7 n$ U- Y; ]& s12#endif
. r& L* [, R- ^' W13#define configUSE_TIMERS                        1                              
; B7 }6 t% |+ |14 //为1时启用软件定时器  w( ^2 n/ k1 c3 ]
从上面的代码我们可以看出,空闲任务的优先级是tskIDLE_PRIORITY为0,也就是说空闲任务的优先级最低。当CPU没事干的时候才执行空闲任务,以待随时切换优先级更高的任务。
如果使用了软件定时器的话,我们还需要创建定时器任务,创建的函数是:
1#if ( configUSE_TIMERS == 1 )
! H  O  O$ L! `: M# e2    BaseType_t xTimerCreateTimerTask( void )/ t; r. O+ M4 f7 ?5 H, y+ }
3
% A! S* a/ G, r& x" a  l
然后还要把中断关一下
1portDISABLE_INTERRUPTS();
+ @% n" e- m$ S6 ^
至于为什么关中断,也有说明:
1/* Interrupts are turned off here, toensure a tick does not occur$ B9 M" F% R) n- C- I8 b
2before or during the call toxPortStartScheduler().  The stacks of& T) _+ X' R, ~4 v# A
3the created tasks contain a status wordwith interrupts switched on7 S% a7 c. J& F% d6 \- V
4so interrupts will automatically getre-enabled when the first task3 k" A: G: x! r7 [
5starts to run. */
" O* _# [6 t5 x5 _1 Z7 h" a
6/ *中断在这里被关闭,以确保不会发生滴答
) I# I' j: B# X6 W 7在调用xPortStartScheduler()之前或期间。堆栈5 ~+ s/ {- g: m9 |6 D& u
8创建的任务包含一个打开中断的状态字
5 k+ y, V. @8 |1 i7 e3 D4 f5 ~ 9因此中断将在第一个任务时自动重新启用$ K# }* r! _2 W1 I* X) Q4 [
10开始运行。*/  i. ^) \1 j4 Q" \% W! B
9 _5 q0 }$ R0 e( C; N5 }
( Y" R/ E* n6 g, v3 K; J( Z
那么如何打开中断呢????这是个很重要的问题
别担心,我们在SVC中断服务函数里面就会打开中断的
看代码:
1__asm void vPortSVCHandler( void )
% m; G$ u( v* O% l, Z* \ 2
{( ?9 F" `) v" _0 c! @# y
3         PRESERVE8
- G# g) o: \: {, _! T; }, Y0 d: v 4         ldr    r3, =pxCurrentTCB  /* Restore the context. */$ h! Y; |1 b8 c% Z+ I
5         ldrr1, [r3]                            /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */4 j; K) t) z+ u, _) W
6         ldrr0, [r1]                            /* Thefirst item in pxCurrentTCB is the task top of stack. */
7 S7 \/ x; ?2 e+ e* y 7         ldmiar0!, {r4-r11}             /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */' B' F0 v7 R2 P% E2 e& b
8         msrpsp, r0                                   /*Restore the task stack pointer. */, s( ^5 E* v# t; j# R( x" R
9         isb
8 H" H" [* e9 @0 e; L10         movr0, #0
' w& [* p# f; F# S  D11         msr  basepri, r08 K# M, ^6 @' n1 U! h5 ^$ B! K' w
12         orrr14, #0xd9 Y/ _% C3 ^$ p
13         bxr14' }: [4 t8 _* v8 X7 b- v: U8 z
14}0 F) t1 s  b5 r( |/ T

  [* t/ N' |& N' f2 [1msr  basepri, r0
8 ?3 b: V" s$ x  Z$ ^, [( {
  就是它把中断打开的。看不懂没所谓,我也不懂汇编,看得懂知道就好啦。

' H7 Q. D2 |* M5 v, c9 K& s1xSchedulerRunning = pdTRUE;1 r/ v; m" h8 U5 i/ L/ C9 T
任务调度开始运行
# ?3 F' r5 K3 Z7 e" b8 X. R3 U" J
1/* If configGENERATE_RUN_TIME_STATS isdefined then the following% @# X. G- G+ D6 g
2macro must be defined to configure thetimer/counter used to generate
" J6 K, R( M1 ]7 J$ y  l* o3the run time counter time base. */

( D6 x; s1 {# [6 Y2 E6 f4portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();& e7 e, C2 ~; i2 w& j

  U6 o% i; n6 v' ?8 C
如果configGENERATE_RUN_TIME_STATS使用时间统计功能,这个宏为1,那么用户必须实现一个宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();用来配置一个定时器或者计数器。

5 ]# q  L0 j- [: n
来到我们的重点了,开启任务调度,那么任务到这了就不会返回了。
1if( xPortStartScheduler() != pdFALSE )5 w0 f# t! F- q4 V
2                   {5 A2 R; E4 N% b, L
3                            /*Should not reach here as if the scheduler is running the
; ^# B) G' y2 }/ Y4                            functionwill not return. */1 S' E& n" a) ~' Y) R4 i
5                   }然后就能开启第一个任务了,感觉好难是吧,我一开始也是觉得的,但是写了这篇文章,觉得还行吧,也不算太难,可能也是在查看代码跟别人的书籍吧,写东西其实还是蛮好的,能加深理解,写过文章的人就知道,懂了不一定能写出来,所以,我还是很希望朋友们能投稿的。杰杰随时欢迎。。。
  w% e" h6 l" [1 m
开始任务就按照套路模板添加自己的代码就好啦,很简单的。
创建任务
1 xTaskCreate((TaskFunction_t )led0_task,   
! Z6 @9 i, E/ W$ M" M 2                (const char*    )"led0_task",  ) P3 ^+ n- E; S6 y
3                (uint16_t       )LED0_STK_SIZE,1 N4 e* K& [5 H5 k8 P
4                (void*          )NULL,                                    8 w. {7 Q. _  H, h
5                (UBaseType_t    )LED0_TASK_PRIO,   
1 v3 q3 i$ w$ Z* M 6                (TaskHandle_t*  )&LED0Task_Handler);    O' V# T% Z. p$ p/ }
7   //创建LED1任务
# h$ O& a" G, q8 q8 P 8   xTaskCreate((TaskFunction_t )led1_task,    # R5 O: E# v* e0 w, w; t5 M  L
9                (const char*    )"led1_task",  
8 r) [" U, z" d$ @) Q3 |10                (uint16_t       )LED1_STK_SIZE,* P8 P" X/ D$ q+ e# m& p
11                (void*          )NULL,
1 o+ v# L$ h2 m% @2 \5 e12                (UBaseType_t    )LED1_TASK_PRIO,: I$ j3 D; _7 d6 O$ R. u% S
13                (TaskHandle_t*  )&LED1Task_Handler);      
; j; a( z2 V1 `* E
5 M# I+ j: S, B8 Q1 O( O( f
创建完任务就开启任务调度
1vTaskStartScheduler();          //开启任务调度
; j  V; @$ [! j* E0 Y
然后具体实现任务函数

  T9 `3 n% o& {  n% }" m 1//LED0任务函数
4 C8 {% w3 v% `: J/ j* z 2void led0_task(void *pvParameters)4 d$ i) q6 O( f8 x- i! }
3
{: v5 F; q" p( t, w7 [9 a  k# h
4   while(1)
. K# j5 ?. g3 i3 p 5    {
8 b: I8 p# c0 |  Z3 g$ u# r! K 6       LED0=~LED0;' q1 F8 ]6 S3 r/ A
7       vTaskDelay(500);8 k' ~8 R+ D' K9 M+ ~
8    }
" u8 k' `5 e1 ?6 C0 H7 F- L% B1 c( ~ 9}  , F( p; D: j% ~7 }: a
10//LED1任务函数
8 n; ~9 j% R" D% x; l7 l: ]+ `/ t# z) z11void led1_task(void *pvParameters)+ \6 ^, F  O+ \- T( Y4 e3 s
12
{, u, u7 L2 \0 x4 D
13   while(1)
3 g4 P* J4 K+ a) t0 j$ J14    {4 O' t4 S, u) g$ W# d
15       LED1=0;
/ N% A2 J+ u, m& {16       vTaskDelay(200);
) T7 Z+ B; ?( \: V/ y17       LED1=1;8 D5 b) I/ M7 |" K
18       vTaskDelay(800);2 c( I8 Y& Q8 x" `! {' A
19    }9 x1 v) u' ~7 q9 I! F
20}
! ]2 e  L  W" ]6 |  p6 ^6 z# h3 i, w4 \
好啦,今天的介绍到这了为止,后面还会持续更新,敬请期待哦~
; }0 i6 U) |5 W

5 z( ]  M$ W/ A# ^/ i
欢迎大家一起来讨论操作系统的知识
我们的群号是:783234154

. E6 x& x# Y! u' o. ]% x7 }& ^  n+ Q; b3 i

5 ]9 t; w' E) K6 }5 C- q/ m' @
【连载】从单片机到操作系统①
5 N8 F4 _0 S0 ?& ~, b% K  P
+ d* w: \$ ]8 H
创客飞梦空间是开源公众号
欢迎大家分享出去
也欢迎大家投稿

- A) n' ^. _+ B; s4 ^/ E+ a- Y# W8 \; W, T0 V

/ d# }' Z3 c: K9 c) z; g* g
收藏 评论16 发布时间:2018-5-26 21:52

举报

16个回答
lilei900512 回答时间:2018-6-1 07:14:03
xiaojie0513 发表于 2018-5-31 18:05
6 S/ A' e- W, k怎么样啊,杰杰水平有限,还要多多指教吖

8 l9 l9 L, s0 \写的很好。我对于单片机的操作系统不是很了解,底层的倒是知道,还有就是了解些Linux系统。
moviexm 回答时间:2018-6-14 08:56:04
          ) d1 |! ?0 L2 W+ B- w4 t
参与/回复主题
3 k! h$ n2 u8 @关闭
6 ~; h! F( h6 J0 m3 nRE: 【连载】从单片机到操作系统④——FreeRTOS创建任务&开启... [修改]
xiaojie0513 回答时间:2018-5-31 18:05:34
lilei900512 发表于 2018-5-31 12:42
" s8 E2 x: N" G' \; i% ^7 M看看怎么样!

/ p/ ~+ J3 A* B- e8 ?& r. i% P怎么样啊,杰杰水平有限,还要多多指教吖
xiaojie0513 回答时间:2018-5-28 12:17:37
看完留个言啊
zero99 回答时间:2018-5-30 13:16:31
竟然看完了  

评分

参与人数 1ST金币 +2 收起 理由
xiaojie0513 + 2 给破总十个赞咯!!

查看全部评分

zero99 回答时间:2018-5-30 16:45:13
谢谢打赏~
king_lv 回答时间:2018-5-31 11:33:21
感谢楼主分享
xiaojie0513 回答时间:2018-5-31 12:14:21
king_lv 发表于 2018-5-31 11:338 j0 Y9 Z# s* q/ T
感谢楼主分享

) s; o2 o+ P5 w
lilei900512 回答时间:2018-5-31 12:42:59
看看怎么样!
heyv11 回答时间:2018-6-7 21:52:39
写的好
xiaojie0513 回答时间:2018-6-7 23:35:52
heyv11 发表于 2018-6-7 21:52
" w  |( t. s- I写的好

0 k( W! n  I0 v, o8 I谢谢支持
billy226-301299 回答时间:2018-6-13 11:20:13
支持楼主,好东西
电子星辰 回答时间:2018-6-13 11:34:49
学习一下
xiaojie0513 回答时间:2018-6-13 14:47:39
billy226-301299 发表于 2018-6-13 11:20
! y. K- @, K( f9 @支持楼主,好东西

, S& \8 u/ N$ b, Q; k3 B/ H, `/ [谢谢支持- Q- u$ K, A3 O+ B3 ~& m6 H/ t# R
谢谢支持
12下一页

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版