以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)3 D Y- i# d" t {# p6 S Q, \
0 Y4 s) A R1 k1 X
+ p# }! B- z1 K) Y7 t- ]后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发6 r& ^& N y) r
( W. t! I$ d' u1 u% l7 B
需要工具 MDK5 自行下载:
& J8 s' m- S" K" M! g0 ~* p: J* x# ?STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
+ D, K. H0 j, T" {! T n4 L2 LSTM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
" \# l/ `1 ~1 [/ k0 b! e. q- E5 Q% r$ Z, `
; E+ q1 W, e# h9 T' y' @安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装2 c7 I- @2 q- K% D
# n6 x: _; i# k$ Q7 j7 H$ {% O9 m* e- {. H1 c( s
* T9 }4 t4 c2 m |+ S4 I, |1 x4 y D' k. I. }" ]. G Y1 D! z
安装之后 新建一个工程 选择STM32F072RBT6/ P( s* o( E8 ~- e9 D9 A' _% N
1 e5 Y! u* D, t: _8 w
PINOUT 勾选 FREERTOS和 USART - S* i6 s4 b |1 |" Q9 B0 e
' X( ~/ o2 x( ~! s因为我们调试可能需要使用# Y, J+ h3 s$ m4 @
) _- p( L) B5 X: u, e5 _8 G
点击软件上方 齿轮键生成 keil 工程 至此: L; b( h# q% W# ~2 w6 n) j$ F2 J
* ^6 q# t6 E( C5 L% z2 ~
MX基于 HAL的库 生成完毕! n1 Z% o4 e+ E% s
- [, y" E: Y& j; j使用MDK 打开工程/ a/ A2 V3 Z9 J, D8 m
从上到下 的组依次为 OS 的C文件
/ o! y+ s' d7 J3 \' l6 @' f" Z.s 启动文件8 I8 r; o7 H& O7 l
用户文件0 c; _( T; k: y% e2 t! p
HAL库文件
6 s1 t# n2 V7 O- `CMSIS中间件文件
$ M: F; P; Y7 @4 O+ m3 ]
4 o G6 G! j( x0 e( s其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码+ { E: g( i+ ?! n% {8 R6 `- {
4 s1 |9 H# b! i
; J \+ F+ I4 n# ?+ C( L% j5 g
6 y- U T2 D# s ]1 \ N7 {( W! m" `1 G
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制- T9 z1 [6 k- S
! k% C* b% W9 {2 _# Q先看看 main.c 的 64 到 105行
+ R6 G- n; B4 d. f- V2 B x0 a
0 @, z1 `$ ?6 |3 @/ N4 u. E; e- int main(void)
+ k1 b* q% y3 M* k" Y - {" d( N; \+ ]1 u# L
. ]5 d# E) n2 l- /* USER CODE BEGIN 1 */) ?: ?$ l' N& \4 D$ `4 `. [6 \& m
8 ^0 A- ]/ c. R! R# R* R- /* USER CODE END 1 */: E1 g+ [( T+ X& f# p. m
: A; U4 T# h6 r, i* g7 ]- /* MCU Configuration----------------------------------------------------------*/
# `* B" ~) X- {/ F* i' M( S
; g4 V! Q0 Z( E: j- /* Reset of all peripherals, Initializes the Flash interface and the Systick. *// A, Q( G( s# `- F. D) v7 V
- HAL_Init();
1 m- a& x3 T2 p* r% a$ G* J - + A1 j. k4 y2 h8 K" W/ P
- /* Configure the system clock */% x2 R) ?% \9 q9 e' Z( z7 x
- SystemClock_Config();
L8 V8 n& x9 y- s' V5 c9 a) p - * H* C+ v3 Q; p; L! v) k
- /* Initialize all configured peripherals */
- G+ A1 j& A3 f( s6 U/ b - MX_GPIO_Init();
5 v: v7 g9 {% N - MX_USART2_UART_Init();+ U' j* J) D0 P, }
. S% R+ I" V, l- /* USER CODE BEGIN 2 */
& K8 V. ?+ `& n( X; V8 t4 Y0 m8 y
$ @/ Q' q# Z$ U n- /* USER CODE END 2 */
. i( {( F. _/ d+ A7 e
! H! k2 o6 W5 O+ o6 k- /* Init code generated for FreeRTOS */
$ Z5 B5 X( }3 W4 s7 U - /* Create Start thread */- }+ v; k( P' Z# Z
- osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);3 s# q3 l% g* F8 p. U
- osThreadCreate (osThread(USER_Thread), NULL);# X' @3 [3 K; j9 d8 J( r1 k' t
) ^- j( W% H$ ^2 @- /* Start scheduler *// T3 @: Q" H$ s; u o
- osKernelStart(NULL, NULL);: w' Z y3 u! m. l$ P& ?5 y' A) A4 K
- # L7 V8 D& B( l0 ]; p) Q# G% r" F
- /* We should never get here as control is now taken by the scheduler */
: o t7 x/ ~1 v, K! O1 o5 a
% T0 v% f* i* ~! X- h! z- /* USER CODE BEGIN 3 */. k6 l' M. x, o; E4 p
- /* Infinite loop */
7 H) P8 e* j+ {7 N/ O - while (1)
, X5 `1 _, i/ {2 M5 N8 T - {9 J* r6 e* z3 \& T" y! v" I3 n
- 4 A. d4 z$ u) C$ T$ O4 n( [( R
- }
/ I' O+ ]( `. H% E - /* USER CODE END 3 */6 d/ q# E* w1 ^% o% S
2 @& Z! r3 O5 q# z! m B- }
复制代码 0 G8 R, p1 A) Z h% `. D
mian函数 C代码的入口 初始化一些硬件后
. P0 u0 k2 t: u+ }9 E" A osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
; Z( j- L! ]& t$ M0 o" g定义了一个 线程 USER_Thread 然后启动OS
( z% R3 ^( B8 T& r8 u2 D注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数
7 c# N# D# r( V
) @/ S7 F# v* T8 T; m宏的第二项参数 StartThread 为线程 入口函数地址。; B" h" @# W$ ~; i: ~" G4 e
7 h9 D* V; Y: [2 c: f9 E- X
至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread
9 u" M+ W' s, m5 U
8 r* s+ z; w" t! m修改 StartThrea函数 如下0 W" w& u, {7 E* W4 |1 U
3 q7 W6 c, ?4 g0 x8 G
3 X& @$ O8 I* b6 r- /* USER CODE BEGIN 4 */
/ S* x7 g% f' G) f" E/ T. S' v - void Nucleo_072_Led(const void *par);7 I& C& U9 s6 C4 u% L
- /* USER CODE END 4 */
7 F; @; l% K6 M; K% E7 u9 k
. ~3 s; N! w# g# `6 b- static void StartThread(void const * argument)" w- }. k- ~. x! ]
- {
% q! n$ f# e! o - 5 b2 L& Y) \4 Q7 ~; [1 m: Z, E! P, t
- /* USER CODE BEGIN 5 */
& V5 c Z! q1 W* W; ~2 i! A. i7 \ - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
. R5 q: A& d% y& {" m6 F9 G - osThreadCreate (osThread(LED_Thread), NULL);: ]. x( d' U+ [
- /* Infinite loop */+ ~: P5 l6 q# S9 R w; }1 z4 u
- for(;;)4 I) u( p1 \3 K4 G& B, u
- {0 C; F# ~+ _, N1 K1 G/ m% T, o
- osDelay(1);
# o6 @0 `% F: U8 ]1 I( d$ | - }8 M* m' s5 V; g. c4 Q/ u1 X9 F, b
- , H3 p' p$ Q l) A# i5 P0 H, n
- /* USER CODE END 5 */ - n) A% ]. o7 |2 V% [
- ( o" Q& p: a0 z6 B$ c5 u
- }
复制代码 - b7 c Y' c1 r4 E! y2 p* j* k3 ^
添加一个 LED 函数
2 |+ I' U1 i9 u2 i/ C- void Nucleo_072_Led(const void *par)
9 I9 y' L8 w# t5 A2 M - {- ^$ ]. B, I0 Z
- GPIO_InitTypeDef GPIO_InitStruct;
0 G3 j" L2 U0 r% @ - __GPIOA_CLK_ENABLE();0 h* j; G$ \. ?6 P3 {) v/ L) J
- , V3 S" l' h! W6 z8 ^+ V
- GPIO_InitStruct.Pin = GPIO_PIN_5;
0 n* Y t' R0 A6 t - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
1 r5 w$ S* }# g I' }* J$ K$ o- I - GPIO_InitStruct.Pull = GPIO_PULLUP;0 _& T8 N6 M( F2 P
- GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
0 C" v" m$ ~6 R' [- @4 x3 h( \6 g
# b% C! o3 p- X6 w8 y( D+ [- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);0 ?* w- V! N3 Y/ q" e: W
- ! P, X. Z+ i3 ~( }' A
- for(;;)
/ S$ }% A5 z. m: l1 i - {
, ^: D) Y5 P5 P - GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁8 R" K4 A' f6 D5 d
- osDelay(500);
* Y% s& l3 s9 b% u3 l) } - }
) C- [: C, U6 U# R8 i! X$ F7 g) O! Z
+ i' }, R' v1 f# e6 ]. \0 n. }- }
复制代码 ! \2 Y- G* y" w0 m+ J. \) u
到这里可以编译下载到板子上运行 观察现象了
$ Q: l5 ~- ^7 u% P4 u! E* |* W8 l( b$ h1 f
下面创建 RXT 的工程 新建一个工程
~2 p; n. e) O6 a1 E3 n
; j/ j7 H3 g1 u* X6 n0 ?4 u$ W, \勾选 如下选项 " M5 ~* }( E7 Z9 C. B
! \5 F `* k& \9 q
5 a) A& K3 L" y/ H$ u" z红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 5 B, Z& [6 O9 w2 B9 k
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库 3 U; {0 S) ^4 t2 [ l
( A4 J) _1 a) U, y6 E把原来的 main.c 复制一份更名为 rtx_main.c2 W. L( o' Z$ v
0 Z3 R+ c' g8 F
4 C+ X: r" @" t! f+ A3 a: M
) z) r3 a1 s$ G7 q+ ?文件添加完毕 9 K$ [1 T$ W" }9 D+ e* j! t' n2 l
8 I3 D& }5 h+ a$ n) U$ h接下来定义 头文件目录和 系统宏
/ H6 m2 p5 ~- x" g0 R
' u4 x7 q L; h9 P' j
3 P- p2 _2 o) A4 }- q1 ~
H9 m5 v) w* p+ I/ k修改 rtx_main.c 下面两处需要修改
& x: J1 M- C0 s9 ^/ [: @
5 }& q1 a* E7 W- @1 P- {
. |6 s, g6 u1 K0 l1 _1 g - osThreadDef( StartThread, osPriorityNormal, 0, 0);
* [$ z' P0 U5 O# C - osThreadCreate (osThread(StartThread), NULL);
3 P6 Y2 L) V- M) l% X4 g$ K" W - }
6 h5 S6 k4 @ Q" w3 l - / g3 T1 ]" _. _( R
- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);. ~: l1 f6 Y. e6 q% @+ F* e! E3 N* a/ m
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改
+ G& X% B9 B9 E0 ~4 @
. T1 C. r- F+ }8 G/ h; k修改 stm32f0xx_hal_conf.h
- v5 u: z" w4 ^4 J* r添加 图示内容 不出意外 下面 将可以直接编译了!!
# B. l, p! f; J% m" L& Z
# ?' d9 D( r5 X1 ~* N0 S
* w/ n: b6 j+ o: r( z m4 z写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了! l |' r$ v$ \7 g/ O+ Z6 k
& Z. \& l0 j6 J" z0 c9 }% W( v8 x下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量
) @7 e3 Q0 h" c( S6 BosSemaphoreCreate参数 count 直接
2 P. Z' K7 G0 V5 h, K9 U& y
' h% }# Y/ e: l/ P2 a传递给 xSemaphoreCreateCounting的两个形参
+ W! K: l' D# G& o; W* b
( V2 E4 s+ J# e0 }原型 9 _* {. w# x4 v: U
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )6 k' Z1 k& g/ D1 m1 u! `8 [3 w
/ f7 ]0 B# b8 }; o W! |( l. N该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值
) _5 z Y5 U, p6 Y+ H8 `! L; I
7 }& `2 ]( O2 T2 @! H基于串口使用信号量 那么需要如下要求$ @0 @3 T3 Z& B- Z$ z; M
假设 usart_sem 为串口使用的信号量2 w% {: [3 J8 _
每收到一个数据 usart_sem ++ 缓冲 1024字节
" {$ I! O' T& M7 Y% I2 H需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据- H# h% ~( b p9 g0 N# D C
% o0 _8 r" C% G2 v \如此我们知道 这个信号量的 最大值应为1024
2 u: H8 _* A/ ^% `可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
$ g" A/ @; F% W; p2 f( M
3 O% @% ~7 j7 P! g) y# J+ A6 i会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次) W: c+ S; L) { Q' I' |% K' t
显然这不是我们想要
* ~/ o0 y8 Z% e) H) i+ h; H; o/ c
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1 * U! D/ \7 z, p
: c9 v% [4 m, O/ ^: E/ S
不懂 这样设计意义何在?& Z8 Q' a5 R; M3 n2 b
* f% D: A% g$ o$ X
2 f# V$ `, v; U' C/ e
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。- ]% m$ A' {: |/ H* y# r* p
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。& ~( L* Q! t4 S
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 )
return xSemaphoreCreateCounting(count, 0);5 v) T( `+ ]4 B$ q8 R7 N
#else7 Z8 O/ ]3 i) O# h7 l4 q6 S
return NULL;
#endif
}
( m* x0 f. x- J( a* p" q+ g! B- E
' }( L5 I4 v" q5 K
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是- G/ r: V: o* }9 q0 u/ V6 Q2 c0 Q7 E
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?9 @3 T% }* n) I# \/ _
先不谈消费者应当随时饥饿,处于阻塞态。6 V1 i3 p7 u4 _# S/ P) W
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用