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

STM32之FreeRTOS

[复制链接]
阿松松 发布时间:2015-1-6 13:45
第一次接触的RTOS就是FreeRTOS,哈哈,资料和大家分享了
5 [$ w5 V) B% c$ t6 n" T) x
4 {# D6 C! k: A% K) W% h; i
, {$ T2 S! b: }* D& w学习操作系统,我并没有一开始就学习UCOS,而是选择了FreeRTOS。FreeRTOS可以方便地搭建在各个平台上,因为汇编相关,都已经由官方完成,我们要做的仅是添加自己的代码,可省去很多工作量。

      问题1:在使用多任务时,我想利用USART输出信息,但是如果直接放在任务中输出,往往会造成字符收发顺序不一致的情况,这是仿真时遇到的实际问题。为解决这个问题,可以在USART输出信息时挂起其它任务,利用vTaskSuspendAll函数挂起,再利用xTaskResumeAll重启内核调度。这样可以保证USART正确发送信息。但是,我觉得还不如为USART建立一个任务,这样子,USART就不会发生发送字符顺序错乱的问题了!而完成任务间的通信,就可以考虑使用队列!

       在此,队列的功能相当于任务与任务之间进行通信的通道。USART的任务可通过查询队列是否为空来决定是否输出。

队列的使用:

       定义队列:

      xQueueHandle USART_Q;

       建立队列:

       xQueueCreate( uxQueueLength, uxItemSize );

       第一个参数为队列中元素个数;第二个参数为队列中元素大小,单位为字节。

       也可以使用

       xQueueGenericCreate( uxQueueLength, uxItemSize, queueQUEUE_TYPE_BASE )创建队列,接下来的三个函数都有两种使用方法。

  T* @( q) a! ^# s; m" z; Q

       发送消息:

      xQueueSend( xQueue, pvItemToQueue, xTicksToWait );

      第一个参数为队列句柄,第二个参数为队列元素,第三个参数为阻塞超时时间,如果在发送时队列已满,这个时间即是任务处于阻塞态等待队列空间有效的最长等待时间。


. |$ y3 g( }$ k$ L8 W

      接收消息:

      xQueueReceive( xQueue, pvBuffer, xTicksToWait )

      一般用法如:while( xQueueReceive( xLCDQueue, &xMessage, portMAX_DELAY ) != pdPASS );

以上为队列用法,因为学习粗浅,不能进行深入分析……

注意,经查看代码,发送元素到队列的过程中,是将元素复制进队列中的,而并不是指针引用。


6 t6 |4 P5 I7 M) a9 {

演示代码:

  1. xQueueHandle  USART1_MSGQ;   //To receive the usart characters's queue& L: O$ y: S% o  Q6 M# l2 b* O/ E

  2. # M! w4 t# S3 Y( f9 Y" e
  3. volatile unsigned long mainDELAY_LOOP_COUNT=0xffff;5 V: g: f5 c- K9 P; ?- L
  4. void vTask1( void *pvParameters )
    0 h3 f. s( l2 m) U6 F
  5. {
    / T0 M3 }( g& [: z. P2 E
  6.     volatile unsigned long ul;3 q5 f$ j& C# X
  7.     for( ;; )
    0 I; l, N) a5 p& ?4 [
  8.    {
    ! w  W; Y6 O% `( g' q
  9.         xQueueSend( USART1_MSGQ, "This is task 1 !\n",portMAX_DELAY);2 Z3 M" P3 p  i! d/ d' a$ I& \
  10.         for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ );
    ; V. {; h# O! Z8 g
  11.    }
    5 X7 w2 R, ~1 U8 s$ y
  12. }& G: \  T" D& O; Z
  13. void vTask2( void *pvParameters )
    & R$ _. P! Z/ q& S$ h' Q
  14. {
    $ A( s; l% `4 K, J% s2 c
  15.    volatile unsigned long ul;. c' T$ ^+ G2 z6 c- d& f* |
  16.    for( ;; )5 m, k7 r, r0 I/ x9 r! z- C3 w
  17.    {- q5 a$ U" z0 ^; k8 |
  18.        xQueueSend( USART1_MSGQ, "This is task 2 !\n",portMAX_DELAY);
    2 B' l5 c. v- M
  19.        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ );5 b% @7 o5 h5 U/ ?4 R1 i/ b  w
  20.     }" E: l2 c% e7 l  R! m  w
  21. }0 U0 u( ^; S; O' U- s
  22. void vTask3( void *pvParameters )# ~" M; P2 L0 ?) i1 Y1 X1 q
  23. {0 ?9 T4 M9 R  D& A3 U) c% _, k# S# d" p
  24.     char str[100];
    : L/ Q4 f" T0 j- f
  25.     volatile unsigned long ul;% U/ O$ ~: y8 }) J/ T4 J
  26.     for( ;; )
    7 z+ \$ h5 e/ [+ e7 V
  27.    {
    6 [4 O7 O3 U: ~/ y5 p% e
  28.        while( xQueueReceive( USART1_MSGQ, str, portMAX_DELAY ) != pdPASS );( v& _& K/ {8 C
  29.        printf(str);
    ' c# p1 b  t% F* i- M" t6 {
  30.     }
    2 [& }; d- a* ~
  31. }
复制代码
0 v0 g5 J! `# T

在main函数中添加:

  1. USART1_MSGQ = xQueueCreate(10,(sizeof(char))*100);
    4 n( s: n1 c! {0 b
  2. xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
    ; z  ]+ q" C! n. w, }) ?, E
  3. xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );2 F5 Q, v5 J  R( g% H5 r  \6 Q. p
  4. xTaskCreate( vTask3, "Task 3", 1000, NULL, 2, NULL );
    4 ^+ H+ @- u5 `2 Q. y
  5. vTaskStartScheduler();* s1 k+ O  K8 [2 l
  6. while(1);
复制代码
1 j: Q3 X2 j; U0 |' H1 h

注意,printf函数已定位到USART1串口上,使用Keil调试的时候,可以通过串口窗口看到输出内容。

8 u& Y3 E/ y3 x' g" t' q

二进制信号量定义与使用:

      定义信号量:

      xSemaphoreHandle xBinarySemaphore;

      vSemaphoreCreateBinary( xBinarySemaphore );

      设置信号量:(在中断处理里面设置的时候,一定要使用以FromISR结尾的函数或宏)

      xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );

xSemaphoreGiveFromISR的第二个参数,。如果调用xSemaphoreGiveFromISR()使得一个任务解除阻塞,并且这个任务的优先级高于当前任务(也就是被中断的任务),那么xSemaphoreGiveFromISR()会在函数内部将*pxHigherPriorityTaskWoken 设为pdTRUE。

       如果xSemaphoreGiveFromISR() 将pxHigherPriorityTaskWoken设为pdTRUE,则在中断退出前应当进行一次上下文切换。这样才能保证中断直接返回到就绪态任务中优先级最高的任务中。

        上下文切换,使用宏portEND_SWITCHING_ISR()

注意:每种处理器架构都有自己的宏定义,一般在portmacro.h文件里面,声明为portEND_SWITCHING_ISR()

        xHigherPriorityTaskWoken参数为portBASE_TYPE类型。取值为pdFALSE或者pdTRUE.

例如:

) v+ h) c  U+ H! G* b7 o
  1. static void __interrupt __far vExampleInterruptHandler( void )
    4 m" u/ ~: Y* Z" j4 Y( C
  2. {% C" q: o! _  \5 G
  3.    static portBASE_TYPE xHigherPriorityTaskWoken;/ Z5 d- C. {9 U+ y% ^. X
  4.    xHigherPriorityTaskWoken = pdFALSE;8 ^6 {2 f% }- A3 G
  5.    xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );& Z- t( V3 e4 b' w# R
  6.    portEND_SWITCHING_ISR(xHigherPriorityTaskWoken );
    , \4 l+ K0 g* K! ^
  7. }
复制代码
6 p9 Z4 Q' M% d4 w

      获取信号量(获取不到,则进入阻塞态):

      xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );

      注意,xSemaphoreTake函数的第二个参数设置为portMAX_DELAY,且在FreeRTOSConig.h 中设定INCLUDE_vTaskSuspend 为1,那么阻塞等待将没有超时限制。

! ]) g# K3 ?- O# w$ x4 O

& `/ R# X' f5 I" v
- B9 v2 {, ~) H( g$ u

计数信号量定义与使用:

       因为之前没有接触过RTOS,在学习的过程中,我就不明白,为什么要搞个计数信号量,有什么意义?还好资料给我解除了这个疑惑。一个二值信号量最多只可以锁存一个中断事件,如果中断延迟处理任务还没有执行完,而中断又发生了多次,就会导致中断信息丢失。如果采用计数信号量,中断次数就能很好地保存下来。

       定义计数信号量:

xSemaphoreHandle为计数信号量的变量类型8 ]- b: k; J9 v$ Z  `, f1 Z- H# h

       创建计数信号量:

xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount,unsigned portBASE_TYPE uxInitialCount );

返回值为xSemaphoreHandle变量

uxMaxCount参数为最大计数值,也就是队列深度。
6 X9 Z8 W& f5 g& x

uxInitialCount参数为计数始值。
% Q+ V4 e, o( Q- f7 z

       设置信号量:(在中断处理里面设置的时候,一定要使用以FromISR结尾的函数或宏)

同样使用函数:xSemaphoreGiveFromISR,也要注意上下文切换。
" w. h6 `( i0 i3 N; g0 B

       获取信号量:

同样使用函数xSemaphoreTake;

$ d* C) e) A0 b$ K8 e
  Y0 _' m9 i$ L) u( ~0 u& n

用于临界值的一组宏:

taskENTER_CRITICAL();进入临界区0 l7 c( i1 n. _) X" @; @

taskEXIT_CRITICAL();退出临界区


4 K0 L; c& ?9 Z" o6 @
赞 收藏 3 评论5 发布时间:2015-1-6 13:45

举报

5个回答
stm32_beginner 回答时间:2015-1-6 13:46:26
学习了 正好正在使用
阿松松 回答时间:2015-1-6 13:46:27
调度器挂起与运行:
% ^, O& V. t9 s- @" `void vTaskSuspendAll( void );  ^' g' Y& S) [* K. ~6 j
portBASE_TYPE xTaskResumeAll( void );
6 O! f+ X8 v4 n9 z2 C" u, {3 Q& w% z; c
互斥量(及二值信号量)5 a+ n1 w' E! O$ S, x
互斥量是一种特殊的二值信号量,用于控制在两个或多个任务间访问共享资源。可以理解为得到互斥量,即可得到资源。但是用完资源,必须归还互斥量。
( Q) ?- Z' |- k2 m+ h互斥量的变量类型为:
1 o3 p- P- ~# {4 E' F/ w3 PxSemaphoreHandle xMutex;1 N' C0 \: ]) v  P
互斥量的创建:# z- f" K: b1 ?- ?+ s- P
xSemaphoreHandle xSemaphoreCreateMutex( void );0 a4 s$ M, j/ l- ]3 _" D( _
互斥量的取得:
2 ]/ N4 U) R1 l# l3 W3 m  `xSemaphoreTake( xMutex, portMAX_DELAY );, C+ L3 f3 h: Y& V' ?; `7 U
互斥量的归还:9 b; z* X" o. `, _" R
xSemaphoreGive( xMutex );
4 |+ e( ^9 B; j5 t2 c运行时的栈侦测等最后会完善。
! ]! `2 s4 n- {9 {+ V; e4 |. U ( ]0 n* i7 n- e! X+ F* A

8 m3 a: j8 B6 BFreeRTOS的软时钟与任务延时阻塞:8 J5 P8 }9 e3 L5 C' B
用硬件定时器有时候并不是很方便,因为得写中断,得分配信号量;而且这类开支会让定时也不能特别精确!如果使用FreeRTOS的软时钟,会比较合适。而且也能省去很多代码。* R! g) I. H* X) I) K- a
1、任务延时阻塞vTaskDelay()函数与! x; s  L: Q& o' I- f7 F7 S" z
vTaskDelay函数的使用
- D# t9 f- y7 h) rINCLUDE_vTaskDelay must be defined as 1 for this function to be available. See the configuration section for more information." _3 Z' q, c- G' ?9 L8 X
Delay a task for a given number of ticks. The actual time that the task remains blocked depends on the tick rate. The constant portTICK_RATE_MS can be used to calculate real time from the tick rate .- r! F; [2 x. N. L5 a- w0 w) ~6 L
INCLUDE_vTaskDelay应该定义为1才能使能该函数。可以通过查看配置部分获取更多信息。函数可通过给定数值延时一个任务。任务的实际阻塞时间依赖于时钟频率。常量portTICK_RATE_MS能用来计算时钟频率的实际时间。
; A& p4 C0 v% l  [例如:
7 s$ ?0 J9 f; g/ \$ w9 ~' [% ]' \/ s- q6 F- r
  1. void vTaskFunction( void * pvParameters )
    - O; q5 F: T. f6 X, {4 f
  2. {
    6 g4 U! r, Q0 W
  3. /* Block for 500ms. */
    / W( h# l, e  ^+ f6 q6 Q0 V
  4. const portTickType xDelay = 500 / portTICK_RATE_MS;0 E7 H$ n: |' p
  5. " [6 j  F/ o" X) }5 ~# Z
  6. for( ;; )$ z% ?% @  w7 v7 L- x( p4 K
  7. {9 M" r, U( j7 N- m3 N- y  F
  8. /* Simply toggle the LED every 500ms, blocking between each toggle. */
    ' i5 H* X* }8 y* N. k$ ~
  9. vToggleLED();2 d. s% s8 w5 o9 H3 j
  10. vTaskDelay( xDelay );* q+ p# l( ~! a" F
  11. }
    " i  M6 c. \6 P& X
  12. }
复制代码
# H% H. F& u. b1 a: c
但是,vTaskDelay并不能给你一个绝对的时延,它是相对于自身的延时!因为任务切换或中断发生等原因会影响vTaskDelay函数的使用。如果要使用绝对的时延,可以使用函数vTaskDelayUntil
+ c& y; R' N2 H* z# E
& s! R' T8 |1 Q; m8 _" F- Y! `vTaskDelayUntil函数的使用
: a& ?" B* F. g$ i8 _2 \# Y. NINCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available./ O3 C, X! F- ]* G) d& E9 L. t) f
使能函数,INCLUDE_vTaskDelayUntil必须设置为1。
- |$ q3 u# }2 G8 R2 d: l, B0 x2 |2 \5 cDelay a task until a specified time. This function can be used by cyclical tasks to ensure a constant execution frequency.
. C5 f7 f3 H( J1 Y- I/ n延迟一个任务直到指定时间。该函数能用于循环任务保持流畅执行。! E; G: G7 q9 b9 W  R+ F
This function differs from vTaskDelay() in one important aspect: vTaskDelay() specifies a time at which the task wishes to unblock relative to the time at which vTaskDelay() is called, whereas vTaskDelayUntil() specifies an absolute time at which the task wishes to unblock.
9 j# R' D2 [7 R该函数与vTaskDelay函数最大不同的一方面在于:vTaskDelay指定的时间是相对于vTaskDelay调用时开始的,然而vTaskDelayUntil指定的时间是绝对的时间,到时间一定解除阻塞。3 \  m9 i  _( X! v3 j% k3 Q
vTaskDelayUntil函数的使用:6 ^% }* C3 H8 I
  1. // Perform an action every 10 ticks.
    ; h, N5 e/ w" `5 _3 C' E0 W2 B* k
  2. void vTaskFunction( void * pvParameters )1 r1 {! q' f: P9 C& I# |& b, Z4 ~
  3. {
    . u$ [2 D" P6 G. k
  4. portTickType xLastWakeTime;% j3 p* [0 C& I- n6 u0 b$ I, F
  5. const portTickType xFrequency = 10;
    + n( l% r- a8 i% j

  6. 7 ^4 r" p/ i2 {- ?6 j, `( {" t
  7. // Initialise the xLastWakeTime variable with the current time.
    ; P& U2 C* R  s6 a6 X- V1 I
  8. xLastWakeTime = xTaskGetTickCount();
    & p2 v/ [1 t& J. ~6 n: M) A4 y
  9. for( ;; )
    8 v$ P( E  C9 ]' K( @7 N) G
  10. {
    ) F2 J* |: b! l& I
  11. // Wait for the next cycle.1 ~2 A1 R2 x9 y, [* h
  12. vTaskDelayUntil( &xLastWakeTime, xFrequency );
    8 c& G, L+ X7 |( u) l  S0 ?" h( ?
  13. // Perform action here.; P& T3 {+ F  g4 M+ z( z  c
  14. }5 }- t; H* W0 ~- o( p
  15. }
复制代码

! U/ }+ y& P; y9 j3 s3 \$ }/ T) \$ t) Z
听说FreeRTOS任务可以同优先级,但是发现同优先级创建3个以上任务就无法调度?不知道是什么原因?
( g; d+ C  P9 V& m6 h原因:一般情况下,刚开始学FreeRTOS都习惯将任务栈空间设置为1000,这意味着将要在系统里面申请4000个字节(栈空间以4字节为单位);创建3个任务,申请的约为12K左右的样子,如果再创建一个任务,也是用的1000; 那么意味着向系统申请16K左右的栈空间。加上空闲任务的栈空间,你FreeRTOSConfig.h文件里面的栈空间默认值为#define configTOTAL_HEAP_SIZE( ( size_t ) ( 17 * 1024 ) );也就是17K。显然运行不了,是因为堆栈空间不足的原因!
: u% s# L; i6 w  |# n8 @解决:两种办法:1、任务栈空间调小点,变成500;2、系统栈空间调大点,变成30K ;一般就足够你跑5到6个任务了。如果再小,跑得更多!
+ c3 X1 @+ k6 r) A. s- ^% K% {6 x% l8 ^
& k- d2 D8 E" s调度器只能调度一次就崩溃了,移植汇编都没问题?2 _0 e( T1 d6 s# s+ S7 w
原因:你的任务函数没放在死循环里面!
( u. X1 x/ C1 {" d. q( v& k: J
$ i- {2 {% g2 ~& I) ?
* l, q. z) V; Y! d发现一个问题,在使用
: A/ T- \# R+ R( OvSemaphoreCreateBinary(xActiveSemaphore);创建二值信号量后,不能立即使用xSemaphoreGive(xActiveSemaphore)释放二值信号量!官网说明如下:
' U5 H; w1 I- D/ @* J4 Z' A; `; fvSemaphoreCreateBinary  is a Macro to release a semaphore. The semaphore must have previously been created with a call to xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or xSemaphoreCreateCounting(), and obtained using sSemaphoreTake().
6 F1 M1 A. _( \: l意思是vSemaphoreCreateBinary  是一个用于释放信号量的宏。其中的信号量必须被xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or xSemaphoreCreateCounting()等函数调用完成后,而且必须被sSemaphoreTake()获取后才能使用!2 {0 w+ B) A# X& j0 C; w
简单地说是要xSemaphoreGive(xActiveSemaphore),必须创建了信号量后才可以!5 ]3 w2 w, G6 [% `1 i1 O& a9 y
can-39743 回答时间:2015-4-9 19:28:08
mark,                                                   
stary666 回答时间:2015-4-10 09:35:49
正在学习使用
wamcncn 回答时间:2015-4-10 15:36:16
标记,学习一下
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版