试验三:信号量应用 创建FreeRTOS任务中断和同步(信号量方式), U. l, c- C3 v- L8 c3 l, q
其实可以这里可以延伸利用信号量来实现串口数据的不定长收发,定长收发使用 “HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize);”即可,而不定长的方法在论坛中有网友也举了详细的创建方法(可在论坛中搜索“不定长”),也有修改HAL_UART_Receive_IT 函数的方法实现。 试验设想:这里是利用FreeRTOS信号量机制实现,主要结合不定长数据的接收,加上FreeRTOS信号量的控制,再通过串口数据接收任务中的消息解析操作,可实现简单的人机交互功能。 # @( J9 d; X+ _) |% G3 a% k
交互机制的选择: 面对消息队列、事件标志组、 信号量等多种交互机制下,可以参考下表进行任务间的交互。 - I7 n% j- ~4 u H1 _, k$ q
本实验选择互斥信号量来实现,相关的FreeRTOS 信号量 API 函数如下图:
" c. J+ ?0 L# [; a: R
使用CubeMX配置串口 DMA的内容略过(参考论坛的不定长DMA配置,FreeRTOS的配置见前篇),待工程建立好后,在代码中增加以下的内容: / i/ l- c3 G: M' O" Y3 b
在main.h中增加定义: 1 a+ ]. T6 r( Z x# k7 k
在main.c中增加定义: - extern SemaphoreHandle_t xScanSemaphore; //串口中断处理的任务
" Q7 O6 _1 X7 K' L# ]! j* I9 J) C
8 A5 R! B* s# I1 s0 z0 B, q+ k- uint8_t ReceiveBuff[RXBUFFERSIZE] = { 0 }; //接收缓冲区7 G: H7 Q( p" b/ |( K0 m
- uint8_t ReceiveInfo[RXBUFFERSIZE]={ 0 }; //接收待识别字符数组
3 _/ i, p0 f! i4 o/ ?7 H$ d0 A* o - 4 g' T% m, [! j" D
- uint8_t Rx_BuffLen, Rx_InfoLen;+ b. ]$ o! O$ s9 D6 r
& A% D3 ^+ L) b) E- void vStartLPUart1ScanTask(void const * argument);
+ {0 o* O) q- Y+ \7 I9 U9 F, n9 D - #define Uart_TASK_PRIORITY ( tskIDLE_PRIORITY + 3UL )
复制代码& R( |! i! ~; w; y/ X
在main函数中,调整增加部分代码内容: - int main(void){- A! M o% T/ W* c, t" v
- HAL_Init();
. V3 k. H" v- w" G5 a4 C - SystemClock_Config();
! h% O$ \! Q" C - ) c% Y& g7 G0 `0 @, u& y
- MX_GPIO_Init();
% F# n" f# ]' a7 O1 F - MX_DMA_Init();6 H+ ~- B0 c- ]
- MX_LPUART1_UART_Init();# d5 n: n& o/ @0 X- Z4 @
- MX_USB_OTG_FS_PCD_Init();
2 ]% u0 z- m0 M7 k) N - . q' E0 m/ y5 H9 p9 D2 u
- MX_NVIC_Init(); //中断初始化中,增加空闲中断的捕获 __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);
2 ^, c. c2 k* L- S
! c0 o: p; [& X+ A% V- /* 开启下一次接收 */) a1 W F" ~; H+ i, ?( K ]8 D7 f
- HAL_UART_Receive_DMA(&hlpuart1,(uint8_t*)ReceiveBuff,RXBUFFERSIZE);
3 r# ?, A+ |0 l' k - //创建信号量
$ J$ w2 X4 T( m0 q: w' w6 h - xScanSemaphore = xSemaphoreCreateBinary();5 {0 W5 Y) ?* k$ h( {6 T, a9 J4 `
- //增加交互任务6 y4 R$ L3 \* a, ?9 m
- xTaskCreate((TaskFunction_t) vStartLPUart1ScanTask, //任务函数3 F% _2 G7 i' X4 O5 q$ v4 t
- "LPUart1S", //任务名称8 l- ]& y2 p Q M' n
- 200, //任务栈大小( D4 A5 ] O5 x5 W
- NULL, //任务函数参数
9 U2 U% m" Y o - Uart_TASK_PRIORITY, //任务执行等级& X! ?* R& i$ H( H* ~
- (TaskHandle_t *) NULL); //任务句柄3 w% l. u% w) n1 @- z
- //启动任务2 D1 l0 X$ |' O
- vTaskStartScheduler();
9 O+ f' @1 h8 I% z( @1 O) \! W! h0 w. l - } F Q; Y4 l/ O
( j8 N/ Y& i5 i; a
) S, Z0 m# P7 X5 [ _6 Q0 W
复制代码
- m9 X7 u; j6 Z) o; u# p( ]
创建串口中断任务内容 - void vStartLPUart1ScanTask(void const * argument){6 `7 C! f. i! Y n% d% k
- uint8_t *strp,*strp1;
& y0 [6 z: E3 I' j6 ~1 G4 c$ K - configASSERT(xScanSemaphore);& k* N( x( `2 @& e
- //获取信号量,等待时间为0,则xSemaphoreTake()在信号量无效时会立刻返回
- Q4 y* d) o( l; T1 N0 @) m - xSemaphoreTake(xScanSemaphore, Scan_BLOCK);
) Q- ^; K# T4 V9 l- p/ f3 S - for (;;) {! a8 r0 P* F9 Z
- //获取信号量,超时时间为portMAX_DELAY值
- O, M2 e" A7 i( ? y6 i5 P - xSemaphoreTake(xScanSemaphore, portMAX_DELAY);
2 x* F7 j: k* P- Q2 _4 ? - /* 打印接收到的数据长度 */
7 b7 J; j) j- f2 O - printf("rx_len=%d:\n%s \n", Rx_InfoLen,ReceiveInfo);
2 F" [5 I6 v! T - 2 |* G3 d3 P1 l- ?
- //回答Hello7 ?: z4 w# W* q4 d9 S, \
- strp=strstr((char *)ReceiveInfo,(char *)"Hello");
" C0 L2 W0 A& a: k - if (NULL != strp){
4 N; R, D9 y4 }( I( _- P3 a - printf("\n Nice to meet you! \n");2 z# C) z( P/ E9 M8 ^; F/ `
- };( h2 z. W' h l9 }$ U& H
# w; `# L: X+ E( Y- //回答guten tag/ guten morgen/ guten abend / guten nacht" ^: U4 v4 E: `7 {- x# ^, r
- strp=strstr((char *)ReceiveInfo,(char *)"Guten");; Z& h1 ?0 E; k4 a. d
- if (NULL != strp){
0 x8 w& t: `, m6 ~; J' @ - strp1=strstr((char *)ReceiveInfo,(char *)"morgen");. h6 ]5 u, @) S& i
- if (NULL != strp1){% h* a. K' E/ ]7 o6 N
- printf("\n Guten Morgen ! \n");
; W& r5 Q" k! s - }
: n, h, p) T2 U4 v# K/ \3 Y - strp1=strstr((char *)ReceiveInfo,(char *)"tag");' D0 E) [0 I3 U) f& s& r
- if (NULL != strp1){
3 V1 N, h% M3 q - printf("\n Guten Tag ! \n");! z- I) u- I; U
- }
! ~; M4 F' f9 `, I - strp1=strstr((char *)ReceiveInfo,(char *)"abend");
% C- q7 |0 B. D* @8 F - if (NULL != strp1){2 e; e6 R7 F, p% b" r8 Z3 {
- printf("\n Guten Abend ! \n");
1 ?% n3 `* ]1 H1 O, p - }) Y, Y- G/ T3 D3 o
- strp1=strstr((char *)ReceiveInfo,(char *)"nacht");0 d4 U5 o1 {- a8 }
- if (NULL != strp1){
* {8 [" [% A1 I" `- J - printf("\n Guten Nacht ! \n");
7 y3 O8 D( p: j' [. ?7 l - }; ?# v) l' Z7 R, Z0 y4 t2 J2 q
- }) s5 Q& y4 N1 Z9 m, F* Z
- /* 清空接收到的数据 */ # g2 W \8 P7 E ], _
- memset(ReceiveInfo, 0x00, Rx_InfoLen);
" F/ H; T% o% g7 W& C( c8 C - Rx_InfoLen=0;6 ~+ [8 X7 y' n4 a2 g, M
- </font>
复制代码严格意义上需要词法、语法分析器,这里就不多说,往深的说就就可以实现类似“micropython”的系统了。这里就不离FreeRTOS 信量太远
% `% G! w) r+ {0 k- I
在stm32l4xx_it.c 文件中设置以下内容 - <font face="新宋体" size="3">extern SemaphoreHandle_t xScanSemaphore;
; b3 T% A7 m' P! }! P0 p1 z* K+ h - extern uint8_t ReceiveInfo[RXBUFFERSIZE];
0 d$ }/ ~& }; w7 j& E7 G+ H! w3 A - extern uint8_t Rx_BuffLen, Rx_InfoLen;
/ j* j$ P/ m2 j# q; Z% J - 3 Y# X( A+ J Z- M1 M$ j+ c. K' s
复制代码将CubeMX生成的LPUART1_IRQHandler(void)函数中增加以下内容: - void LPUART1_IRQHandler(void) {" g) e+ p$ N L0 r" J
- ………) y: Q! }1 R$ o+ o
- uint32_t temp;& [* ?% b- Y: ?0 {
- static BaseType_t xHigherPriorityTaskWoken;- M$ `: N3 ]/ ^+ s
- HAL_UART_IRQHandler(&hlpuart1);
; n c' n$ M3 z' a
4 z: |6 q# C1 O# | m! z2 Q8 H" P- /* 如果是串口1IDLE中断 */- V0 q( T# j% E
- if (RESET != __HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_IDLE)) {
, J1 c1 N: B6 f3 g' T0 R3 b4 q# u - /* 停止DMA接收 */1 ^, ^8 b' D8 v9 x5 c
- HAL_UART_DMAStop(&hlpuart1);
+ M( k* H: c+ K
' V5 R8 A% {7 g% V* [, x- /* 获取DMA当前还有多少未填充 */9 u5 I6 t" Z: y$ R3 B [
- temp = __HAL_DMA_GET_COUNTER(&hdma_lpuart_rx);
. i+ u: C" V+ l+ |5 H' q* c: D: S6 D1 O
5 y. R6 e/ `! `: [- /* 清除中断标志 */% Q8 x/ `" U/ ^4 s/ h. Z+ J) _
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
' U9 S# T7 i9 K1 M; l2 z( L - " ~! y+ w9 u* ]* g# M2 I
- /* 计算串口接收到的数据个数 */
& G; N) W- q; t9 s `/ }+ Q) Y! ] - Rx_BuffLen = RXBUFFERSIZE - temp;$ G. V; R. F9 g5 `7 M- i9 B4 R/ j
- Rx_InfoLen = Rx_BuffLen;
3 I5 i( ~; _5 R' w3 @/ h4 I2 O - memcpy(ReceiveInfo,ReceiveBuff,Rx_InfoLen);
9 I: ^) S. V( e! g/ E3 M - memset(ReceiveBuff, 0x00, Rx_BuffLen);
- X$ s, c8 x. Z5 `& w) x, b* m& y - /* 接收数据长度清零 */* H; }1 ?5 L1 b; \4 w
- Rx_BuffLen = 0;( W5 I/ X7 m; F! V7 |2 W
- }
i9 `% z+ y$ ?5 R8 w( n - $ L! A) a, V( @. l, A
- if (utime_tick >= 200) { j1 I4 @' R) y$ O8 z1 v: ]5 r! z
- xSemaphoreGiveFromISR(xScanSemaphore, &xHigherPriorityTaskWoken);( Z! }3 p% k9 P. G! m
- utime_tick = 0;3 A- U! P2 t- i
- }1 A# G% ?. D' U: `/ J$ y+ K# ^
- portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
' K% r* e3 _% ^# m; k8 d. ` - …………* Q6 N* [1 z( c- k
- }/ `0 b0 B: O6 ^& T
( n% B: A# [' y1 G+ U9 W
复制代码
0 B& E4 E" p/ o3 y0 B- _
编译项目,下载执行,打开串口工具,分别输入 Hello、Guten morgen、Gutentag、Guten abend、Guten nacht能从串口中得到开发板返回的以下内容:
& d$ C/ G' N& Z( m2 B- D
/ O! c) \. y' K! j D+ ?4 N3 t! c
没有严格的词法、语法分析, 就不能正确识别处理 “ Hello Guten tag" 之类的指令了,当然回复也是错误的。人机交互的内容就可参考状态机、模糊逻辑、人工智能等内容。 3 u& A$ L+ X( B) e( F$ M5 u
在增加这个信号量任务的时候,还可以将LED的任务、按键中断同时执行,通过控制LED的不同闪烁,同步返回不同的状态。 也可以配合音频文件、音频流等方式,发出标准的语音模式。可以将实验更加丰富、更加有趣。 + K( p( y1 L5 `2 y! a: @3 i) {9 a
这样基本的基于FreeRTOS的信号量的人机交互就初步完成。 注意,创建多个任务时“FreeRTOSConfig.h”中#define configTOTAL_HEAP_SIZE 的值大小,太小了,某些任务不会被成功创建;
7 y1 I' |5 R7 r
0 C* ?" y+ d' G. Q! Q9 u 《低功耗MCU运行FreeRTOS》培课程的准备内容(一)5 w: Z1 ^$ \6 B: Z' \# e/ M
《低功耗MCU运行FreeRTOS》培课程回顾(二), \0 n) d. F% Y: `/ e' e
《低功耗MCU运行FreeRTOS》培训课程(三) Atollic环境实验& `2 Z4 N+ J% m; z
《低功耗MCU运行FreeRTOS》培训课程(四) 创建任务( d6 f$ y$ w A5 {& A4 S, w$ e! f/ d
《低功耗MCU运行FreeRTOS》培训课程(六)消息队列的使用
- [, I% R1 s+ C% W0 a9 e/ ^7 {# b2 D t% p c& S: C; W$ d
|