以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)
/ `, K" l' f: c. L1 ^( _+ h( ~$ B* w- q$ r" {
7 t$ f$ L2 D% G) i
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发
0 d8 L7 }% V. ~9 q& g7 f" v. o3 M+ @9 K9 r" H( ^
需要工具 MDK5 自行下载:; Y f% M4 m9 X7 u/ @2 o
STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
8 W. `7 T9 ~; [3 E% _9 Y( w# MSTM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
+ u0 P$ G. C1 a. q s) Y/ E6 \" ]) \5 [6 c6 e7 x' M4 |
6 d6 z; R. \' }+ ^- L7 o$ }安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装
; v w- d% e: @0 \
! K9 _: o2 P1 ]# Z, F1 ]. Q! W9 n( `3 L4 K6 G
( J' g# X9 D! l! D2 o
# `/ U+ y( s! J0 S; f& W
安装之后 新建一个工程 选择STM32F072RBT6$ t. J1 F7 K% _
1 z( k4 v, r/ I# I
PINOUT 勾选 FREERTOS和 USART
& L( f1 O6 y( h$ \
2 u r o# w) p因为我们调试可能需要使用
% w/ d7 N, }6 @/ r8 ^1 T9 {8 v) p: H, w1 S/ s- N. T+ C
点击软件上方 齿轮键生成 keil 工程 至此
& u* x1 z+ M+ d: @) P# O
" t' J- ?- d( T( _5 J. qMX基于 HAL的库 生成完毕3 @ ^1 b% x2 U' s7 D( B, F
5 A" _1 T" P% q3 C4 Y- {2 V' Q使用MDK 打开工程
9 O) o$ y, Q8 I1 c1 M! _3 r& U从上到下 的组依次为 OS 的C文件
7 F) f, S+ l1 y- h.s 启动文件) J( R( Y3 t. a* z
用户文件6 G4 d" R5 @1 `" J% R
HAL库文件
6 p! l$ |1 k/ zCMSIS中间件文件7 A/ N# m2 ^( i" g; J2 u& a9 F
5 w3 g ]0 {; s4 A其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码8 @: {, W9 }! q, Y; k
% U' j( H6 i2 J2 x- m
( U0 g. d5 m. t% T+ l Z3 r
+ V7 l5 }2 m4 u" Y5 h. N
6 `$ u, m* |8 Q& Q/ D& t
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制
) N: b3 `5 c# I& [ g6 W# _
; o% q( x; ^$ a$ U# }先看看 main.c 的 64 到 105行 ! L& [- d0 e. R6 K9 \ ?4 b/ U
) S ?- M! ?# s% }& g1 l& e' y- int main(void)
+ Y6 J3 s+ ]5 s - {3 t5 M& |5 ]. l- H8 p# C
$ h0 ^- d" G; i6 Q. s/ j- /* USER CODE BEGIN 1 */, g; i" b9 f4 h; ?! n2 l5 _
( x+ o ~6 G# l; ~/ a9 i( t% W6 R- /* USER CODE END 1 */6 l; N9 V% ?$ A( z) t
( v3 z& T$ P. x* w1 @9 {' M- /* MCU Configuration----------------------------------------------------------*/2 W, ^: ~; E+ S! [0 J
- # {: I4 N% m; _) R3 n! ^
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
/ F# ^8 G1 }8 D9 P0 ?* g - HAL_Init();
R: i) N' C3 o, I* C$ v
# \4 J' ~' J; g+ e- /* Configure the system clock */% p6 n- E8 K) K- Y
- SystemClock_Config();$ I# \! q9 p' ?. [1 |+ R& ^' X
* m+ |8 x! n5 y- /* Initialize all configured peripherals */1 Z j& ^2 ~4 ?# m5 k! _
- MX_GPIO_Init();- n$ T; @1 c1 G
- MX_USART2_UART_Init();
; L; G% A$ r1 P2 \ O# u, K7 g - 3 K5 T. P% v* I O: Q' R0 I
- /* USER CODE BEGIN 2 */5 A+ y6 p" @# f" R) l
0 P5 \! }" Y( B% l2 u" T1 b- /* USER CODE END 2 */3 n' O; `+ N( d. k! C2 _: N; B; I
- # `7 N0 O) L: C' J9 c4 l: X
- /* Init code generated for FreeRTOS */1 _1 _- B% n( n# g# D. {, H
- /* Create Start thread */
, V( d( ?" x& _2 v) V6 x - osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
: E; p% ~/ z6 S# Z; K2 m2 r - osThreadCreate (osThread(USER_Thread), NULL);" }/ ^) Y$ B( o$ d
( }- A: |* \* r) j- /* Start scheduler */
$ s) x& o; n2 Y, F5 x, U @' b - osKernelStart(NULL, NULL); {$ K; g0 b, J" n3 N1 d
& k$ c- m- k! D- r# U. k. f- /* We should never get here as control is now taken by the scheduler */
6 g+ Y9 U6 f. Y4 M9 q
- ~: J+ G& R6 u3 O% P3 A- /* USER CODE BEGIN 3 */
+ l( I$ E1 ?2 f/ ^* ]* ^ - /* Infinite loop */
2 k7 `: B. f: _: e0 r4 U - while (1)1 t& y4 V2 W+ `/ S, W3 V
- {
& S, ]$ y& i3 q; h! |+ v
4 v6 o9 Z k% }5 \" F& o5 ?+ s- }
! y% p G; n7 ~9 c7 ]4 S$ G - /* USER CODE END 3 */
) ~2 R# k9 W. f) U% K1 U( _; _ - S: F' J3 D( C8 P: P$ @. @: X
- }
复制代码
1 i# w; m% G$ @& ]" P$ _mian函数 C代码的入口 初始化一些硬件后 * p$ I# ^, H' ?
osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL); / r4 s T3 X5 d! q8 r
定义了一个 线程 USER_Thread 然后启动OS
3 u T" e2 b3 M' D( k3 \, ?注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数, U0 o" ^, L$ p; w/ B% B* J
7 c0 T- I$ C- l9 g" S: [
宏的第二项参数 StartThread 为线程 入口函数地址。; d7 X' h8 h3 _8 }4 i6 T. H' N
" I- O% m9 p9 }; h/ |9 Z至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread
1 D$ x! N+ S( r" x: \2 |
+ w- A! ?' u8 x) q1 a) ^& z/ ^修改 StartThrea函数 如下- N. Z' K `. A0 |0 `- T+ T# q$ @
" ^* M' U: A2 `( X! j) U2 B* ^- ; D' w. B2 h8 h8 h
- /* USER CODE BEGIN 4 */
1 \8 Z8 n0 |- l& I7 _ - void Nucleo_072_Led(const void *par);
. }+ E* v6 U2 u1 h8 ~ - /* USER CODE END 4 */
) d- e6 f* c7 o$ F; ^ A - 3 l9 `- W6 O, ]* ~) s5 i0 v# b$ h% e
- static void StartThread(void const * argument)! J7 ?) b2 [2 I
- {
. h/ ^- @) m0 _; l9 N K
9 l( |0 u0 c. l& I6 ~# N- /* USER CODE BEGIN 5 */
. ~$ }& A. X' i# F - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
) Q% W& v5 m: g: [* m - osThreadCreate (osThread(LED_Thread), NULL);, |: ?9 \9 x& k) S ?
- /* Infinite loop */" S0 _1 X- k& D) R0 L# w
- for(;;)
, c% f: Z- `. q1 f - {- x+ Q2 ]4 C1 H$ s1 D
- osDelay(1);
$ v# Q# t# E, L l& q+ K" ? - }- u z O6 }+ z# |* W% t, r( A9 z# ^
# I5 ?5 J- O* J) F* c; C- /* USER CODE END 5 */
, R7 f1 \) D/ Z7 e
, ^; t( j1 Q( g3 d- }
复制代码 7 W( n" E3 t a
添加一个 LED 函数+ s9 v; ~/ K' `1 L6 Q1 B+ O9 I1 |
- void Nucleo_072_Led(const void *par)
p% l; f* F! `$ s9 b* V - {
, I( F3 }* z/ f0 r: J - GPIO_InitTypeDef GPIO_InitStruct;+ k1 W" |5 ~+ v" N
- __GPIOA_CLK_ENABLE();
2 ?# @9 R. u I( g0 G4 c Q# g4 B -
& z: w$ v" X6 I) \& \: U- G - GPIO_InitStruct.Pin = GPIO_PIN_5;
3 X. G$ ?& O- y* }; f. T - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;0 D) ~% }1 ? A! d! T7 h2 @ |. J' |
- GPIO_InitStruct.Pull = GPIO_PULLUP;( m( s9 s i# g \8 M
- GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
# O C' j' i3 u4 V - & l8 | K, R$ g; N
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
0 C/ U/ ], E' ?5 i& ~ - , {' {+ o8 T) ]4 K
- for(;;)
0 \# J- b% q& F7 F5 q8 i+ G - {0 i1 _$ l3 z* y: ?8 S, |' A
- GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁0 h$ K- M- Q( j( v/ {' Z
- osDelay(500);$ P1 [" {2 z/ H; u- {; l! q, u
- }
* Z2 w0 s. C/ X9 l5 o4 p
1 m# J$ A$ ^$ k! h& s; ]- }
复制代码 ) Q" l e9 l3 q$ w6 {
到这里可以编译下载到板子上运行 观察现象了" m+ K! @! o" k' M; A
$ P7 e. O+ L7 _( W1 F0 z
下面创建 RXT 的工程 新建一个工程
6 } a; R7 h- l! [( P' ^( n' j4 j" U( g0 ^/ ]+ m8 b* X0 E @1 h
勾选 如下选项 / [' D& Y/ m7 Y% d" v3 p
& T$ F: V# `; x, ~
) g8 a5 u2 C* Q# J8 S m5 E
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 ; j5 u& B8 p1 R4 h
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库 & K) C# T6 h9 e0 B
% I4 J8 C5 g+ E1 @% ~# l把原来的 main.c 复制一份更名为 rtx_main.c
4 A- [( `1 F0 C) s! ]# ^" \, e* M" N1 M) j9 }" D# O* H3 B
0 F1 w# ?* ~" Y q& P
6 m5 h- R; b) X8 {文件添加完毕 3 B* l( r) K) P" M+ x# F; D7 }
) |- ]: b; `7 l+ [1 G
接下来定义 头文件目录和 系统宏
/ N7 A, g; p( [- n& w/ J
. e m3 o7 y/ C/ t- j! @
/ G! u$ g) F8 m" c+ }
+ G, _6 k1 Q. s% S* u
修改 rtx_main.c 下面两处需要修改
0 T; B5 G& A2 h, w: a5 K
) H: \$ i' t. Y& \9 a- {
! E2 a9 X, R+ U: a - osThreadDef( StartThread, osPriorityNormal, 0, 0);' F) ~, w9 B, y$ x' B
- osThreadCreate (osThread(StartThread), NULL);
* r/ H* O! b' t# T8 } - }
) w; y; r# ?9 ?6 u1 S7 r4 H- j) n - ' L& U( G$ B" s+ U H* m0 ^* g; @
- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);7 U; a2 j \. m* e' h
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改
* S+ A& B% K& W+ U0 \, w v8 h5 [! {4 M
修改 stm32f0xx_hal_conf.h
- Y4 s% O0 D3 G# O, L添加 图示内容 不出意外 下面 将可以直接编译了!!
# ^8 I: p6 j# G5 a) b
- n$ j/ h5 X5 Z2 `) Z# F1 i; r1 o _% F! ]+ f$ H {5 p; M
写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了
4 H) k$ c7 T I/ u" C# Y; }5 i* V+ a0 v$ j' w- H i. ?
下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量
$ Y/ l3 O2 q# ?& gosSemaphoreCreate参数 count 直接9 L7 S6 Z; e! L# y% ~
. V- U! V4 v: }- h5 M
传递给 xSemaphoreCreateCounting的两个形参
, ?: l' Z& o% Q6 Z
% l# r- j# B: H) m6 e% A* W
原型 H6 J3 F/ c- r7 B! l; m
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
- t9 {8 h/ M8 n& c/ p! ^, I/ N" }1 N
该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值" ^6 [- ^/ d, l/ Z- C
# o/ i: g2 M" b+ J- |3 B
基于串口使用信号量 那么需要如下要求8 y( f* w( l. m7 [$ @, I% ~
假设 usart_sem 为串口使用的信号量
7 {" m1 Q2 n+ i! R( G每收到一个数据 usart_sem ++ 缓冲 1024字节 $ r6 ^# E* Z( U
需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据& J2 Y- o7 V7 O/ n* K
, T8 \5 m" ^4 P9 g2 t4 r
如此我们知道 这个信号量的 最大值应为1024
, S8 N( ?- W7 h" F; s8 Q/ H0 K可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
0 R' @% T+ w" {# @
5 \1 n" o. O- U( K! I( n会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次
5 K: w- F: n3 V. w. L% M显然这不是我们想要 & k O5 a: C/ F( G1 d! d
5 R V# X4 D1 l& \5 c
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1 ( ~5 { C- E! H/ D, ]
/ V* X2 F( w$ `不懂 这样设计意义何在?. s2 d# M% _- Q9 {* ^9 b! h
' Q. a* N: g0 h' d5 C) P; z- q; ]& t/ ^
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。4 z0 Z/ O3 M$ r$ Y& i) I0 T) n, q
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。+ _! I8 P2 E3 e8 k& t* x+ ?0 L
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 ) , Q1 l# e9 s( a- p3 t
return xSemaphoreCreateCounting(count, 0);; r, P( u: p$ n: X7 W& `1 t
#else/ V" a- B$ H# `5 C% }6 r3 B
return NULL;
#endif8 j1 w1 a. J! }1 v1 V; N0 ]
}/ o3 o3 R" x! ~ H
6 m5 y5 l" B/ y* S
+ t0 r) V/ l; R d3 K# O6 r+ J
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。, |) }5 l# [% ]6 o1 M: h4 |$ t. q
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用