试验三:信号量应用 创建FreeRTOS任务中断和同步(信号量方式)
( l/ } ~) I. e E
其实可以这里可以延伸利用信号量来实现串口数据的不定长收发,定长收发使用 “HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize);”即可,而不定长的方法在论坛中有网友也举了详细的创建方法(可在论坛中搜索“不定长”),也有修改HAL_UART_Receive_IT 函数的方法实现。 试验设想:这里是利用FreeRTOS信号量机制实现,主要结合不定长数据的接收,加上FreeRTOS信号量的控制,再通过串口数据接收任务中的消息解析操作,可实现简单的人机交互功能。
. [5 b M& F1 C9 @- k9 k交互机制的选择: 面对消息队列、事件标志组、 信号量等多种交互机制下,可以参考下表进行任务间的交互。
# p1 v: c: B% @( X& U r
本实验选择互斥信号量来实现,相关的FreeRTOS 信号量 API 函数如下图:
* Y6 i) a' u) {' K6 F7 r
使用CubeMX配置串口 DMA的内容略过(参考论坛的不定长DMA配置,FreeRTOS的配置见前篇),待工程建立好后,在代码中增加以下的内容: 8 v" x% e0 o [ a, d
在main.h中增加定义: 5 \9 d7 H$ j) n, @" r/ ] f
在main.c中增加定义: - extern SemaphoreHandle_t xScanSemaphore; //串口中断处理的任务
& z" h* B1 L4 w" J - ; h q& G1 h- e+ m0 Q$ k) D
- uint8_t ReceiveBuff[RXBUFFERSIZE] = { 0 }; //接收缓冲区
! V! H2 V8 i& Y( v1 d) i2 x - uint8_t ReceiveInfo[RXBUFFERSIZE]={ 0 }; //接收待识别字符数组# K6 ]8 J! s' P& T
7 i; H/ G: }. L0 X4 S- uint8_t Rx_BuffLen, Rx_InfoLen;' J! a2 c; h1 E5 V5 X, a
- 4 \1 P4 y5 j4 i7 W
- void vStartLPUart1ScanTask(void const * argument);9 `$ n2 K5 ?9 Y2 v/ P. r
- #define Uart_TASK_PRIORITY ( tskIDLE_PRIORITY + 3UL )
复制代码( q0 [+ U/ j* q5 g9 Q3 H
在main函数中,调整增加部分代码内容: - int main(void){# q g! U; G4 A! r1 ~0 N/ M
- HAL_Init();
" Q I5 J( f) ~- i4 N - SystemClock_Config();# i# j' _7 j' R7 ^* y, z% ^8 Q6 v
- ; u0 C0 E+ H3 R# F0 r6 M) ?5 w4 I
- MX_GPIO_Init();
4 z: J: u3 ?, s7 w! t- m - MX_DMA_Init();
9 V0 w: L" P$ S& h& n3 l0 A0 T - MX_LPUART1_UART_Init();9 ~& f! b$ G5 q) ^7 ^& s+ ~/ k
- MX_USB_OTG_FS_PCD_Init();* P, Z# n9 t# V5 X* O
) p$ Q4 W8 X+ {7 c/ R% I, U- MX_NVIC_Init(); //中断初始化中,增加空闲中断的捕获 __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);
- x4 [1 r9 C a0 L c
8 a/ ]( r, S/ ?5 K) a! H/ M% j- /* 开启下一次接收 */
6 r( E( @% z3 ~& G8 \ - HAL_UART_Receive_DMA(&hlpuart1,(uint8_t*)ReceiveBuff,RXBUFFERSIZE);1 [: F' _4 u, ~2 Q: ~2 n3 P
- //创建信号量
@' E! N3 m* j0 w% S% X& g( Q& Z - xScanSemaphore = xSemaphoreCreateBinary(); D* r( z+ O2 m! Y5 d) A+ [$ U
- //增加交互任务
6 b4 L6 Y: p, s% S) z) K - xTaskCreate((TaskFunction_t) vStartLPUart1ScanTask, //任务函数7 P; [' v" b8 C/ ]
- "LPUart1S", //任务名称
* N; C4 V4 C$ g) e0 g2 @6 b - 200, //任务栈大小% C! ]" d1 k1 s
- NULL, //任务函数参数$ E' D: [! S: W2 d7 A5 [ H; Z
- Uart_TASK_PRIORITY, //任务执行等级9 i- n$ R8 n* H0 g
- (TaskHandle_t *) NULL); //任务句柄
% }: b7 l$ ]0 G, b1 C. q - //启动任务
9 q- D4 ]* C" z4 U9 T - vTaskStartScheduler();
, K' {# K) ^9 K. J c# G - }$ D/ h7 D: m' h& e
- " Y1 d) Q# A: O+ ^" n* U% q/ x
{) O- s3 _, v: y+ t9 `
复制代码( H0 J9 p# k$ H# r4 B
创建串口中断任务内容 - void vStartLPUart1ScanTask(void const * argument){
* |8 m; S8 W+ H$ T - uint8_t *strp,*strp1;. t- B; M; } s7 [2 y0 W& J
- configASSERT(xScanSemaphore);! E* U. i3 Q& w$ m. F
- //获取信号量,等待时间为0,则xSemaphoreTake()在信号量无效时会立刻返回. {8 |1 R1 y3 y. G( p
- xSemaphoreTake(xScanSemaphore, Scan_BLOCK);
& Z* z, }% B: o' e - for (;;) {! |5 u) X4 Q1 f) t
- //获取信号量,超时时间为portMAX_DELAY值: E* e; x: l& |% O4 @ \
- xSemaphoreTake(xScanSemaphore, portMAX_DELAY);( e! [, a1 M$ r8 Q
- /* 打印接收到的数据长度 */
, w8 V. d1 @3 z - printf("rx_len=%d:\n%s \n", Rx_InfoLen,ReceiveInfo);
8 E, S5 Q1 F- E7 V! u% t4 G
3 W5 l& ?- W, g& p+ [) X( @- //回答Hello
; O* Y; {, }7 w8 u7 b2 t3 b - strp=strstr((char *)ReceiveInfo,(char *)"Hello");
" B8 U' J# k8 m7 \; G% q% B: P - if (NULL != strp){
# y, J% V4 j" @* B9 u+ k' O" c - printf("\n Nice to meet you! \n");
" x# ^, k( w' L( _* o j - };: q4 q3 r7 ?2 Z( N
/ @4 a. y g- ?. I+ K# o) {- //回答guten tag/ guten morgen/ guten abend / guten nacht
7 f" L J4 w" P$ h' _ - strp=strstr((char *)ReceiveInfo,(char *)"Guten");
) i& Y( Y! w1 |7 a8 ^/ x - if (NULL != strp){( \9 p0 L3 E' H7 f
- strp1=strstr((char *)ReceiveInfo,(char *)"morgen");9 r9 s, U3 o# ]
- if (NULL != strp1){& b% Y4 ^1 Q- N- W+ m, [! G# k
- printf("\n Guten Morgen ! \n");
S$ H+ [& ?6 i% ?% U1 }. @ - }' X+ A2 K( D* G3 H" o
- strp1=strstr((char *)ReceiveInfo,(char *)"tag");+ X i b5 a3 w* e2 Z- K
- if (NULL != strp1){% {- U9 O& F6 f( S: n3 b
- printf("\n Guten Tag ! \n");
8 l0 `8 r) y9 e5 s' M - }
" V/ ]( S! h7 b! Z - strp1=strstr((char *)ReceiveInfo,(char *)"abend");
4 s/ l0 F u8 e7 T, [4 b4 x - if (NULL != strp1){" `- \& P# T `8 _# W- Z4 W( H
- printf("\n Guten Abend ! \n");% \: R( {/ x5 x: H$ P, v
- }
1 s! y; H5 q" j% Q0 {6 R - strp1=strstr((char *)ReceiveInfo,(char *)"nacht");0 B0 L+ q: T. F
- if (NULL != strp1){+ J' m* }' f6 E' D6 `
- printf("\n Guten Nacht ! \n");
' c% p% A9 Q: Z* s$ k5 J6 K+ D2 F - }
5 i3 A3 k3 A3 ]* _4 B - }
* `6 \0 o: w6 L$ F9 \& g2 y - /* 清空接收到的数据 */ . W2 x& S4 D8 K f
- memset(ReceiveInfo, 0x00, Rx_InfoLen);
6 G+ p7 A" `9 U5 p3 D - Rx_InfoLen=0;
% E! J4 A" O7 w/ W. L - </font>
复制代码严格意义上需要词法、语法分析器,这里就不多说,往深的说就就可以实现类似“micropython”的系统了。这里就不离FreeRTOS 信量太远 9 Q$ u/ X3 O$ Y4 g. X; T2 P( |
在stm32l4xx_it.c 文件中设置以下内容 - <font face="新宋体" size="3">extern SemaphoreHandle_t xScanSemaphore;
& P8 h b5 y% Q" }- s E0 L n - extern uint8_t ReceiveInfo[RXBUFFERSIZE];
$ [, n) q$ S. M$ a, s4 U( ` - extern uint8_t Rx_BuffLen, Rx_InfoLen;
! g8 e" m% p) F" b5 l8 Q* s
+ ?$ f/ f) N3 `- [
复制代码将CubeMX生成的LPUART1_IRQHandler(void)函数中增加以下内容: - void LPUART1_IRQHandler(void) {5 y) @1 A7 m. @! a) V$ T- T) T
- ………
' Z( ], l& |* W2 E( v# C$ g* n - uint32_t temp;8 K1 ?8 k& _% z( E! x
- static BaseType_t xHigherPriorityTaskWoken;
+ O+ v( I! m/ D) L - HAL_UART_IRQHandler(&hlpuart1);
9 a% |# a$ i" G
6 W: ]4 |* T2 z0 K- /* 如果是串口1IDLE中断 */' n: ~# q# l" ^; x
- if (RESET != __HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_IDLE)) {
3 c/ e% A! \' s6 \& y) N - /* 停止DMA接收 */
4 b, |$ `( U5 L: [) \* [ - HAL_UART_DMAStop(&hlpuart1);/ S$ Q0 E8 w! C% o& W/ h( m$ F
3 ?: p4 b5 _$ M- R$ x0 e- /* 获取DMA当前还有多少未填充 */
. [7 y/ V' k! i, R0 d - temp = __HAL_DMA_GET_COUNTER(&hdma_lpuart_rx); l8 K! U& V' I m% m# {
- # I1 w5 V& ]% S5 o) O# B
- /* 清除中断标志 */3 L( A2 Z% g9 y' o6 k
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
, F! k8 a8 P, G8 `" O
4 w- @7 `; r9 e, A$ j- /* 计算串口接收到的数据个数 */5 q: T( ~8 }* f3 c8 ~! x4 C0 I# W
- Rx_BuffLen = RXBUFFERSIZE - temp;
& i) l e; T; x' C3 T" d+ i4 R' u/ ] - Rx_InfoLen = Rx_BuffLen;
3 T. v3 t* R3 { - memcpy(ReceiveInfo,ReceiveBuff,Rx_InfoLen);
2 Q& g& p3 c$ A. I - memset(ReceiveBuff, 0x00, Rx_BuffLen);
|, P& c( z* a: S - /* 接收数据长度清零 */8 b7 A6 L8 F9 {! P4 b1 |% u8 l7 p. I5 v/ M$ Z
- Rx_BuffLen = 0;
1 S' o8 Y& f+ E& ]4 j6 a6 L; P - }2 ?# N. l/ ?9 T; n# F5 ~: T6 o
8 A& `+ t1 A1 \7 A4 I- if (utime_tick >= 200) {
, Z1 z" w5 f2 H& B ?) c9 \ - xSemaphoreGiveFromISR(xScanSemaphore, &xHigherPriorityTaskWoken);; f+ U1 T* Z' k& E% h/ T n
- utime_tick = 0;
# b( z* d! V$ t - }
% u! b( Y4 {; M - portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
# F' w' Q5 d3 ~ - …………% N/ Q$ l; U% M
- }# w/ u% v, D& H9 [ e
' Q! W6 j" g$ ^6 i+ \9 z; P
复制代码
* y& l* ^9 U: x4 K. t
编译项目,下载执行,打开串口工具,分别输入 Hello、Guten morgen、Gutentag、Guten abend、Guten nacht能从串口中得到开发板返回的以下内容: 9 m5 U+ x# Y) Y4 w) f
! O+ g8 h' P" a' s% d
没有严格的词法、语法分析, 就不能正确识别处理 “ Hello Guten tag" 之类的指令了,当然回复也是错误的。人机交互的内容就可参考状态机、模糊逻辑、人工智能等内容。
! c- N9 f9 L: S8 L
在增加这个信号量任务的时候,还可以将LED的任务、按键中断同时执行,通过控制LED的不同闪烁,同步返回不同的状态。 也可以配合音频文件、音频流等方式,发出标准的语音模式。可以将实验更加丰富、更加有趣。 4 r6 c3 Y! P/ E$ L- r
这样基本的基于FreeRTOS的信号量的人机交互就初步完成。 注意,创建多个任务时“FreeRTOSConfig.h”中#define configTOTAL_HEAP_SIZE 的值大小,太小了,某些任务不会被成功创建;
: k( S. G$ H& C5 J l! z
' e5 T, j' v" j8 Q8 K9 _3 K" v( V 《低功耗MCU运行FreeRTOS》培课程的准备内容(一)
2 M5 R8 D; G" W; P. R2 M《低功耗MCU运行FreeRTOS》培课程回顾(二)+ m( M/ b+ Y- n6 |9 T
《低功耗MCU运行FreeRTOS》培训课程(三) Atollic环境实验0 _9 O5 x2 X9 g* t. s, Y
《低功耗MCU运行FreeRTOS》培训课程(四) 创建任务
. ]% Y4 v+ u! G+ I& q+ G1 ~1 f1 P《低功耗MCU运行FreeRTOS》培训课程(六)消息队列的使用
9 z' {7 i* A; i
8 @2 f4 n" p. i1 D# n! e0 V" L" d# t |