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

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

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

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

' \9 E  k, F% e3 U# v- d, ^
本实验选择互斥信号量来实现,相关的FreeRTOS 信号量 API 函数如下图:
19_1.jpg
* \- n* b! n1 M; z$ I8 ]1 Q1 F7 P
使用CubeMX配置串口 DMA的内容略过(参考论坛的不定长DMA配置,FreeRTOS的配置见前篇),待工程建立好后,在代码中增加以下的内容:

. L, K& {- L3 ?8 f/ ]8 ^& a
在main.h中增加定义:
  1. #define RXBUFFERSIZE 255
复制代码

, C7 f& }) [3 J8 J: v
在main.c中增加定义:
  1. extern SemaphoreHandle_t xScanSemaphore;   //串口中断处理的任务
    3 O" o4 h9 f* N8 I+ _2 {
  2. + g2 t% d: ~2 |- x& [* R; U* U
  3. uint8_t ReceiveBuff[RXBUFFERSIZE] = { 0 }; //接收缓冲区  l0 R4 S/ z( s+ v$ h% j
  4. uint8_t ReceiveInfo[RXBUFFERSIZE]={ 0 };   //接收待识别字符数组
      e. i  d1 X2 G+ k6 I. \8 N
  5. 3 }. W, H! r* @% Z
  6. uint8_t Rx_BuffLen, Rx_InfoLen;
      [  z. D& E& U0 P% ^6 W

  7. 4 M2 ^2 E! J, n- I4 @& |. V
  8. void vStartLPUart1ScanTask(void const * argument);2 S0 Q# h, g" i, c. P4 L+ V) h
  9. #define Uart_TASK_PRIORITY   ( tskIDLE_PRIORITY + 3UL )
复制代码

; c* v5 e' |& |
在main函数中,调整增加部分代码内容:
  1. int main(void){
    2 x" ?5 a& R. S! X9 T5 F! q
  2.   HAL_Init();
    ( B4 \! w. Q  {2 G
  3.   SystemClock_Config();
    8 x' G: ^# d; U: ~! ^$ i

  4. / J) a- i( p, {" H- T# C
  5.   MX_GPIO_Init();
    + i& p3 ~( `/ N& t
  6.   MX_DMA_Init();
    9 }- n; L- D' B5 I4 U
  7.   MX_LPUART1_UART_Init();
    - C% E# ~6 m: m* ~6 M3 J% {! t( f
  8.   MX_USB_OTG_FS_PCD_Init();1 v) b0 I; u* V5 ^- d
  9. 2 r3 Y0 A- M1 N
  10.   MX_NVIC_Init();  //中断初始化中,增加空闲中断的捕获 __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);& d' y# }$ P: ~" X) q4 Q
  11. ' c# V: Q' l/ L. I( N- i4 V
  12.   /*        开启下一次接收        */
    - ?8 d4 s3 @- {* L/ Y
  13.   HAL_UART_Receive_DMA(&hlpuart1,(uint8_t*)ReceiveBuff,RXBUFFERSIZE);
      d4 I* D2 N3 u7 e
  14.   //创建信号量
    4 a! k; ?' z/ [! T* J4 `' |
  15.   xScanSemaphore = xSemaphoreCreateBinary();3 q$ b2 R" V: o
  16.   //增加交互任务; i; y3 z( `# `( j% e
  17.   xTaskCreate((TaskFunction_t) vStartLPUart1ScanTask, //任务函数; {  w, W4 i0 h9 {. J! K, V
  18.                     "LPUart1S",              //任务名称
    1 v5 e9 \3 a2 e$ v  I* y1 Y
  19.                     200,                     //任务栈大小
    # {: f# h! S- |+ @& |6 j
  20.                     NULL,                    //任务函数参数
    8 g4 x" h! v+ j$ h9 X# c
  21.                     Uart_TASK_PRIORITY,      //任务执行等级4 m6 R: G, g/ l  d
  22.                     (TaskHandle_t *) NULL);  //任务句柄/ _$ Y. h- @6 |# i. z: C( L2 Y
  23.    //启动任务
    4 n; N, H% O1 S# u5 _
  24.    vTaskStartScheduler();5 H2 t& X- g9 O9 a
  25. }
    1 W5 I+ P' Y8 F4 r% b% m% R
  26. 5 S' W" k8 p) [. r5 Q$ P

  27. 7 h$ \$ @5 q3 z3 {- R/ W$ x2 j, G9 c
复制代码
( t5 X) z9 P& B0 }  b* q2 m
创建串口中断任务内容
  1. void vStartLPUart1ScanTask(void const * argument){& g$ X6 d+ s! H" I4 W: \' h
  2.     uint8_t *strp,*strp1;
    : r( f/ R* Z. p1 s
  3.     configASSERT(xScanSemaphore);1 }8 p; ^- T) u8 h5 t" T5 T
  4.     //获取信号量,等待时间为0,则xSemaphoreTake()在信号量无效时会立刻返回
    % S2 e3 ]1 r2 h9 @1 c
  5.     xSemaphoreTake(xScanSemaphore, Scan_BLOCK);
    9 e0 K+ I9 V* ]$ |. E
  6.     for (;;) {
    5 R+ @( B0 j) g( }- n8 j3 _1 [+ R
  7.        //获取信号量,超时时间为portMAX_DELAY值2 h! k$ R4 x4 O. Y
  8.        xSemaphoreTake(xScanSemaphore, portMAX_DELAY);
    " u* y/ I1 ^. u3 W8 i1 L9 L7 T  Y4 P
  9.        /*        打印接收到的数据长度 */
      T0 j, f1 W1 s7 R2 F; S' c2 M
  10.        printf("rx_len=%d:\n%s \n", Rx_InfoLen,ReceiveInfo);
    2 x; A6 U3 h1 Q
  11. * V' [/ E, U" J3 e% Q  o" U: z
  12.         //回答Hello7 o+ {, }: O; X
  13.         strp=strstr((char *)ReceiveInfo,(char *)"Hello");/ m$ g  N; X0 L& g" `& C
  14.         if (NULL != strp){, V1 E# z! c6 A) D8 q
  15.             printf("\n Nice to meet you! \n");- f! s% F8 ]" u+ [0 Z6 \
  16.         };- M9 W) e$ c3 y5 s% W+ e2 }
  17. , H1 }7 D2 d( T/ m1 i
  18.          //回答guten tag/ guten morgen/ guten abend / guten nacht! H9 Q4 M* Q6 g; e
  19.          strp=strstr((char *)ReceiveInfo,(char *)"Guten");
    3 l7 ^  a) [6 s" B4 Q1 g1 V! Q9 Y
  20.          if (NULL != strp){
    $ G1 C- w6 |; w! a- G; f0 B: s7 N- v
  21.             strp1=strstr((char *)ReceiveInfo,(char *)"morgen");/ m  f7 x8 r* d' i# S
  22.             if (NULL != strp1){9 j% J& q8 \- _+ e: @9 p
  23.                 printf("\n Guten Morgen ! \n");
    $ g/ ^5 P3 [* v
  24.             }
    7 i! G8 |- b+ c1 Q0 M8 j
  25.             strp1=strstr((char *)ReceiveInfo,(char *)"tag");. R2 Z" z& W( r
  26.             if (NULL != strp1){
    - W) m, q6 |: |& j
  27.                  printf("\n Guten Tag ! \n");
    / C& i1 B$ y9 @3 M3 E7 D) a8 L
  28.             }
    9 A; @" r3 x9 \0 r3 F5 D$ ~/ w* ?
  29.             strp1=strstr((char *)ReceiveInfo,(char *)"abend");
    1 J9 U: b  [" U1 F
  30.             if (NULL != strp1){& K7 f6 f3 E/ I+ ]( }
  31.                  printf("\n Guten Abend ! \n");
    - O- E/ |: y1 o5 i
  32.             }
    ' }4 u& s' v, m% |9 K
  33.             strp1=strstr((char *)ReceiveInfo,(char *)"nacht");0 o0 h# @; x1 Y( T  N
  34.             if (NULL != strp1){
    , p$ e5 C6 r! j7 Y- O7 G3 g7 D8 ]
  35.                 printf("\n Guten Nacht ! \n");
    $ `8 s  l$ y  L& P9 p
  36.             }
    ; z5 G3 i* h* s# j  @" \; \/ s
  37.        }( e# u0 E: f- A. V  n# s9 e  Z
  38.        /*        清空接收到的数据        */ 5 W: R# G) M8 }: ]! h
  39.        memset(ReceiveInfo, 0x00, Rx_InfoLen);9 @! S. t$ z$ I/ ]7 O, o# G. H; A1 F5 G, Z  h
  40.        Rx_InfoLen=0;
    $ c" n6 ^8 N% p$ P" X0 _/ \
  41.        </font>
复制代码
严格意义上需要词法、语法分析器,这里就不多说,往深的说就就可以实现类似“micropython”的系统了。这里就不离FreeRTOS 信量太远
  l' D9 G' |9 d+ n4 C7 C
在stm32l4xx_it.c 文件中设置以下内容
  1. <font face="新宋体" size="3">extern SemaphoreHandle_t xScanSemaphore;4 Q7 _. ]; o" P/ t* V9 s% v) m+ e
  2. extern uint8_t ReceiveInfo[RXBUFFERSIZE];) Y. H; f) `  ?' D. k9 j$ _0 m
  3. extern uint8_t  Rx_BuffLen, Rx_InfoLen;- Y0 C+ t" v! M( x4 \7 k  b# _
  4. & l# p+ b) I8 k
复制代码
将CubeMX生成的LPUART1_IRQHandler(void)函数中增加以下内容:
  1. void LPUART1_IRQHandler(void) {
    ' A/ j' H4 b  ~# T
  2.     ………
    * D2 I) Z# A. k/ S( ^
  3.     uint32_t temp;0 r8 e: R' J2 V' b# ?8 P; Q# h
  4.     static BaseType_t xHigherPriorityTaskWoken;
    % z# ]( r% @5 K/ h, \9 ^
  5.     HAL_UART_IRQHandler(&hlpuart1);
    / @+ U, a4 ~. i/ t' i
  6. " ~1 u- n. `$ v9 I( j" D# T# }  M8 `
  7.     /* 如果是串口1IDLE中断        */9 @2 L0 r) f0 ~) s7 X4 i0 w+ C
  8.     if (RESET != __HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_IDLE)) {
    " d) U- m  i+ u& }+ v& `, u# f
  9.        /*        停止DMA接收        */
    ) Z' z  v& b" T: @+ w0 \
  10.        HAL_UART_DMAStop(&hlpuart1);  M5 g! @# V4 k) m; g

  11. ) w. w* x8 N7 ]' I; H& b) f0 `
  12.        /*        获取DMA当前还有多少未填充        */# G& w  X! c$ g7 g8 O/ r, `
  13.        temp = __HAL_DMA_GET_COUNTER(&hdma_lpuart_rx);
    7 e$ i# U: L5 t$ h! q& D

  14. 4 d% {* U: p" M+ h9 V
  15.        /*        清除中断标志        */
    # P5 p. e* ^. R* N0 n, Z8 [' A
  16.        __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);: K9 l9 |2 z# M

  17. + Z7 \% a$ J# s. b* Y% s6 n' L( L6 ^
  18.        /*        计算串口接收到的数据个数        */8 X& |9 i; Z# C
  19.        Rx_BuffLen = RXBUFFERSIZE - temp;
    4 e7 @: \/ g8 j2 q
  20.        Rx_InfoLen = Rx_BuffLen;- ?. T' ~' V5 z: g; b, F& }- Y7 k
  21.        memcpy(ReceiveInfo,ReceiveBuff,Rx_InfoLen);
    6 u( Q8 i) M; |3 q
  22.        memset(ReceiveBuff, 0x00, Rx_BuffLen);
    % N* q  C* R' `( O2 B- l" N% z  L
  23.        /*        接收数据长度清零        */
    7 @/ C- v  s. l, i9 u: q6 N
  24.        Rx_BuffLen = 0;- \8 l6 J+ y7 ~9 j
  25.     }! |! q% C8 U' K" `0 T" C

  26. 7 q- X2 `$ @5 ?6 t: I1 D. T
  27.     if (utime_tick >= 200) {. {; @! d2 O- O$ e
  28.         xSemaphoreGiveFromISR(xScanSemaphore, &xHigherPriorityTaskWoken);
      q' a) x. X' o, k8 z
  29.         utime_tick = 0;
    + d8 D; E+ `) q- V$ D( `" r
  30.      }
    , x) g) }1 x& i9 l  ~) ^
  31.      portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
    : F  o& V$ G6 N; U4 Q4 U0 b( [
  32.      …………8 a1 k* o& R' S2 I3 n. [; E* C
  33. }8 h0 J+ K7 Q  R: h1 V6 R

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

举报

0个回答

所属标签

相似分享

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