以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)
) q O# v! Y# f0 p* L4 P/ o
9 D" M7 T: \# P5 a' l ^& k; R) n9 u
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发
) `7 K/ _3 F7 W- O L& Z1 X2 T( B6 j& k
需要工具 MDK5 自行下载:- }3 j; G: S P% S
STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
" D+ {+ k7 s, ]- _5 I" b( q) `STM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669
+ C4 n3 |( X% `" y u8 w/ ?9 M8 ^, K0 f8 Y
2 T; f7 s2 G( h5 b5 f8 b) l; n安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装
% o( d5 q8 l' ` }9 V- a: Q
7 D2 Y- n1 K; y- }) {! C' ?" c% ~. `4 v4 M& z2 Z2 L" g$ z' I
* v* J1 \4 P) d
2 A9 j& c* x t8 y. c5 x, w; W+ e安装之后 新建一个工程 选择STM32F072RBT6, H; a. g8 c7 x' m- z
3 }! A: U" P% _. R
PINOUT 勾选 FREERTOS和 USART % z# V, y+ E2 @* M5 `6 q
- z; d3 E* ^0 C; w& C因为我们调试可能需要使用
( U* t# d) q& u5 L6 g$ a7 b+ R9 ?& m& i7 [
点击软件上方 齿轮键生成 keil 工程 至此
{8 L. G: y! `7 ^3 _
8 l! o# l$ ^- VMX基于 HAL的库 生成完毕
* T; t. `8 }9 C9 v! h$ M
8 [& B- Q; {! X5 L; ~使用MDK 打开工程
# Z8 _1 {9 X I$ v从上到下 的组依次为 OS 的C文件
* T# f; q; J) E.s 启动文件
& m$ Y' z' V( A( {$ A( ~用户文件
9 U7 Y& \7 f% e, J: z" @HAL库文件 C7 U, y9 C. a& ~
CMSIS中间件文件6 C% d4 u# s! \: o! j
( K" [& _3 h4 S9 @) p其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码
7 S5 H0 G6 p; q( i6 \% F4 T. U! [
2 l. R2 ]4 e/ N5 e7 E5 y. B
1 W8 i7 x6 x& p( {8 @5 @0 W( }( a( ~
9 n; c1 ?$ w- j3 i
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制2 S( c$ I8 Z& @1 K+ n
9 j7 G" g1 a: |5 c4 O先看看 main.c 的 64 到 105行
. H# |( t7 B2 O0 M% U
7 }: V# }7 A9 A( V# L/ u- int main(void)
- { }: X" k3 \. i: a$ l8 e6 m, S - {
, ~* I! d4 v' {& R
9 C9 }$ r, |' Y- {& x- /* USER CODE BEGIN 1 */2 p& |" j# V$ Z
0 P8 @1 [/ ~1 G+ p7 g' N- n+ P6 [, A8 _, m- /* USER CODE END 1 */
W. o! G( c( @2 t( ^8 \ ~: S
& T+ j3 w: T1 y' r3 T- /* MCU Configuration----------------------------------------------------------*/: j/ }. L, E9 D+ M2 v! N
- : \3 h( S3 d, r3 A, a& W3 g: H
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */4 {/ t4 y' M$ x5 V, r% S
- HAL_Init();
6 A0 ?. o1 h8 y9 x4 W9 H
- O/ u% z% q- x+ d- /* Configure the system clock */* b4 C8 Q1 i7 @) Q8 Y& A
- SystemClock_Config();
# \/ L; n' }: H h- l/ z
Q% U/ I4 L1 x& N& J- /* Initialize all configured peripherals */
& x! @: y' K! v9 P, G& L - MX_GPIO_Init();
1 N: I6 D, ]- a2 N6 b" F - MX_USART2_UART_Init();
% f& w# L/ A$ U: e" W - # _: o/ D9 @: I. `$ |
- /* USER CODE BEGIN 2 */
* F, H: A* L5 m7 ~3 E: V8 D - * }* X) C% {+ p2 w/ {: A5 w# [
- /* USER CODE END 2 */( _6 e/ _2 s( u+ {7 W5 G1 _
- ' t# h3 v& E% S+ I: |% o Q
- /* Init code generated for FreeRTOS */
9 r4 T( v3 P2 I$ E, |/ h9 | - /* Create Start thread */. u, K8 T1 D6 L. `! l9 K6 T
- osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);# t* @5 U. x8 A3 J! @# K9 Q
- osThreadCreate (osThread(USER_Thread), NULL);* i, M, q$ `3 H/ K" K
4 I; K3 i0 E. L" f; m4 \- /* Start scheduler */
4 \- k) a. i! J. A7 J. I' p - osKernelStart(NULL, NULL);) U+ E) l& f# R! T# {" p: P
8 }$ \' ] i! S ?9 k- /* We should never get here as control is now taken by the scheduler */
6 n1 N# B/ k( M1 W i
5 N4 \; [: L& R# w8 O! H Q- /* USER CODE BEGIN 3 */9 H1 m t9 e: k
- /* Infinite loop */, X7 A0 I: M i# D
- while (1)
( i: i. U/ W0 S j3 f n. u& o - {
. |9 T5 ]$ R- i+ z# |
+ Q8 M0 i$ V- M# p, ?& K- }
1 d. g4 ]) Q- Y3 a - /* USER CODE END 3 */. |& M1 r! D7 P* B2 R5 ?! B% x ^9 B
- 2 A6 G" t( k$ L1 \# T C
- }
复制代码
. G# z; P' e7 I5 E9 V1 j, Cmian函数 C代码的入口 初始化一些硬件后 5 r+ f* B# }% R( _ n2 q
osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL); . \2 _5 E$ C! z0 K' ?
定义了一个 线程 USER_Thread 然后启动OS
/ t$ u9 _' Q1 @/ ?- y+ y2 d* c注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数" R/ g9 t' t* W
+ H. Q3 L- H0 T! ]' p
宏的第二项参数 StartThread 为线程 入口函数地址。
0 G4 V2 s7 S' `! M& G
% T+ x+ `; h5 Q* E* X2 j+ R2 V至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread
0 g$ ?! S$ R5 p! f0 b3 t2 a. I0 Q' e/ y8 t1 ?
修改 StartThrea函数 如下
* _. ?4 Z# w6 [' L+ X+ _* j5 ?
) Y6 R0 ]( I. l" j9 |1 U+ `5 Q- # Q6 E* g- q+ u4 S; m
- /* USER CODE BEGIN 4 */
! p6 p" c) j% ~( X3 [+ h7 f - void Nucleo_072_Led(const void *par);
) \1 W: U/ [/ _6 s f: C; k3 B: s% F - /* USER CODE END 4 */( M8 w. t& X% ~
- 7 N5 n8 S, j- f& K% @
- static void StartThread(void const * argument), Z4 H. {# A/ [. ?) n) x# r
- {
, g7 X6 l0 G4 Y0 I( p4 | - 7 n" d+ y; B S# _. \6 Z
- /* USER CODE BEGIN 5 */
3 ~# d4 G: {. X - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
5 \/ k) z8 u% d& e B: { - osThreadCreate (osThread(LED_Thread), NULL);4 R9 W0 f& l& R& Q8 F
- /* Infinite loop */
4 [& U6 H# c/ ^4 `% v( C8 u4 t - for(;;)
) c. `: j" v6 `2 c( J' T - {
( y$ G. f1 C2 x9 W3 h# d - osDelay(1);8 R$ [6 o Y q7 u* @
- }
3 r* s. A- L/ i4 l3 _
9 H! S0 l4 M9 s7 z- /* USER CODE END 5 */ , B R$ p3 ], q
" n/ ]: G* ?( Y2 }- }
复制代码 4 ^& V5 D, N! U q+ j1 Y5 }6 M
添加一个 LED 函数; W' Y- Q- J, H6 i: ?
- void Nucleo_072_Led(const void *par)# e) b$ q3 Z$ w) \, p8 [
- {7 }1 _4 T; K" p" ]" X8 p
- GPIO_InitTypeDef GPIO_InitStruct;3 g+ \/ B. f+ U+ ^
- __GPIOA_CLK_ENABLE();
( ~& W. ~8 h1 u8 p9 j: s% k - 7 n! s9 ^: o8 L' e/ c1 Z
- GPIO_InitStruct.Pin = GPIO_PIN_5;: \- N b! L& e6 T: \8 m/ t
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
( {6 B' ^2 g4 `0 |; {; B - GPIO_InitStruct.Pull = GPIO_PULLUP;
2 ~6 p0 i8 \+ a& J# ^! ]" V, G4 u - GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
7 d( t0 O6 H7 b6 v1 `( S( x8 C - S: s( t8 {/ a3 _6 X) z
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
4 k% h) e: F2 q0 e+ ?& o4 F3 O3 l2 X -
( m: a" q% e0 A& E( C9 B; _ - for(;;)5 q$ r* c/ U* E' K" x
- {: M" H, J( k4 g6 B
- GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁5 v$ f9 ~4 t; E1 C
- osDelay(500);# a$ T" k8 p j: E, L* i" }
- }* S# h3 e8 \9 r/ p
: v6 p6 U# ~/ E4 z- }
复制代码 , K8 p7 Q" K; Z7 L4 v
到这里可以编译下载到板子上运行 观察现象了
' e2 Y6 ~2 `3 [- v0 n& G/ {8 Z" }4 H1 ^/ c. o5 W _" E
下面创建 RXT 的工程 新建一个工程 4 M! {" \- M8 i7 |
8 L7 j; y X9 ^9 R. D* f勾选 如下选项
1 Z: f% m: O5 K# ]/ Z) F
3 z) P$ s. D+ O- Y
& |) P I) ]( I0 I5 n/ n
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 6 C( k& B* l* h5 u. P, t' n
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库 # y% x- P m+ E
4 g. o6 ^0 T8 W' K把原来的 main.c 复制一份更名为 rtx_main.c0 b, H! h, ~7 ~( m' V6 n; `
' u3 h- e( _4 h& o( n/ u
" J1 W4 l) S; B2 O( E* i" D* W
* S) `# W# X+ d* Q( S) V文件添加完毕
# Z: m/ W; q- `0 m. V% o `" P/ U, }& h: E& m f# ~& U$ O
接下来定义 头文件目录和 系统宏 + _) K( o% c: p3 W T
7 J/ ? I ?' ^( R
) r. u$ p% |% \& {
: d* U5 U# j7 g
修改 rtx_main.c 下面两处需要修改
+ U5 n; T u9 {, Y: K
. I$ F/ w1 k6 x9 | E: r% Z- {2 b1 u. _8 I# K
- osThreadDef( StartThread, osPriorityNormal, 0, 0);8 B" l7 z) Y. V9 \5 n8 A, W: r
- osThreadCreate (osThread(StartThread), NULL);
5 m* n* J( B% M. C1 w - }
! Z3 M$ y2 o& p5 a9 ]' U: j* T - : i7 e$ s( E3 ? a& G) j
- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);1 A% ^) X/ T9 L
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改
2 j D! T2 }) V5 L8 f3 g4 R# E: Y7 A& q4 S( A9 j E
修改 stm32f0xx_hal_conf.h
9 b5 o+ M1 V% q, Y添加 图示内容 不出意外 下面 将可以直接编译了!!
1 `- K2 o) M! f- B9 M7 Q$ J) |* r$ b
( w# b6 {4 Z4 t
7 ?$ v% h/ l+ Z4 Y( y; r6 f6 L8 y3 ^" t写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了4 f+ {7 K% }$ g, c$ l, G- V2 Z
6 I0 }0 k" B/ a- `
下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量 * c/ v+ w' a4 e r7 i. J
osSemaphoreCreate参数 count 直接 n7 Y: v3 `/ |
! d1 E2 v; n' j. O6 {, h3 B" T传递给 xSemaphoreCreateCounting的两个形参 3 z7 {! `% S- M) d. B* m/ r
. o5 b+ z/ J9 ?* L8 C& e' x
原型
/ `5 B3 h. z, c& v7 d#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )5 E( W) v0 R' E# x3 M
/ m* z7 G6 K! W: e5 Z7 \) [$ K
该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值
" E6 M1 B% R( S* O" Q
0 V" E$ ~& M( C$ }5 L. s& n: C基于串口使用信号量 那么需要如下要求
2 @* I5 {% N/ C% C- L) ~假设 usart_sem 为串口使用的信号量, r" o. A; k$ C
每收到一个数据 usart_sem ++ 缓冲 1024字节
# ] k5 T8 _0 p/ {3 a' I. `需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据0 q5 X N; t# U+ M
1 `' s) I' }; M, M' x- N. R+ y* T如此我们知道 这个信号量的 最大值应为1024 * u( l) ~7 `( R/ J- y7 S- q- X
可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
- Z4 B) L/ R6 m. o% B c- ~, m) q) v1 j6 [1 ], g
会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次
5 E. x- X$ d r% N- Q显然这不是我们想要 : J5 ?, x" ?3 T! l! }- d- g
+ S# a$ |% |, J6 j
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
" g) U6 z' N' R% l9 C
' I R7 V# ~3 }, f不懂 这样设计意义何在?
4 y$ z3 z: `* {) \! O% p# w2 I+ A( Q) Y( s$ Y, Y: @8 U
: ^& P: @7 a( v( \1 L3 b; P& R
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。6 W8 ~7 c" P' M
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 ) 1 c5 y& b& V$ [0 }( }+ }- F
return xSemaphoreCreateCounting(count, 0); c: o4 @" f7 V3 p* r$ v6 s4 j' v: ?
#else2 ]2 p; n* H- X7 U) f& _2 i
return NULL;
#endif6 o) ~* g. Z1 V+ x
}8 ^' v A8 n; L8 R d. @* X `8 E7 g! h
, h8 |8 O p3 r" D6 {$ f1 V8 R+ I
1 d; h' L! D* Y% W
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是 C7 c+ H; M1 I) m# o' j
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?5 |1 T/ o/ Z1 D2 M
先不谈消费者应当随时饥饿,处于阻塞态。
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用