试验三:信号量应用 创建FreeRTOS任务中断和同步(信号量方式)
& M9 f+ i4 |* n" \& P/ @
其实可以这里可以延伸利用信号量来实现串口数据的不定长收发,定长收发使用 “HAL_UART_Receive_IT(&huart2,Rx_Buffer,RxBufferSize);”即可,而不定长的方法在论坛中有网友也举了详细的创建方法(可在论坛中搜索“不定长”),也有修改HAL_UART_Receive_IT 函数的方法实现。 试验设想:这里是利用FreeRTOS信号量机制实现,主要结合不定长数据的接收,加上FreeRTOS信号量的控制,再通过串口数据接收任务中的消息解析操作,可实现简单的人机交互功能。 & K; e3 B" W t8 c
交互机制的选择: 面对消息队列、事件标志组、 信号量等多种交互机制下,可以参考下表进行任务间的交互。
' \9 E k, F% e3 U# v- d, ^
本实验选择互斥信号量来实现,相关的FreeRTOS 信号量 API 函数如下图: * \- n* b! n1 M; z$ I8 ]1 Q1 F7 P
使用CubeMX配置串口 DMA的内容略过(参考论坛的不定长DMA配置,FreeRTOS的配置见前篇),待工程建立好后,在代码中增加以下的内容:
. L, K& {- L3 ?8 f/ ]8 ^& a在main.h中增加定义:
, C7 f& }) [3 J8 J: v
在main.c中增加定义: - extern SemaphoreHandle_t xScanSemaphore; //串口中断处理的任务
3 O" o4 h9 f* N8 I+ _2 { - + g2 t% d: ~2 |- x& [* R; U* U
- uint8_t ReceiveBuff[RXBUFFERSIZE] = { 0 }; //接收缓冲区 l0 R4 S/ z( s+ v$ h% j
- uint8_t ReceiveInfo[RXBUFFERSIZE]={ 0 }; //接收待识别字符数组
e. i d1 X2 G+ k6 I. \8 N - 3 }. W, H! r* @% Z
- uint8_t Rx_BuffLen, Rx_InfoLen;
[ z. D& E& U0 P% ^6 W
4 M2 ^2 E! J, n- I4 @& |. V- void vStartLPUart1ScanTask(void const * argument);2 S0 Q# h, g" i, c. P4 L+ V) h
- #define Uart_TASK_PRIORITY ( tskIDLE_PRIORITY + 3UL )
复制代码
; c* v5 e' |& |
在main函数中,调整增加部分代码内容: - int main(void){
2 x" ?5 a& R. S! X9 T5 F! q - HAL_Init();
( B4 \! w. Q {2 G - SystemClock_Config();
8 x' G: ^# d; U: ~! ^$ i
/ J) a- i( p, {" H- T# C- MX_GPIO_Init();
+ i& p3 ~( `/ N& t - MX_DMA_Init();
9 }- n; L- D' B5 I4 U - MX_LPUART1_UART_Init();
- C% E# ~6 m: m* ~6 M3 J% {! t( f - MX_USB_OTG_FS_PCD_Init();1 v) b0 I; u* V5 ^- d
- 2 r3 Y0 A- M1 N
- MX_NVIC_Init(); //中断初始化中,增加空闲中断的捕获 __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);& d' y# }$ P: ~" X) q4 Q
- ' c# V: Q' l/ L. I( N- i4 V
- /* 开启下一次接收 */
- ?8 d4 s3 @- {* L/ Y - HAL_UART_Receive_DMA(&hlpuart1,(uint8_t*)ReceiveBuff,RXBUFFERSIZE);
d4 I* D2 N3 u7 e - //创建信号量
4 a! k; ?' z/ [! T* J4 `' | - xScanSemaphore = xSemaphoreCreateBinary();3 q$ b2 R" V: o
- //增加交互任务; i; y3 z( `# `( j% e
- xTaskCreate((TaskFunction_t) vStartLPUart1ScanTask, //任务函数; { w, W4 i0 h9 {. J! K, V
- "LPUart1S", //任务名称
1 v5 e9 \3 a2 e$ v I* y1 Y - 200, //任务栈大小
# {: f# h! S- |+ @& |6 j - NULL, //任务函数参数
8 g4 x" h! v+ j$ h9 X# c - Uart_TASK_PRIORITY, //任务执行等级4 m6 R: G, g/ l d
- (TaskHandle_t *) NULL); //任务句柄/ _$ Y. h- @6 |# i. z: C( L2 Y
- //启动任务
4 n; N, H% O1 S# u5 _ - vTaskStartScheduler();5 H2 t& X- g9 O9 a
- }
1 W5 I+ P' Y8 F4 r% b% m% R - 5 S' W" k8 p) [. r5 Q$ P
7 h$ \$ @5 q3 z3 {- R/ W$ x2 j, G9 c
复制代码( t5 X) z9 P& B0 } b* q2 m
创建串口中断任务内容 - void vStartLPUart1ScanTask(void const * argument){& g$ X6 d+ s! H" I4 W: \' h
- uint8_t *strp,*strp1;
: r( f/ R* Z. p1 s - configASSERT(xScanSemaphore);1 }8 p; ^- T) u8 h5 t" T5 T
- //获取信号量,等待时间为0,则xSemaphoreTake()在信号量无效时会立刻返回
% S2 e3 ]1 r2 h9 @1 c - xSemaphoreTake(xScanSemaphore, Scan_BLOCK);
9 e0 K+ I9 V* ]$ |. E - for (;;) {
5 R+ @( B0 j) g( }- n8 j3 _1 [+ R - //获取信号量,超时时间为portMAX_DELAY值2 h! k$ R4 x4 O. Y
- xSemaphoreTake(xScanSemaphore, portMAX_DELAY);
" u* y/ I1 ^. u3 W8 i1 L9 L7 T Y4 P - /* 打印接收到的数据长度 */
T0 j, f1 W1 s7 R2 F; S' c2 M - printf("rx_len=%d:\n%s \n", Rx_InfoLen,ReceiveInfo);
2 x; A6 U3 h1 Q - * V' [/ E, U" J3 e% Q o" U: z
- //回答Hello7 o+ {, }: O; X
- strp=strstr((char *)ReceiveInfo,(char *)"Hello");/ m$ g N; X0 L& g" `& C
- if (NULL != strp){, V1 E# z! c6 A) D8 q
- printf("\n Nice to meet you! \n");- f! s% F8 ]" u+ [0 Z6 \
- };- M9 W) e$ c3 y5 s% W+ e2 }
- , H1 }7 D2 d( T/ m1 i
- //回答guten tag/ guten morgen/ guten abend / guten nacht! H9 Q4 M* Q6 g; e
- strp=strstr((char *)ReceiveInfo,(char *)"Guten");
3 l7 ^ a) [6 s" B4 Q1 g1 V! Q9 Y - if (NULL != strp){
$ G1 C- w6 |; w! a- G; f0 B: s7 N- v - strp1=strstr((char *)ReceiveInfo,(char *)"morgen");/ m f7 x8 r* d' i# S
- if (NULL != strp1){9 j% J& q8 \- _+ e: @9 p
- printf("\n Guten Morgen ! \n");
$ g/ ^5 P3 [* v - }
7 i! G8 |- b+ c1 Q0 M8 j - strp1=strstr((char *)ReceiveInfo,(char *)"tag");. R2 Z" z& W( r
- if (NULL != strp1){
- W) m, q6 |: |& j - printf("\n Guten Tag ! \n");
/ C& i1 B$ y9 @3 M3 E7 D) a8 L - }
9 A; @" r3 x9 \0 r3 F5 D$ ~/ w* ? - strp1=strstr((char *)ReceiveInfo,(char *)"abend");
1 J9 U: b [" U1 F - if (NULL != strp1){& K7 f6 f3 E/ I+ ]( }
- printf("\n Guten Abend ! \n");
- O- E/ |: y1 o5 i - }
' }4 u& s' v, m% |9 K - strp1=strstr((char *)ReceiveInfo,(char *)"nacht");0 o0 h# @; x1 Y( T N
- if (NULL != strp1){
, p$ e5 C6 r! j7 Y- O7 G3 g7 D8 ] - printf("\n Guten Nacht ! \n");
$ `8 s l$ y L& P9 p - }
; z5 G3 i* h* s# j @" \; \/ s - }( e# u0 E: f- A. V n# s9 e Z
- /* 清空接收到的数据 */ 5 W: R# G) M8 }: ]! h
- memset(ReceiveInfo, 0x00, Rx_InfoLen);9 @! S. t$ z$ I/ ]7 O, o# G. H; A1 F5 G, Z h
- Rx_InfoLen=0;
$ c" n6 ^8 N% p$ P" X0 _/ \ - </font>
复制代码严格意义上需要词法、语法分析器,这里就不多说,往深的说就就可以实现类似“micropython”的系统了。这里就不离FreeRTOS 信量太远 l' D9 G' |9 d+ n4 C7 C
在stm32l4xx_it.c 文件中设置以下内容 - <font face="新宋体" size="3">extern SemaphoreHandle_t xScanSemaphore;4 Q7 _. ]; o" P/ t* V9 s% v) m+ e
- extern uint8_t ReceiveInfo[RXBUFFERSIZE];) Y. H; f) ` ?' D. k9 j$ _0 m
- extern uint8_t Rx_BuffLen, Rx_InfoLen;- Y0 C+ t" v! M( x4 \7 k b# _
- & l# p+ b) I8 k
复制代码将CubeMX生成的LPUART1_IRQHandler(void)函数中增加以下内容: - void LPUART1_IRQHandler(void) {
' A/ j' H4 b ~# T - ………
* D2 I) Z# A. k/ S( ^ - uint32_t temp;0 r8 e: R' J2 V' b# ?8 P; Q# h
- static BaseType_t xHigherPriorityTaskWoken;
% z# ]( r% @5 K/ h, \9 ^ - HAL_UART_IRQHandler(&hlpuart1);
/ @+ U, a4 ~. i/ t' i - " ~1 u- n. `$ v9 I( j" D# T# } M8 `
- /* 如果是串口1IDLE中断 */9 @2 L0 r) f0 ~) s7 X4 i0 w+ C
- if (RESET != __HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_IDLE)) {
" d) U- m i+ u& }+ v& `, u# f - /* 停止DMA接收 */
) Z' z v& b" T: @+ w0 \ - HAL_UART_DMAStop(&hlpuart1); M5 g! @# V4 k) m; g
) w. w* x8 N7 ]' I; H& b) f0 `- /* 获取DMA当前还有多少未填充 */# G& w X! c$ g7 g8 O/ r, `
- temp = __HAL_DMA_GET_COUNTER(&hdma_lpuart_rx);
7 e$ i# U: L5 t$ h! q& D
4 d% {* U: p" M+ h9 V- /* 清除中断标志 */
# P5 p. e* ^. R* N0 n, Z8 [' A - __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);: K9 l9 |2 z# M
+ Z7 \% a$ J# s. b* Y% s6 n' L( L6 ^- /* 计算串口接收到的数据个数 */8 X& |9 i; Z# C
- Rx_BuffLen = RXBUFFERSIZE - temp;
4 e7 @: \/ g8 j2 q - Rx_InfoLen = Rx_BuffLen;- ?. T' ~' V5 z: g; b, F& }- Y7 k
- memcpy(ReceiveInfo,ReceiveBuff,Rx_InfoLen);
6 u( Q8 i) M; |3 q - memset(ReceiveBuff, 0x00, Rx_BuffLen);
% N* q C* R' `( O2 B- l" N% z L - /* 接收数据长度清零 */
7 @/ C- v s. l, i9 u: q6 N - Rx_BuffLen = 0;- \8 l6 J+ y7 ~9 j
- }! |! q% C8 U' K" `0 T" C
7 q- X2 `$ @5 ?6 t: I1 D. T- if (utime_tick >= 200) {. {; @! d2 O- O$ e
- xSemaphoreGiveFromISR(xScanSemaphore, &xHigherPriorityTaskWoken);
q' a) x. X' o, k8 z - utime_tick = 0;
+ d8 D; E+ `) q- V$ D( `" r - }
, x) g) }1 x& i9 l ~) ^ - portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
: F o& V$ G6 N; U4 Q4 U0 b( [ - …………8 a1 k* o& R' S2 I3 n. [; E* C
- }8 h0 J+ K7 Q R: h1 V6 R
* {9 w' x" U' R
复制代码
4 f* K8 Q2 q) W( i2 {* k2 j0 r2 B
编译项目,下载执行,打开串口工具,分别输入 Hello、Guten morgen、Gutentag、Guten abend、Guten nacht能从串口中得到开发板返回的以下内容:
* x. ]' Q% j5 s
" ?1 T6 j! Q7 y' b D" g
没有严格的词法、语法分析, 就不能正确识别处理 “ Hello Guten tag" 之类的指令了,当然回复也是错误的。人机交互的内容就可参考状态机、模糊逻辑、人工智能等内容。 . }9 X1 `9 j- J7 G+ @( {3 X' |* Q$ v
在增加这个信号量任务的时候,还可以将LED的任务、按键中断同时执行,通过控制LED的不同闪烁,同步返回不同的状态。 也可以配合音频文件、音频流等方式,发出标准的语音模式。可以将实验更加丰富、更加有趣。
" R, f' X1 T3 C" k
这样基本的基于FreeRTOS的信号量的人机交互就初步完成。 注意,创建多个任务时“FreeRTOSConfig.h”中#define configTOTAL_HEAP_SIZE 的值大小,太小了,某些任务不会被成功创建; v) G- E7 \( i2 _0 O
1 g) e* d7 }+ G' B( w( E
《低功耗MCU运行FreeRTOS》培课程的准备内容(一)
& ^/ E# N6 L; f J# P8 w( K) G6 v& T《低功耗MCU运行FreeRTOS》培课程回顾(二)
0 L3 @# f* Y* I5 z% y, z* j《低功耗MCU运行FreeRTOS》培训课程(三) Atollic环境实验
) d. D G" G9 @5 g( R. O2 r《低功耗MCU运行FreeRTOS》培训课程(四) 创建任务 f1 o# C3 g9 K$ Y! P
《低功耗MCU运行FreeRTOS》培训课程(六)消息队列的使用
) b2 D I7 V6 ?% t4 B |
+ d+ c9 j5 |" J" R0 n |