请选择 进入手机版 | 继续访问电脑版

你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

《低功耗MCU运行FreeRTOS》培训课程(五)信号量实现人机交互

[复制链接]
wolfgang 发布时间:2018-5-12 18:25
试验三:信号量应用
创建FreeRTOS任务中断和同步(信号量方式)
21.jpg

( 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
交互机制的选择:
  面对消息队列、事件标志组、 信号量等多种交互机制下,可以参考下表进行任务间的交互。
22.jpg

# p1 v: c: B% @( X& U  r
本实验选择互斥信号量来实现,相关的FreeRTOS 信号量 API 函数如下图:
19_1.jpg

* Y6 i) a' u) {' K6 F7 r
使用CubeMX配置串口 DMA的内容略过(参考论坛的不定长DMA配置,FreeRTOS的配置见前篇),待工程建立好后,在代码中增加以下的内容:
8 v" x% e0 o  [  a, d
在main.h中增加定义:
  1. #define RXBUFFERSIZE 255
复制代码
5 \9 d7 H$ j) n, @" r/ ]  f
在main.c中增加定义:
  1. extern SemaphoreHandle_t xScanSemaphore;   //串口中断处理的任务
    & z" h* B1 L4 w" J
  2. ; h  q& G1 h- e+ m0 Q$ k) D
  3. uint8_t ReceiveBuff[RXBUFFERSIZE] = { 0 }; //接收缓冲区
    ! V! H2 V8 i& Y( v1 d) i2 x
  4. uint8_t ReceiveInfo[RXBUFFERSIZE]={ 0 };   //接收待识别字符数组# K6 ]8 J! s' P& T

  5. 7 i; H/ G: }. L0 X4 S
  6. uint8_t Rx_BuffLen, Rx_InfoLen;' J! a2 c; h1 E5 V5 X, a
  7. 4 \1 P4 y5 j4 i7 W
  8. void vStartLPUart1ScanTask(void const * argument);9 `$ n2 K5 ?9 Y2 v/ P. r
  9. #define Uart_TASK_PRIORITY   ( tskIDLE_PRIORITY + 3UL )
复制代码
( q0 [+ U/ j* q5 g9 Q3 H
在main函数中,调整增加部分代码内容:
  1. int main(void){# q  g! U; G4 A! r1 ~0 N/ M
  2.   HAL_Init();
    " Q  I5 J( f) ~- i4 N
  3.   SystemClock_Config();# i# j' _7 j' R7 ^* y, z% ^8 Q6 v
  4. ; u0 C0 E+ H3 R# F0 r6 M) ?5 w4 I
  5.   MX_GPIO_Init();
    4 z: J: u3 ?, s7 w! t- m
  6.   MX_DMA_Init();
    9 V0 w: L" P$ S& h& n3 l0 A0 T
  7.   MX_LPUART1_UART_Init();9 ~& f! b$ G5 q) ^7 ^& s+ ~/ k
  8.   MX_USB_OTG_FS_PCD_Init();* P, Z# n9 t# V5 X* O

  9. ) p$ Q4 W8 X+ {7 c/ R% I, U
  10.   MX_NVIC_Init();  //中断初始化中,增加空闲中断的捕获 __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);
    - x4 [1 r9 C  a0 L  c

  11. 8 a/ ]( r, S/ ?5 K) a! H/ M% j
  12.   /*        开启下一次接收        */
    6 r( E( @% z3 ~& G8 \
  13.   HAL_UART_Receive_DMA(&hlpuart1,(uint8_t*)ReceiveBuff,RXBUFFERSIZE);1 [: F' _4 u, ~2 Q: ~2 n3 P
  14.   //创建信号量
      @' E! N3 m* j0 w% S% X& g( Q& Z
  15.   xScanSemaphore = xSemaphoreCreateBinary();  D* r( z+ O2 m! Y5 d) A+ [$ U
  16.   //增加交互任务
    6 b4 L6 Y: p, s% S) z) K
  17.   xTaskCreate((TaskFunction_t) vStartLPUart1ScanTask, //任务函数7 P; [' v" b8 C/ ]
  18.                     "LPUart1S",              //任务名称
    * N; C4 V4 C$ g) e0 g2 @6 b
  19.                     200,                     //任务栈大小% C! ]" d1 k1 s
  20.                     NULL,                    //任务函数参数$ E' D: [! S: W2 d7 A5 [  H; Z
  21.                     Uart_TASK_PRIORITY,      //任务执行等级9 i- n$ R8 n* H0 g
  22.                     (TaskHandle_t *) NULL);  //任务句柄
    % }: b7 l$ ]0 G, b1 C. q
  23.    //启动任务
    9 q- D4 ]* C" z4 U9 T
  24.    vTaskStartScheduler();
    , K' {# K) ^9 K. J  c# G
  25. }$ D/ h7 D: m' h& e
  26. " Y1 d) Q# A: O+ ^" n* U% q/ x

  27.   {) O- s3 _, v: y+ t9 `
复制代码
( H0 J9 p# k$ H# r4 B
创建串口中断任务内容
  1. void vStartLPUart1ScanTask(void const * argument){
    * |8 m; S8 W+ H$ T
  2.     uint8_t *strp,*strp1;. t- B; M; }  s7 [2 y0 W& J
  3.     configASSERT(xScanSemaphore);! E* U. i3 Q& w$ m. F
  4.     //获取信号量,等待时间为0,则xSemaphoreTake()在信号量无效时会立刻返回. {8 |1 R1 y3 y. G( p
  5.     xSemaphoreTake(xScanSemaphore, Scan_BLOCK);
    & Z* z, }% B: o' e
  6.     for (;;) {! |5 u) X4 Q1 f) t
  7.        //获取信号量,超时时间为portMAX_DELAY值: E* e; x: l& |% O4 @  \
  8.        xSemaphoreTake(xScanSemaphore, portMAX_DELAY);( e! [, a1 M$ r8 Q
  9.        /*        打印接收到的数据长度 */
    , w8 V. d1 @3 z
  10.        printf("rx_len=%d:\n%s \n", Rx_InfoLen,ReceiveInfo);
    8 E, S5 Q1 F- E7 V! u% t4 G

  11. 3 W5 l& ?- W, g& p+ [) X( @
  12.         //回答Hello
    ; O* Y; {, }7 w8 u7 b2 t3 b
  13.         strp=strstr((char *)ReceiveInfo,(char *)"Hello");
    " B8 U' J# k8 m7 \; G% q% B: P
  14.         if (NULL != strp){
    # y, J% V4 j" @* B9 u+ k' O" c
  15.             printf("\n Nice to meet you! \n");
    " x# ^, k( w' L( _* o  j
  16.         };: q4 q3 r7 ?2 Z( N

  17. / @4 a. y  g- ?. I+ K# o) {
  18.          //回答guten tag/ guten morgen/ guten abend / guten nacht
    7 f" L  J4 w" P$ h' _
  19.          strp=strstr((char *)ReceiveInfo,(char *)"Guten");
    ) i& Y( Y! w1 |7 a8 ^/ x
  20.          if (NULL != strp){( \9 p0 L3 E' H7 f
  21.             strp1=strstr((char *)ReceiveInfo,(char *)"morgen");9 r9 s, U3 o# ]
  22.             if (NULL != strp1){& b% Y4 ^1 Q- N- W+ m, [! G# k
  23.                 printf("\n Guten Morgen ! \n");
      S$ H+ [& ?6 i% ?% U1 }. @
  24.             }' X+ A2 K( D* G3 H" o
  25.             strp1=strstr((char *)ReceiveInfo,(char *)"tag");+ X  i  b5 a3 w* e2 Z- K
  26.             if (NULL != strp1){% {- U9 O& F6 f( S: n3 b
  27.                  printf("\n Guten Tag ! \n");
    8 l0 `8 r) y9 e5 s' M
  28.             }
    " V/ ]( S! h7 b! Z
  29.             strp1=strstr((char *)ReceiveInfo,(char *)"abend");
    4 s/ l0 F  u8 e7 T, [4 b4 x
  30.             if (NULL != strp1){" `- \& P# T  `8 _# W- Z4 W( H
  31.                  printf("\n Guten Abend ! \n");% \: R( {/ x5 x: H$ P, v
  32.             }
    1 s! y; H5 q" j% Q0 {6 R
  33.             strp1=strstr((char *)ReceiveInfo,(char *)"nacht");0 B0 L+ q: T. F
  34.             if (NULL != strp1){+ J' m* }' f6 E' D6 `
  35.                 printf("\n Guten Nacht ! \n");
    ' c% p% A9 Q: Z* s$ k5 J6 K+ D2 F
  36.             }
    5 i3 A3 k3 A3 ]* _4 B
  37.        }
    * `6 \0 o: w6 L$ F9 \& g2 y
  38.        /*        清空接收到的数据        */ . W2 x& S4 D8 K  f
  39.        memset(ReceiveInfo, 0x00, Rx_InfoLen);
    6 G+ p7 A" `9 U5 p3 D
  40.        Rx_InfoLen=0;
    % E! J4 A" O7 w/ W. L
  41.        </font>
复制代码
严格意义上需要词法、语法分析器,这里就不多说,往深的说就就可以实现类似“micropython”的系统了。这里就不离FreeRTOS 信量太远
9 Q$ u/ X3 O$ Y4 g. X; T2 P( |
在stm32l4xx_it.c 文件中设置以下内容
  1. <font face="新宋体" size="3">extern SemaphoreHandle_t xScanSemaphore;
    & P8 h  b5 y% Q" }- s  E0 L  n
  2. extern uint8_t ReceiveInfo[RXBUFFERSIZE];
    $ [, n) q$ S. M$ a, s4 U( `
  3. extern uint8_t  Rx_BuffLen, Rx_InfoLen;
    ! g8 e" m% p) F" b5 l8 Q* s

  4. + ?$ f/ f) N3 `- [
复制代码
将CubeMX生成的LPUART1_IRQHandler(void)函数中增加以下内容:
  1. void LPUART1_IRQHandler(void) {5 y) @1 A7 m. @! a) V$ T- T) T
  2.     ………
    ' Z( ], l& |* W2 E( v# C$ g* n
  3.     uint32_t temp;8 K1 ?8 k& _% z( E! x
  4.     static BaseType_t xHigherPriorityTaskWoken;
    + O+ v( I! m/ D) L
  5.     HAL_UART_IRQHandler(&hlpuart1);
    9 a% |# a$ i" G

  6. 6 W: ]4 |* T2 z0 K
  7.     /* 如果是串口1IDLE中断        */' n: ~# q# l" ^; x
  8.     if (RESET != __HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_IDLE)) {
    3 c/ e% A! \' s6 \& y) N
  9.        /*        停止DMA接收        */
    4 b, |$ `( U5 L: [) \* [
  10.        HAL_UART_DMAStop(&hlpuart1);/ S$ Q0 E8 w! C% o& W/ h( m$ F

  11. 3 ?: p4 b5 _$ M- R$ x0 e
  12.        /*        获取DMA当前还有多少未填充        */
    . [7 y/ V' k! i, R0 d
  13.        temp = __HAL_DMA_GET_COUNTER(&hdma_lpuart_rx);  l8 K! U& V' I  m% m# {
  14. # I1 w5 V& ]% S5 o) O# B
  15.        /*        清除中断标志        */3 L( A2 Z% g9 y' o6 k
  16.        __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
    , F! k8 a8 P, G8 `" O

  17. 4 w- @7 `; r9 e, A$ j
  18.        /*        计算串口接收到的数据个数        */5 q: T( ~8 }* f3 c8 ~! x4 C0 I# W
  19.        Rx_BuffLen = RXBUFFERSIZE - temp;
    & i) l  e; T; x' C3 T" d+ i4 R' u/ ]
  20.        Rx_InfoLen = Rx_BuffLen;
    3 T. v3 t* R3 {
  21.        memcpy(ReceiveInfo,ReceiveBuff,Rx_InfoLen);
    2 Q& g& p3 c$ A. I
  22.        memset(ReceiveBuff, 0x00, Rx_BuffLen);
      |, P& c( z* a: S
  23.        /*        接收数据长度清零        */8 b7 A6 L8 F9 {! P4 b1 |% u8 l7 p. I5 v/ M$ Z
  24.        Rx_BuffLen = 0;
    1 S' o8 Y& f+ E& ]4 j6 a6 L; P
  25.     }2 ?# N. l/ ?9 T; n# F5 ~: T6 o

  26. 8 A& `+ t1 A1 \7 A4 I
  27.     if (utime_tick >= 200) {
    , Z1 z" w5 f2 H& B  ?) c9 \
  28.         xSemaphoreGiveFromISR(xScanSemaphore, &xHigherPriorityTaskWoken);; f+ U1 T* Z' k& E% h/ T  n
  29.         utime_tick = 0;
    # b( z* d! V$ t
  30.      }
    % u! b( Y4 {; M
  31.      portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
    # F' w' Q5 d3 ~
  32.      …………% N/ Q$ l; U% M
  33. }# w/ u% v, D& H9 [  e

  34. ' 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
19_2.jpg
! 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
收藏 3 评论0 发布时间:2018-5-12 18:25

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版