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

STM32 如何快速创建 FREERTOS和RTX 工程  

[复制链接]
lovewyufeng 发布时间:2015-1-8 12:08
以刚拿到手的 Nucleo 072为例  其他板子用户 请举一反三哈 (工程文件在帖末)
) q  O# v! Y# f0 p* L4 P/ o
9 D" M7 T: \# P5 a' l  ^& k; R) n9 u
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用  方便AT指令类外设开发
) `7 K/ _3 F7 W- O  L& Z1 X2 T( B6 j& k
需要工具  MDK5 自行下载:- }3 j; G: S  P% S
STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
" D+ {+ k7 s, ]- _5 I" b( q) `STM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
+ C4 n3 |( X% `" y  u8 w/ ?9 M8 ^, K0 f8 Y

2 T; f7 s2 G( h5 b5 f8 b) l; n安装 cubeMX  由于使用MX下载固件库速度那是不说了相当慢啊  所以下载 STM32CUBEF0固件库然后下图安装
% o( d5 q8 l' `  }9 V- a: Q
7 D2 Y- n1 K; y- }) {! C' ?" c% ~. `4 v4 M& z2 Z2 L" g$ z' I
QQ截图20150108101548.jpg QQ截图20150108101556.jpg
* v* J1 \4 P) d
2 A9 j& c* x  t8 y. c5 x, w; W+ e安装之后  新建一个工程 选择STM32F072RBT6, H; a. g8 c7 x' m- z
3 }! A: U" P% _. R
PINOUT 勾选  FREERTOS和 USART % z# V, y+ E2 @* M5 `6 q
QQ截图20150108101539.jpg
- z; d3 E* ^0 C; w& C因为我们调试可能需要使用
( U* t# d) q& u5 L6 g$ a7 b+ R9 ?& m& i7 [
点击软件上方  齿轮键生成  keil 工程  至此
  {8 L. G: y! `7 ^3 _
8 l! o# l$ ^- VMX基于 HAL的库 生成完毕
* T; t. `8 }9 C9 v! h$ M
8 [& B- Q; {! X5 L; ~使用MDK 打开工程
# Z8 _1 {9 X  I$ v从上到下 的组依次为  OS 的C文件
* T# f; q; J) E.s 启动文件
& m$ Y' z' V( A( {$ A( ~用户文件
9 U7 Y& \7 f% e, J: z" @HAL库文件  C7 U, y9 C. a& ~
CMSIS中间件文件6 C% d4 u# s! \: o! j

( K" [& _3 h4 S9 @) p其中 在 第一组中的 cmsis_os.c 中实现了  cmsis_os  到FREERTOS 的中间层转换   稍后会讨论其中一处代码
7 S5 H0 G6 p; q( i6 \% F4 T. U! [
2 l. R2 ]4 e/ N5 e7 E5 y. B QQ截图20150108101924.jpg
1 W8 i7 x6 x& p( {8 @5 @0 W( }( a( ~
9 n; c1 ?$ w- j3 i
接下来  添加自己的代码  首先添加 072 上面LED吧  板子不在身边  记得是PA5 控制2 S( c$ I8 Z& @1 K+ n

9 j7 G" g1 a: |5 c4 O先看看  main.c 的 64 到 105行
. H# |( t7 B2 O0 M% U

  1. 7 }: V# }7 A9 A( V# L/ u
  2. int main(void)
    - {  }: X" k3 \. i: a$ l8 e6 m, S
  3. {
    , ~* I! d4 v' {& R

  4. 9 C9 }$ r, |' Y- {& x
  5.   /* USER CODE BEGIN 1 */2 p& |" j# V$ Z

  6. 0 P8 @1 [/ ~1 G+ p7 g' N- n+ P6 [, A8 _, m
  7.   /* USER CODE END 1 */
      W. o! G( c( @2 t( ^8 \  ~: S

  8. & T+ j3 w: T1 y' r3 T
  9.   /* MCU Configuration----------------------------------------------------------*/: j/ }. L, E9 D+ M2 v! N
  10. : \3 h( S3 d, r3 A, a& W3 g: H
  11.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */4 {/ t4 y' M$ x5 V, r% S
  12.   HAL_Init();
    6 A0 ?. o1 h8 y9 x4 W9 H

  13. - O/ u% z% q- x+ d
  14.   /* Configure the system clock */* b4 C8 Q1 i7 @) Q8 Y& A
  15.   SystemClock_Config();
    # \/ L; n' }: H  h- l/ z

  16.   Q% U/ I4 L1 x& N& J
  17.   /* Initialize all configured peripherals */
    & x! @: y' K! v9 P, G& L
  18.   MX_GPIO_Init();
    1 N: I6 D, ]- a2 N6 b" F
  19.   MX_USART2_UART_Init();
    % f& w# L/ A$ U: e" W
  20. # _: o/ D9 @: I. `$ |
  21.   /* USER CODE BEGIN 2 */
    * F, H: A* L5 m7 ~3 E: V8 D
  22. * }* X) C% {+ p2 w/ {: A5 w# [
  23.   /* USER CODE END 2 */( _6 e/ _2 s( u+ {7 W5 G1 _
  24. ' t# h3 v& E% S+ I: |% o  Q
  25.   /* Init code generated for FreeRTOS */
    9 r4 T( v3 P2 I$ E, |/ h9 |
  26.   /* Create Start thread */. u, K8 T1 D6 L. `! l9 K6 T
  27.   osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);# t* @5 U. x8 A3 J! @# K9 Q
  28.   osThreadCreate (osThread(USER_Thread), NULL);* i, M, q$ `3 H/ K" K

  29. 4 I; K3 i0 E. L" f; m4 \
  30.   /* Start scheduler */
    4 \- k) a. i! J. A7 J. I' p
  31.   osKernelStart(NULL, NULL);) U+ E) l& f# R! T# {" p: P

  32. 8 }$ \' ]  i! S  ?9 k
  33.   /* We should never get here as control is now taken by the scheduler */
    6 n1 N# B/ k( M1 W  i

  34. 5 N4 \; [: L& R# w8 O! H  Q
  35.   /* USER CODE BEGIN 3 */9 H1 m  t9 e: k
  36.   /* Infinite loop */, X7 A0 I: M  i# D
  37.   while (1)
    ( i: i. U/ W0 S  j3 f  n. u& o
  38.   {
    . |9 T5 ]$ R- i+ z# |

  39. + Q8 M0 i$ V- M# p, ?& K
  40.   }
    1 d. g4 ]) Q- Y3 a
  41.   /* USER CODE END 3 */. |& M1 r! D7 P* B2 R5 ?! B% x  ^9 B
  42. 2 A6 G" t( k$ L1 \# T  C
  43. }
复制代码

. G# z; P' e7 I5 E9 V1 j, Cmian函数  C代码的入口  初始化一些硬件后 5 r+ f* B# }% R( _  n2 q
  osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
  osThreadCreate (osThread(USER_Thread), NULL);
  /* Start scheduler */
  osKernelStart(NULL, NULL);
. \2 _5 E$ C! z0 K' ?
定义了一个 线程 USER_Thread 然后启动OS  
/ t$ u9 _' Q1 @/ ?- y+ y2 d* c注意 osThreadDef  是一个宏  定义一个用于描述  线程的结构体  并不是执行函数" R/ g9 t' t* W
+ H. Q3 L- H0 T! ]' p
宏的第二项参数 StartThread 为线程 入口函数地址。
0 G4 V2 s7 S' `! M& G
% T+ x+ `; h5 Q* E* X2 j+ R2 V至此mian函数的工作结束了 OS将转向 就绪线程并永不返回  也就是执行StartThread  
0 g$ ?! S$ R5 p! f0 b3 t2 a
. I0 Q' e/ y8 t1 ?
修改  StartThrea函数 如下
* _. ?4 Z# w6 [' L+ X+ _* j5 ?
) Y6 R0 ]( I. l" j9 |1 U+ `5 Q
  1. # Q6 E* g- q+ u4 S; m
  2. /* USER CODE BEGIN 4 */
    ! p6 p" c) j% ~( X3 [+ h7 f
  3. void Nucleo_072_Led(const void *par);
    ) \1 W: U/ [/ _6 s  f: C; k3 B: s% F
  4. /* USER CODE END 4 */( M8 w. t& X% ~
  5. 7 N5 n8 S, j- f& K% @
  6. static void StartThread(void const * argument), Z4 H. {# A/ [. ?) n) x# r
  7. {
    , g7 X6 l0 G4 Y0 I( p4 |
  8. 7 n" d+ y; B  S# _. \6 Z
  9.   /* USER CODE BEGIN 5 */
    3 ~# d4 G: {. X
  10.   osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
    5 \/ k) z8 u% d& e  B: {
  11.   osThreadCreate (osThread(LED_Thread), NULL);4 R9 W0 f& l& R& Q8 F
  12.   /* Infinite loop */
    4 [& U6 H# c/ ^4 `% v( C8 u4 t
  13.   for(;;)
    ) c. `: j" v6 `2 c( J' T
  14.   {
    ( y$ G. f1 C2 x9 W3 h# d
  15.     osDelay(1);8 R$ [6 o  Y  q7 u* @
  16.   }
    3 r* s. A- L/ i4 l3 _

  17. 9 H! S0 l4 M9 s7 z
  18.   /* USER CODE END 5 */ , B  R$ p3 ], q

  19. " n/ ]: G* ?( Y2 }
  20. }
复制代码
4 ^& V5 D, N! U  q+ j1 Y5 }6 M
添加一个 LED 函数; W' Y- Q- J, H6 i: ?
  1. void Nucleo_072_Led(const void *par)# e) b$ q3 Z$ w) \, p8 [
  2. {7 }1 _4 T; K" p" ]" X8 p
  3.         GPIO_InitTypeDef GPIO_InitStruct;3 g+ \/ B. f+ U+ ^
  4.   __GPIOA_CLK_ENABLE();
    ( ~& W. ~8 h1 u8 p9 j: s% k
  5.         7 n! s9 ^: o8 L' e/ c1 Z
  6.            GPIO_InitStruct.Pin = GPIO_PIN_5;: \- N  b! L& e6 T: \8 m/ t
  7.     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    ( {6 B' ^2 g4 `0 |; {; B
  8.     GPIO_InitStruct.Pull = GPIO_PULLUP;
    2 ~6 p0 i8 \+ a& J# ^! ]" V, G4 u
  9.     GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    7 d( t0 O6 H7 b6 v1 `( S( x8 C
  10.   S: s( t8 {/ a3 _6 X) z
  11.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    4 k% h) e: F2 q0 e+ ?& o4 F3 O3 l2 X
  12.         
    ( m: a" q% e0 A& E( C9 B; _
  13.         for(;;)5 q$ r* c/ U* E' K" x
  14.         {: M" H, J( k4 g6 B
  15.                 GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁5 v$ f9 ~4 t; E1 C
  16.                 osDelay(500);# a$ T" k8 p  j: E, L* i" }
  17.         }* S# h3 e8 \9 r/ p

  18. : v6 p6 U# ~/ E4 z
  19. }
复制代码
, K8 p7 Q" K; Z7 L4 v
到这里可以编译下载到板子上运行 观察现象了
' e2 Y6 ~2 `3 [- v0 n& G/ {8 Z" }4 H1 ^/ c. o5 W  _" E
下面创建 RXT 的工程  新建一个工程  4 M! {" \- M8 i7 |

8 L7 j; y  X9 ^9 R. D* f勾选 如下选项
1 Z: f% m: O5 K# ]/ Z) F
3 z) P$ s. D+ O- Y QQ截图20150108112304.jpg & |) P  I) ]( I0 I5 n/ n
红框 不要添加  不知为何  楼主添加 MDK的 startup 编译通不过 6 C( k& B* l* h5 u. P, t' n
F4 的工程没有包含  HAL  接下来 需要自行添加HAL 库 # y% x- P  m+ E

4 g. o6 ^0 T8 W' K把原来的 main.c 复制一份更名为  rtx_main.c0 b, H! h, ~7 ~( m' V6 n; `

' u3 h- e( _4 h& o( n/ u QQ截图20150108113501.jpg
" J1 W4 l) S; B2 O( E* i" D* W
* S) `# W# X+ d* Q( S) V文件添加完毕
# Z: m/ W; q- `0 m. V% o  `" P/ U, }& h: E& m  f# ~& U$ O
接下来定义 头文件目录和 系统宏 + _) K( o% c: p3 W  T
7 J/ ?  I  ?' ^( R
QQ截图20150108112609.jpg ) r. u$ p% |% \& {
: d* U5 U# j7 g
修改 rtx_main.c  下面两处需要修改
+ U5 n; T  u9 {, Y: K
. I$ F/ w1 k6 x9 |  E: r% Z
  1.   {2 b1 u. _8 I# K
  2.   osThreadDef( StartThread, osPriorityNormal, 0, 0);8 B" l7 z) Y. V9 \5 n8 A, W: r
  3.   osThreadCreate (osThread(StartThread), NULL);
    5 m* n* J( B% M. C1 w
  4.         }
    ! Z3 M$ y2 o& p5 a9 ]' U: j* T
  5. : i7 e$ s( E3 ?  a& G) j
  6. osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);1 A% ^) X/ T9 L
  7.   osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码
不知为何  ST写的 中间件和 MDK的 接口有一点差距  所以 创建线程的地方需要如上修改
2 j  D! T2 }) V5 L8 f3 g4 R# E: Y7 A& q4 S( A9 j  E
修改 stm32f0xx_hal_conf.h
9 b5 o+ M1 V% q, Y添加 图示内容  不出意外  下面 将可以直接编译了!!
1 `- K2 o) M! f- B9 M7 Q$ J) |* r$ b QQ截图20150108112936.jpg ( w# b6 {4 Z4 t

7 ?$ v% h/ l+ Z4 Y( y; r6 f6 L8 y3 ^" t写的有些多了  本来想 继续写 RTOS 基于串口的  应用  太长了 下次发帖写了4 f+ {7 K% }$ g, c$ l, G- V2 Z
6 I0 }0 k" B/ a- `
下面提出一个讨论 MX 创建的  工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量  * c/ v+ w' a4 e  r7 i. J
osSemaphoreCreate参数 count 直接  n7 Y: v3 `/ |

! d1 E2 v; n' j. O6 {, h3 B" T传递给 xSemaphoreCreateCounting的两个形参  3 z7 {! `% S- M) d. B* m/ r
QQ截图20150108115258.jpg . o5 b+ z/ J9 ?* L8 C& e' x
原型
/ `5 B3 h. z, c& v7 d#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )5 E( W) v0 R' E# x3 M
/ m* z7 G6 K! W: e5 Z7 \) [$ K
该宏创建一个 数值型信号量 第一个参数是 信号量最大数值  第二个则为  初始化值
" E6 M1 B% R( S* O" Q
0 V" E$ ~& M( C$ }5 L. s& n: C基于串口使用信号量  那么需要如下要求
2 @* I5 {% N/ C% C- L) ~假设 usart_sem 为串口使用的信号量, r" o. A; k$ C
每收到一个数据 usart_sem ++  缓冲 1024字节
# ]  k5 T8 _0 p/ {3 a' I. `需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活  获取数据0 q5 X  N; t# U+ M

1 `' s) I' }; M, M' x- N. R+ y* T如此我们知道 这个信号量的 最大值应为1024 * u( l) ~7 `( R/ J- y7 S- q- X
可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
- Z4 B) L/ R6 m. o% B  c- ~, m) q) v1 j6 [1 ], g
会出现这样的问题 !  此信号量 被赋予初值1024  意味着 这个信号量将可以被osSemaphoreWait 1024次
5 E. x- X$ d  r% N- Q显然这不是我们想要 : J5 ?, x" ?3 T! l! }- d- g
+ S# a$ |% |, J6 j
通常  我们需要的数值型信号量  最大值可以很大  但是初值 基本为0,或1
" g) U6 z' N' R% l9 C
' I  R7 V# ~3 }, f不懂  这样设计意义何在?
4 y$ z3 z: `* {) \! O% p# w2 I+ A( Q) Y( s$ Y, Y: @8 U
: ^& P: @7 a( v( \1 L3 b; P& R
FREERTOS.zip (4.76 MB, 下载次数: 809)
赞 收藏 14 评论97 发布时间:2015-1-8 12:08

举报

97个回答
我是东哥 回答时间:2015-1-24 07:31:09
本帖最后由 我是东哥 于 2015-1-24 09:21 编辑 6 m; f+ K1 n8 z  V* E9 T1 j
lovewyufeng 发表于 2015-1-23 12:56
4 G, D2 z: O1 {+ ~% z事实上  只要这个计数不为0 都能被 wait 到

5 ~: `  i& ~; L$ y9 ~不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
) i& V* \( {% q1 `9 q; f) s仔细看了一下内部的实现,确实我说错了。
+ w# g7 M; F+ r- E% G在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。6 W8 ~7 c" P' M
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 )        1 c5 y& b& V$ [0 }( }+ }- F
  return xSemaphoreCreateCounting(count, 0);  c: o4 @" f7 V3 p* r$ v6 s4 j' v: ?
#else2 ]2 p; n* H- X7 U) f& _2 i
  return NULL;
) A8 V1 I0 v: {9 f: F#endif6 o) ~* g. Z1 V+ x
}8 ^' v  A8 n; L8 R  d. @* X  `8 E7 g! h
, h8 |8 O  p3 r" D6 {$ f1 V8 R+ I
1 d; h' L! D* Y% W

% a! E3 N7 s2 J, N. m% P% q1 K# X: `
! \9 T$ A8 a" {$ t5 n+ t
我是东哥 回答时间:2015-1-23 07:50:34
lovewyufeng 发表于 2015-1-23 00:48
8 Y  ]0 W  [- c# I- M( w反过来理解是吧,说得通。可是2 j# g2 G8 [0 L& }
有个问题。,生产一个就-1的话应该 take获取信号咯?   反过来消费应该pos ...
( j. i- i+ l6 N/ n4 O% \7 T4 P
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
, J0 Y. [' J$ b- d, d所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
lovewyufeng 回答时间:2015-1-23 00:48:46
我是东哥 发表于 2015-1-21 12:181 ^, B1 M3 \1 m1 d
这个其实也没有不合理,看你怎么理解,资源一开始为0,也可以理解为空闲池一开始是满的。生产者生产了一 ...
  y% B( P: @' Q, U# g9 O2 i
反过来理解是吧,说得通。可是  C7 c+ H; M1 I) m# o' j
有个问题。,生产一个就-1的话应该 take获取信号咯?   反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?5 |1 T/ o/ Z1 D2 M
先不谈消费者应当随时饥饿,处于阻塞态。
% s, B; e  l/ \8 o' B* e9 x生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
lovewyufeng 回答时间:2015-1-8 12:08:29
先把沙发占掉
oipk 回答时间:2015-1-8 12:11:23
楼上的,额,我第二个小板凳。楼主写的好,张姿势了。
liuqihui-347226 回答时间:2015-1-8 12:12:11
不错不错,看起来蛮好用的,支持一下
cqtnheyao 回答时间:2015-1-8 12:12:59
地板要站住
麟狮蕟 回答时间:2015-1-8 12:13:04
手指一抖经验到手
harvardx 回答时间:2015-1-8 12:13:49
有钱为何不顶帖 .何况还是高质量的 .cubemx 势在必得的趋势.支持一下.
木易-357428 回答时间:2015-1-8 12:16:11
看看 顶一下4 Q3 W; Z* E/ R) l, a) h
cqtnheyao 回答时间:2015-1-8 12:19:01
harvardx 发表于 2015-1-8 12:13  U& _& K6 V2 o5 `7 Z
有钱为何不顶帖 .何况还是高质量的 .cubemx 势在必得的趋势.支持一下.
8 ?; R9 I! c" q: E5 h  I; O  L8 X' ~2 j
据说库很大,效率低?求告知!
netlhx 回答时间:2015-1-8 12:19:08
很好,图文并茂
lovewyufeng 回答时间:2015-1-8 12:19:26
harvardx 发表于 2015-1-8 12:13
& R8 u  N% x! ]有钱为何不顶帖 .何况还是高质量的 .cubemx 势在必得的趋势.支持一下.

) [  ?8 s. t1 C多谢老大 支持  继续加油
lovewyufeng 回答时间:2015-1-8 12:23:05
cqtnheyao 发表于 2015-1-8 12:19
" S  S) q/ X& B6 V$ N7 _9 Q3 {$ E据说库很大,效率低?求告知!
, {$ G, L# ?) u3 u
确实 有这个毛病  楼主的贴初始化使用库  LED翻转使用的寄存器  请关注楼主 下篇将 发表 USART基于 OS的应用
wamcncn 回答时间:2015-1-8 12:38:19
不错,刚好 072的板子快到了
cxtarm 回答时间:2015-1-8 12:50:20
cubemx 好用吗
木易-357428 回答时间:2015-1-8 12:51:24
看看 路过

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版