|
本帖最后由 xiaojie0513 于 2018-5-29 13:42 编辑 1 v' n/ r7 \+ t& b- N7 m 创客的兄弟姐妹们大家好,我是杰杰。又到了更新的时候了。 听首歌缓解一下心情。 开始今天的内容之前,先补充一下上篇文章【连载】从单片机到操作系统③——走进FreeRTOS的一点点遗漏的知识点。 1BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,9 J: l5 g$ ]0 t- e" Z1 V# `2 const char * const pcName, 3 uint16_t usStackDepth,' p, M1 v; o/ r 4 void *pvParameters,4 @& V9 x2 k, y" g7 c5 d9 n 5 UBaseType_t uxPriority,5 w2 f' s% D- `6 p- {3 _) ^ 6 TaskHandle_t *pvCreatedTask$ [4 Y% h! W1 ^8 _) I. J 7 );. i' B! k2 p8 L, ^( R6 F$ j 8创建任务中的堆栈大小问题,在task.h中有这样子的描述: 9/** 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 0 n! N# c) s5 c: Q) v 11* usStackDepth is defined as 100, 200 byteswill be allocated for stack storage.) G- ]& [1 T& y+ y3 j, H3 T, l- \ 12*/ 代码可左右滑动 当任务创建时,内核会分为每个任务分配属于任务自己的唯一堆栈。usStackDepth 值用于告诉内核为它应该分配多大的栈空间。 这个值指定的是栈空间可以保存多少个字(word) ,而不是多少个字节(byte)。 文档也有说明,如果是16位宽度的话,假如usStackDepth = 100;那么就是200个字节(byte)。 当然,我用的是stm32,32位宽度的, usStackDepth=100;那么就是400个字节(byte)。 : C$ x- t7 g; Y4 [* g9 B, t0 m( C" X 好啦,补充完毕。下面正式开始我们今天的主题。 我自己学的是应用层的东西,很多底层的东西我也不懂,水平有限,出错了还请多多包涵。 其实我自己写文章的时候也去跟着火哥的书看着底层的东西啦,但是本身自己也是不懂,不敢乱写。所以,这个《从单片机到操作系统》系列的文章,我会讲一点底层,更多的是应用层,主要是用的方面。 按照一般的写代码的习惯,在main函数里面各类初始化完毕了,并且创建任务成功了,那么,可以开启任务调度了。 1int main(void)2{' G# p2 M% W+ X; ? j 3 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4 # i u' X4 X6 F 4 Delay_Init(); //延时函数初始化 - @! v3 P5 q# ] 5 Uart_Init(115200); //初始化串口 6 LED_Init(); //初始化LED 7 KEY_Init(); 7 l) \* ^! I( }# z" s 8 //创建开始任务 9 xTaskCreate((TaskFunction_t )start_task, //任务函数 10 (const char* )"start_task", //任务名称 11 (uint16_t )START_STK_SIZE, //任务堆栈大小 12 (void* )NULL, //传递给任务函数的参数 13 (UBaseType_t )START_TASK_PRIO, //任务优先级0 O8 M7 ~8 z3 t) r a1 z; `. l 14 (TaskHandle_t* )&StartTask_Handler); //任务句柄 15 vTaskStartScheduler(); //开启任务调度 16} 来大概看看分析一下创建任务的过程,虽然说会用就行,但是也是要知道了解一下的。 注意:下面说的创建任务均为xTaskCreate(动态创建)而非静态创建。 1pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); * F( U" ^ R% @9 k) F: T% v& ]2/*lint !e961 MISRA exception as the casts are only redundant for some ports. */9 J% j2 ~1 Y' P2 w$ a 3 if( pxStack != NULL )8 E, q: u1 T& X# P6 h6 n3 x9 t& H 4 {( N0 \# w9 c+ V, J) X 5 /* Allocate space for the TCB. */ 6 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );, O6 ]) u. R) b 7 /*lint !e961 MISRA exception as the casts are only redundant for some paths. */& ]' f0 b1 V- w( X- j 8 if( pxNewTCB != NULL )+ e/ {$ w" N1 F% I# [( X0 c7 X5 D 9 { 10 /* Store the stack location in the TCB. */ 11 pxNewTCB->pxStack = pxStack; 12 } 13 else j% z6 ^0 D5 ]; e' F! ` 14 { 15 /* The stack cannot be used as the TCB was not created. Free 16 it again. */ 17 vPortFree( pxStack );8 q0 ?9 F# s9 t# X! h! t 18 }4 K; b" F" c9 Q$ v! K/ x$ l 19 } 20 else2 x$ r+ Y- l1 b- L: c, J/ F/ J6 { 21 {) O3 j" u- w0 v 22 pxNewTCB = NULL; 23 } 24 } 首先是利用pvPortMalloc给任务的堆栈分配空间,if( pxStack != NULL )如果内存申请成功,就接着给任务控制块申请内存。pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );同样是使用pvPortMalloc();如果任务控制块内存申请失败则释放 之前已经申请成功的任务堆栈的内存vPortFree( pxStack ); 然后就初始化任务相关的东西,并且将新初始化的任务控制块添加到列表中prvAddNewTaskToReadyList( pxNewTCB ); 最后返回任务的状态,如果是成功了就是pdPASS,假如失败了就是返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; ; n& \# l7 D2 a) \1prvInitialiseNewTask( pxTaskCode, $ x- K0 O" M4 ^: Q6 m/ B' H, U/ g& q 2 pcName, 3 ( uint32_t ) usStackDepth, 4 pvParameters,- G5 o0 M# x% J 5 uxPriority, " Q* @& P* W6 V 6 pxCreatedTask,5 e5 n' d+ d4 z" N( U 7 pxNewTCB, 9 z! a( a. _; x5 e& S 8 NULL ); 9 prvAddNewTaskToReadyList( pxNewTCB ); 10 xReturn = pdPASS; 11 }' e- p! m' }: V. Q9 S 12 else, R2 j9 K' x }+ d 13 { 14 xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;# f3 O8 k+ e3 V4 d {% ^; e6 L, ^ 15 }- i+ b4 P# Q n& Z! \& u 16 return xReturn; 17 }( n% c$ U' v4 E/ [ 18// 相关宏定义 19#define pdPASS ( pdTRUE ) 20#define pdTRUE ( ( BaseType_t ) 1 ) 21/* FreeRTOS error definitions. */ 22#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) 8 M M' Y3 \/ d5 i3 a 具体的static void prvInitialiseNewTask(()实现请参考FreeRTOS的tasks.c文件的767行代码。具体的static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )实现请参考FreeRTOS的tasks.c文件的963行代码。 因为这些是tasks.c中的静态的函数,仅供xTaskCreate创建任务内部调用的,我们无需理会这些函数的实现过程,当然如果需要请自行了解。 ) n2 l! o, m( [创建完任务就开启任务调度了: 1vTaskStartScheduler(); //开启任务调度8 b) M3 h) L0 R" f0 A2 {2 f; o 在任务调度里面,会创建一个空闲任务(我们将的都是动态创建任务,静态创建其实一样的) 1xReturn = xTaskCreate( prvIdleTask,! r8 l3 M" ?# M" U2 "IDLE", configMINIMAL_STACK_SIZE,2 j3 o, |* N0 X( r1 v. E7 s 3 ( void * ) NULL, 4 ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),; l7 x0 Q3 _/ m7 |8 u 5 &xIdleTaskHandle ); z1 r( V2 z( N8 A0 @ 6/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ 7 }3 ?: n$ p% o! r* U1 h5 a @1 x 8相关宏定义: 9#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U )" Y! P c) J8 [; G) P* c 10#ifndef portPRIVILEGE_BIT 11 #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) 12#endif 13#define configUSE_TIMERS 1 # ?+ o: R! |* }$ I! j$ T* a9 w 14 //为1时启用软件定时器 从上面的代码我们可以看出,空闲任务的优先级是tskIDLE_PRIORITY为0,也就是说空闲任务的优先级最低。当CPU没事干的时候才执行空闲任务,以待随时切换优先级更高的任务。 如果使用了软件定时器的话,我们还需要创建定时器任务,创建的函数是: 1#if ( configUSE_TIMERS == 1 )3 r: r* f9 v8 Z2 X% D% X' V2 BaseType_t xTimerCreateTimerTask( void ) 3 然后还要把中断关一下 1portDISABLE_INTERRUPTS();至于为什么关中断,也有说明: 1/* Interrupts are turned off here, toensure a tick does not occur$ d d5 @) v( y6 j' @5 c, V2before or during the call toxPortStartScheduler(). The stacks of 3the created tasks contain a status wordwith interrupts switched on 4so interrupts will automatically getre-enabled when the first task/ C* T2 Z( U" B7 J3 Q% g4 v) S& @ 5starts to run. */ 6/ *中断在这里被关闭,以确保不会发生滴答/ `. w% c6 A6 u4 z 7在调用xPortStartScheduler()之前或期间。堆栈( \; M1 x; [/ U6 g 8创建的任务包含一个打开中断的状态字 9因此中断将在第一个任务时自动重新启用 10开始运行。*/+ [8 v y5 G3 {& _ & H9 H5 V$ U8 A 那么如何打开中断呢????这是个很重要的问题 别担心,我们在SVC中断服务函数里面就会打开中断的 看代码: 1__asm void vPortSVCHandler( void )0 S2 Q/ K- S" g* y5 v F' B p; T2{6 L' Y+ x8 L' H1 H' I3 {: n 3 PRESERVE86 P( H* A' R* k' p% C0 c 4 ldr r3, =pxCurrentTCB /* Restore the context. */ 5 ldrr1, [r3] /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */ 6 ldrr0, [r1] /* Thefirst item in pxCurrentTCB is the task top of stack. */ 7 ldmiar0!, {r4-r11} /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */ 8 msrpsp, r0 /*Restore the task stack pointer. */5 j: a4 {" r9 o# e) V 9 isb e! g, ~' P/ T! u3 m0 x( \5 Q! I 10 movr0, #0 11 msr basepri, r0 12 orrr14, #0xd 13 bxr143 m' K8 x; c; R, ^6 h9 b. O 14} 1msr basepri, r08 {5 t3 Q5 q" g9 D. U/ b 就是它把中断打开的。看不懂没所谓,我也不懂汇编,看得懂知道就好啦。 1 A4 K* F, z: Y' [& c, ]1xSchedulerRunning = pdTRUE; 任务调度开始运行 9 }: w7 Y: I# {$ {5 g' _: f: x1/* If configGENERATE_RUN_TIME_STATS isdefined then the following7 ~# h, |* P$ q# t) ^7 c* i 2macro must be defined to configure thetimer/counter used to generate; `3 P. E7 A! D1 B* O 3the run time counter time base. */; W4 M# B% O: q# y: |/ D% `- S& I5 x 4portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();4 V2 v5 l# _6 `0 b1 ] ' [& H5 l9 O2 U7 W7 y 如果configGENERATE_RUN_TIME_STATS使用时间统计功能,这个宏为1,那么用户必须实现一个宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();用来配置一个定时器或者计数器。 5 _% t. M7 J; C5 {来到我们的重点了,开启任务调度,那么任务到这了就不会返回了。 1if( xPortStartScheduler() != pdFALSE )2 { 3 /*Should not reach here as if the scheduler is running the/ B, w( h7 G3 n) O+ a 4 functionwill not return. */ 5 }然后就能开启第一个任务了,感觉好难是吧,我一开始也是觉得的,但是写了这篇文章,觉得还行吧,也不算太难,可能也是在查看代码跟别人的书籍吧,写东西其实还是蛮好的,能加深理解,写过文章的人就知道,懂了不一定能写出来,所以,我还是很希望朋友们能投稿的。杰杰随时欢迎。。。 开始任务就按照套路模板添加自己的代码就好啦,很简单的。 先创建任务: 1 xTaskCreate((TaskFunction_t )led0_task, , e: a. L9 q/ ]8 k2 (const char* )"led0_task", 3 (uint16_t )LED0_STK_SIZE,# W' }/ ^- n6 O) F 4 (void* )NULL, 5 (UBaseType_t )LED0_TASK_PRIO, 6 (TaskHandle_t* )&LED0Task_Handler); 7 //创建LED1任务 8 xTaskCreate((TaskFunction_t )led1_task, , Q8 q |, a: b p 9 (const char* )"led1_task", 10 (uint16_t )LED1_STK_SIZE,( k3 }2 u% ~* D; v4 }& S0 M 11 (void* )NULL, 12 (UBaseType_t )LED1_TASK_PRIO,) M4 I6 b R3 \: K$ F2 T 13 (TaskHandle_t* )&LED1Task_Handler); + o0 h1 D: b X8 v 创建完任务就开启任务调度: 1vTaskStartScheduler(); //开启任务调度然后具体实现任务函数: 1//LED0任务函数 2void led0_task(void *pvParameters) 3{" W" X7 F% A( u v 4 while(1)/ S( @( S y3 [5 U% f" N3 x( S 5 {: R N0 g/ b. |9 x 6 LED0=~LED0; 7 vTaskDelay(500);# J. l% b! v" _ j% y, g 8 }) C2 J" e* R: a6 A" y, ]; R 9} 4 x* r9 ?' I: I 10//LED1任务函数 11void led1_task(void *pvParameters) 12{ 13 while(1)! t. Q5 R! |$ G- w 14 {5 e7 T, [2 h a2 X 15 LED1=0;7 @: I( W& ~0 b 16 vTaskDelay(200); 17 LED1=1;, I' F9 Y9 H K0 V 18 vTaskDelay(800); 19 } 20}1 p) q4 Y. f- n 好啦,今天的介绍到这了为止,后面还会持续更新,敬请期待哦~ , z* _* k$ G) m) ~! h2 `4 @# B+ e6 Y8 {+ B A; K6 h) i 欢迎大家一起来讨论操作系统的知识 我们的群号是:783234154 $ s- X0 h0 }6 W( u* M( j/ Y# q8 n- `! \5 M 【连载】从单片机到操作系统①' D; R7 ?% w @. S7 C* n+ t 【连载】从单片机到操作系统②& F% u4 H' L9 i6 Y 创客飞梦空间是开源公众号 欢迎大家分享出去 也欢迎大家投稿 : x) u9 ?) w- t% t6 w+ q- y/ c2 i4 U1 h- t0 O' \5 ]2 w |
微信公众号
手机版
写的很好。我对于单片机的操作系统不是很了解,底层的倒是知道,还有就是了解些Linux系统。
参与/回复主题
关闭
RE: 【连载】从单片机到操作系统④——FreeRTOS创建任务&开启... [修改]
怎么样啊,杰杰水平有限,还要多多指教吖
评分
查看全部评分
谢谢支持
谢谢支持
谢谢支持