以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)
8 a4 \1 n0 v( l" `% a; F8 [
/ J. v7 {( h( h$ K. M% C
" |: r& U- u; b: q9 S+ r! J: e后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发 I! y, P/ ?" B; K( W W0 j
; r' m; e9 M. D; a! ^需要工具 MDK5 自行下载:
) [0 F- c# t0 W7 b1 ]! }" qSTM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
8 }9 P. ^1 k, H, k9 |STM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669. B( L! Y, ~' X# ]$ |& z/ p/ E) u* L
& T0 n4 M E3 I, `& V t
9 D' h) i4 J9 j; |) u+ J
安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装5 w6 D! X; g9 d* A! }2 |
9 M/ [# s" P* l" t. ?1 j a0 C5 V" e
% A; N- K8 \& i- F1 q& e# s
2 S9 T" I6 f& k
/ ^/ _; R( f3 x! R" v9 p& Z安装之后 新建一个工程 选择STM32F072RBT6: i9 v5 C& V7 k6 @5 A3 C% L0 N; F
% g2 g9 P% x z/ iPINOUT 勾选 FREERTOS和 USART
) Y A8 p" Y1 J# ~. `& x! ~
6 A F7 f9 x; Y& k
因为我们调试可能需要使用
2 I. A( N( ]/ L' o- m+ i' g& s6 i9 j) O& z0 F: A
点击软件上方 齿轮键生成 keil 工程 至此
, q8 Y* l$ h |" K2 s9 i% C- K" w0 ^
MX基于 HAL的库 生成完毕 _+ u. b& A D: \1 L- H; d
1 }) t& G) n H9 D& ^; e7 G/ V) h* a
使用MDK 打开工程
7 |: ^) D" ?+ v" L从上到下 的组依次为 OS 的C文件
1 p) k) O( m3 ~/ _' V g( P" I.s 启动文件; Q5 d! O& ^2 [' U& _/ O' [
用户文件
* Y, k E C0 l2 Y1 S4 C0 C2 nHAL库文件* u& f8 p, ^8 `
CMSIS中间件文件
7 ] {% Y+ H) a4 S% e$ K6 l" @# }9 w9 Y0 r! Y
其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码
( E4 V6 l6 C$ y; N
% b: e4 O7 S! Y* P. E
2 ]. w& ?9 b n) y @3 Q* v2 j
# h' B) I( l5 n( R% D( ]- A$ o6 W6 R% c3 N9 S6 B' e k
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制
; |0 C" g, ~% T& }3 n% R" S# T- @' ^8 h" ~3 k" c
先看看 main.c 的 64 到 105行 ( y1 W2 P/ U2 E* f/ |
- 3 _9 q. G$ A' @/ @. a- s5 o
- int main(void)* F+ ~* G1 l2 Y. l4 a, u( s( ]
- {
7 D% U9 H1 X6 P, @ - - R9 v7 A. Z( M% ~' ]% _7 r
- /* USER CODE BEGIN 1 */9 |0 V0 }3 K g: Y" V9 c( v$ O
- 6 A* l! \& j9 d { L& u, [6 W9 s
- /* USER CODE END 1 */# v+ M- S& B2 a4 i& `7 e3 ?$ `( d
- & T, q2 g" F- F7 O; i# r
- /* MCU Configuration----------------------------------------------------------*/
0 I$ A5 g2 x2 _) z% a) x7 ^9 _1 t0 ? - ; p6 n2 O; {0 h
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */8 U) ^( w! m# N! p
- HAL_Init();( K9 S) v3 u5 `
- " \) @( F6 ^$ o0 {% S
- /* Configure the system clock */; h' Q1 C4 M7 G
- SystemClock_Config();
2 x& x+ h3 Q% n# K) s3 y: u - $ i! e9 K$ _! U3 j6 x' x2 n+ O
- /* Initialize all configured peripherals */
U6 S& R! E* R2 s9 q - MX_GPIO_Init();. e" e+ X @: ?' @
- MX_USART2_UART_Init(); `3 s# L7 y& x8 B
- * X2 ?8 d4 R; d
- /* USER CODE BEGIN 2 */
2 J4 J4 q) C4 l! o
* B, G/ U% v+ o$ i. I" h: t3 e- /* USER CODE END 2 */) M7 k5 d3 P1 X4 b
- ( Z( Q- x- ?/ J- F; k
- /* Init code generated for FreeRTOS */
4 y6 i; a1 r3 P1 i - /* Create Start thread */
; X$ _9 Q. M# k9 l+ C j1 L1 o - osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);
( F, a2 [0 e$ Z0 Q9 U" j; } - osThreadCreate (osThread(USER_Thread), NULL);
' D% s$ ~6 \6 f- e* ?/ d, r - . O1 ~$ b3 r* j: }# _
- /* Start scheduler */4 T0 a3 I1 }* o6 J3 ^$ j T: `
- osKernelStart(NULL, NULL);5 v. R3 v# f) S- D; U
- 6 Q( j: d/ c" c; @% h2 R" v
- /* We should never get here as control is now taken by the scheduler */: l$ b% ^ n' F. C, @. s: U0 d
- : Z7 z4 d' ?5 r$ H
- /* USER CODE BEGIN 3 */+ j( {1 `+ {: N0 q M. c) ]
- /* Infinite loop */
; D2 ~& z! q; p# C: a5 W - while (1)% L# X) ~4 c- M0 N0 I* h8 g
- {5 n f& ?5 q$ x
- + V- I! @! @" G4 _/ ?
- }
: I# U; i" @6 N' W: z9 @2 N - /* USER CODE END 3 */
/ n9 J3 L$ _, f) l& U2 ^
/ {- R& T! j/ l2 Y% I: y# [2 S2 a- }
复制代码 ' P" n8 h1 z3 A2 X1 P
mian函数 C代码的入口 初始化一些硬件后
o: m$ Z7 S' }7 t3 t osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
6 ?% ]# k7 Q# d/ y' [& m$ _定义了一个 线程 USER_Thread 然后启动OS
6 T# w: ~% z( M6 ^& S2 E! \注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数- J2 n3 i/ ^. U1 j
4 D2 v% L. h$ y3 N! m) `6 Z9 i+ M p
宏的第二项参数 StartThread 为线程 入口函数地址。9 F8 _- Q4 U( e" R; m
( m0 j) V' |% p3 F
至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread " o+ o! p# { B! M1 h( ^
2 l* D6 j3 ^: ~) H) |! ]! m( g2 C修改 StartThrea函数 如下
" ^, h3 \- F/ J7 M3 Z1 |0 p2 u6 c& ?
; o- H; Y b- w+ T
" t; ]: ]5 c+ B; T- /* USER CODE BEGIN 4 */
' N% C* n0 L5 k+ n' n! } - void Nucleo_072_Led(const void *par);2 A$ S7 W! X6 b1 N
- /* USER CODE END 4 */* Q2 h: d' g+ h
- 8 V0 z8 k- f- k
- static void StartThread(void const * argument)
+ b! F6 F4 E& C, F% t4 [# L# t - {* V6 J' o0 O5 z y5 I; U
( x9 Z* f3 g+ l8 g8 t- l1 g9 s- /* USER CODE BEGIN 5 */
4 T6 h# h* w: v# W4 Z7 B - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);8 r. W$ H+ L) ~
- osThreadCreate (osThread(LED_Thread), NULL);( s5 r) S+ c+ y
- /* Infinite loop */; R) c- X4 b- k d! ^* H
- for(;;)
: b3 b" ~8 L0 [ - {
; Y9 i4 I: l7 ^% G - osDelay(1);" z0 n: r% g# F5 t9 P* j" M9 i, b
- }9 f s4 D2 B9 c5 F% w
- 1 C+ X3 l2 Z! a: y$ s7 [2 x( x
- /* USER CODE END 5 */ : N7 a* k! m- z/ _& t
- ! Y, Y- ?0 n4 M' m7 \- Y
- }
复制代码
# u9 q- F& D; n+ n添加一个 LED 函数
$ }% J2 q* f3 D- void Nucleo_072_Led(const void *par)8 J, K# X6 @4 g+ f/ b, h# L
- {
9 b, w9 E1 h/ k5 M9 [+ L& {& g' j - GPIO_InitTypeDef GPIO_InitStruct;
! S" C( Q: \2 T _0 a# H - __GPIOA_CLK_ENABLE();6 q$ _6 L5 \' I$ D3 y% E: e3 f
-
/ z% t S' C! e" I* K0 r' w" K! t - GPIO_InitStruct.Pin = GPIO_PIN_5;
9 ?3 i7 ~1 X! `" [ - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
! S5 c z4 E, u: N0 v - GPIO_InitStruct.Pull = GPIO_PULLUP;
$ y3 C* n* t8 h - GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
# z0 T1 [ [& }: R6 a - 2 p/ i- M" u0 U6 l9 l/ v m
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
5 M Q4 u$ w) u& j: {+ {' b& r - + k/ b+ B. \7 @& C5 x
- for(;;)
0 e0 H" T* n9 ~ - {
0 A6 R, d- F. H* d" a - GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁
- s/ |4 U: c1 t - osDelay(500);
/ O, a# Y6 A9 L$ G5 X; l- k) d - }
; }% f, J4 a2 E- w+ J( O+ K; J$ O
8 k9 k4 |) t5 J- }
复制代码 $ V* R9 |& u/ `4 _4 @
到这里可以编译下载到板子上运行 观察现象了% c/ `8 ?3 I1 D; A
' ~% V: t/ J( r$ Q9 M) t6 h
下面创建 RXT 的工程 新建一个工程
3 ^- J7 P$ Y( M/ u- L! L( a5 r) j* Q& q% b: P7 j( C8 D9 q: @, D
勾选 如下选项 $ N) H* o9 k( Q% @2 ?
. z1 ]& P1 T# o, J2 u
( H4 C T6 K! H7 M" F
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 , D7 p- L! q. N0 X7 z3 O& Y! W1 Y
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库 ( ]: `$ b6 F# y( C3 I/ s) \
& I4 w7 o h. U# U O# w, j6 A7 P把原来的 main.c 复制一份更名为 rtx_main.c* w" k7 r* O$ g- ^7 T
0 B( M$ T( y- b$ V w
: V6 o' T0 P4 r. H
" |( i3 J* l3 f* j) b% Z$ M! Q文件添加完毕 - |( A3 O3 e* n7 \0 O$ D" `$ b
) {/ M4 g" w! J# l, ~ ~0 z
接下来定义 头文件目录和 系统宏
1 K1 U1 U/ u* O! f4 m9 S1 z
& E5 I$ \; g, U4 {6 k
/ C7 r2 j0 o2 ?! O0 |
! B: E: j- |. ?1 m" q3 |5 [修改 rtx_main.c 下面两处需要修改 + y2 J; h) I' @ j3 ?1 y f
: [5 m1 R& G, K6 ^: I- {# E# S- G5 g! J' n8 L8 {
- osThreadDef( StartThread, osPriorityNormal, 0, 0);
4 i: f, d! p( r+ X& a - osThreadCreate (osThread(StartThread), NULL);9 c R! g9 n7 C5 [
- }5 i* D% k1 |( m5 l% E! h6 j
- . Q3 w( X* \: o; f
- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);
; c3 y2 ~- L4 j C( Z% K# o2 y4 ? - osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改% m, V# ~4 f1 o' X+ M+ C) Q* j
! B5 n4 s/ |; a5 x; I* V$ p. D( V
修改 stm32f0xx_hal_conf.h 8 Z/ ^* b( \ R: c
添加 图示内容 不出意外 下面 将可以直接编译了!!
0 A. ^* x$ Z8 v+ D, H6 ]$ U
9 G; o1 N0 j: A
# m; d; v3 ~ A7 f7 e
写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了8 D6 u7 r. u# L! C
: r7 [, K4 W: R! a& _- `1 |5 K+ U下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量 + d6 a% w& ?% P6 `
osSemaphoreCreate参数 count 直接+ \3 f+ ~+ {4 z8 @
; H( `& P" n9 ?% R$ r传递给 xSemaphoreCreateCounting的两个形参 ) ~7 U! L1 t" q# u: `) T" @$ A# }! a3 p
G4 _; Q0 k4 R5 k( f9 h* ]原型
6 k3 V! {- H1 ~$ o* P& N+ n#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
1 B Y. Y7 p+ ?" Y2 l9 J6 X- L/ Y
: d: k" ~9 o1 t, j" H# L该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值" V* s+ F/ ~/ i% M5 N% _4 M
" l5 c7 i$ H' h$ F
基于串口使用信号量 那么需要如下要求' o$ j' t$ M6 v5 [
假设 usart_sem 为串口使用的信号量
0 _2 A+ L9 z8 i9 i" {每收到一个数据 usart_sem ++ 缓冲 1024字节
8 J! A2 j$ X- l) R! Y; u需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据
% Q( W. t( I# X0 p* F4 g! B
3 R% H F1 F, `% W& N如此我们知道 这个信号量的 最大值应为1024
$ C$ e I, r. h7 Q6 X# H) _可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
6 o* X% u+ E8 Z/ G7 m. v3 }
% s+ \1 }6 n7 f会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次5 K! S. [1 Y' d* B% O
显然这不是我们想要
+ m& \' |& ~1 V7 ]2 L n( H& E
' A7 c% ^' G: _通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
( ~! r4 t+ J/ |3 n' {* ~9 H) g
& M! r1 F. ^" d; R$ @不懂 这样设计意义何在?
2 ]' p( M3 ?2 [8 ]5 W! ^& t6 H/ I0 l; w* { t1 ^
6 ~' m& o8 J7 B( `9 T
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。( c1 k4 |4 _( L' u2 y8 e4 K
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 )
return xSemaphoreCreateCounting(count, 0); J& ~ _6 N3 s
#else; N6 E1 N- T$ j5 g9 F# `
return NULL;
#endif/ V3 ]4 ^/ @6 L9 W0 k
}
9 ]" | Z9 N3 n% C& E/ v
6 K3 A, m- x; w" q8 c' i
. |5 y% ] P5 @7 u7 X# V5 G( s3 S
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是6 W1 t( I% M8 j* h
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用