以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)
! V: T: ^ A9 k: f( V
. p7 l$ N3 K4 o& @; p1 R/ `# D. G" ~! b! W) ]
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发6 B9 Q6 ~5 t) u P7 o- }5 k: |9 r" z
% R/ x0 I E+ V, F. ?2 h需要工具 MDK5 自行下载:! m+ _) J( d6 [
STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984# \3 u/ v' ~( D+ W) K6 u
STM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669 ]; s! [1 \, q( |/ i d$ Y$ n' E
1 L+ k3 h$ X, h+ }# w
3 C* l/ S! t+ D/ [7 d安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装
- a/ s4 g4 {5 R% _- ? y$ ^# V& n) e" v
$ F/ `0 G: k. {. G, H
' z3 w3 r( m5 v6 v
, W4 {/ m& e/ Y% r$ s! g
安装之后 新建一个工程 选择STM32F072RBT6/ R3 j$ _3 T; m2 T* M
0 p6 @, z( ]5 a; K/ W L) ~. s
PINOUT 勾选 FREERTOS和 USART
) F) W( Y/ m% B3 V
2 L0 U; l R2 v3 o4 Q- O7 ~) k* [因为我们调试可能需要使用6 i6 u* W2 q, h: z4 \7 H; x
* K. C6 ^3 F. s' n4 s2 i点击软件上方 齿轮键生成 keil 工程 至此1 S; s( `2 c( U
3 z7 O/ l# R* J7 v% k kMX基于 HAL的库 生成完毕
. i8 H! T3 ?; e
) ~- k7 p1 r+ d4 D使用MDK 打开工程
' p5 n; ?7 g& R8 _, o6 C从上到下 的组依次为 OS 的C文件1 \) ^5 D3 q2 e: ?7 O
.s 启动文件; }! W; t/ i* A# p, U7 @5 p
用户文件* G" p5 ?, h: W
HAL库文件 t, l% {# J3 E7 q
CMSIS中间件文件5 E" W5 b* j% r; Q$ ~
" F% m9 w" Z& T* f其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码" I5 c3 c) H4 A( S! V8 H
8 [5 B* T) S2 P2 I1 e
# S2 m, }6 V" @, U* m# X; ?5 g
- h0 W. O7 n k1 |! h" |0 y) }1 K/ [. L
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制9 I4 {9 ]% G* L' w
x {0 C( c' X0 u1 k
先看看 main.c 的 64 到 105行
8 a2 n* O+ u# F' U+ i4 t* j- ' u. J, i+ Q. s; \5 x% @
- int main(void)
/ _3 P, }+ M) }; s! s) U+ R# n6 Q( q - {' E8 V9 w3 W$ t* L1 ^8 |
' y( Y+ Z* f; X: M- /* USER CODE BEGIN 1 */
4 E9 Z6 G. k: f" ]. b) F' V
" l) B0 f6 y; D# p- /* USER CODE END 1 */
' w- ~4 J+ g4 x
2 j+ ^% P8 U/ U# s- /* MCU Configuration----------------------------------------------------------*/
! Q; w& W4 n: R2 c* B, j7 V; _0 q
1 ~& B( _% O' Y7 d7 C" ^/ E- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */6 p! a5 ^8 v5 _0 p4 Y; W
- HAL_Init();
% J+ p- g1 d" A1 F - / c) k" |" q" \. k7 m
- /* Configure the system clock *// z0 I+ O7 C/ p+ J1 H
- SystemClock_Config();# i; R0 `6 s. A5 t" _- `7 K
- " k; P3 }$ R. c3 J. s3 k
- /* Initialize all configured peripherals */
. h- z8 h( V6 W6 O: ?- \ - MX_GPIO_Init();9 x, k% G0 g0 p, u8 D: p+ N% j
- MX_USART2_UART_Init();- s1 K L$ B* _+ Z
- " D% ]; R* y8 m, U- j4 {9 M3 n7 N" E
- /* USER CODE BEGIN 2 */# _* T+ J- h- W. I$ Z1 o. g
) {5 q, s: g! _+ g- /* USER CODE END 2 */
7 T# [ _ c, {) L0 o7 m2 k - : L9 @* t3 @# ]3 c
- /* Init code generated for FreeRTOS */
+ @; U H+ _# r) h# z9 F; p - /* Create Start thread */& e8 S6 G! E% @% \$ Z
- osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
; B+ @0 e- x. u" `% o& \ - osThreadCreate (osThread(USER_Thread), NULL);: `' d J6 j3 A: O
- 7 M! Y* F! o1 n3 Q; Y8 q: D( `
- /* Start scheduler */6 [0 B: g& d: C: n6 l
- osKernelStart(NULL, NULL);
& N2 Y& D$ A0 K* t' R. F- R2 o: a - / g: Q+ r- c5 J. S* m. n2 C; [
- /* We should never get here as control is now taken by the scheduler */
9 p4 u- H) D' P5 R9 P/ K - " X! ^* p( I! T# C, c
- /* USER CODE BEGIN 3 */
3 Z8 @9 v* _ q% }4 b( p4 l5 W - /* Infinite loop */
8 P X: X+ v' [, U1 C0 Z - while (1)
8 X1 }6 k' K# L) f - {
- D. P/ _* t. C2 ~& Q# V8 o - 6 O5 K; f' g: y2 |( M- x7 @0 q
- }) h4 S# g! Y( R/ j: k
- /* USER CODE END 3 */
& h, A$ A2 s, |: X
5 I- ^: {, f& }. I( X1 `- }
复制代码 % U! ~2 J. m; L+ _8 Z) e3 v
mian函数 C代码的入口 初始化一些硬件后
$ `- Z, D& q1 U osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
1 C2 M1 S+ I* w x/ \定义了一个 线程 USER_Thread 然后启动OS
& A1 s, b. R. D0 G注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数' G& o* l) B. J9 W
( x9 { x. b R宏的第二项参数 StartThread 为线程 入口函数地址。. P0 B2 W8 ]: Q6 i6 @' `7 ~
- ~- c- Y* u/ i4 n# D
至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread * L5 k! _) }- d X. ^( L
' @* e# S7 w: c# ^: R7 @, \2 Q修改 StartThrea函数 如下
0 o# s, Q9 S$ w& D$ {# r0 v- I$ x9 c8 g# [( D; J2 \' ?9 A
- : A" H; {: F; Z6 N6 o# |( l
- /* USER CODE BEGIN 4 */
A0 c5 Q; x: m u - void Nucleo_072_Led(const void *par);
- G) J0 w/ S8 y8 @- G, s - /* USER CODE END 4 */& S* Q, a7 ^; Y7 a0 |- R) F
$ y {. x; R5 U9 E4 j4 m5 n& ]- static void StartThread(void const * argument)6 w- }; Q! e# {
- {4 I$ h7 S8 r# V. P
- ' N$ C+ F+ [5 v( e4 j& p* \
- /* USER CODE BEGIN 5 */
8 d' \: d3 K4 e - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);: V6 d3 K3 c& u
- osThreadCreate (osThread(LED_Thread), NULL);
( ?3 [4 X/ ^ ]0 C( f: `4 [' N5 o, B - /* Infinite loop */
2 }9 |) n2 b* ]4 h - for(;;)' `' E& o7 t1 J% I- a0 x
- {+ p J, q# K: Z7 ~
- osDelay(1);9 h! u1 e6 `7 [2 L/ `; l
- }
( o$ ~1 m9 J5 G
/ e( h, ~; v! _9 \6 u# U- /* USER CODE END 5 */
; l3 I9 b, i" E% X" z
- j$ i8 c9 K9 v! r) Q% a. {# u- }
复制代码 $ \. k- j5 Z, O" W$ ^/ [" R
添加一个 LED 函数9 F% `4 d8 {9 t3 \4 I
- void Nucleo_072_Led(const void *par)
6 [& y! t7 e, L& i/ {" ~2 j7 c/ w5 {- Y - {
: s( y6 x; u ]9 z/ k' Y - GPIO_InitTypeDef GPIO_InitStruct;
! p8 z8 O8 c% t1 ?+ t - __GPIOA_CLK_ENABLE();
- O; V; g! g% \ -
" y2 r2 A! z7 F/ O - GPIO_InitStruct.Pin = GPIO_PIN_5;5 B8 b* ]" K: \% {6 ~
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
; ?+ H8 k; ?) k& ?$ Z - GPIO_InitStruct.Pull = GPIO_PULLUP;
* I0 g1 ^. s z6 o* ` - GPIO_InitStruct.Speed = GPIO_SPEED_LOW;6 v9 V4 I( q1 P+ l$ `% _ c
- & O& o% X$ E2 c1 h* P. u
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);, s8 z, ~& |8 |( x2 l3 w2 M2 n& n
-
3 q c! R1 S0 M7 p - for(;;), `# T3 N" E4 K7 u. `, z+ w' r
- {" ^! v+ F0 E& t5 D9 o
- GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁: s& ]0 e8 o' Q4 J u8 p
- osDelay(500);1 O% ^" T- a( l# j* s8 m% m5 \
- } s5 v/ }* v+ F3 n! P; r# k
& }) i9 O8 o. p7 }- }
复制代码 ! \5 j4 u7 Q. {- E" M
到这里可以编译下载到板子上运行 观察现象了
3 I# | e5 ?3 m% e2 j$ k; Q8 \$ V4 R# _! {
下面创建 RXT 的工程 新建一个工程 : V9 l" ~- s6 k5 g$ T+ Y
& X* }8 N& L; x. M* m( L1 m3 t( \勾选 如下选项 # b2 m5 o+ V/ I2 a- q
& b6 N% O- X9 e9 U9 b% D' f8 i+ ~& c
+ Y# Z3 ^* Y' h9 ~2 @红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过
, g( N, D4 Q i% f* g- I+ h" HF4 的工程没有包含 HAL 接下来 需要自行添加HAL 库
$ h$ X ^7 ~/ `2 W8 h1 R9 G% f0 P" v4 A" {3 W G: O
把原来的 main.c 复制一份更名为 rtx_main.c* n) @0 o4 _& j" O
7 C$ k4 Y3 H5 ]) M: Y) w" T8 g
5 F1 D. u0 n1 t# v7 x5 i
4 ^) F. ?# a" g, A3 \
文件添加完毕 + A- y$ ]! T4 L9 o, l& J2 O
# W; x! ~' u5 D7 x5 T$ }接下来定义 头文件目录和 系统宏
0 U- h% z2 i! X$ r, D
9 S* V2 k- _, b) D8 f5 R
# |8 w$ I+ H- [* W; s5 h# l/ i7 ]# c; V( \
修改 rtx_main.c 下面两处需要修改
$ E+ }9 r3 h+ Q% x$ p; }2 y& f& Y( e, n ]; Q# x5 r
- {
/ ]3 i# G2 c1 ?/ S0 S: I+ L* Z7 A, D - osThreadDef( StartThread, osPriorityNormal, 0, 0);1 F& S' E& V7 _/ w7 Q9 b4 k* L
- osThreadCreate (osThread(StartThread), NULL);, v, R A8 |$ u) [% B' q
- }
, G5 I' x& `. [( @ T4 Q - 4 A* j( b! l( m
- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);0 [/ G2 |2 ?/ s) [) e
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改; t0 v% L, f. I. U5 d" G+ q' O
7 S( U- R' c/ [: L' H
修改 stm32f0xx_hal_conf.h
! Y7 a1 e2 q6 z2 o$ _, o# b" v4 ~0 R添加 图示内容 不出意外 下面 将可以直接编译了!!: H& b$ n: L9 ~, v! k# A( ?; ]
+ ~! J C* p4 T: }% [0 y% H
7 u7 r& z/ ]7 N* K# [$ `( Q: z写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了2 W+ Q0 Q L6 t! P
; b2 \: @# k& V8 w, t T/ m
下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量
$ W* d2 F+ t- U2 v% K+ G4 eosSemaphoreCreate参数 count 直接
0 O% T! Q Y- g0 ^6 Y# z2 [
2 w" R+ i' [9 I2 U/ k0 \$ b- O传递给 xSemaphoreCreateCounting的两个形参
2 s7 U0 A) b5 q5 r6 y6 G8 w% Q
P* ]+ Y+ G! a
原型 # v! k% T+ M& W: E$ ~
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )+ ]! `, y4 e6 L3 h/ ^
}5 J6 ]/ u$ c& w4 a; `
该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值
* ?- ]0 m% A6 G+ E1 f- a4 p$ c- v3 g! x4 O/ l
基于串口使用信号量 那么需要如下要求$ m/ b, |# p7 B& ^
假设 usart_sem 为串口使用的信号量% J3 `+ Z" n+ @3 E6 L
每收到一个数据 usart_sem ++ 缓冲 1024字节
% C* Y: j& J/ j9 e$ o# b' {需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据0 k+ }. `% e& n0 X9 R. f" d
) j0 n; ]8 G# Q7 M9 L
如此我们知道 这个信号量的 最大值应为1024 ; D# W$ Q9 E2 h2 k
可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
# L$ [2 g5 h+ E/ g) J7 E* X2 ~3 T2 i" f* p2 g* k! R
会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次
3 f f2 d+ X# i/ ~5 }& \" T) z显然这不是我们想要
5 t8 p* b. r7 p S9 i2 V. L0 K, s* `6 z* D
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
# G' u- r9 m( ~7 |. z
! [/ G2 w+ b9 V+ B4 Q7 D. n不懂 这样设计意义何在?
$ V5 K$ I( ?$ H2 d3 Z# K# |
' X) Z0 }" S" |. ?
: y1 K4 I8 H. i1 W8 o: ]5 ]% Q9 ]
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。" D# U3 r% N) `/ n
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 )
return xSemaphoreCreateCounting(count, 0);
#else
return NULL;0 S& {/ K* K2 V& t$ U+ Q, ~
#endif; o- G o( z- E
}
, n u& ?6 Z% Z5 H- g( M5 d
/ ]9 r' e' H3 m$ i$ x7 p6 e
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是( O, i- J9 Z8 g$ ^
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。9 e4 F' F, v% N& K$ w
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用