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

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

[复制链接]
xiaojie0513 发布时间:2018-5-26 21:52
本帖最后由 xiaojie0513 于 2018-5-29 13:42 编辑
+ T- o6 Q9 Q9 ?; p/ c: d, \7 X4 ?9 C' H2 c1 {; j
创客的兄弟姐妹们大家好,我是杰杰。又到了更新的时候了。
听首歌缓解一下心情。

) r" h2 q( X+ B
开始今天的内容之前,先补充一下上篇文章【连载】从单片机到操作系统③——走进FreeRTOS的一点点遗漏的知识点。
1BaseType_t xTaskCreate(       TaskFunction_t pvTaskCode,9 }& H' T% z$ W" |& H- d
2                              const char * const pcName,8 J- d- c  R; ^" D. t
3                              uint16_t usStackDepth,
- s0 j) Z" P. H* d' t 4                              void *pvParameters,5 K. ]  L1 X( N; {) H5 s/ N$ v
5                              UBaseType_t uxPriority,; X1 a  f; C* b( F9 f* B
6                              TaskHandle_t *pvCreatedTask8 }' T+ r+ z" Z7 r9 q
7                          )
;4 l( H) M) y! }8 D
8创建任务中的堆栈大小问题,在task.h中有这样子的描述:
* P) E0 r  f0 ~- U2 E+ z: n 9/**
) m$ h0 h. z- R. Z: K" }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  ; T/ ?! N/ o( y' r; Q! c
11* usStackDepth is defined as 100, 200 byteswill be allocated for stack storage.* G1 o/ S+ w  \
12*/
代码可左右滑动

4 E/ Y2 l6 k# R/ n$ L$ a
  当任务创建时,内核会分为每个任务分配属于任务自己的唯一堆栈。usStackDepth 值用于告诉内核为它应该分配多大的栈空间。
这个值指定的是栈空间可以保存多少个字(word) ,而不是多少个字节(byte)
文档也有说明,如果是16位宽度的话,假如usStackDepth = 100;那么就是200个字节(byte)。
当然,我用的是stm32,32位宽度的, usStackDepth=100;那么就是400个字节(byte)。

' V' D" Z; j6 m5 T  d& v* r9 {& S
  好啦,补充完毕。下面正式开始我们今天的主题。


. e/ Q1 T! z/ @+ ^  `0 ~
  我自己学的是应用层的东西,很多底层的东西我也不懂,水平有限,出错了还请多多包涵。
  其实我自己写文章的时候也去跟着火哥的书看着底层的东西啦,但是本身自己也是不懂,不敢乱写。所以,这个《从单片机到操作系统》系列的文章,我会讲一点底层,更多的是应用层,主要是用的方面。
* w7 ]3 H! A: F0 K0 W8 l
按照一般的写代码的习惯,在main函数里面各类初始化完毕了,并且创建任务成功了,那么,可以开启任务调度了。
1int main(void)
( |( F) n8 w0 {7 C5 v 2
{
* o/ p5 Q  q, [9 o. ~8 G' { 3    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4   
, J7 X& b& U4 g; ~5 D 4    Delay_Init();                       //延时函数初始化     
& {& N6 X" t$ S0 p0 H  t; f; U 5    Uart_Init(115200);                  //初始化串口( u4 M; T/ V9 p  t6 A) j6 Z; n
6    LED_Init();                     //初始化LED
6 y3 M% U" i* @" P4 M4 S7 H* e) V 7    KEY_Init();
: Q' p- {0 g; W: q9 o; g; q$ Y 8    //创建开始任务6 ^- Z! l! I. N! N
9    xTaskCreate((TaskFunction_t )start_task,            //任务函数
! [, J9 J' S* _  g, Y, N10                (const char*    )"start_task",          //任务名称- z2 n. F& [# ?) @3 D
11                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
, ?; O, B. b  {  b  M8 d12                (void*          )NULL,                  //传递给任务函数的参数  [$ t) H/ X# N$ q4 t
13                (UBaseType_t    )START_TASK_PRIO,       //任务优先级3 ~/ i; r6 `$ @+ ~+ o# c% {3 f
14                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              + K) {& Q* @2 o. s$ U
15    vTaskStartScheduler();          //开启任务调度
1 e9 P  w; Y3 g: [16}/ k# C. s" T+ R, r9 E
  Q; M" Q0 Z* q0 k: I
  来大概看看分析一下创建任务的过程,虽然说会用就行,但是也是要知道了解一下的。
注意:下面说的创建任务均为xTaskCreate(动态创建而非静态创建。
1pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
5 C; B4 L$ ]# F7 T 2/*lint !e961 MISRA exception as the casts are only redundant for some ports. */
9 N( n1 u) O( g6 f. K 3            if( pxStack != NULL )6 d- S3 m- p& Z
4            {
" }! v$ q3 c+ L6 X) O+ K' g 5                /* Allocate space for the TCB. */- [2 ~, L0 o6 x% W. `2 t% n9 I. T
6                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
6 s+ `8 n6 _/ z 7                /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
' l. k* P; b  J- d! {& _1 M 8                if( pxNewTCB != NULL )4 h5 Q+ W7 X5 z
9                {- |6 a+ W* S3 P2 c
10                    /* Store the stack location in the TCB. */
+ d" [/ `. f  b& v11                    pxNewTCB->pxStack = pxStack;
; g3 h5 {: s7 B' A" o" M12                }- j5 u+ \6 ]1 q6 i3 D( [, G
13                else
" H  c1 a, L& E5 j& Y14                {6 V0 n) W( G3 c* l' e; O* r3 o
15                    /* The stack cannot be used as the TCB was not created.  Free
9 L+ b5 d- G* ?- g; H16                    it again. */

/ `1 h( y8 s1 X8 p8 L4 K  b17                    vPortFree( pxStack );
8 ^% `9 D8 ?! g6 L5 P18                }6 a4 F- c: X: k# n
19            }
& P  C( z9 e4 J0 [- Y, u20            else
" q0 V4 U+ Q) H8 J5 i, M21            {
# D9 Y9 g- p; q. r$ o22                pxNewTCB = NULL;+ F( Q6 X3 X. I
23            }
; w8 f7 T  \$ a0 {8 I& M24        }
  首先是利用pvPortMalloc给任务的堆栈分配空间,if( pxStack != NULL )如果内存申请成功就接着给任务控制块申请内存。pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );同样是使用pvPortMalloc();如果任务控制块内存申请失败则释放 之前已经申请成功的任务堆栈的内存vPortFree( pxStack );
  然后就初始化任务相关的东西,并且将新初始化的任务控制块添加到列表中prvAddNewTaskToReadyList( pxNewTCB );
  最后返回任务的状态,如果是成功了就是pdPASS,假如失败了就是返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;

0 m+ z8 b$ h! ?0 u/ h: Y% M- O 1prvInitialiseNewTask(     pxTaskCode,
1 H* W/ s* @5 ^2 D' e 2                          pcName, / v7 r' `5 n5 B4 [$ E# z) L
3                         ( uint32_t ) usStackDepth,* k9 V, Q' ^* f$ B8 u! n
4                          pvParameters,
2 \- D( U( H0 M 5                          uxPriority,
! u' U  j* h/ {3 i4 U! P 6                         pxCreatedTask,
; [) Z- S4 M8 L& D- C# t! Z% m' v 7                          pxNewTCB,
) |+ i6 u. s% } 8                         NULL );
1 S. P. d  w4 Z+ f. Z. p, _* |4 f 9            prvAddNewTaskToReadyList( pxNewTCB );- @, @: A9 J3 [7 O
10            xReturn = pdPASS;4 _) B. v8 B% R/ ]. b/ P" I/ e3 ]3 x
11        }6 B. Q0 I0 T5 K/ D- d# \1 P5 b
12        else) ]: u* I; q5 ?; P# C' U$ @
13        {' b( E5 m) b7 K, i9 _
14            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
; i. {$ L0 a, b: L6 v15        }
4 u$ |2 _4 O4 [# a# |* o# i' e16        return xReturn;
- W$ m: X$ y1 k, b17    }& O2 o8 D2 }0 ~, }, l, v* @' N
18// 相关宏定义
. {4 c, G! p" M19#define pdPASS            ( pdTRUE )
+ M% b6 I3 B: X- A+ u% L# l% H  w20#define pdTRUE            ( ( BaseType_t ) 1 )# {) \* ^; `  s3 S: h
21/* FreeRTOS error definitions. */' ^' l  o. n3 t+ h  ?4 s
22#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY    ( -1 )
: D2 |( O3 ~  p1 y9 b% F% D$ }' P8 c1 q8 l
  具体的static void prvInitialiseNewTask(()实现请参考FreeRTOS的tasks.c文件的767行代码。具体的static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )实现请参考FreeRTOS的tasks.c文件的963行代码。

8 r/ n0 r& o" C2 z
  因为这些是tasks.c中的静态的函数,仅供xTaskCreate创建任务内部调用的,我们无需理会这些函数的实现过程,当然如果需要请自行了解。

& _1 ^3 y- o1 I/ `
创建完任务就开启任务调度了:
1vTaskStartScheduler();          //开启任务调度
( c  n8 M+ R5 g4 f+ W/ s/ v

$ E6 ]" `8 v& a0 u
在任务调度里面,会创建一个空闲任务(我们将的都是动态创建任务,静态创建其实一样的)
1xReturn = xTaskCreate(    prvIdleTask,
; L5 f' G+ c' x* H% K 2                          "IDLE", configMINIMAL_STACK_SIZE,5 x0 |0 s/ P- C8 J: u$ O
3                          ( void * ) NULL,
8 x. c6 P3 V5 r. }; e5 G 4                          ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
' p1 ~6 Q& M+ m3 g2 J* R, r 5                          &xIdleTaskHandle ); 6 [7 y8 U5 ?( u$ i! T
6/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */) U) |" T$ p- {9 ^3 n. L+ ^
7    }* o5 Z5 c  K/ W4 z1 I+ i2 X7 c" c2 e
8相关宏定义:
  e, E. ]# M8 y4 j4 F 9#define tskIDLE_PRIORITY            ( ( UBaseType_t ) 0U )
3 A2 g8 J' k3 x3 Z7 R9 z10#ifndef portPRIVILEGE_BIT
1 o4 j4 m1 _- Y/ w3 y11    #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 )
# r; W9 j) \6 W2 S. f- v& n6 L12#endif, m. c% t; w4 o& b7 c2 B" k- u
13#define configUSE_TIMERS                        1                              . A* h6 [) j: X3 w
14 //为1时启用软件定时器
1 W, |0 r! k9 ^/ U2 j3 V: S" R
从上面的代码我们可以看出,空闲任务的优先级是tskIDLE_PRIORITY为0,也就是说空闲任务的优先级最低。当CPU没事干的时候才执行空闲任务,以待随时切换优先级更高的任务。
如果使用了软件定时器的话,我们还需要创建定时器任务,创建的函数是:
1#if ( configUSE_TIMERS == 1 )
9 i8 }/ F+ Z6 N; S2    BaseType_t xTimerCreateTimerTask( void )
) X) P8 F* [5 V# o9 S4 n! n# F3

% @8 }! y$ N! T2 K$ c4 Q' O
然后还要把中断关一下
1portDISABLE_INTERRUPTS();
  Y/ ^4 H( S- j5 R. ~
至于为什么关中断,也有说明:
1/* Interrupts are turned off here, toensure a tick does not occur
+ h1 _/ q. n% p- H/ [; l, y 2before or during the call toxPortStartScheduler().  The stacks of8 ^+ s) C+ z# o6 a0 W9 k
3the created tasks contain a status wordwith interrupts switched on
# |2 e3 c2 E- A8 U7 ^( e6 F 4so interrupts will automatically getre-enabled when the first task
: ^5 o' ^+ a' @- K, a 5starts to run. */

- c' \7 ~! W+ u! F# A 6/ *中断在这里被关闭,以确保不会发生滴答; K/ w* t, e3 g1 l, I' h
7在调用xPortStartScheduler()之前或期间。堆栈- |# e2 Z! I; L* M( R8 P
8创建的任务包含一个打开中断的状态字
+ W& T+ L% d2 M3 d2 Z 9因此中断将在第一个任务时自动重新启用3 }% H  M' J7 w7 V/ b  P% V, x
10开始运行。*/$ T" o6 i- g; x) L# J( u8 @
. x" d' _. z! B8 C& u
3 _! A6 a2 D  E' X
那么如何打开中断呢????这是个很重要的问题
别担心,我们在SVC中断服务函数里面就会打开中断的
看代码:
1__asm void vPortSVCHandler( void )
+ p3 L, z4 G6 y 2
{0 x  E9 l- D6 t' K2 T2 k$ ]2 d
3         PRESERVE8" @% u# e1 L7 y# j: f9 e
4         ldr    r3, =pxCurrentTCB  /* Restore the context. */4 c) |) S. g5 z0 R$ O' _( ^: N
5         ldrr1, [r3]                            /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */
/ q" \3 h, S7 z2 L5 {) n8 y 6         ldrr0, [r1]                            /* Thefirst item in pxCurrentTCB is the task top of stack. */
6 h/ I3 o6 t: n. u# |* @ 7         ldmiar0!, {r4-r11}             /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */$ n  L& d6 T* [  i2 D9 @5 G0 C
8         msrpsp, r0                                   /*Restore the task stack pointer. */
- |6 ~( ~$ b" H/ W( W; O$ g 9         isb
3 t% v3 V4 N: V8 R# ]! P0 t! T' K10         movr0, #0" e" _! K; d- X. Q
11         msr  basepri, r0
" b! v3 l8 A) Y5 p; }12         orrr14, #0xd6 D' h, X8 v+ Q; F
13         bxr147 F5 s: h* X; Y
14}
- W$ h2 m, b4 W- h, t/ w% |4 `# `4 y* P9 {0 h/ \
1msr  basepri, r0. T2 }% ]; x6 H5 H) T
  就是它把中断打开的。看不懂没所谓,我也不懂汇编,看得懂知道就好啦。
" p" s* f$ b" b- M
1xSchedulerRunning = pdTRUE;
' R0 D1 t. t3 H3 z3 A$ g! P7 C
任务调度开始运行

7 H+ U6 K" @5 I) c3 {% `& d1/* If configGENERATE_RUN_TIME_STATS isdefined then the following
7 X, V; A2 k, D+ g$ z2macro must be defined to configure thetimer/counter used to generate5 w' B' v$ f, w9 m) k/ }/ m0 q
3the run time counter time base. */
$ h( N" ]3 A  u" E0 [
4portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
$ ^  J$ j7 |: H" W; T: L7 V' F+ X- [9 }  y* e6 \, v7 C
如果configGENERATE_RUN_TIME_STATS使用时间统计功能,这个宏为1,那么用户必须实现一个宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();用来配置一个定时器或者计数器。

7 e; W) m4 V8 H7 ~% X9 k7 x
来到我们的重点了,开启任务调度,那么任务到这了就不会返回了。
1if( xPortStartScheduler() != pdFALSE )
' m$ r+ l# U; ]% z, I7 s: @5 M2                   {
/ T; X6 g/ B- {4 p. _/ k3                            /*Should not reach here as if the scheduler is running the: R, V4 |1 n1 h
4                            functionwill not return. */' h' ~- O" f4 o& U! o
5                   }然后就能开启第一个任务了,感觉好难是吧,我一开始也是觉得的,但是写了这篇文章,觉得还行吧,也不算太难,可能也是在查看代码跟别人的书籍吧,写东西其实还是蛮好的,能加深理解,写过文章的人就知道,懂了不一定能写出来,所以,我还是很希望朋友们能投稿的。杰杰随时欢迎。。。
7 Z, V8 R/ I% ]& I
开始任务就按照套路模板添加自己的代码就好啦,很简单的。
创建任务
1 xTaskCreate((TaskFunction_t )led0_task,    & h8 E  Z0 D' l, C" Y) d
2                (const char*    )"led0_task",  + m7 l5 @/ V7 S
3                (uint16_t       )LED0_STK_SIZE,3 Q& Q# C+ ?, @6 _7 [, ~
4                (void*          )NULL,                                    
$ V4 h3 _8 a, a, W, ]# g  D6 W 5                (UBaseType_t    )LED0_TASK_PRIO,   
/ @+ u, f/ Y3 j9 b0 h; p& ~5 i" o 6                (TaskHandle_t*  )&LED0Task_Handler);  + m: y( B0 x8 h
7   //创建LED1任务
1 T. D: ^1 j8 d1 i; J1 | 8   xTaskCreate((TaskFunction_t )led1_task,   
& }0 Z- F+ w. n  Q 9                (const char*    )"led1_task",  
) b: T; `! Q3 I$ d10                (uint16_t       )LED1_STK_SIZE,- i( o' [+ j0 T; v
11                (void*          )NULL,
+ z$ X# t) T. M/ Q12                (UBaseType_t    )LED1_TASK_PRIO,
1 ]! a& R% e! P7 ]% j! |5 S13                (TaskHandle_t*  )&LED1Task_Handler);      
" f2 K: @; ^! _1 a; y1 P
3 b' `4 L8 f+ L3 e+ I" ]+ O, y
创建完任务就开启任务调度
1vTaskStartScheduler();          //开启任务调度

0 o1 N% g  T3 I- {1 _  ?
然后具体实现任务函数

' t; a3 P/ R& a( n# U. f 1//LED0任务函数. Q- M/ _. l$ \& `+ }0 C
2void led0_task(void *pvParameters)5 J0 p* Q! d; V
3
{
6 R9 a& a2 R9 t9 y0 n 4   while(1)- T2 r* u) H! l" x* Q
5    {
3 E- _7 E5 }* X: P 6       LED0=~LED0;0 u" K0 a$ |2 K* Y- e
7       vTaskDelay(500);! a( K. O# X; J' i. A! y( R6 u: q
8    }
9 F6 E. _; @. c 9}  
8 g$ A7 R# S  }+ ^# o10//LED1任务函数
& p" e7 M% }3 J) m* u, v11void led1_task(void *pvParameters)& C5 S. s3 K% u9 A; D
12
{
; J, a) |& o, d/ a9 |1 G1 H! h13   while(1)
* h3 l7 b4 T4 }+ h14    {" L% O' m3 V! b/ D
15       LED1=0;
  S8 L& A# e$ i" h( T  k16       vTaskDelay(200);
5 v# S" M! B8 K5 e) K( |! a17       LED1=1;4 ^/ E0 u3 T; z9 C
18       vTaskDelay(800);
/ z, a/ E) v  f" O1 h. z6 @( I19    }, b+ M" M! X$ t  n
20}3 z$ k. e7 c' }3 F3 n2 D/ ]
6 X4 `/ c3 y; U  `4 W
好啦,今天的介绍到这了为止,后面还会持续更新,敬请期待哦~

6 i  y7 [+ w3 D$ N9 i% u
; _: @# h& R1 B5 h- x3 ^& }, w
欢迎大家一起来讨论操作系统的知识
我们的群号是:783234154
$ O) ]/ ~. w  F2 U8 a
. I6 Q6 c6 n0 ^2 K: c
- k" g- v% v% x- H) r) t7 ~

% K2 h5 |! h# C9 [
创客飞梦空间是开源公众号
欢迎大家分享出去
也欢迎大家投稿

, Y/ `* r; k2 Y) }+ O# b
/ _0 h2 W- R: T/ ?. V) U$ E  u, I+ W" N  O. D# |' b% l
收藏 评论16 发布时间:2018-5-26 21:52

举报

16个回答
lilei900512 回答时间:2018-6-1 07:14:03
xiaojie0513 发表于 2018-5-31 18:05
. a1 H' B$ u9 W2 C/ }# D9 B怎么样啊,杰杰水平有限,还要多多指教吖
' U6 h1 E8 _9 p6 e1 y9 C
写的很好。我对于单片机的操作系统不是很了解,底层的倒是知道,还有就是了解些Linux系统。
moviexm 回答时间:2018-6-14 08:56:04
         
; v& k. \  D, V6 y9 _5 y+ e" P5 g. U0 Z" x参与/回复主题
; `, N, [5 ]2 Y# l( T关闭
7 @# t" H* t  [; K  bRE: 【连载】从单片机到操作系统④——FreeRTOS创建任务&开启... [修改]
xiaojie0513 回答时间:2018-5-31 18:05:34
lilei900512 发表于 2018-5-31 12:42) V2 |. b6 ]& ]6 c$ A, j
看看怎么样!

$ a; f: a0 S) W- S3 m+ \0 `怎么样啊,杰杰水平有限,还要多多指教吖
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:33
/ E6 D1 O" G5 i& T7 n1 q感谢楼主分享
, e# ]) x- X. q& a$ J
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:523 a) B  x" d! j! \) A6 J. W( ^" a' ^
写的好
: N; {- }" f% C* h2 e* b
谢谢支持
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/ h) J" R; v/ `1 ?- T9 s6 G
支持楼主,好东西

$ W" t6 y& i; l( p9 q6 e谢谢支持
+ ~/ S* N$ J" [0 |0 }谢谢支持
12下一页

所属标签

相似分享

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