以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)
# F# c; J( Z H. w8 I; E! e; v/ m7 C) r: M; F5 U
2 ?5 e: M+ Q6 ?" r2 Z# W2 f后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发
9 B/ W/ @* @! z, C5 |8 p6 q" N- ~! }5 B
需要工具 MDK5 自行下载:
) g$ A. {- ` z x2 n5 H4 ^, i% wSTM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-2149846 Z& ~ }0 U* Q4 v
STM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669) |. }) ], L6 g) u: I
' f# o0 L2 H& k7 d3 f/ o$ [( }- s' o1 q3 b+ L
安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装; M, H* P# X' \2 V2 b+ H1 @8 D
0 x; N5 K, h0 b3 K
! V- E8 c5 g; o. y) E5 }5 w$ K
9 ^6 J, y5 ]5 ?, [8 q) Q2 z/ j3 P- I' `, D1 C5 F( f
安装之后 新建一个工程 选择STM32F072RBT6% F3 f J+ w8 o, z# i$ S6 f
* Y! s3 @ d* H. W' iPINOUT 勾选 FREERTOS和 USART ; ]& \( y: \/ A2 Y O" r6 V
& B1 F( a8 Y' p- g
因为我们调试可能需要使用5 i& E# W4 L& K7 n
8 k$ v! W2 Q/ v: q+ `& X! C
点击软件上方 齿轮键生成 keil 工程 至此
5 ~* b. i* R' g( ?: H9 r; {; h. q# v" p/ O
MX基于 HAL的库 生成完毕1 u" H$ a, r. j2 U; R! ^* g' n" H
8 X! \* P- g; P0 ^' L4 @8 E4 `使用MDK 打开工程
/ Y( t" d8 C1 J从上到下 的组依次为 OS 的C文件5 |" }+ ]8 k( H( l% R3 b0 l0 \6 S
.s 启动文件
9 X* m, m6 ~4 H, ~( z+ ~用户文件2 t" M0 [- ^ o
HAL库文件
- R. i1 }( |" m7 w5 {) _0 A6 ^CMSIS中间件文件8 R5 N# v8 k* Q
" t1 T" G5 ^7 A/ \其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码! ^! C; | W" j7 a
, W7 ?3 I: g/ p! ~
x/ N/ R3 N* e5 H% e
7 L7 ^5 g; V# Y: p$ O* V. i* o3 |* x* d' b
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制, T. \7 A9 t/ O" {: I: ~! T! t
5 z" Z/ \5 E7 s: ^% e- N6 ~
先看看 main.c 的 64 到 105行
% C, W1 Y* }9 h5 X. N! v0 ~
% X, b( | t ?% S4 R3 j3 ~- int main(void)
9 a9 \& A) i% c2 P - {; b5 x7 P; i. B. F
$ N4 _3 j1 L$ Z/ z& e5 e% k5 _- /* USER CODE BEGIN 1 */5 ]* u% s4 V- s6 L2 h; g) w( j9 F
0 o# F. o# V, t @- /* USER CODE END 1 */- \* y' R! n! F) m, `# \" B% G
- 5 C; I- _( K( n" r& {# g
- /* MCU Configuration----------------------------------------------------------*/
% t7 _: a1 {7 z& r3 A; G# ]: x( [
3 l; V" d [' Y3 p0 d5 f- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */( j$ v& W& q& W P0 o X
- HAL_Init();$ O/ e. X" }& a, g0 z3 d3 z
- + L9 O! D$ M9 j% v4 L, @
- /* Configure the system clock */) i y. @) H3 o* \# o% _. V
- SystemClock_Config();% i/ W: i; o- Y6 ]8 M* |3 @3 c
& X: K2 _2 @' e& `0 z; P- /* Initialize all configured peripherals */2 a) Z5 x. v; |# U8 u
- MX_GPIO_Init();
8 O/ v8 ~6 q' y D - MX_USART2_UART_Init();) ]; X, s' y1 Z& {
, L4 C% Y" Q- O7 P9 q8 D. i6 Z" L- ]- /* USER CODE BEGIN 2 */' Q5 B$ [0 T& s4 y9 x
- ( C( j+ N0 Z! m/ n
- /* USER CODE END 2 */& b0 T5 ]% B: j( A
- 2 }2 O) m( ~( W1 F+ A" R6 Z- J
- /* Init code generated for FreeRTOS */; T0 i0 T& g2 p2 G. v, k
- /* Create Start thread */
8 [5 J9 \6 O2 \1 q& v4 [ - osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);; e$ o, \+ F+ ]4 @. m) S2 H
- osThreadCreate (osThread(USER_Thread), NULL);7 V# {9 v/ R6 {" }
- 7 f# E( {! i# c& |. i4 ]) C5 Y
- /* Start scheduler */
% y. J7 W1 H! a5 I, O - osKernelStart(NULL, NULL);
5 Z/ T4 ]4 M8 u% N' `0 ^6 l. [ - " g; s7 X. S# `4 l& s1 J
- /* We should never get here as control is now taken by the scheduler */3 Y7 R9 t$ X, ]$ P8 h6 t
2 A3 N) B: ~1 h- /* USER CODE BEGIN 3 */
" R# U) S4 |9 ?4 R0 _ - /* Infinite loop */7 M6 Z% q, q4 g) q8 K
- while (1). r! a$ u( F% U: i
- {
! s" d$ |0 x6 l% B" Y. [9 m - ' G, V ` ?7 {) E4 R; Y
- }# n% J5 t" f" q, R3 d8 _
- /* USER CODE END 3 */6 f: g, E t" A' w
- . C3 m6 p ~. ^+ ^1 H2 {; C4 u
- }
复制代码 + e+ ]3 {5 [! V
mian函数 C代码的入口 初始化一些硬件后 - z/ {) _( I, Q. F; l( V
osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
+ p3 h$ @/ W: e, w) P5 E定义了一个 线程 USER_Thread 然后启动OS
$ N) o& K) c, }: }注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数
- Q0 V* W. ]4 L4 S# e8 N! o# m$ L6 F; W1 q8 w2 }
宏的第二项参数 StartThread 为线程 入口函数地址。
6 k8 p; Z& n; [+ x* D' }; L M5 t
至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread ' t- V- k' q" r2 G. b: E
6 X! Q7 ^/ I! U& N8 U& e
修改 StartThrea函数 如下
( X" E8 m- D5 M" w$ E
- h; _! {; A4 n$ w6 ~' }7 G
O- Q$ n* z/ Q# K$ Y4 O9 t- /* USER CODE BEGIN 4 */
E; V( e( j3 f6 e9 G - void Nucleo_072_Led(const void *par);
& e1 ]* c" [" ~ - /* USER CODE END 4 */% z" G. ?5 x4 o4 f: o
- , x, s# o! J/ Y! k# D+ N' O
- static void StartThread(void const * argument) e' p" y, t6 L I# W
- {, A4 j7 l! v. M
- Z- s; Z* m3 v4 W; C- /* USER CODE BEGIN 5 */0 k" o4 \1 l; C% Q
- osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
+ j5 v2 ~$ Q; V* t - osThreadCreate (osThread(LED_Thread), NULL);
; ?3 I. c$ v& U/ _2 w - /* Infinite loop */$ W* V; M' Z+ e
- for(;;)
5 L; U* _9 d, j# F( Z. g - {; B5 Z' P/ y8 s5 }7 D! e
- osDelay(1);' D# o- H9 a% }3 t' i5 c
- }
* f, W; R, S2 {$ F) u - * o& k. s7 a. r( I: a5 s( B
- /* USER CODE END 5 */ 0 B2 ]1 @* |3 d2 |# B5 P7 [
- / ~' w& G c0 _8 S' h* G- A" M
- }
复制代码
; N# }4 ~* f$ X& w7 |$ L# N8 [/ i添加一个 LED 函数
- | n0 A( I, q, c: b- void Nucleo_072_Led(const void *par). n1 h8 C! q7 z9 I
- {* f+ A) h5 K& W: D; U/ y( P
- GPIO_InitTypeDef GPIO_InitStruct;
0 u6 x: m6 o/ z/ j3 N - __GPIOA_CLK_ENABLE();
- e1 P# n4 m5 e: I1 X1 O& [+ V9 D; q( a - ! m' V4 r7 n* _% `: @. f" r( F6 O
- GPIO_InitStruct.Pin = GPIO_PIN_5;
( g4 D3 w* z) j0 n5 ?5 v% o5 K - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
& {2 Z* F- c- R& Z/ n) n - GPIO_InitStruct.Pull = GPIO_PULLUP;3 K! j3 Z0 I4 }' I% y+ S
- GPIO_InitStruct.Speed = GPIO_SPEED_LOW;) L/ e; h9 j5 n. S& l2 M. E; V
0 j; M- t( E3 s0 E$ k9 k- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
# X& ?+ |; W( {5 @ - / M, I9 E) e8 C
- for(;;)
. x* X! {+ k/ `3 _1 s* P! z- p - {2 G) m2 W$ U" a8 x, A9 _) }
- GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁2 k/ J: ?8 W) |/ T1 ]; q
- osDelay(500);
4 K' X! J4 q+ S2 f! D - }
' v. X p6 D6 e
& }0 P0 E) H5 R J! q/ U) p& q* c- }
复制代码 * c! k2 S# @; \4 J
到这里可以编译下载到板子上运行 观察现象了7 D5 U/ G) a$ j5 t. a
9 p; E$ X- m$ H下面创建 RXT 的工程 新建一个工程
0 z' d- d ]# N9 y" ~- N/ {5 T/ N% E* w. O" U2 T2 j2 t/ F" Z T
勾选 如下选项 8 L! l$ N$ a3 r8 Q: ]( w0 k, @
. \; B+ j' S! O' j" b
) l7 ~3 w3 A4 R
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过
3 y& k3 {8 r! k% _3 A% xF4 的工程没有包含 HAL 接下来 需要自行添加HAL 库
. M0 U' [+ Q* X$ `/ ?+ n1 p0 t! |8 U/ H6 g
把原来的 main.c 复制一份更名为 rtx_main.c: ~! M" A4 f. R4 D$ L" L; @5 A
3 n a4 W3 {. {" O6 C. H
8 z( q( z* L) G H6 T3 s) `1 S% ^ g( e/ m3 i/ I) C
文件添加完毕
8 q# S' a5 J2 k' g( {
: p9 ~& h: g% P0 p' s6 s接下来定义 头文件目录和 系统宏
, b6 @5 X- r4 ` J/ r' o: k, c! E+ P" v& _! p/ k3 ]
4 E1 ?' b$ P3 f; u9 g( h! m- D! w) g9 Z/ z4 T' ^8 r
修改 rtx_main.c 下面两处需要修改 3 D7 w5 [0 g; Q0 w- `/ W) o4 E
1 o6 o# w* o4 U2 l' r- t3 S
- {
7 |$ a; u5 o: T+ K; O: a' L - osThreadDef( StartThread, osPriorityNormal, 0, 0);
+ Y3 @. r( X, m7 A7 J - osThreadCreate (osThread(StartThread), NULL);
0 V# y b- K$ E" ]& u7 ? r6 Q - }" t# X! b7 B; \) f; [0 q
9 S5 ]9 G% v& }3 f- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);1 p ]! T) g2 R8 v9 t
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改- b( B1 D* T/ U+ ?# G1 D
6 V; M. d+ m* X {修改 stm32f0xx_hal_conf.h . G, ] j& ]5 x, F+ q5 n% c: k
添加 图示内容 不出意外 下面 将可以直接编译了!!& Y5 Y! j8 B- T' L O5 M
: d3 }0 i- x* r/ u* Q% K+ a
% a U5 q' [" w1 w7 k" T写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了
7 I: _+ G0 A5 m- ?* ^+ |: A$ t4 m
下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量
0 O) O( L3 R! c; k7 qosSemaphoreCreate参数 count 直接
# G! [2 c; \4 u
. q' u @; `& H. w1 X( R* H5 |* P传递给 xSemaphoreCreateCounting的两个形参
# l5 f% S7 |& w5 u( J# r/ k
. h! W) d7 G9 {( E# r2 ~% J
原型
5 _7 F6 l+ H$ L9 g, k: C3 B4 c" R#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
! A" X" L. p7 l4 J) ?6 s
, k8 b }$ k G( [; `该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值
+ E+ v# o; ?/ E" `6 M
8 x- Y+ e0 T4 }' Y" u, u% {基于串口使用信号量 那么需要如下要求; r$ [5 Z! y. l. G
假设 usart_sem 为串口使用的信号量
5 x2 p9 f- p3 r$ P每收到一个数据 usart_sem ++ 缓冲 1024字节
1 y( f i5 o/ @& w/ D* W需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据
, E! f# K. u! Y8 M# t$ T
, z+ \- i U: U如此我们知道 这个信号量的 最大值应为1024 ) j4 R5 A" n6 K6 G; [6 b) n+ v
可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024); ) k" {) ?# f6 Q3 r: A+ L& q
" k4 v3 Z( l/ m5 \
会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次
# b" b' P8 D& p) G/ J9 S9 _显然这不是我们想要
! D7 x7 z9 L8 ^$ f1 { K
, p* ?- M, {, V7 H# l4 J) }通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
! ?: ]6 A7 F+ Q `8 t& ]3 i {
" n' j, S+ i& S不懂 这样设计意义何在?
6 @1 W6 o' w1 E4 V. D# W* h( K, w" ~9 l6 _& X
: b4 j) Y! b O. a$ r- |/ O3 V
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。+ _ v$ R! b$ ]" q& a
仔细看了一下内部的实现,确实我说错了。
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 ) 2 h" M+ x! K5 x' E
return xSemaphoreCreateCounting(count, 0);) ]& F0 g! M. B3 @: F( i
#else+ `4 d, V5 ^! ^
return NULL;3 S' f8 p# ^/ T# t
#endif+ y. N8 c& ?* ?, i2 I3 y4 \* Y
}9 p9 g1 X b' Z' n
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。: M" C, d; {% M8 u @ Y
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?! e& a! ~2 K1 h. u& a$ ~
先不谈消费者应当随时饥饿,处于阻塞态。
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用