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

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

[复制链接]
xiaojie0513 发布时间:2018-5-26 21:52
本帖最后由 xiaojie0513 于 2018-5-29 13:42 编辑 % a6 l3 M2 S4 l% W5 H  L3 @
" q$ }+ t# N2 X- A
创客的兄弟姐妹们大家好,我是杰杰。又到了更新的时候了。
听首歌缓解一下心情。

+ M8 Z! e& b9 Z. Q
开始今天的内容之前,先补充一下上篇文章【连载】从单片机到操作系统③——走进FreeRTOS的一点点遗漏的知识点。
1BaseType_t xTaskCreate(       TaskFunction_t pvTaskCode,; a; n# |( ~+ ^1 G  N' q3 T
2                              const char * const pcName,
( g" o$ `5 M2 z 3                              uint16_t usStackDepth,1 J/ x0 j( D( h5 D. b" u: [; W0 b
4                              void *pvParameters,+ Q* w% t; [6 m  [- Y5 L
5                              UBaseType_t uxPriority,
' v& k$ @' O; T 6                              TaskHandle_t *pvCreatedTask9 G/ h7 q, p8 x+ |. _# `
7                          )
;
( p( ~, H+ j/ D  X2 h 8创建任务中的堆栈大小问题,在task.h中有这样子的描述:% a( B+ p8 B6 k* \, j% u
9/**" u6 q' d- h5 @; A  Z; e7 S( I
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  8 q; N* x& Z9 r) F5 [
11* usStackDepth is defined as 100, 200 byteswill be allocated for stack storage.0 l$ h# t9 K6 K* e. ~
12*/
代码可左右滑动
! y* w/ Y# Z7 J+ y3 U
  当任务创建时,内核会分为每个任务分配属于任务自己的唯一堆栈。usStackDepth 值用于告诉内核为它应该分配多大的栈空间。
这个值指定的是栈空间可以保存多少个字(word) ,而不是多少个字节(byte)
文档也有说明,如果是16位宽度的话,假如usStackDepth = 100;那么就是200个字节(byte)。
当然,我用的是stm32,32位宽度的, usStackDepth=100;那么就是400个字节(byte)。
/ ], N$ r' \) u
  好啦,补充完毕。下面正式开始我们今天的主题。


9 s: \# q' z2 O
  我自己学的是应用层的东西,很多底层的东西我也不懂,水平有限,出错了还请多多包涵。
  其实我自己写文章的时候也去跟着火哥的书看着底层的东西啦,但是本身自己也是不懂,不敢乱写。所以,这个《从单片机到操作系统》系列的文章,我会讲一点底层,更多的是应用层,主要是用的方面。

8 X3 z. Y( W- m) q
按照一般的写代码的习惯,在main函数里面各类初始化完毕了,并且创建任务成功了,那么,可以开启任务调度了。
1int main(void)4 M& K  v& |( O: B# s, n' o
2
{! u) J7 T, Q6 N, Z! r
3    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4    7 r: H7 g3 n3 g0 [8 s: Q
4    Delay_Init();                       //延时函数初始化     
. ^1 j$ l( p& R7 i' ~% f 5    Uart_Init(115200);                  //初始化串口
9 {( _) ?1 g0 g. O7 W 6    LED_Init();                     //初始化LED
5 ^$ m3 R3 Q' f4 X% I  k: k 7    KEY_Init(); ! f5 r5 f  S, L  z
8    //创建开始任务
0 f/ v5 Z3 y- G. l+ I9 R  w( ` 9    xTaskCreate((TaskFunction_t )start_task,            //任务函数: ], k  H  u6 K" ~3 p
10                (const char*    )"start_task",          //任务名称
) `1 s# n/ e& }4 S: |4 {- o11                (uint16_t       )START_STK_SIZE,        //任务堆栈大小* E7 G: |' {% L5 T( F+ Z) v
12                (void*          )NULL,                  //传递给任务函数的参数% M; X& L0 a! p/ ]
13                (UBaseType_t    )START_TASK_PRIO,       //任务优先级( U, z& o/ n9 `2 Q" v5 i: @8 a
14                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
! z2 v' {# U0 W) Z8 s' J$ E/ K2 c15    vTaskStartScheduler();          //开启任务调度0 X' c! K2 ?7 U! H! n
16}
* x; m4 n; l7 P* |3 j

; \/ e' Q3 S2 h- q5 ?
  来大概看看分析一下创建任务的过程,虽然说会用就行,但是也是要知道了解一下的。
注意:下面说的创建任务均为xTaskCreate(动态创建而非静态创建。
1pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 8 j% m3 J7 R! L2 m
2/*lint !e961 MISRA exception as the casts are only redundant for some ports. */) D& {9 J0 g  k- v% _( `& Q
3            if( pxStack != NULL ); t7 i/ V$ z* I# ^) o$ o
4            {5 N' S* v! D$ @+ f- F, A! x
5                /* Allocate space for the TCB. */
9 L; C4 L* o, W- \" g/ P, [ 6                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
& l0 K2 [/ ^7 R5 T 7                /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
; V# ~. P4 a3 x8 F  [  b+ K 8                if( pxNewTCB != NULL )
# {) U5 Q& ~. D& Y* q 9                {! D; n8 m) X: R6 c
10                    /* Store the stack location in the TCB. */
! v! g# P8 z5 I/ W6 y11                    pxNewTCB->pxStack = pxStack;! H: V, A# @. j9 l5 F$ n* U) \
12                }$ q- O& Y% [& r+ K- X; ?: y
13                else! J- k  ~5 z7 ^$ a: Z2 u/ M8 F
14                {
5 w& D. o, t  i5 e15                    /* The stack cannot be used as the TCB was not created.  Free8 |% ~$ I2 o; W! ]/ ~) G2 @3 ]: }
16                    it again. */

& Q4 |3 s& p5 m2 V) A% c17                    vPortFree( pxStack );* O& S' |1 k2 w+ F. e
18                }6 A0 i! P. L0 X1 S' q& C
19            }
2 i6 i" t; }+ k  L20            else$ \: R+ C+ B' b1 V
21            {
. m9 k6 O$ L( Z' _22                pxNewTCB = NULL;
# U! T% M: v$ y4 h# Z7 B23            }
7 N9 t2 d  C% x# z) p8 Z24        }
  首先是利用pvPortMalloc给任务的堆栈分配空间,if( pxStack != NULL )如果内存申请成功就接着给任务控制块申请内存。pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );同样是使用pvPortMalloc();如果任务控制块内存申请失败则释放 之前已经申请成功的任务堆栈的内存vPortFree( pxStack );
  然后就初始化任务相关的东西,并且将新初始化的任务控制块添加到列表中prvAddNewTaskToReadyList( pxNewTCB );
  最后返回任务的状态,如果是成功了就是pdPASS,假如失败了就是返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
# N# S) p/ R. C! h/ ~
1prvInitialiseNewTask(     pxTaskCode, ) ]/ ~- M0 R4 q8 c
2                          pcName,
4 l# _6 U8 X+ i4 C( d5 F" N2 G 3                         ( uint32_t ) usStackDepth,
% B  v* F' H- I  B' A3 V5 Y2 S; p 4                          pvParameters,2 x$ H$ O2 z0 p+ G
5                          uxPriority,
  T8 ~! _# }: k1 w 6                         pxCreatedTask,
1 ^$ y' D  c; g1 R0 ? 7                          pxNewTCB,
$ T& q8 ^% o/ H& M 8                         NULL );
$ M0 ?, Y+ c& o# o 9            prvAddNewTaskToReadyList( pxNewTCB );8 m, n7 a$ W5 g( t7 x' ]
10            xReturn = pdPASS;
# |' M" c" U" r1 n  U11        }4 H3 Z! f+ I3 |$ A( g/ O
12        else( K+ w' N- [% O+ v& _" `3 f1 J
13        {
: e+ K0 ^  V  v$ v! q5 c$ \14            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
" y5 O4 r1 S" |15        }4 f1 j8 a# B. m
16        return xReturn;5 Z6 e# b0 U# h5 z, Y
17    }
4 _9 H2 T* M; d' B18// 相关宏定义
- x5 k' v5 T6 t+ d: H) h19#define pdPASS            ( pdTRUE )6 y: F8 ?4 X! W
20#define pdTRUE            ( ( BaseType_t ) 1 )
; m4 |; f9 j9 R( H21/* FreeRTOS error definitions. */& `8 l& G  M6 H9 |
22#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY    ( -1 )
1 z/ ~# Y0 u, O% M" Q) j! }  s. S1 |9 U4 @" T
  具体的static void prvInitialiseNewTask(()实现请参考FreeRTOS的tasks.c文件的767行代码。具体的static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )实现请参考FreeRTOS的tasks.c文件的963行代码。

8 C& Y9 M7 b( q4 ]
  因为这些是tasks.c中的静态的函数,仅供xTaskCreate创建任务内部调用的,我们无需理会这些函数的实现过程,当然如果需要请自行了解。
+ T: `. e: o+ T2 m8 o, N
创建完任务就开启任务调度了:
1vTaskStartScheduler();          //开启任务调度
, x+ ^- G+ b; C: r3 |# H
% @6 |7 B& \8 j$ T
在任务调度里面,会创建一个空闲任务(我们将的都是动态创建任务,静态创建其实一样的)
1xReturn = xTaskCreate(    prvIdleTask,  c4 B( q+ H5 ?$ p8 g5 c* q5 p! n
2                          "IDLE", configMINIMAL_STACK_SIZE,
8 }, ~7 o/ S! Y; | 3                          ( void * ) NULL,1 P5 Z! m8 F& J. P3 l/ p; ?: j7 y4 \
4                          ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),0 T6 g" ?- |6 z0 T" d, K, [7 Z
5                          &xIdleTaskHandle ); ( R3 `& F* V: A& t
6/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */- F+ z1 x3 F4 u$ d& `1 H0 G
7    }% u; h6 G( j* F) b) k
8相关宏定义:: d" O# G1 b" h
9#define tskIDLE_PRIORITY            ( ( UBaseType_t ) 0U )
, A! ~% w8 i+ ~+ s- e10#ifndef portPRIVILEGE_BIT0 ~# t; X2 M0 t2 n: F
11    #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 )
7 ~5 X- Y# t: f1 ]. X% K9 R12#endif
5 u- m4 Z5 D: w5 f7 [( Z- }1 ^( X3 T13#define configUSE_TIMERS                        1                              % u# Y2 }8 r! G8 b& U- n
14 //为1时启用软件定时器
4 F7 R) B+ D7 I7 @: d
从上面的代码我们可以看出,空闲任务的优先级是tskIDLE_PRIORITY为0,也就是说空闲任务的优先级最低。当CPU没事干的时候才执行空闲任务,以待随时切换优先级更高的任务。
如果使用了软件定时器的话,我们还需要创建定时器任务,创建的函数是:
1#if ( configUSE_TIMERS == 1 )
1 b" p3 G5 B! L, ^  L4 l2    BaseType_t xTimerCreateTimerTask( void ): _, Z0 D' E: j( r( y! ~  C
3
/ O. t8 C+ M; l0 ?% O% M- n- |
然后还要把中断关一下
1portDISABLE_INTERRUPTS();
* r" }! H3 }# m- P6 ^$ ?) {
至于为什么关中断,也有说明:
1/* Interrupts are turned off here, toensure a tick does not occur; z6 ^/ W* g+ S+ ]% ?  l
2before or during the call toxPortStartScheduler().  The stacks of
5 k& q, E6 S" m$ V% P" p" @ 3the created tasks contain a status wordwith interrupts switched on
! t5 ^; h+ W! o& h+ d 4so interrupts will automatically getre-enabled when the first task. L3 b6 h1 s" D/ b! e0 A& w# B
5starts to run. */
: _; l8 `3 K4 T" G
6/ *中断在这里被关闭,以确保不会发生滴答
+ R: h. t/ X  z- R 7在调用xPortStartScheduler()之前或期间。堆栈9 s, \( Z; ]) E& i( o
8创建的任务包含一个打开中断的状态字
3 h8 c# |6 r* ?$ f 9因此中断将在第一个任务时自动重新启用
; ]6 C# v: u5 U0 b10开始运行。*/
& O& }: g9 q8 _
( H, k* w# n# S7 P
- r% h$ I6 G, e8 \# k6 E  @
那么如何打开中断呢????这是个很重要的问题
别担心,我们在SVC中断服务函数里面就会打开中断的
看代码:
1__asm void vPortSVCHandler( void )
; N6 y& d! S, R0 I# Z$ e4 U$ }: l 2
{- K6 `( l; O* ]- e
3         PRESERVE8$ E  W" N7 f% H# ^* l, J8 {
4         ldr    r3, =pxCurrentTCB  /* Restore the context. */; ~2 L8 d6 h' i3 J+ T* f
5         ldrr1, [r3]                            /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */
# f& K7 t$ X# J& E* b( F! z) T 6         ldrr0, [r1]                            /* Thefirst item in pxCurrentTCB is the task top of stack. */) @9 i  K5 @! I$ f
7         ldmiar0!, {r4-r11}             /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */
: ~. x% A. `& }" v# g2 d6 R5 W 8         msrpsp, r0                                   /*Restore the task stack pointer. */. t6 W* b9 V4 h
9         isb# \% x( s4 _* L( R% I
10         movr0, #05 D. Z- T( U, ]. G
11         msr  basepri, r0; C( M- [9 q/ m6 r& N5 u
12         orrr14, #0xd
2 X# c5 {; [7 F7 A1 y* Y13         bxr14
5 j7 C( x. p" l5 O14}' l- N# W- _4 y* ~8 C
! f* d! R% ?0 B5 w: r. }3 M: M3 T
1msr  basepri, r09 m& E# F3 X! t
  就是它把中断打开的。看不懂没所谓,我也不懂汇编,看得懂知道就好啦。
) X4 x3 H" U4 [  M8 d+ k- m
1xSchedulerRunning = pdTRUE;
$ r% V2 D  A; Z  G1 t7 D" t: c5 c$ Q
任务调度开始运行
. ?1 C$ b4 q! p8 b
1/* If configGENERATE_RUN_TIME_STATS isdefined then the following# u1 ~  x/ y7 B/ V: j* \9 z
2macro must be defined to configure thetimer/counter used to generate; y6 d- y7 R; v4 b5 X; N8 g- n* l
3the run time counter time base. */
% H: Q1 ^2 Q  Q( ]+ O- v
4portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();+ N4 j0 Q3 I  P# P9 @+ e

# c& r6 Z2 z3 K: _" l4 m
如果configGENERATE_RUN_TIME_STATS使用时间统计功能,这个宏为1,那么用户必须实现一个宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();用来配置一个定时器或者计数器。

- R; \5 t% Y7 d; e7 Q# G$ y
来到我们的重点了,开启任务调度,那么任务到这了就不会返回了。
1if( xPortStartScheduler() != pdFALSE )
" z/ r- p" w  M- V2                   {. N$ c( V. h! e, s# m2 b
3                            /*Should not reach here as if the scheduler is running the
3 |9 ^- a) C* o7 v0 S& K9 d4                            functionwill not return. */
3 q- y* n" O5 `5                   }然后就能开启第一个任务了,感觉好难是吧,我一开始也是觉得的,但是写了这篇文章,觉得还行吧,也不算太难,可能也是在查看代码跟别人的书籍吧,写东西其实还是蛮好的,能加深理解,写过文章的人就知道,懂了不一定能写出来,所以,我还是很希望朋友们能投稿的。杰杰随时欢迎。。。

9 S+ ?! p3 }. q' I
开始任务就按照套路模板添加自己的代码就好啦,很简单的。
创建任务
1 xTaskCreate((TaskFunction_t )led0_task,    / P; S3 x5 q. [
2                (const char*    )"led0_task",  
: h: J0 K" U5 f& k# N 3                (uint16_t       )LED0_STK_SIZE,: [7 ^' E% F2 ?# T; Z
4                (void*          )NULL,                                    
  J1 k' ]. P. v, y 5                (UBaseType_t    )LED0_TASK_PRIO,   
! s. ?7 V4 o8 E/ _0 R/ H 6                (TaskHandle_t*  )&LED0Task_Handler);  1 \7 {& W( J) l1 _2 V" t0 F
7   //创建LED1任务% a: [0 K+ p7 g$ U# O3 ^0 ~' n
8   xTaskCreate((TaskFunction_t )led1_task,   
0 ^( J7 x4 D% A: D, z 9                (const char*    )"led1_task",  
+ \; I* m  l, @10                (uint16_t       )LED1_STK_SIZE,
: i1 @! H3 C+ X) p. }11                (void*          )NULL,, a) X/ ?% a8 ^9 R- q. N
12                (UBaseType_t    )LED1_TASK_PRIO,
2 Z6 V3 Z$ y4 u& n1 I13                (TaskHandle_t*  )&LED1Task_Handler);      
* q; b! q, g6 G8 H4 b; q1 ?' p) X  q
创建完任务就开启任务调度
1vTaskStartScheduler();          //开启任务调度
" @) W3 [( w0 D* ]( M
然后具体实现任务函数
4 f9 y4 M1 M2 s# F" m& s
1//LED0任务函数
! F4 A6 i2 x  t  \/ G, L' U 2void led0_task(void *pvParameters)( V: n  ^& Q$ H8 ^: H
3
{8 f7 \, j. q6 L7 E5 |. O1 `
4   while(1)$ _5 h& w' z$ p
5    {7 e* M2 z7 i4 }  a9 d! U
6       LED0=~LED0;' l3 `+ }5 O, F
7       vTaskDelay(500);; q2 V: v% c$ O  p. ~
8    }
1 e  q& h# v; m* Z 9}  
0 Y/ n/ z, M& R, h4 ^2 h) l0 r10//LED1任务函数
5 w# [, g  c" }& C0 @11void led1_task(void *pvParameters)  g# x( q; ^* Y2 r# W6 k
12
{3 ]. Z9 V' Y8 k
13   while(1)
# Y; H9 c  x' S  T7 c! Y14    {5 F  D9 p& J3 U2 u5 n6 A4 K
15       LED1=0;
: A2 H, _" u% K) s+ _16       vTaskDelay(200);/ c* c! [2 y! ^: b
17       LED1=1;
8 D! m  j. x0 L7 N! Q18       vTaskDelay(800);' \9 D7 z6 o1 b
19    }8 J3 u5 v  L& b0 D1 s! F
20}
+ E: g. w8 i% r3 h" U. z
& C! q) {' ?/ N& B+ f7 c
好啦,今天的介绍到这了为止,后面还会持续更新,敬请期待哦~
% p  B5 Q+ ~/ s% G
" q( r3 z1 i) ?5 u8 u+ t
欢迎大家一起来讨论操作系统的知识
我们的群号是:783234154
6 h& w* q  b3 `2 Y. V( ~4 D  W

- O: v0 q$ D& a8 l0 p4 [! ^# f2 b+ X0 V( Z2 n
【连载】从单片机到操作系统①
6 c0 L! ]% ^! A2 J$ g! i8 H
【连载】从单片机到操作系统②$ d* `6 M$ p$ W* t8 i4 R1 L2 c+ k# l9 j
5 B0 v7 \! ?( J# I
创客飞梦空间是开源公众号
欢迎大家分享出去
也欢迎大家投稿
5 H& R' F+ S. |8 x- e8 W/ X; z5 }2 C

, J& l% \0 n, Y' r6 a! L1 X+ |2 ~" ~
收藏 评论16 发布时间:2018-5-26 21:52

举报

16个回答
lilei900512 回答时间:2018-6-1 07:14:03
xiaojie0513 发表于 2018-5-31 18:05
5 @7 G# T$ s+ {1 f怎么样啊,杰杰水平有限,还要多多指教吖

' o8 k5 z% D* {! a& u) n9 j写的很好。我对于单片机的操作系统不是很了解,底层的倒是知道,还有就是了解些Linux系统。
moviexm 回答时间:2018-6-14 08:56:04
         
4 t  v& X  x, l" j4 `参与/回复主题8 P* L2 J3 e7 y; j8 P; J4 X
关闭
  t9 K) Z9 z& p( Z4 a6 |+ V) WRE: 【连载】从单片机到操作系统④——FreeRTOS创建任务&开启... [修改]
xiaojie0513 回答时间:2018-5-31 18:05:34
lilei900512 发表于 2018-5-31 12:42# p" Q) }6 t2 o5 ?
看看怎么样!

$ T9 S" K( Y8 [3 ~: y' f1 T怎么样啊,杰杰水平有限,还要多多指教吖
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* \, X7 T# _/ A0 J4 R6 B
感谢楼主分享

4 o3 P% T7 [* V/ t
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
9 P3 a  Z5 u$ i3 ?7 N6 ^写的好

- O5 l4 r! D2 n- D! X, j9 @谢谢支持
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
3 M+ w% }/ Q- D0 u4 t# ]支持楼主,好东西

$ W8 h* i% q" k0 W谢谢支持% [- R1 `  ]9 O  v) H- Z8 H
谢谢支持
12下一页

所属标签

相似分享

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