以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)/ x) f' G! v8 ?+ \, @8 m
# g2 O) w1 `' _( H H. S8 P, a
5 A$ _( O" y' i
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发
4 u7 X4 \* i! Z% J( k! |' O' c! h% Z$ K; K a# H+ u1 i& Q
需要工具 MDK5 自行下载:8 j" u! m2 U( @
STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
. [1 r( W/ t9 fSTM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
& ?% D9 v' W& _' P. R5 x1 E# {" n4 p" l$ s) y
! `1 ]; V! U' V! A/ X r. @. y) y
安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装9 l( y$ M I3 m: O7 J1 P
, d: R: c7 D( k: Q. K
+ ], s+ M. x! S
& C9 {0 N) Q) _. n0 Q6 K
7 w6 v- e- \2 n$ F$ a/ J. t, p安装之后 新建一个工程 选择STM32F072RBT6
7 ~. h7 e( [% v" ]
5 z$ q" u' l) L/ s7 [4 T% iPINOUT 勾选 FREERTOS和 USART
3 b) G; h, D+ y/ T1 [0 Z6 ^3 A
' Q9 E4 Z8 ^" R! B$ ~& G% ]9 a
因为我们调试可能需要使用
/ ]2 z4 j& D* l; F- F9 ~7 f% U, }/ H4 y9 b. {( F# c' V& `+ r
点击软件上方 齿轮键生成 keil 工程 至此& }& O- r3 O" R
% ?6 E1 j0 j' O0 |MX基于 HAL的库 生成完毕: M. K$ {4 X! [: T1 W
+ U) X1 B! P* f3 P$ Q$ }7 o使用MDK 打开工程2 X( p- G# {2 m& i9 c) v9 E
从上到下 的组依次为 OS 的C文件( n7 ? A( z* e0 P
.s 启动文件" z( U% w; Y; X6 |# ?( u& C
用户文件1 Q2 s- p8 q. A$ [, n
HAL库文件
1 v @. }; v3 pCMSIS中间件文件
3 j t" |7 v; q* E) |: i0 P4 U+ j' |
# A0 k$ m$ W p1 R" c, Y其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码- W6 V3 A4 b& x
3 V; l# I! O6 n0 x+ e! A
2 @" _1 C; j# e1 F- T% Y3 [1 s
6 l* X) y' M9 t- l1 ]" m: C3 k: o/ x& w6 {6 m$ I, X. [- V
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制8 V* C+ I; @& h9 O& m, E. [
6 c+ v# \& t! j
先看看 main.c 的 64 到 105行 4 X$ V( W& D$ h, T7 l5 I
5 J" z* b' t. C' u2 p/ l9 k- int main(void)
- T, ~/ J9 _ `. M& L3 S - {: E) k) ^, }1 Y. Z* q" [+ t! k* W
& r2 p! u: v) c2 n' t0 H1 B5 e- /* USER CODE BEGIN 1 */
& r! O3 v5 v8 S1 a - " s9 y3 S$ L7 n3 E/ Y
- /* USER CODE END 1 */ O! @' y0 o$ h3 H/ O% O
- ! _6 D e8 d$ @/ R, n
- /* MCU Configuration----------------------------------------------------------*/* F2 `3 N, B# J q, P: w9 t, }% Y
7 n: y; I1 T3 ?" j- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
8 Q5 W+ D4 ^# C6 B - HAL_Init();' n0 G7 ]8 U6 N; s0 M* ]8 I
- 4 p. C/ F F, W" D
- /* Configure the system clock */
, F) h, M( M3 Z! e! R - SystemClock_Config();
2 i5 A" Z$ [+ I4 L! z9 h - & B: n0 R$ Z* [7 I! u* W& l
- /* Initialize all configured peripherals */' ^1 z& t' G6 W! j5 D i
- MX_GPIO_Init();) _9 _" o! a! t
- MX_USART2_UART_Init();" m; R2 Q, q' H! q3 z2 }/ v
- + R# l& e" f5 a- Y
- /* USER CODE BEGIN 2 */2 h7 A2 z z& i8 Z
- * [) d; a3 @9 l% d6 k: ]* o
- /* USER CODE END 2 */
9 e0 ?' z4 R! Y( d$ G# e& p, F
2 ] l2 m- E5 b$ e) s. F3 y' {- /* Init code generated for FreeRTOS */) P4 I6 g2 k/ |, K7 S
- /* Create Start thread */! M) o% w7 U: d- M" d
- osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);. E/ [. k& |% C1 W+ N
- osThreadCreate (osThread(USER_Thread), NULL);" ]: p) W$ U: K* N1 T; b' t% z8 T
) ~" {0 O. H& k* F- /* Start scheduler */; e0 |: n: t/ p$ q
- osKernelStart(NULL, NULL);
+ k0 u2 a# C$ R! s# U7 P8 X8 t - " c# g8 M6 ?) k$ ]# c
- /* We should never get here as control is now taken by the scheduler */5 j! Y1 D2 |" U3 W
$ a' o; y6 ?1 B9 k2 Y- /* USER CODE BEGIN 3 */# ^: |& U& M- x, {+ ?
- /* Infinite loop */
1 [. Y1 b% i0 Y6 S4 ]3 ` - while (1)* G3 J5 N' s8 Q- B+ D
- {* ?5 U" A/ T& k! V1 G" I b( T7 x
: C* U- L9 F, N- }
+ V" w- o7 a- h. E - /* USER CODE END 3 */
# w* q) \+ k+ c% ^7 s; U ~ - 0 `$ R6 g7 K0 K9 U/ S M& I+ Z, a
- }
复制代码 - \# a' R; c- \+ c8 q& c: D
mian函数 C代码的入口 初始化一些硬件后
( x7 m9 m- _( S% }' }! j osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
% r' ^ C ?1 K4 @6 h: b定义了一个 线程 USER_Thread 然后启动OS
6 L3 H2 Z4 `% W" @% x注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数8 B( ?* m$ Y: N. Y0 ~2 b6 f
' G5 }% [* A" A9 V' r9 x宏的第二项参数 StartThread 为线程 入口函数地址。3 d4 v% Q. N( @
8 \5 \4 f% r( A! {8 f& t! G. y# \2 A至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread * e5 Y4 A# d9 }# p& v# r
1 z$ Q t; i. o! a" s- F修改 StartThrea函数 如下
2 @% P) p7 }, Q7 @, q1 \' ~% c9 M9 k2 _ F& y
- 5 {" m* I8 s K3 h
- /* USER CODE BEGIN 4 */
8 V3 B- E" o9 M" n6 m' i8 j - void Nucleo_072_Led(const void *par);
. u2 f( w( u6 N3 { - /* USER CODE END 4 */0 Z" f$ L3 ]% W& n- ~0 ]
$ G& Z' Z a! F3 I- static void StartThread(void const * argument)
3 G6 _0 V: |* }! g - {' n$ ~& v0 v3 k
- ! L% R9 R# {- J% ?0 w# e+ {" F
- /* USER CODE BEGIN 5 */
( C/ I3 L; G6 b" v$ @( c% W - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);$ M- }1 e% h$ K
- osThreadCreate (osThread(LED_Thread), NULL);/ ?: v, z/ Z! H; R C2 K; t
- /* Infinite loop */4 w& q9 n4 A! n" p
- for(;;)
" y! {7 O1 ]6 Z- K - {
. n# W! q! l* G7 U7 \1 O/ j) } - osDelay(1);
& _* i2 ]( v, o0 e5 h - }
! Q3 B( g- A4 k+ Y4 J7 V) \! f - : [! y/ W$ c- r5 H J. A
- /* USER CODE END 5 */
: w3 ?5 E2 \7 l& z; t \& i; v3 j7 b
) m+ X. j8 J& z$ X- }
复制代码
1 f H: {+ ~8 u( H! S添加一个 LED 函数5 S. X+ k' S- M/ a- z% S
- void Nucleo_072_Led(const void *par)
/ M f) s0 ?/ M) a) c q - {
( R1 x1 b6 g, U$ K% H - GPIO_InitTypeDef GPIO_InitStruct;0 R2 x, N* n, g1 z2 w
- __GPIOA_CLK_ENABLE();
4 J; \7 K0 x, g# ]* M -
2 o$ d" O- l+ s% R - GPIO_InitStruct.Pin = GPIO_PIN_5;2 u; N! M0 z9 h( K2 f
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;" e+ e, b0 O# p6 B
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- E/ o/ A/ U3 U C+ L, f2 j" a; \& K - GPIO_InitStruct.Speed = GPIO_SPEED_LOW;0 s% A' \7 }. K
& P! s7 \) P* D' l- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);( m( d# U& U4 \6 }2 X7 t
- _7 r/ t' p, J
- for(;;) U g" \& ~$ D) g& y* [ W7 v# U
- {3 H6 R. l7 t+ J# l8 z& r6 g% _
- GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁
* }* y1 z3 i, V Z' B3 \8 _ - osDelay(500);# O5 \0 K$ f9 a; {. s# S8 \
- }( K5 N# Y _. M8 X, ]% Y
, }, F9 c* i& z9 V( ]- }
复制代码 / J) {7 B( B: q) \) H
到这里可以编译下载到板子上运行 观察现象了& z$ J, N( L) k3 @$ |
! e5 G! B8 [2 c5 x# V
下面创建 RXT 的工程 新建一个工程
% w: m3 k* M: B+ j. U) I% d5 I2 F$ h
勾选 如下选项 ! Y8 ^) b% }0 V5 h
) K% B- Q* j. {( W, C- h9 c
5 ~+ @4 z) Z5 F& @' S
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过
7 |2 G3 m/ x5 e i* PF4 的工程没有包含 HAL 接下来 需要自行添加HAL 库
+ s [# ?5 b* U! I& D" S( {. s/ C& s) S; |
把原来的 main.c 复制一份更名为 rtx_main.c" q* y* v) ]0 O7 A( U: O
3 g* s$ N# |9 o: Q
& ^; X G& F7 W# @9 Z5 H, }5 b! x4 C( _: O; E' g1 s6 {
文件添加完毕
% S8 X' i9 Q- e0 [: N8 j. y0 y& \$ i' K
接下来定义 头文件目录和 系统宏
# E& ^( V( f, P( f2 ]" D) y, c" T' Y! q; l! d% E* r" Z/ f
! N- j1 i% R- F2 ]4 h0 l
$ B* H8 q1 T8 L, V0 U0 l, k修改 rtx_main.c 下面两处需要修改
3 t+ o( a/ H# _+ Z4 w/ d f6 t' R7 k
- {. ]% ]9 k) x" N2 P6 S
- osThreadDef( StartThread, osPriorityNormal, 0, 0);
s" N/ {3 d* j1 F* s4 o - osThreadCreate (osThread(StartThread), NULL);
1 |: j+ d8 z& w$ F3 D8 d - }0 s+ C5 d. V) D$ Z
: n& ?9 s+ y E1 ~) _- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);1 f1 x2 Y& \4 b- U$ q7 d, X* f( l2 {
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改
) e4 x- a0 Y" l+ p* M/ \' D3 b! ?$ m' N( y: M+ P
修改 stm32f0xx_hal_conf.h
. ]% u1 o8 n/ g1 r) X添加 图示内容 不出意外 下面 将可以直接编译了!!
3 m. h% @7 Y! ~& m6 u
+ q R' z- b5 E6 ]# v" G2 }' A5 ?1 Y6 P+ \
写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了8 m5 W$ H; `# C/ `1 Y ]
' `5 x9 h7 z, ~/ @. d. ?下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量 3 j( t3 M5 j3 {: E9 C: i! R8 {2 \! W
osSemaphoreCreate参数 count 直接
* f! [- V) P( c( E3 X$ Y3 j& D+ ^0 ~, e c7 t
传递给 xSemaphoreCreateCounting的两个形参
, ]1 t/ W; G, V5 u
( ~2 ?7 z* g; K7 {
原型 * [0 u& Q. J* R
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
$ l4 V r% V/ i0 U9 Q) ]
* X" a& j [" o1 ^1 I3 N该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值8 x5 K0 `8 ?! x" w) J
0 V4 f% b. p: |! u基于串口使用信号量 那么需要如下要求
3 W2 @" \% h$ k; k, b# @假设 usart_sem 为串口使用的信号量
2 p4 a5 A! q8 {, n2 M0 _3 c' U每收到一个数据 usart_sem ++ 缓冲 1024字节 3 A u$ w1 o5 n1 Z: T
需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据, ]$ H- J: w+ ]! a. ]5 F# v+ x$ Q
: Q- B1 k4 ~' }9 ^: v如此我们知道 这个信号量的 最大值应为1024 5 d( P5 L) o! r, o5 `
可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024); 8 y7 q! n5 u( z" Y
7 l3 Y& r" \! L
会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次
0 U+ A2 B' m% h, v3 `显然这不是我们想要 7 R" H; b* k# y
/ L* B3 F; T7 X& C' W
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1 4 z0 S( D' T6 q# Z9 q# F
3 y. M, |$ `. _+ I不懂 这样设计意义何在?
" \; h6 k9 P0 ?/ w, \6 T
4 b3 |- C$ J1 W2 V! N' `
$ Q) @" [. B8 A+ a5 ~8 B
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 )
return xSemaphoreCreateCounting(count, 0);
#else
return NULL;3 j' l+ A2 C- H! J7 F1 o6 y
#endif
}5 q0 u5 W, y9 m, U/ a
1 q: @+ W+ b# e3 K
+ U0 V0 Q/ H. r7 o' C6 w
你陷在细节里面了,看看人家的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是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用