以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)9 a, J" N6 G% E5 y( W# w
3 l, d+ D' t+ [ |5 X1 e& }8 D" X6 ]; T( e+ t) [
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发
# N2 D% W( h% ?+ e7 j+ c4 g% P1 x( s5 {* l! _# {9 Z% T
需要工具 MDK5 自行下载:
" @6 @/ c- `" l N r" WSTM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
! }# ]( O) Y. G- [5 V$ nSTM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
1 t( n/ `3 s# y! E. A9 j t6 {4 f1 q5 X. \' |
8 P7 a0 ?+ |' S3 ?4 ^- L4 z
安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装
- y2 P) n! u. H9 P. ?5 a1 q' S1 M1 e; o
. g. }, _0 S# P# i1 D0 J ^6 j4 ]% b! [. b
k/ S! {4 O5 }
8 F F( H( C* X e9 C3 o安装之后 新建一个工程 选择STM32F072RBT6
: D) i' o+ @/ ^- H/ a) m r' c# J7 w
PINOUT 勾选 FREERTOS和 USART ' M! I) {9 U& q: o0 P
R ^4 L0 \# l4 e7 b* {9 Z0 p5 M
因为我们调试可能需要使用$ g& E8 [4 j' ^+ H, l# b9 N6 i+ k
+ E# n; R) x( v: R/ D w点击软件上方 齿轮键生成 keil 工程 至此
+ T2 ~# C$ O9 A% S6 A
# b- [8 J$ O T) WMX基于 HAL的库 生成完毕
$ M' @: Q6 N( @ G, A* J! m2 B: W$ A# r0 G0 f: s
使用MDK 打开工程1 k! i( Z& h' ?6 A- ?
从上到下 的组依次为 OS 的C文件 J7 A: D/ S2 W; k8 d1 g2 q
.s 启动文件
& C" l! G5 A5 `/ c2 w. [2 u用户文件
" g; A# M! v7 KHAL库文件
" c6 m+ h# n2 l9 `4 t2 M7 hCMSIS中间件文件! l; |8 o ?* F Q# Q
) s3 i4 W: n" ?/ @! P其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码, I$ w3 D2 E1 e* _
! P1 Y n( F$ U) i' W3 s* H: N
6 `6 K) W3 X3 ^9 `( m4 p% Q
) @+ K. B8 Z* D3 B/ ] a( ^6 I1 m0 d# k# Y) U
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制
9 I! w8 Y9 ?+ ?
5 R3 Q2 H% K* v先看看 main.c 的 64 到 105行 . T$ C# O9 a, _* b( O
8 x v( r7 c! O W. F! g( u3 d- int main(void)* P+ F4 h: ]/ S7 b0 Z, {- }/ [1 s
- {8 l) @8 H: i1 t$ _' {, S
- & D$ F8 a2 ^) ?% H
- /* USER CODE BEGIN 1 */4 Z* E$ b _9 O* Q8 G
9 s# M8 K* H5 x0 C5 D- /* USER CODE END 1 */- V* z+ W: r# T. E, K( ^
4 w8 n* Z0 y6 l. K2 m$ d5 B$ E. s- /* MCU Configuration----------------------------------------------------------*/- _" L2 Y* ?- J2 C0 n
% @- o7 a) H6 j- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
4 k$ B8 v( j9 n% v$ B& Q - HAL_Init();
( V& V/ m- E7 v0 ~ - 5 y1 A# }) S2 W# W- K$ P0 H
- /* Configure the system clock */' T! ^+ e3 o( [
- SystemClock_Config();/ a% [" u) f5 F a5 w ^
3 j4 d9 W$ [" v% r1 Z- /* Initialize all configured peripherals */
/ A% j# ]% i1 O" R - MX_GPIO_Init();
) p% m6 a V9 w. }2 c. R% x - MX_USART2_UART_Init();
7 |* P5 I) J' g! K - , s7 e D. _' d g3 q7 {) c5 L' Y
- /* USER CODE BEGIN 2 */' U4 ] [; E4 f, o
- * G5 w$ J+ k$ }) I
- /* USER CODE END 2 */* x" A% T7 \' Z H2 v1 L9 h+ @. |
- N1 P% {, \3 S- C+ s! s5 x
- /* Init code generated for FreeRTOS */
( X d5 T8 a& z - /* Create Start thread */0 c0 N4 K) \' x# w
- osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
& w- F; x5 f4 K, F; I8 ^6 F. D - osThreadCreate (osThread(USER_Thread), NULL);4 n% O2 h" S$ b- m' h/ T
$ K1 s: \+ u& m: l" u, T! }) K- /* Start scheduler */
# ~/ i7 A# } n, A) f2 X - osKernelStart(NULL, NULL);* u3 y# e+ ?9 C: a! B
}) A( G! D; D0 T( _4 e- /* We should never get here as control is now taken by the scheduler */" z+ X8 t5 `/ F
4 i8 t7 z x; w( L0 G- /* USER CODE BEGIN 3 */! k+ j0 Y$ i. c: U8 z
- /* Infinite loop */6 u) ], m+ c1 M" h' T$ Z, ~
- while (1)7 m E% C) R3 Z; i9 g9 ^9 B9 t
- {
1 A& M5 x; U; `9 M& F" Z
6 p3 T1 g1 m4 q2 j- }& \. B' E' b$ y! n4 J3 V$ ?
- /* USER CODE END 3 */0 ?8 X5 i* b S) }
- / i4 ?3 o: r, m+ w3 j1 ^" g6 i
- }
复制代码 % }: K+ X$ E: p0 F
mian函数 C代码的入口 初始化一些硬件后 % F5 ]. j# f0 j5 S# V; d
osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL); 6 J, Z1 N1 d# H' s/ z
定义了一个 线程 USER_Thread 然后启动OS ; N/ I( b( k) I' x; ? V7 J
注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数# Q: F* s2 f$ R* J$ I
- ]5 b& \' h6 l7 x- G
宏的第二项参数 StartThread 为线程 入口函数地址。: u) I" E( b; V4 v
$ H; w1 {" v7 {
至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread 0 A s" G3 B) l5 r7 {
2 n: e) |& m A. m7 B修改 StartThrea函数 如下
1 ~4 k3 {% }, o3 w$ j, U+ W7 i! N( x8 [! H4 `
+ {( d+ \: Y q; s/ b- /* USER CODE BEGIN 4 */2 P5 R$ l8 D4 |) J- Q
- void Nucleo_072_Led(const void *par);
8 T* O. X( j- ]+ B, W7 u3 D6 }. s - /* USER CODE END 4 */
* x/ y) ^" O6 u$ p6 J4 l
2 L" R- H4 ~+ L% r- static void StartThread(void const * argument)4 `9 X6 M- ^5 h1 h6 U6 c. U
- {; n4 X% F# h% G0 H+ h
; r) O, \$ v8 W3 I% u# h- /* USER CODE BEGIN 5 */
* b A0 v, q+ h5 O - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
/ }- `: v' J0 w( g/ O' O) W - osThreadCreate (osThread(LED_Thread), NULL);9 j8 L& D$ @ u
- /* Infinite loop */. n) E+ z3 f6 T" b! U0 z" D+ M
- for(;;)- E$ @& S' a# W9 d* C7 e
- {
! P1 V5 R! G r! O( g" ]* J8 n& C/ ^1 g - osDelay(1);
, M1 d, O4 }3 u( A% u- K - }
t# y* y1 o, H1 [ - 0 x: ~& S- R5 g7 Y, V5 S( H7 U
- /* USER CODE END 5 */
, Y3 y, ?2 t6 W3 n% p) i - " u/ x2 b2 K& a7 X% u& p4 {- s! H
- }
复制代码
. t. d6 [' j3 m1 r$ V3 y& d' g/ ~# p添加一个 LED 函数# N& y; N* x2 R' I. N3 h6 u
- void Nucleo_072_Led(const void *par)
8 \& L2 I' {! G$ A - {! z! l5 ~. L: c, j( d5 ?9 }
- GPIO_InitTypeDef GPIO_InitStruct;! C1 |, B0 k. M+ Z) M/ y1 J5 A
- __GPIOA_CLK_ENABLE();
3 N/ x' x, ]7 |: J p - % U+ n! f' z5 @0 c+ @
- GPIO_InitStruct.Pin = GPIO_PIN_5;4 o$ B& S/ O7 [ i% H
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
7 M F8 `+ _; q2 l - GPIO_InitStruct.Pull = GPIO_PULLUP;
1 l2 r+ Y' U. l. Y2 r# Q - GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
$ G* U1 Y1 u8 a4 H0 i; R+ @4 U: y! E
- D! z, m( h" [4 W8 t+ ^- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);+ h6 A3 o1 V. H5 H9 L" O! Z
- @2 ]" `' |3 M; B6 e
- for(;;)! a& G9 i& H5 j
- {
0 E7 d. e; M, J/ {" z9 @" s# T2 } - GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁7 ]' E3 ^. C; |0 R7 j
- osDelay(500);* T( T g; X4 S( ]5 Y' e) {
- }. T! ]1 E/ S. \/ x: i
( p3 |( {5 {! ~- }
复制代码 5 p. i! Z* }' P- L/ a" V/ ~; m
到这里可以编译下载到板子上运行 观察现象了
" h8 t K# u9 R+ r6 E: P
, \% ?- Y+ N6 j下面创建 RXT 的工程 新建一个工程
w# z6 H1 g: a! K9 q" i+ j, y) R0 T. H$ s
勾选 如下选项 ! T1 @6 F% d1 x1 a0 P p; |6 Q3 X. A
* P* ~. d" A/ ?5 q. F3 s
6 a/ ]5 v% d; N& X红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 9 L# C( ^5 K8 a, |5 D: I' t% |
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库 1 M: ]2 y, b; S: s. m, k" c/ i
1 T: P& r' p. @$ {4 L2 o把原来的 main.c 复制一份更名为 rtx_main.c
; @8 h* B3 E$ T- w5 h6 q
3 Q- w6 F; A# B+ D' P! c7 G" Y
# a& n( f, s- N! X5 Q" X, d
9 T# r4 Z; U3 |) O/ K7 k
文件添加完毕
; x$ Q6 a( }+ H+ |1 d0 N' l- V0 ?5 a1 t3 `$ R$ r1 m( q- r
接下来定义 头文件目录和 系统宏 A7 ]) [1 {" I9 m
; f4 }" U+ t% L) J! |/ M* t/ Z/ {
5 ?9 A$ u1 _' E) P& n' B" b
, |6 l! k! H8 f) \- W h$ I修改 rtx_main.c 下面两处需要修改
5 \3 i# B3 [- T5 @; z; L" P1 [$ w4 t Y* I8 Z2 X g( a: \ p6 N
- {9 r: u6 v6 x, v- G- E; ~
- osThreadDef( StartThread, osPriorityNormal, 0, 0);4 n# a2 @" t7 k4 y+ J# T
- osThreadCreate (osThread(StartThread), NULL);
( Z% d9 Z1 \5 @8 E; E - }
' `# C- Y( s9 n6 ~ @7 n/ c+ @ - 9 P# P) {/ f$ A" S3 q. A
- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);
' {3 J+ o6 N/ Z. g% y - osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改
" Y: C0 i7 `( h% A* Z4 q4 G: o7 y/ X/ C0 u
修改 stm32f0xx_hal_conf.h
* `; l/ N9 a% @8 [+ M添加 图示内容 不出意外 下面 将可以直接编译了!!- ` m4 [. g% u- c# g
! _. X% K" H' ~' }+ F
4 t% n8 k2 C& w% u7 ?
写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了; n# \ s% w3 r- E" F; \/ N+ g
/ o& P- J% t- Z$ N* \; ?8 ^下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量
' E+ N; Z% s) G, @( |$ hosSemaphoreCreate参数 count 直接
) c0 c6 u5 h3 k: Y% [1 A( @) I" s) o9 @9 g1 M
传递给 xSemaphoreCreateCounting的两个形参 , _: ] G; N* G' A" c( \* U: A# w1 ]
- m! [; q7 k5 }6 S8 g
原型 7 X1 l4 {6 s8 w! b7 i) Q. n
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
6 E* M& t9 D. i$ z H1 q9 u
5 J0 I* s6 [0 Q! q$ }5 Q该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值2 L* o8 h9 w" ]
' G3 u5 P9 C/ _1 ^- X9 M# ~
基于串口使用信号量 那么需要如下要求
% M% G% j$ u* R) |1 W; K" s7 l假设 usart_sem 为串口使用的信号量* w x6 C5 G/ I# U. V% s
每收到一个数据 usart_sem ++ 缓冲 1024字节 7 F% }6 E5 ]2 s( [/ l5 X% `2 _& F F
需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据
" ^* i& ?. U' k: b4 d
: z9 E% F) C4 t& G+ e$ h如此我们知道 这个信号量的 最大值应为1024
/ @. X' x8 I# b+ k, H可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024); ( R R4 T& g1 E3 I5 h8 f- P( p m% o
6 M @$ l) v& ^0 A
会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次/ i: j6 U. D, \- m7 Y
显然这不是我们想要
& y: o2 y; r8 b t8 m& j9 e8 c! N! C/ @' ~9 O; p4 R# i: a# b
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
$ @, Q) a+ Y/ @6 O' w4 V
1 E# D1 _! ^* x- m: M4 a; I不懂 这样设计意义何在?$ d) Q5 K& D% r8 w
- Y" E6 L3 G' M& x8 t8 w8 m/ K1 f, u2 s
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 ) 9 E0 ^' R$ ^' o6 T; I8 \9 _
return xSemaphoreCreateCounting(count, 0);" k7 J* g$ c+ [
#else) [( c7 W/ |) b ?6 B9 R/ M& Y
return NULL;4 y9 R) V& ^+ f+ m- h: V
#endif
}
* G9 P/ F- y8 x
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。, k: z1 I( w/ U3 s, P
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用