以刚拿到手的 Nucleo 072为例 其他板子用户 请举一反三哈 (工程文件在帖末)
+ @( q7 s7 K. D. V8 o- L% F- c, e$ F3 B, q( ]
7 Z8 W, U- @: L& r9 j7 D& K1 Z8 I
后续帖子:【干货】Nucleo072 usart 基于RTOS的应用 方便AT指令类外设开发! x! ~2 t) I7 v" `, ^
8 p% T" f) s3 m4 j5 L. ?/ c, p需要工具 MDK5 自行下载:) a0 b1 ~, a- F3 v& M5 Y$ N
STM32CUBEMX https://www.stmcu.org.cn/document/detail/index/id-214984
; E2 I$ s% x) p, t; W1 c) y- iSTM32CUBEF0 https://www.stmcu.org.cn/document/detail/index/id-216669+ K+ R, u8 o% f4 Q. q8 m" I
) ]6 K \; v8 B0 a! j- z; X, s& i4 j0 k$ @
安装 cubeMX 由于使用MX下载固件库速度那是不说了相当慢啊 所以下载 STM32CUBEF0固件库然后下图安装: H5 C. \/ _3 r* S; {
h9 _# S7 S3 b7 n4 [! r# I$ N& Q" S
" }8 X* x& X; s: k( {- S) B. V
A- L* V2 M @+ r4 W
安装之后 新建一个工程 选择STM32F072RBT6- V+ l1 A! m4 E% H
9 V( j8 s5 l3 g4 VPINOUT 勾选 FREERTOS和 USART 0 V: f" `, s" U- I5 l1 d& z
8 O' g+ L1 x, U7 y' k因为我们调试可能需要使用
% i# }% h- v$ ]9 F7 N3 D2 i9 ^4 e% t" S
点击软件上方 齿轮键生成 keil 工程 至此" x/ I0 C( L! J1 `; a
! |$ Y |3 |8 v7 k* qMX基于 HAL的库 生成完毕1 G5 H3 u* A8 c% R6 x
9 R4 w, {2 S- P使用MDK 打开工程
3 u+ N, b/ g$ ^+ c/ x, Z从上到下 的组依次为 OS 的C文件: @$ [# V! Q' f/ Z2 |1 q! Z0 Z, e
.s 启动文件: h- q6 Q- a. @6 c, O
用户文件
3 b5 L3 Y! p) P& IHAL库文件
. o0 ~$ y4 y, m! R3 ?2 v; tCMSIS中间件文件
' i( l1 F2 c. A" v8 x, |
$ e, e9 e' @! _ k& i# l; g& a其中 在 第一组中的 cmsis_os.c 中实现了 cmsis_os 到FREERTOS 的中间层转换 稍后会讨论其中一处代码; \ v; g$ q8 \9 V0 ~
e& T- f, f& O7 Z' e
. P8 F8 c3 w* s' y: F
6 y$ ~: ^% t4 v2 }) d! ~2 ~6 S( g, X; O% }: K) @
接下来 添加自己的代码 首先添加 072 上面LED吧 板子不在身边 记得是PA5 控制5 S; c/ ?( v" m, D1 o; c7 U0 j
9 j" f, O! B7 K/ s; r先看看 main.c 的 64 到 105行
8 x) y( P7 b: K8 m$ r4 R- 6 g- `6 v2 \! d/ | @; p o
- int main(void)$ H. t, ?; K. H, Q& b( Y8 D
- {
# `% b7 u( ^! v- _
+ O6 G$ d$ |4 c$ _: {- /* USER CODE BEGIN 1 */) V% _: W9 _! e3 M" g1 P
- ) F. `- {/ q9 [/ M3 L2 Y
- /* USER CODE END 1 */
1 A( `* q: R* |0 b1 p& S - % U6 F# ^6 C2 v; m' ~/ q. q+ d
- /* MCU Configuration----------------------------------------------------------*/ U, U" i- H- p7 J
- : N* j" K% Z% b% Z# j* e
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */' h% e6 S3 j4 l
- HAL_Init();( Z( T4 @' O' \1 j
- % {! X4 `+ ^! w- r6 r3 |
- /* Configure the system clock */
# D& ~% H5 @) U' e4 l - SystemClock_Config();/ r: X: t* U7 s
" K7 g3 e$ r9 ~: B* U- /* Initialize all configured peripherals */
+ s% L# D9 [3 A7 o" i' w: o - MX_GPIO_Init();9 g. b' Y( I/ \) b4 |7 c
- MX_USART2_UART_Init();
/ y9 I% I1 ^$ K2 }, x* e y' W
5 `$ b0 i) O/ p- /* USER CODE BEGIN 2 */: p" v9 r$ `0 u
- $ r" M8 Z5 g. E. F7 |
- /* USER CODE END 2 */; H @' X/ \1 ?4 I
4 Z4 s/ @' B& p! F+ u$ |- /* Init code generated for FreeRTOS */ N1 I4 d6 X7 a% N7 D0 D; P
- /* Create Start thread */
9 y9 C" }) S# } - osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);3 l5 K' Z1 }# M: @/ ^
- osThreadCreate (osThread(USER_Thread), NULL);( ~, ^; `9 v; u! Y! H
- " @7 n, M* ?9 ~6 s' M3 C
- /* Start scheduler */
0 v& k+ n M- W! U) ?- R - osKernelStart(NULL, NULL);# \3 P, j& S- c7 S+ H
- - a3 \3 J9 \& f% b3 ^
- /* We should never get here as control is now taken by the scheduler */
+ O8 I% a5 c3 r& G, \ - ! w4 n8 L: a1 x' }1 U& D
- /* USER CODE BEGIN 3 */
' J$ a* ^% w+ Q* D - /* Infinite loop */) v6 E" P: i" h) X# ]. C
- while (1)
& [$ W4 w( _/ y2 Y* H. C' C5 t - {. U3 L% t6 N. Z* K* V" A
1 |2 N1 {: u8 F: g, f& w! V- }* |$ g2 t/ D$ F$ h+ C D+ z, U
- /* USER CODE END 3 */
" B, p0 p+ L( X7 S - 8 C8 x N. b* U# l# Y
- }
复制代码 ! h2 s8 I6 G' ]; [
mian函数 C代码的入口 初始化一些硬件后
! H- Y4 f( R g* @' s6 j osThreadDef(USER_Thread, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE); osThreadCreate (osThread(USER_Thread), NULL); /* Start scheduler */ osKernelStart(NULL, NULL);
! H' d# [. x" R定义了一个 线程 USER_Thread 然后启动OS C0 K, K9 h: z# A, o, v; r6 d |
注意 osThreadDef 是一个宏 定义一个用于描述 线程的结构体 并不是执行函数6 d o& u; C& [1 @- y) O. d
3 `% h9 c" M5 R% D7 j) l
宏的第二项参数 StartThread 为线程 入口函数地址。1 D2 j0 \ q7 w
( x E. H& m# A+ k9 @ D7 e; Y至此mian函数的工作结束了 OS将转向 就绪线程并永不返回 也就是执行StartThread
/ X% @# ~' M/ I+ ^/ s8 j% F+ L0 F5 z+ `. R( l2 k* b
修改 StartThrea函数 如下
. c( B8 I8 U$ E& m" W5 Q& H% v* Z- H* X# w# p# J. a( P
' ~- K& L) t: b+ ]3 Y' u- /* USER CODE BEGIN 4 */
5 ?4 j( W. s5 h - void Nucleo_072_Led(const void *par);
1 q8 }! d o2 ^9 C1 C( y; f- s - /* USER CODE END 4 */2 m7 D/ f9 G& e( {, y [
8 v9 P% k2 I9 ]6 q' k% w- static void StartThread(void const * argument)
4 a3 e3 L" d; a! V6 o$ o; Q& o$ v - {' p/ ^5 G/ C3 z9 w& j
3 x2 J- n8 b5 x- /* USER CODE BEGIN 5 */
" R- [2 e( U% o/ O& N, c - osThreadDef(LED_Thread, Nucleo_072_Led, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);6 q6 [ E. c" `5 I3 z
- osThreadCreate (osThread(LED_Thread), NULL);* [& }* j, V, G/ Q( G
- /* Infinite loop */7 z7 i9 `5 a# V* v1 F- k
- for(;;)
# }9 ?# H3 J2 [& t# R1 }* [* G - {
1 @! t6 X& s: i - osDelay(1);
$ Q3 j7 q" c, C' x8 I; @ - }
! G) J& w* S& [* X1 B3 S. M - 9 H$ j$ f$ k# G4 v! Y$ K! K9 e
- /* USER CODE END 5 */
; M# i" p& S5 p7 ]$ Z) K3 n! S - * G' c) ` L5 {# ]
- }
复制代码 0 v4 _' w$ a4 k8 q% U% P9 u8 K
添加一个 LED 函数
1 O9 k' x: c- m' p: T( _4 r- void Nucleo_072_Led(const void *par)
4 g$ n8 |8 Q) L! a* t - {
/ V7 Z! N! X% \& { - GPIO_InitTypeDef GPIO_InitStruct;
1 \0 [9 ~* y" l" G - __GPIOA_CLK_ENABLE();% k3 K/ _( W' @* D6 O
- 6 y7 `/ |, @5 N% i, C
- GPIO_InitStruct.Pin = GPIO_PIN_5;
1 d& S0 f& s2 S* u U - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;4 b$ u: Z. L- x& X6 {) Z
- GPIO_InitStruct.Pull = GPIO_PULLUP;& a/ a+ x2 [: G3 L6 G9 w& e# W8 ]
- GPIO_InitStruct.Speed = GPIO_SPEED_LOW;; _6 B3 F' j, a4 `! {; [% b8 K
. c. i1 i( Y; E' G- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);* T( N4 \: ]. f: Z
- + ]7 Q' F. Q( ?7 h
- for(;;)8 K1 ^* _5 K7 j3 O( v; L
- {
& L# l8 r% w. t* J- X - GPIOA->ODR^=GPIO_PIN_5;// PA5取反 LED闪烁; o6 k* m$ d% j/ Z# D+ Y. {
- osDelay(500);5 S) Y& o1 O1 H# I
- }
' H, c+ w5 L2 R: N
. |. M& L% d7 m- }
复制代码
5 ?' \) P3 s- t/ b# j# D到这里可以编译下载到板子上运行 观察现象了5 |5 t6 H# z/ z% {
0 w8 p5 o6 \, Q, d* m8 O. U
下面创建 RXT 的工程 新建一个工程
" t/ L8 V J9 ]! r" `$ k$ Q
+ l* U/ F* L I7 e8 f5 P勾选 如下选项 # Z# i* X) n7 i W* Z
7 w; n0 r7 X# i# R/ Z9 x+ O
; v. [. s3 e' ^. c- x$ k# K2 L# r
红框 不要添加 不知为何 楼主添加 MDK的 startup 编译通不过 - t; H# y# x% J0 @8 d5 H1 z
F4 的工程没有包含 HAL 接下来 需要自行添加HAL 库 * A% |4 a- d$ X: u6 Q2 \, d( A
# b! y3 M, J8 ^. n/ p
把原来的 main.c 复制一份更名为 rtx_main.c6 w' }/ [5 d! D
+ p! O( k1 r) @ ?/ W3 _
! B& p3 q& [* z0 q
' H% A2 Y* l! E" z& d7 C文件添加完毕
: w: c$ h9 e" F |: n" e, a7 ~6 a* r1 b' ~- U
接下来定义 头文件目录和 系统宏
7 z6 |+ Q2 q% @1 B, x' ]0 I' c0 ]" A3 P9 d
+ k* X, B |+ l* y, I$ N6 P
% K9 P K; j* F
修改 rtx_main.c 下面两处需要修改
2 b1 {# u2 x. C+ Q; ?. Z9 E: Y& R8 a( l7 o4 s; k
- {3 b& A: ]2 t. y1 T% ]
- osThreadDef( StartThread, osPriorityNormal, 0, 0);
$ [+ r) a& o M; k - osThreadCreate (osThread(StartThread), NULL);
6 Y; n9 q& C0 p/ I1 h$ \# L v - }1 |2 g6 \0 T& G0 o
$ j# ~# j& ^# Q0 F* G' P- osThreadDef( Nucleo_072_Led, osPriorityNormal, 0, 0);# P, C& U/ a! t7 T4 Q
- osThreadCreate (osThread(Nucleo_072_Led), NULL);
复制代码 不知为何 ST写的 中间件和 MDK的 接口有一点差距 所以 创建线程的地方需要如上修改& B$ x- G7 b" [1 ]- [* A4 S# n
) H/ r6 g) X4 R- J修改 stm32f0xx_hal_conf.h - U, v* ?* ?+ T. W
添加 图示内容 不出意外 下面 将可以直接编译了!!6 ]: m$ N" \$ C- d- i4 t
6 \- R- K. s$ D# e6 t* a
. }) E9 w7 z7 Q* Z写的有些多了 本来想 继续写 RTOS 基于串口的 应用 太长了 下次发帖写了
$ Z [# \& e% }0 \( `& |1 Q+ p! p9 a* z" V+ G8 ]' @
下面提出一个讨论 MX 创建的 工程 FREERTOS 中 cmsis_os.c 中 创建一个信号量 ' O+ m. O5 M0 r9 v( w) J
osSemaphoreCreate参数 count 直接
* U5 l7 }) a: R) U% j6 D/ K5 B% R! j6 e# G" a# C: I' y
传递给 xSemaphoreCreateCounting的两个形参
2 I2 Q/ U5 P/ H; s) j7 Y# y
. A. y7 L. n1 L! i9 e原型
% n! i. c7 q6 y# e* N) m#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
" D4 D5 J' I& T
x$ n) z/ N7 S `0 {, d& j& X该宏创建一个 数值型信号量 第一个参数是 信号量最大数值 第二个则为 初始化值) o/ w* w1 m7 n9 w. g
) n5 |' f6 q7 f基于串口使用信号量 那么需要如下要求# x# C9 ~% I5 s+ R
假设 usart_sem 为串口使用的信号量/ h( a2 @( f/ s3 J/ v, k a: A
每收到一个数据 usart_sem ++ 缓冲 1024字节 2 M/ W! \. l0 M# `2 ^0 o+ X! x% }
需要数据的线程 osSemaphoreWait(usart_sem ); 当有数据时 线程被激活 获取数据& g6 k3 R9 A" o: a& f9 L( {
5 Y6 V/ m1 S+ L; d: G x# a3 g如此我们知道 这个信号量的 最大值应为1024
* t0 Y% d& ^' c8 @可是使用 ST 的cmsis_os osSemaphoreCreate 创建一个信号亮 osSemaphoreCreate(0,1024);
& b% i1 z; ^; g% ^$ w/ v
& X: y8 `( c6 U6 x会出现这样的问题 ! 此信号量 被赋予初值1024 意味着 这个信号量将可以被osSemaphoreWait 1024次
$ W0 I. i7 S& ]6 {# R8 \7 u显然这不是我们想要 7 ^" P8 A ]6 H1 F3 S! j1 z) R ^
/ \% _$ ~) b8 b8 \- p# R
通常 我们需要的数值型信号量 最大值可以很大 但是初值 基本为0,或1
& Q3 Q, R) h/ a6 }
* P! Y2 _2 f8 p" C2 N8 X. N不懂 这样设计意义何在?+ x9 {3 \& f" ^7 R$ m; Q3 ]
) o4 X6 T5 E- I4 d& h v2 L
1 | m* V3 y" L Z! p0 X! U
FREERTOS.zip
(4.76 MB, 下载次数: 809)
|
不好意思,确实被代码的名称误导了,以前用的基本上只用二值的semphore或者直接用queue了。
仔细看了一下内部的实现,确实我说错了。- m3 k- L2 p' p% _
在init_counter和max_counter一样的情况下,确实整个逻辑是要按照资源池来解释的,也就是确实生产者take(这个take代表获取空闲资源才能生产),消费者release(这个代表空闲资源返回)。我觉得这种情况semphore更像一个可重入的锁,处理线程拿走资源池后,直接处理完后再归还,像是个recursive mutex。
前面说的那个问题,直接用queue可能会更好。另外,今天试了一下最新的stm32cubemx和固件版本,代码已经改了,#if (configUSE_COUNTING_SEMAPHORES == 1 )
return xSemaphoreCreateCounting(count, 0);
#else
return NULL;
#endif
}
* D0 a$ m" |: A+ `
/ L! F! e: `& y
' S, ^1 R% D4 \* C( G! G9 [7 h
你陷在细节里面了,看看人家的API是怎么样写的,osSemaphoreRelease和osSemaphoreWait,我们在生产者里面应该要osSemaphoreRelease,而在消费者里面osSemaphoreWait,是吧?字面理解,相当于生产的人释放了一个东西,然后消费者就等待生产者的这个东西。再看里面的实现,osSemaphoreRelease里面是xSemaphoreGive而不是take,而osSemaphoreWait里面是xSemaphoreTake而不是give。
所谓的反过来理解只是一种理解方法,说take和post的逻辑也反的话是不对的。不要老想着什么时候去post,什么去take。cmsis_os的API意思很明了,生产者生产了东西就应该release,消费者就应该Wait,这不是很好理解吗?
反过来理解是吧,说得通。可是2 j. w# @4 @( L1 f$ j
有个问题。,生产一个就-1的话应该 take获取信号咯? 反过来消费应该post 释放信号咯? 那么问题是 post是非阻塞立即返回的而take是阻塞的?
先不谈消费者应当随时饥饿,处于阻塞态。# f% p8 r) t& B9 G! V, M2 g3 _
生产一般在中断里吧,当take-到0的时候 阻塞线程阻塞谁呢
据说库很大,效率低?求告知!
多谢老大 支持 继续加油
确实 有这个毛病 楼主的贴初始化使用库 LED翻转使用的寄存器 请关注楼主 下篇将 发表 USART基于 OS的应用