以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末). Z( t+ O* b% j7 W
0 F' N) ]# w2 \/ S$ A* U' z5 F- Z5 m. ?5 N
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发
8 V. w$ I( `! r1 t8 T# i8 N5 o+ q5 l
需要工具 MDK5 自行下载:
& E2 o* ?" E* S& l% `STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984 X4 u% `3 b8 v1 }4 e
STM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
A1 I" `" N# G( E
2 g) K" V: v C) b/ V
7 X0 L: |6 e i/ Y9 N! Y安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装
* N) n) K6 W( C8 z6 Y. C) \1 S2 O3 P/ X3 l% _
; k F$ }# ?: j9 H, Y( x7 N5 s9 o) C
! f6 ^! }# |) p
; U% d- `- H, o1 a ~8 \安装之后 新建一个工程 选择STM32F072RBT6, E' r9 u# t* i, _/ i$ i
3 ]8 B. K& C% e' c9 i! }0 \
PINOUT 勾选 FREERTOS和 USART ! H' c9 t) J. k8 t: _& c' `
/ p+ ` x ]6 R' ^/ l" {" ~$ i
因为我们调试可能需要使用. L) G. }& ?. X/ A6 O! }3 U
& @/ r5 C' L! k2 Z
点击软件上方 齿轮键生成 keil 工程 至此" O9 O7 X. [ I0 ?/ A0 Y$ }+ t: X
+ A. a" M7 o3 V& I# ~! OMX基于 HAL的库 生成完毕
, N8 c0 ~/ P2 D4 J: E4 b4 k8 u9 u
使用MDK 打开工程
* t3 `! z' F8 I从上到下 的组依次为 OS 的C文件
1 q% @9 o6 n: ~) k7 o.s 启动文件$ s5 _2 i& N- W" z/ { o
用户文件6 c W2 R. {4 @! S% n
HAL库文件0 q4 _, a' U. @0 g
CMSIS中间件文件+ W R- u. |4 m# W" x
; \9 d% `9 ^# C) K其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码
- T- K z: Z8 {1 Z: g) F2 W* r6 X+ @+ n; p) A
* ~- ?0 h9 z- o: n% I6 a7 u3 O" y$ L% f/ r: `+ _+ ^& t
5 ~8 ~) n" S( O! B, Y7 Y: [接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制
' E" ^$ r1 \4 c- l5 o% a. ]1 Q2 Q, w: x# l, F6 \/ A
先看看 main.c 的 64 到 105行 - K, W. M; }4 M; z
8 S" Z# t" r! w4 h5 h" m! n& d/ H- int main(void)
0 R8 X* N, t2 D - {
! ^' ?8 p* v* h, w) a$ X* ~ - @* e5 K4 z( i, M) y" |8 T& g
- /* USER CODE BEGIN 1 */
; B' |3 m7 M- L. {; [
: I- G6 J0 E9 C/ ?- /* USER CODE END 1 */! L2 b' \6 \4 x2 S2 X5 H, E
8 x9 Y- O+ h3 ~ Q7 z! H7 ^+ F- /* MCU Configuration----------------------------------------------------------*// k7 L J- E4 j/ w- h
7 S( c# Q4 j# K9 H8 ~- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
) Z! {2 f0 Y! g1 |( m8 y% X( S9 b - HAL_Init();$ ~2 c1 I4 G0 L0 Z1 y
- ) v2 U2 A' c c. }- X+ j
- /* Configure the system clock */
' B1 J* g7 ^* I: Z1 }4 W - SystemClock_Config();9 u( ?, R/ b3 l z# n. s! f
' f2 f0 j5 F; e* _+ r4 u3 V- /* Initialize all configured peripherals */9 S8 I, {# l) c4 x& J4 @4 u. J
- MX_GPIO_Init();
1 [! C& ~& ? D/ X - MX_USART2_UART_Init();- ~' I' s* p0 }# \1 g+ [$ K
- 0 k, R( A! q5 l" R" n) B1 \% Q
- /* USER CODE BEGIN 2 */
O! m e2 M3 _! n7 o, K5 }& L - / M* p& ?; T7 P) j* z+ E9 a* W
- /* USER CODE END 2 */
( I3 U/ `) d1 U$ u" d4 u8 g
' J) C5 U" p2 A! e* V! N. f( f* J- /* Init code generated for FreeRTOS */1 u. U; E/ f! |; e" @, `
- /* Create Start thread */
& z# {& L9 d7 |5 c4 j" w# a - osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);: s" ]! r( ~' k6 F
- osThreadCreate (osThread(USER_Thread), NULL);, s4 n2 }& A" a; x- ?8 _
5 I3 G' _5 d, v9 W- /* Start scheduler */7 a1 t" n; d* D1 f: L8 Q, G A
- osKernelStart(NULL, NULL);
. Q- F# k+ } u3 p" G
4 t5 Q; l+ ?" K1 Z4 Q* r+ C- /* We should never get here as control is now taken by the scheduler */1 z% M h/ R0 q7 {: S
; O* \3 V( C0 ~0 R& |2 X- L1 ~8 N! Y! d- /* USER CODE BEGIN 3 */
& H7 G& S% p% w1 R1 d+ }/ I* z - /* Infinite loop */% D4 ^7 o3 a) z& Y
- while (1)
2 U. ~( K- `7 y6 e$ c - {
4 ]9 L# N" `+ Z- l - $ h3 v8 v( c0 v. [0 k' `" \4 D
- }. E6 g$ c; C& F G, r
- /* USER CODE END 3 */
- {& L5 s1 s: V% L - # k/ ]: l0 V' x
- }
复制代码
5 h. N& T) r6 V$ w7 }mian函数 C代码的入口 初始化一些硬件后 % v+ \6 n$ z5 G/ Z+ A
osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
4 D: z" n, A+ ^3 l5 T8 Q+ ]定义了一个 线程 USER_Thread 然后启动OS $ h1 R' k9 x+ o3 K6 S
注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数* p0 r# ]8 _1 w% b) Q: s [
+ W) z( \' J, K* h# d7 {
宏的第二项参数 StartThread 为线程 入口函数地址。, y& W+ K3 _9 _: N% W' s3 S
2 E" f: }9 o1 U% h6 f2 u至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread 3 ^0 i) G; ]# e% H' u" ~% T; W
: y" A+ }4 r+ \8 r& ^# }# w
修改 StartThrea函数 如下* t6 z* y: J5 j7 U
2 V: G& K) j; b# U" \- ( ^' [$ t+ O5 R. ?0 E" l. T5 ]$ G
- /* USER CODE BEGIN 4 */( b! Q9 z) n) \7 b
- void Nucleo_072_Led(const void *par);
+ P0 i( Z/ a. }1 e* s% T - /* USER CODE END 4 */. K( m9 w6 z8 U* c: p2 _1 V0 b
% ?; ?; |" T4 h! a6 T6 y0 M- static void StartThread(void const * argument)
9 s! F8 p8 J" O) | - {1 L) P# ]' U }% L: L' L
; n* b9 c# |2 m. i q I- /* USER CODE BEGIN 5 */: T" I9 U( n# v8 D, {# {
- osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);( _$ i" B) [. I. b1 L
- osThreadCreate (osThread(LED_Thread), NULL);
5 X8 M: c1 v0 i/ B - /* Infinite loop */# G: V; G# T# w! S- j1 ^
- for(;;)
6 H, k! w o7 @6 W* j) g - {, Z L/ b7 |! c$ s
- osDelay(1);
e/ k0 t% T2 h - }5 T) j! A1 l, @) C
# Y& s5 v8 k" Q, w( M, d- /* USER CODE END 5 */ + C/ ], e9 z+ g# E, A1 b+ k
' J) z9 D$ x- s/ L) q3 O- }
复制代码 8 P/ {, {) j- N
添加一个 LED 函数
4 h& |& U/ K' {: _5 `, A- void Nucleo_072_Led(const void *par)2 X" T' f! v; G% H6 o: e/ i
- {
9 S- M& ?& L" v0 f& ~" i; {+ T - GPIO_InitTypeDef GPIO_InitStruct;
+ n. T+ H: g6 M, I5 Q) D' b - __GPIOA_CLK_ENABLE();
: V7 B5 z( C$ _5 Q# c6 v - 2 V$ R0 i6 A1 U# M5 H4 }
- GPIO_InitStruct.Pin = GPIO_PIN_5;
8 R! B& \) l* i6 r - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
' r0 n0 Y( n; ?5 s+ G4 A* g - GPIO_InitStruct.Pull = GPIO_PULLUP;
. y- P& g9 a1 j9 g# a! L7 C! K - GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
7 `( i0 z% l% M1 i4 x' H, p: k
9 G. L0 ?+ ^3 g- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);9 S' R5 \( O6 X) h
- ' a# m: e* t' e: p8 _( N
- for(;;)
; [* C% O- D+ S# N" I - {0 T/ {) Q" [# I$ w0 a0 I5 Q
- GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁
& M: Z& _2 @4 ^3 n1 q) x( f4 V - osDelay(500);2 y# j/ J2 S% |# V
- }4 x! R8 T% G) ~0 [7 t# U
- 8 ?3 Z/ _6 O, ^7 x U8 F
- }
复制代码 $ w2 O! ^# ]7 b8 r# E1 _5 \% c
到这里可以编译下载到板子上运行 观察现象了5 q# Z: e6 ]: R8 w, V
3 k$ L ~, I6 n. ], L$ n! q$ Z
下面创建 RXT 的工程 新建一个工程 0 v% N7 y9 i/ w3 f7 i, B
; l" e4 `# _) E$ V0 Y
勾选 如下选项
- b6 X+ l* J* [# k
7 s- I: n0 u5 i- S
$ F/ B0 w" r: W
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 . s* m2 X) S$ c( p6 j
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库
/ X, ^8 B$ O' @( e! y3 d7 S) c: F- o* b6 Q( D
把原来的 main.c 复制一份更名为 rtx_main.c9 Y0 r1 e) {) C8 b' o, V
) p' l t g* j$ u0 V" ^
( q1 O& ?; a% K- R" V' E/ C" S; p+ a1 `
, m; R) i7 T7 V- [
文件添加完毕
$ ?2 C" p4 [/ k! Q
) [2 N* h1 C, l9 e+ `% o( B& @0 o4 W接下来定义 头文件目录和 系统宏 ; b4 d7 V8 w3 s9 H" W! v( ~
! U* M# O0 k( C3 f# \
, S; c: e5 m6 E- y9 Y
1 ]3 v5 f0 q8 b0 V; U9 v# Z, j, `
修改 rtx_main.c 下面两处需要修改
/ _1 s: T+ q% P4 t" P g* L
( |" Z5 p6 H: a( ~- {! @0 D3 p" ?# b' C7 q+ p0 g. e
- osThreadDef( StartThread, osPriorityNormal, 0, 0);
) B9 w% H9 J* T+ h* u - osThreadCreate (osThread(StartThread), NULL);
2 z6 T# |" w: E2 s. k - }
# t7 v4 L4 Q# [* {$ M. i, W
- o& {; P# i, g/ g6 D- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);
7 ]( M- J9 p% F( P$ i9 X2 V - osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改: h* ]% j: Z4 R/ g1 P, Y' }* q
, d8 Z# I) A$ L& ^0 i
修改 stm32f0xx_hal_conf.h
4 {9 P3 Q/ g6 e( K' h, J4 b0 J# q# v添加 图示内容 不出意外 下面 将可以直接编译了!!6 j/ q0 @" ^! r8 C
- W) `* v& M8 Q3 a4 i! m4 d
4 ]' w! Y) d: |( ?8 j
写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了
* n) u3 R2 _, L) m& l/ ?. I; E7 A2 C* W" ?+ k. b
下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量 @: g# l4 R' d
osSemaphoreCreate参数 count 直接
# Z4 G2 S% G& b3 _# b9 s0 c4 Q M% J8 b& A
传递给 xSemaphoreCreateCounting的两个形参
' q; i0 E- m9 F
; X* h2 C+ N, i5 D) j9 u! l+ H
原型 1 F$ o4 \. p; V# R+ Z
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )8 U1 N$ Z' @1 D$ W& R
! I+ u- c# j! d2 R$ x* v* P$ S该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值 b& n& v: y) s- L' v
# X8 A' E p* @2 p% `# y: E
基于串口使用信号量 那么需要如下要求
# D% ^4 ~& d9 e假设 usart_sem 为串口使用的信号量8 w9 p* T5 w3 s0 {0 R- M
每收到一个数据 usart_sem ++ 缓冲 1024字节 ) `, u! y4 y& n3 h0 C M% t, t7 X
需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据( t: h% m" g) A2 ?3 W9 o
$ L+ H* k9 T* i( C& c
如此我们知道 这个信号量的 最大值应为1024 2 E2 Y5 u" Z/ M3 }- V* @
可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
/ J3 w4 I" ^% L) H' e8 m2 a2 o4 a( v' A/ C2 ?/ y
会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次; w: S2 G% h! U) c# I
显然这不是我们想要
9 N7 J' R1 ~0 E
R9 F- J7 V6 [ H5 ?通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
8 e9 @& t4 U0 R0 d! {+ ^9 d7 j6 z. a7 J- ]3 f$ u1 W
不懂 这样设计意义何在?/ q! f5 q/ Y" j' w
4 {8 C. p( J0 { D9 e
0 C7 [! [6 @9 V2 S4 B
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。2 s9 `2 k3 b- w; U0 J4 b0 R
仔细看了一下内部的实现,确实我说错了。8 q/ u. F7 L1 x" q# v" R2 i; v. L; V
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 )
return xSemaphoreCreateCounting(count, 0);
#else
return NULL;9 A7 d( _: H# H5 F. I" z8 a
#endif
}7 {8 b8 Y1 }6 a" F6 i
: t. H W X- |4 W( r6 d
( @# o5 J" L7 W6 R6 l8 w
, R4 S( m$ f; _* `/ K. b5 @6 l" ]
6 o( l" E n) J' g5 O* q4 W" r0 d
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。# d( M2 l- Y" M2 B, R
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用