硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4820|回复: 9
收起左侧

[FreeRTOS教程] 第27章 FreeRTOS任务消息邮箱

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-9-8 14:42:59 | 显示全部楼层 |阅读模式



第27章      FreeRTOS任务消息邮箱



      本章节为大家讲解FreeRTOS消息队列(消息队列长度固定为1)的另一种实现方式----基于任务通知(Task Notifications)的消息队列,这里我们将这种方式实现的消息队列(消息队列长度固定为1)称之为任务消息邮箱。这种方式实现的消息队列效率更高,需要的RAM空间更小。当然,缺点也是有的,它没有第20章介绍的消息队列实现的功能全面(消息邮箱就是将消息队列长度设置为1的情况)。
      本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
27.1 任务通知(TaskNotifications)介绍
27.2 任务消息邮箱
27.3 任务消息邮箱API函数
27.4 实验例程说明(任务间通信)
27.5 实验例程说明(中断方式)
27.6      总结



27.1  任务通知(TaskNotifications)介绍


      (说明:第24,25,26和27章这部分基础知识是相同的)
      FreeRTOS每个已经创建的任务都有一个任务控制块(task control block),任务控制块就是一个结构体变量,用于记录任务的相关信息。结构体变量中有一个32位的变量成员ulNotifiedValue是专门用于任务通知的。
      通过任务通知方式可以实现计数信号量,二值信号量,事件标志组和消息邮箱(消息邮箱就是消息队列长度为1的情况)。使用方法与前面章节讲解的事件标志组和信号量基本相同,只是换了不同的函数来实现。任务通知方式实现的计数信号量,二值信号量,事件标志组和消息邮箱是通过修改变量ulNotifiedValue实现的:
(1)设置接收任务控制块中的变量ulNotifiedValue可以实现消息邮箱。
(2)如果接收任务控制块中的变量ulNotifiedValue还没有被其接收到,也可以用新数据覆盖原有数据,这就是覆盖方式的消息邮箱。
(3)设置接收任务控制块中的变量ulNotifiedValuebit0-bit31数值可以实现事件标志组。
(4)设置接收任务控制块中的变量ulNotifiedValue数值进行加一或者减一操作可以实现计数信号量和二值信号量。
        介绍了这么多,那么问题来了,采用这种方式有什么优势呢?根据官方的测试数据,唤醒由于信号量和事件标志组而处于阻塞态的任务,速度提升了45%,而且这种方式需要的RAM空间更小。但这种方式实现的信号量和事件标志组也有它的局限性,主要表现在以下两个方面:
(1)任务通知方式仅可以用在只有一个任务等待信号量,消息邮箱或者事件标志组的情况,不过实际项目项目中这种情况也是最多的。
(2)使用任务通知方式实现的消息邮箱替代前面章节讲解的消息队列时,发送消息的任务不支持超时等待,即消息队列中的数据已经满了,可以等待消息队列有空间可以存新的数据,而任务通知方式实现的消息邮箱不支持超时等待。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 14:46:39 | 显示全部楼层
27.2 任务消息邮箱


      前面第20章,我们对消息队列进行了讲解,而消息邮箱就是将消息队列的长度设置为1的情况。本章节讲解的任务消息邮箱与第20章讲解的消息队列长度是1时要实现的功能是一样的,不同的是调用的函数和消息存储的位置:
    (1)任务消息邮箱是通过任务控制块中的一个32位变量ulNotifiedValue对数据进行存取。第20章讲解的消息队列创建后会有自己可以存取数据的空间。
    (2)任务消息邮箱是通过函数xTaskNotifyWait()替代第20章讲解的函数xQueueReceive()实现从消息邮箱获取数据。
    (3)任务消息邮箱是通过函数xTaskNotify() 和 xQueueSendFromISR ()替代第20章讲解的函数xQueueSend () 和 xEventGroupSetBitsFromISR实现向消息邮箱存入数据。
    (4)任务消息邮箱通过函数xTaskNotify向消息邮箱中发送数据时,如果消息邮箱中上次的数据还没有处理,不支持超时等待,而消息队列的函数xQueueSend是支持超时等待的。
  
      具体任务间任务消息邮箱的实现过程和中断方式任务消息邮箱的实现过程可以参看第20.1.2小节和20.1.3小节即可。实际项目中,如果使用任务消息邮箱和消息队列都能实现相应功能,强烈建议使用任务消息邮箱。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 14:53:24 | 显示全部楼层
27.3 任务消息邮箱API函数


      使用如下9个函数可以实现FreeRTOS的任务消息邮箱:
    (1)    xTaskNotifyGive()
    (2)    vTaskNotifyGiveFromISR()
    (3)    ulTaskNotifyTake()
    (4)    xTaskNotify()
    (5)    xTaskNotifyAndQuery()
    (6)    xTaskNotifyAndQueryFromISR()
    (7)    xTaskNotifyFromISR()
    (8)    xTaskNotifyWait()
    (9)    xTaskNotifyStateClear()
      关于这9个函数的讲解及其使用方法可以看FreeRTOS在线版手册:
27.1.jpg

这里我们重点的说以下3个函数:
    (1)    xTaskNotify
    (2)    xTaskNotifyFromISR
    (3)    xTaskNotifyWait
因为本章节配套的例子使用的是这3个函数。

27.3.1 函数xTaskNotify


函数原型:
  1. BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, /* 任务句柄 */
  2.                          uint32_t ulValue,          /* 更新任务控制块中的变量ulNotifiedValue */
  3.                          eNotifyAction eAction );   /* 任务通知模式设置 */
复制代码
函数描述:
函数xTaskNotify通过设置任务控制块中的变量ulNotifiedValue可以在任务代码中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息通知(见27.1说明)。
(1)第1个参数是任务句柄。
(2)第2个参数是用来更新任务控制块中的32位变量ulNotifiedValue
(3)第3个参数是任务通知模式设置,支持以下5个参数:
27.2.png

(4)返回值,根据上面第3个参数的说明,将其设置为eSetValueWithoutOverwrite,有可能返回pdFALSE,其余所有情况都返回值pdPASS
使用这个函数要注意以下问题:
1.     任务创建后,任务控制块中的变量ulNotifiedValue初始计数值是0。
2.     默认配置此函数可以使用的的宏定义已经在FreeRTOS.h文件中使能:
      #define configUSE_TASK_NOTIFICATIONS        1
      当然,如果用户不需要使用任务通知功能相关的函数,可以在FreeRTOSConfig.h文件中配置此宏定义为0来禁止,这样创建的每个任务可以节省8个字节的需求。
3.     此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xTaskNotifyFromISR
4.     根据FreeRTOS的建议,实现二值信号量和计数信号量时使用函数xTaskNotifyGive()替代此函数xTaskNotify()。
使用举例:
  1. static TaskHandle_t xHandleTaskMsgPro = NULL;/* 任务句柄,操作此句柄前一定要保证相应任务已经创建 */
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: vTaskTaskUserIF
  5. *    功能说明: 接口消息处理。
  6. *    形    参: pvParameters 是在创建该任务时传递的形参
  7. *    返 回 值: 无
  8. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  9. *********************************************************************************************************
  10. */
  11. static void vTaskTaskUserIF(void *pvParameters)
  12. {
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.    
  17.     while(1)
  18.     {
  19.          ucKeyCode = bsp_GetKey();
  20.         
  21.          if (ucKeyCode != KEY_NONE)
  22.          {
  23.               switch (ucKeyCode)
  24.               {
  25.                   
  26.                        /* K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式 */
  27.                    case KEY_DOWN_K2:
  28.                        printf("K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式\\r\\n");
  29.                        xTaskNotify(xHandleTaskMsgPro,      /* 目标任务 */
  30.                                      ucCount++,              /* 发送数据 */
  31.                                      eSetValueWithOverwrite);/* 如果目标任务上次的数据还没有处理,会被覆盖 */      
  32.                        break;
  33.                   
  34.                    /* K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式 */
  35.                    case KEY_DOWN_K3:
  36.                        printf("K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式\\r\\n");
  37.                        /* 非覆盖模式的数据发送 */
  38.                        if(xTaskNotify(xHandleTaskMsgPro, ucCount++, eSetValueWithoutOverwrite) == pdPASS)
  39.                        {
  40.                             /* 目标任务的消息邮箱数据被更新 */
  41.                             printf("任务vTaskMsgPro的消息邮箱被更新\\r\\n");
  42.                        }
  43.                        else
  44.                        {
  45.                             /*  目标任务的消息邮箱数据未更新,因为上次的数据还没有处理,不能进行覆盖 */
  46.                             printf("任务vTaskMsgPro的消息邮箱数据未被更新\\r\\n");
  47.                        }
  48.                   
  49.                    /* 其他的键值不处理 */
  50.                    default:                    
  51.                        break;
  52.               }
  53.          }
  54.         
  55.          vTaskDelay(20);
  56.      }
  57. }
复制代码

27.3.2 函数xTaskNotifyFromISR


函数原型:
  1. BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, /* 任务句柄 */
  2.                                 uint32_t ulValue,           /* 更新任务控制块中的变量ulNotifiedValue */
  3.                                 eNotifyAction eAction,   /* 任务通知模式设置 */
  4.                              BaseType_t *pxHigherPriorityTaskWoken ); /* 高优先级任务是否被唤醒的状态保存 */
复制代码
函数描述:
函数xTaskNotifyFromISR通过设置任务控制块中的变量ulNotifiedValue可以在中断服务程序中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息通知(见27.1说明)。
(1)第1个参数是任务句柄。
(2)第2个参数是用来更新任务控制块中的32位变量ulNotifiedValue
(3)第3个参数是任务通知模式设置,支持以下5个参数:
27.3.png

(4)第4个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是pdTRUE,说明有高优先级任务要执行,否则没有。
(5)返回值,根据上面第3个参数的说明,将其设置为eSetValueWithoutOverwrite,有可能返回pdFALSE,其余所有情况都返回值pdPASS
使用这个函数要注意以下问题:
1.     任务创建后,任务控制块中的变量ulNotifiedValue初始计数值是0。
2.     默认配置此函数可以使用的的宏定义已经在FreeRTOS.h文件中使能:
       #define configUSE_TASK_NOTIFICATIONS        1
      当然,如果用户不需要使用任务通知功能相关的函数,可以在FreeRTOSConfig.h文件中配置此宏定义为0来禁止,这样创建的每个任务可以节省8个字节的需求。
3.     此函数是用于中断服务程序中调用的,故不可以在任务代码中调用此函数,任务代码中使用的是xTaskNotify
4.     根据FreeRTOS的建议,实现二值信号量和计数信号量时使用函数vTaskNotifyGiveFromISR ()替代此函数xTaskNotifyFromISR()。
使用举例:
  1. static TaskHandle_t xHandleTaskMsgPro = NULL;/* 任务句柄,操作此句柄前一定要保证相应任务已经创建 */
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: TIM1_IRQHandler
  5. *    功能说明: 定时器中断。                     
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static uint32_t g_uiCount = 0; /* 设置为静态变量,方便查看数据更新 */
  11. static void TIM1_IRQHandler (void)
  12. {
  13.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  14.      /* 中断消息处理,此处省略 */
  15.      ……
  16.      xTaskNotifyFromISR(xHandleTaskMsgPro,     /* 目标任务 */
  17.                          g_uiCount++,           /* 发送数据 */
  18.                          eSetValueWithOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据会被覆盖 */
  19.                         &xHigherPriorityTaskWoken);
  20.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  21.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  22. }
  23. /*
  24. *********************************************************************************************************
  25. *    函 数 名: TIM2_IRQHandler
  26. *    功能说明: 定时器中断。                     
  27. *    形    参: 无
  28. *    返 回 值: 无
  29. *********************************************************************************************************
  30. */
  31. static void TIM2_IRQHandler (void)
  32. {
  33.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  34.      /* 中断消息处理,此处省略 */
  35.      ……
  36.    
  37.      /* 通过此函数的返回值可以判断是否发送成功,这里没有做判断 */
  38.      xTaskNotifyFromISR(xHandleTaskMsgPro,       /* 目标任务 */
  39.                       g_uiCount++,              /* 发送数据 */
  40.                       eSetValueWithoutOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据不会被覆盖 */
  41.                       &xHigherPriorityTaskWoken);
  42.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  43.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  44. }
复制代码

27.3.3 函数xTaskNotifyWait


函数原型:
  1. BaseType_t xTaskNotifyWait(
  2. /* 设置函数执行前清零任务控制块中变量ulNotifiedValue那些位 */
  3. uint32_t ulBitsToClearOnEntry,
  4. /*设置函数退出前清零任务控制块中变量ulNotifiedValue那些位 */
  5. uint32_t ulBitsToClearOnExit,
  6. /* 保存任务控制块中的变量ulNotifiedValue到指针变量pulNotifiedValue所指向的存储单元 */
  7. uint32_t *pulNotificationValue,
  8. /* 等待消息通知的最大等待时间 */                    
  9. TickType_t xTicksToWait );
复制代码
函数描述:
函数xTaskNotifyWait可以在任务代码中实现任务事件标志组,任务计数信号量,任务消息邮箱和任务二值信号量四种方式的消息获取(见27.1说明)。
    (1)第1个参数ulBitsToClearOnEntry用于函数执行之前,将任务控制块中的变量ulNotifiedValue进行如下操作 :     
                                            ulNotifiedValue&= ~ulBitsToClearOnEntry
       简单的说就是参数ulBitsToClearOnEntry哪个位是1,那么变量ulNotifiedValue的那个位就会被清零。比如ulBitsToClearOnEntry = 0x01表示将变量ulNotifiedValue的bit0清零,又比如ulBitsToClearOnEntry = 0xffffffff表示将变量ulNotifiedValue的所有位清零。
    (2)第2个参数 ulBitsToClearOnExit用于函数退出前,将任务控制块中的变量ulNotifiedValue进行如下操作
                                           ulNotifiedValue&= ~ ulBitsToClearOnExit
       简单的说就是参数ulBitsToClearOnExit哪个位是1,那么变量ulNotifiedValue的那个位就会被清零。比如ulBitsToClearOnExit= 0x01表示将变量ulNotifiedValue的bit0清零,又比如ulBitsToClearOnExit= 0xffffffff表示将变量ulNotifiedValue的所有位清零。
    (3)第3个参数用于将任务控制块中的变量ulNotifiedValue保存到此参数指针所指向的存储单元。如果此参数没有用上,可以将其设置为NULL。
    (4)第4个参数是没有消息时,等待消息的最大等待时间,单位系统时钟节拍。
    (5)返回值,如果成功接收到消息返回pdTRUE,否则返回pdFALSE,比如在设置的超时时间内没有收到消息。
使用这个函数要注意以下问题:
1.     任务创建后,任务控制块中的变量ulNotifiedValue初始计数值是0。
2.     默认配置此函数可以使用的的宏定义已经在FreeRTOS.h文件中使能:
      #define configUSE_TASK_NOTIFICATIONS        1
      当然,如果用户不需要使用任务通知功能相关的函数,可以在FreeRTOSConfig.h文件中配置此宏定义为0来禁止,这样创建的每个任务可以节省8个字节的需求。
3.     如果用户将FreeRTOSConfig.h文件中的宏定义INCLUDE_vTaskSuspend配置为1且第2个参数配置为portMAX_DELAY,那么此函数会永久等待直到消息可用。
4.     根据FreeRTOS的建议,实现二值信号量和计数信号量时使用函数ulTaskNotifyTake ()替代此函数xTaskNotifyWait ()。
使用举例:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskMsgPro
  4. *    功能说明: 使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 3
  8. *********************************************************************************************************
  9. */
  10. static void vTaskMsgPro(void *pvParameters)
  11. {
  12.      BaseType_t xResult;
  13.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  14.      uint32_t ulValue;
  15.    
  16.     while(1)
  17.     {
  18.          /*
  19.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  20.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  21.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  22.                    的那个位就会被清零。
  23.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  24.         
  25.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  26.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  27.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  28.                    的那个位就会被清零。
  29.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  30.         
  31.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  32.          */
  33.         
  34.          xResult = xTaskNotifyWait(0x00000000,     
  35.                                       0xFFFFFFFF,     
  36.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  37.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  38.         
  39.          if( xResult == pdPASS )
  40.          {
  41.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  42.          }
  43.          else
  44.          {
  45.               /* 超时 */
  46.               bsp_LedToggle(1);
  47.               bsp_LedToggle(4);
  48.          }
  49.     }
  50. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:08:45 | 显示全部楼层
27.4 实验例程说明(任务间通信)



27.4.1 STM32F103开发板实验


配套例子:
    V4-331_FreeRTOS实验_任务消息邮箱
实验目的:
    1.     学习FreeRTOS的任务通知实现消息邮箱。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式。
    3.     K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式。
    4.     本实验通过函数xTaskNotify和xTaskNotifyWait实现消息邮箱的功能。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :LED闪烁。
              vTaskMsgPro任务:使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据。
              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
       FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK           0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_vTaskDelayUntil           1
  35. #define INCLUDE_vTaskDelay                1
  36. /* Cortex-M specific definitions. */
  37. #ifdef __NVIC_PRIO_BITS
  38.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  39.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  40. #else
  41.      #define configPRIO_BITS              4        /* 15 priority levels */
  42. #endif
  43. /* The lowest interrupt priority that can be used in a call to a "set priority"
  44. function. */
  45. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  46. /* The highest interrupt priority that can be used by any interrupt service
  47. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  48. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  49. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  50. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 72000000 )   
        系统主频72MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
27.4.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
      #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
      #definetskREADY_CHAR          ( 'R' ) 任务就绪
      #definetskDELETED_CHAR           ( 'D' )  任务删除
      #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
     #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 17 * 1024 ) )
系统栈大小分配:
27.5.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /*
  13.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  14.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  15.          系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
  16.      */
  17.    
  18.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitKey();     /* 初始化按键 */
  22. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucCount = 0;
  13.      uint8_t ucKeyCode;
  14.      uint8_t pcWriteBuffer[500];
  15.    
  16.     while(1)
  17.     {
  18.          ucKeyCode = bsp_GetKey();
  19.         
  20.          if (ucKeyCode != KEY_NONE)
  21.          {
  22.               switch (ucKeyCode)
  23.               {
  24.                    /* K1键按下 打印任务执行情况 */
  25.                    case KEY_DOWN_K1:         
  26.                        printf("=================================================\\r\\n");
  27.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  28.                        vTaskList((char *)&pcWriteBuffer);
  29.                        printf("%s\\r\\n", pcWriteBuffer);
  30.                   
  31.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  32.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  33.                        printf("%s\\r\\n", pcWriteBuffer);
  34.                        break;
  35.                   
  36.                        /* K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式 */
  37.                    case KEY_DOWN_K2:
  38.                        printf("K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式\\r\\n");
  39.                        xTaskNotify(xHandleTaskMsgPro,      /* 目标任务 */
  40.                                      ucCount++,              /* 发送数据 */
  41.                                      eSetValueWithOverwrite);/* 如果目标任务上次的数据还没有处理,会被覆盖 */      
  42.                        break;
  43.                   
  44.                    /* K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式 */
  45.                    case KEY_DOWN_K3:
  46.                        printf("K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式\\r\\n");
  47.                        /* 非覆盖模式的数据发送 */
  48.                        if(xTaskNotify(xHandleTaskMsgPro, ucCount++, eSetValueWithoutOverwrite) == pdPASS)
  49.                        {
  50.                             /* 目标任务的消息邮箱数据被更新 */
  51.                             printf("任务vTaskMsgPro的消息邮箱被更新\\r\\n");
  52.                        }
  53.                        else
  54.                        {
  55.                             /*  目标任务的消息邮箱数据未更新,因为上次的数据还没有处理,不能进行覆盖 */
  56.                             printf("任务vTaskMsgPro的消息邮箱数据未被更新\\r\\n");
  57.                        }
  58.                   
  59.                    /* 其他的键值不处理 */
  60.                    default:                    
  61.                        break;
  62.               }
  63.          }
  64.         
  65.          vTaskDelay(20);
  66.      }
  67. }
  68. /*
  69. *********************************************************************************************************
  70. *    函 数 名: vTaskLED
  71. *    功能说明: LED闪烁
  72. *    形    参: pvParameters 是在创建该任务时传递的形参
  73. *    返 回 值: 无
  74. *   优 先 级: 2
  75. *********************************************************************************************************
  76. */
  77. static void vTaskLED(void *pvParameters)
  78. {
  79.      TickType_t xLastWakeTime;
  80.      const TickType_t xFrequency = 200;
  81.      /* 获取当前的系统时间 */
  82.     xLastWakeTime = xTaskGetTickCount();
  83.    
  84.     while(1)
  85.     {
  86.          bsp_LedToggle(2);
  87.          bsp_LedToggle(3);
  88.         
  89.           /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  90.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  91.     }
  92. }
  93. /*
  94. *********************************************************************************************************
  95. *    函 数 名: vTaskMsgPro
  96. *    功能说明: 使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据
  97. *    形    参: pvParameters 是在创建该任务时传递的形参
  98. *    返 回 值: 无
  99. *   优 先 级: 3
  100. *********************************************************************************************************
  101. */
  102. static void vTaskMsgPro(void *pvParameters)
  103. {
  104.      BaseType_t xResult;
  105.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  106.      uint32_t ulValue;
  107.    
  108.     while(1)
  109.     {
  110.          /*
  111.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  112.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  113.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  114.                    的那个位就会被清零。
  115.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  116.         
  117.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  118.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  119.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  120.                    的那个位就会被清零。
  121.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  122.         
  123.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  124.          */
  125.         
  126.          xResult = xTaskNotifyWait(0x00000000,     
  127.                                       0xFFFFFFFF,     
  128.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  129.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  130.         
  131.          if( xResult == pdPASS )
  132.          {
  133.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  134.          }
  135.          else
  136.          {
  137.               /* 超时 */
  138.               bsp_LedToggle(1);
  139.               bsp_LedToggle(4);
  140.          }
  141.     }
  142. }
  143. /*
  144. *********************************************************************************************************
  145. *    函 数 名: vTaskStart
  146. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  147. *    形    参: pvParameters 是在创建该任务时传递的形参
  148. *    返 回 值: 无
  149. *   优 先 级: 4
  150. *********************************************************************************************************
  151. */
  152. static void vTaskStart(void *pvParameters)
  153. {
  154.     while(1)
  155.     {
  156.           /* 按键扫描 */
  157.          bsp_KeyScan();
  158.         vTaskDelay(10);
  159.     }
  160. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:13:41 | 显示全部楼层
27.4.2   STM32F407开发板实验


配套例子:
    V5-331_FreeRTOS实验_任务消息邮箱
实验目的:
    1.     学习FreeRTOS的任务通知实现消息邮箱。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式。
    3.     K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式。
    4.     本实验通过函数xTaskNotify和xTaskNotifyWait实现消息邮箱的功能。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :LED闪烁。
              vTaskMsgPro任务:使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据。
              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. /* Ensure stdint is only used by the compiler, and not the assembler. */
  6. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  7. #include <stdint.h>
  8. extern volatile uint32_t ulHighFrequencyTimerTicks;
  9. #endif
  10. #define configUSE_PREEMPTION         1
  11. #define configUSE_IDLE_HOOK          0
  12. #define configUSE_TICK_HOOK          0
  13. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  14. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  15. #define configMAX_PRIORITIES         ( 5 )
  16. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  17. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  18. #define configMAX_TASK_NAME_LEN      ( 16 )
  19. #define configUSE_TRACE_FACILITY      1
  20. #define configUSE_16_BIT_TICKS       0
  21. #define configIDLE_SHOULD_YIELD      1
  22. /* Run time and task stats gathering related definitions. */
  23. #define configGENERATE_RUN_TIME_STATS                1
  24. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  25. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  26. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  27. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  28. /* Co-routine definitions. */
  29. #define configUSE_CO_ROUTINES             0
  30. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  31. /* Set the following definitions to 1 to include the API function, or zero
  32. to exclude the API function. */
  33. #define INCLUDE_vTaskPrioritySet     1
  34. #define INCLUDE_uxTaskPriorityGet         1
  35. #define INCLUDE_vTaskDelete               1
  36. #define INCLUDE_vTaskCleanUpResources 0
  37. #define INCLUDE_vTaskSuspend              1
  38. #define INCLUDE_vTaskDelayUntil           1
  39. #define INCLUDE_vTaskDelay                1
  40. /* Cortex-M specific definitions. */
  41. #ifdef __NVIC_PRIO_BITS
  42.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  43.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  44. #else
  45.      #define configPRIO_BITS              4        /* 15 priority levels */
  46. #endif
  47. /* The lowest interrupt priority that can be used in a call to a "set priority"
  48. function. */
  49. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  50. /* The highest interrupt priority that can be used by any interrupt service
  51. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  52. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  53. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  54. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
       系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
         定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
27.6.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
    #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
27.7.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
硬件外设初始化
硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucCount = 0;
  13.      uint8_t ucKeyCode;
  14.      uint8_t pcWriteBuffer[500];
  15.    
  16.     while(1)
  17.     {
  18.          ucKeyCode = bsp_GetKey();
  19.         
  20.          if (ucKeyCode != KEY_NONE)
  21.          {
  22.               switch (ucKeyCode)
  23.               {
  24.                    /* K1键按下 打印任务执行情况 */
  25.                    case KEY_DOWN_K1:         
  26.                        printf("=================================================\\r\\n");
  27.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  28.                        vTaskList((char *)&pcWriteBuffer);
  29.                        printf("%s\\r\\n", pcWriteBuffer);
  30.                   
  31.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  32.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  33.                        printf("%s\\r\\n", pcWriteBuffer);
  34.                        break;
  35.                   
  36.                        /* K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式 */
  37.                    case KEY_DOWN_K2:
  38.                        printf("K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式\\r\\n");
  39.                        xTaskNotify(xHandleTaskMsgPro,      /* 目标任务 */
  40.                                      ucCount++,              /* 发送数据 */
  41.                                      eSetValueWithOverwrite);/* 如果目标任务上次的数据还没有处理,会被覆盖 */      
  42.                        break;
  43.                   
  44.                    /* K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式 */
  45.                    case KEY_DOWN_K3:
  46.                        printf("K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式\\r\\n");
  47.                        /* 非覆盖模式的数据发送 */
  48.                        if(xTaskNotify(xHandleTaskMsgPro, ucCount++, eSetValueWithoutOverwrite) == pdPASS)
  49.                        {
  50.                             /* 目标任务的消息邮箱数据被更新 */
  51.                             printf("任务vTaskMsgPro的消息邮箱被更新\\r\\n");
  52.                        }
  53.                        else
  54.                        {
  55.                             /*  目标任务的消息邮箱数据未更新,因为上次的数据还没有处理,不能进行覆盖 */
  56.                             printf("任务vTaskMsgPro的消息邮箱数据未被更新\\r\\n");
  57.                        }
  58.                   
  59.                    /* 其他的键值不处理 */
  60.                    default:                    
  61.                        break;
  62.               }
  63.          }
  64.         
  65.          vTaskDelay(20);
  66.      }
  67. }
  68. /*
  69. *********************************************************************************************************
  70. *    函 数 名: vTaskLED
  71. *    功能说明: LED闪烁
  72. *    形    参: pvParameters 是在创建该任务时传递的形参
  73. *    返 回 值: 无
  74. *   优 先 级: 2
  75. *********************************************************************************************************
  76. */
  77. static void vTaskLED(void *pvParameters)
  78. {
  79.      TickType_t xLastWakeTime;
  80.      const TickType_t xFrequency = 200;
  81.      /* 获取当前的系统时间 */
  82.     xLastWakeTime = xTaskGetTickCount();
  83.    
  84.     while(1)
  85.     {
  86.          bsp_LedToggle(2);
  87.          bsp_LedToggle(3);
  88.         
  89.           /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  90.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  91.     }
  92. }
  93. /*
  94. *********************************************************************************************************
  95. *    函 数 名: vTaskMsgPro
  96. *    功能说明: 使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据
  97. *    形    参: pvParameters 是在创建该任务时传递的形参
  98. *    返 回 值: 无
  99. *   优 先 级: 3
  100. *********************************************************************************************************
  101. */
  102. static void vTaskMsgPro(void *pvParameters)
  103. {
  104.      BaseType_t xResult;
  105.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  106.      uint32_t ulValue;
  107.    
  108.     while(1)
  109.     {
  110.          /*
  111.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  112.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  113.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  114.                    的那个位就会被清零。
  115.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  116.         
  117.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  118.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  119.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  120.                    的那个位就会被清零。
  121.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  122.         
  123.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  124.          */
  125.         
  126.          xResult = xTaskNotifyWait(0x00000000,     
  127.                                       0xFFFFFFFF,     
  128.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  129.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  130.         
  131.          if( xResult == pdPASS )
  132.          {
  133.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  134.          }
  135.          else
  136.          {
  137.               /* 超时 */
  138.               bsp_LedToggle(1);
  139.               bsp_LedToggle(4);
  140.          }
  141.     }
  142. }
  143. /*
  144. *********************************************************************************************************
  145. *    函 数 名: vTaskStart
  146. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  147. *    形    参: pvParameters 是在创建该任务时传递的形参
  148. *    返 回 值: 无
  149. *   优 先 级: 4
  150. *********************************************************************************************************
  151. */
  152. static void vTaskStart(void *pvParameters)
  153. {
  154.     while(1)
  155.     {
  156.           /* 按键扫描 */
  157.          bsp_KeyScan();
  158.         vTaskDelay(10);
  159.     }
  160. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:19:33 | 显示全部楼层
27.4.3   STM32F429开发板实验


配套例子:
      V6-331_FreeRTOS实验_任务消息邮箱
实验目的:
      1.     学习FreeRTOS的任务通知实现消息邮箱。
实验内容:
      1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
      2.      K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式。
      3.      K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式。
      4.      本实验通过函数xTaskNotify和xTaskNotifyWait实现消息邮箱的功能。
      5.      各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :LED闪烁。
              vTaskMsgPro任务:使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据。
              vTaskStart任务   :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_vTaskDelayUntil           1
  35. #define INCLUDE_vTaskDelay                1
  36. /* Cortex-M specific definitions. */
  37. #ifdef __NVIC_PRIO_BITS
  38.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  39.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  40. #else
  41.      #define configPRIO_BITS              4        /* 15 priority levels */
  42. #endif
  43. /* The lowest interrupt priority that can be used in a call to a "set priority"
  44. function. */
  45. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  46. /* The highest interrupt priority that can be used by any interrupt service
  47. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  48. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  49. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  50. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码

几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
       定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、#define configUSE_COUNTING_SEMAPHORES   1
        使能计数信号量。
7、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
       定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
27.8.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
      #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
      #definetskREADY_CHAR           ( 'R' ) 任务就绪
      #definetskDELETED_CHAR           ( 'D' )  任务删除
      #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
27.9.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码

硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  20.      bsp_InitUart();    /* 初始化串口 */
  21.      bsp_InitKey();     /* 初始化按键变量 */
  22.      bsp_InitExtIO();   /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  23.      bsp_InitLed();     /* 初始LED指示灯端口 */
  24. }
复制代码

FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码

四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucCount = 0;
  13.      uint8_t ucKeyCode;
  14.      uint8_t pcWriteBuffer[500];
  15.    
  16.     while(1)
  17.     {
  18.          ucKeyCode = bsp_GetKey();
  19.         
  20.          if (ucKeyCode != KEY_NONE)
  21.          {
  22.               switch (ucKeyCode)
  23.               {
  24.                    /* K1键按下 打印任务执行情况 */
  25.                    case KEY_DOWN_K1:         
  26.                        printf("=================================================\\r\\n");
  27.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  28.                        vTaskList((char *)&pcWriteBuffer);
  29.                        printf("%s\\r\\n", pcWriteBuffer);
  30.                   
  31.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  32.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  33.                        printf("%s\\r\\n", pcWriteBuffer);
  34.                        break;
  35.                   
  36.                        /* K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式 */
  37.                    case KEY_DOWN_K2:
  38.                        printf("K2键按下,发送消息邮箱数据给任务vTaskMsgPro,覆盖方式\\r\\n");
  39.                        xTaskNotify(xHandleTaskMsgPro,      /* 目标任务 */
  40.                                      ucCount++,              /* 发送数据 */
  41.                                      eSetValueWithOverwrite);/* 如果目标任务上次的数据还没有处理,会被覆盖 */      
  42.                        break;
  43.                   
  44.                    /* K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式 */
  45.                    case KEY_DOWN_K3:
  46.                        printf("K3键按下,发送消息邮箱数据给任务vTaskMsgPro,非覆盖方式\\r\\n");
  47.                        /* 非覆盖模式的数据发送 */
  48.                        if(xTaskNotify(xHandleTaskMsgPro, ucCount++, eSetValueWithoutOverwrite) == pdPASS)
  49.                        {
  50.                             /* 目标任务的消息邮箱数据被更新 */
  51.                             printf("任务vTaskMsgPro的消息邮箱被更新\\r\\n");
  52.                        }
  53.                        else
  54.                        {
  55.                             /*  目标任务的消息邮箱数据未更新,因为上次的数据还没有处理,不能进行覆盖 */
  56.                             printf("任务vTaskMsgPro的消息邮箱数据未被更新\\r\\n");
  57.                        }
  58.                   
  59.                    /* 其他的键值不处理 */
  60.                    default:                    
  61.                        break;
  62.               }
  63.          }
  64.         
  65.          vTaskDelay(20);
  66.      }
  67. }
  68. /*
  69. *********************************************************************************************************
  70. *    函 数 名: vTaskLED
  71. *    功能说明: LED闪烁
  72. *    形    参: pvParameters 是在创建该任务时传递的形参
  73. *    返 回 值: 无
  74. *   优 先 级: 2
  75. *********************************************************************************************************
  76. */
  77. static void vTaskLED(void *pvParameters)
  78. {
  79.      TickType_t xLastWakeTime;
  80.      const TickType_t xFrequency = 200;
  81.      /* 获取当前的系统时间 */
  82.     xLastWakeTime = xTaskGetTickCount();
  83.    
  84.     while(1)
  85.     {
  86.          bsp_LedToggle(2);
  87.          bsp_LedToggle(3);
  88.         
  89.           /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  90.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  91.     }
  92. }
  93. /*
  94. *********************************************************************************************************
  95. *    函 数 名: vTaskMsgPro
  96. *    功能说明: 使用函数xTaskNotifyWait接收任务vTaskTaskUserIF发送的消息邮箱数据
  97. *    形    参: pvParameters 是在创建该任务时传递的形参
  98. *    返 回 值: 无
  99. *   优 先 级: 3
  100. *********************************************************************************************************
  101. */
  102. static void vTaskMsgPro(void *pvParameters)
  103. {
  104.      BaseType_t xResult;
  105.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  106.      uint32_t ulValue;
  107.    
  108.     while(1)
  109.     {
  110.          /*
  111.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  112.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  113.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  114.                    的那个位就会被清零。
  115.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  116.         
  117.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  118.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  119.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  120.                    的那个位就会被清零。
  121.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  122.         
  123.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  124.          */
  125.         
  126.          xResult = xTaskNotifyWait(0x00000000,     
  127.                                       0xFFFFFFFF,     
  128.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  129.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  130.         
  131.          if( xResult == pdPASS )
  132.          {
  133.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  134.          }
  135.          else
  136.          {
  137.               /* 超时 */
  138.               bsp_LedToggle(1);
  139.               bsp_LedToggle(4);
  140.          }
  141.     }
  142. }
  143. /*
  144. *********************************************************************************************************
  145. *    函 数 名: vTaskStart
  146. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  147. *    形    参: pvParameters 是在创建该任务时传递的形参
  148. *    返 回 值: 无
  149. *   优 先 级: 4
  150. *********************************************************************************************************
  151. */
  152. static void vTaskStart(void *pvParameters)
  153. {
  154.     while(1)
  155.     {
  156.           /* 按键扫描 */
  157.          bsp_KeyScan();
  158.         vTaskDelay(10);
  159.     }
  160. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:35:52 | 显示全部楼层
27.5 实验例程说明(中断方式)



27.5.1 STM32F103开发板实验


配套例子:
     V4-332_FreeRTOS实验_任务消息邮箱(中断方式)
实验目的:
     1.     学习FreeRTOS的任务通知实现任务消息邮箱(中断方式)。
实验内容:
     1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
     2.     K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送同步信号。
     3.     K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据。
     4.     本实验通过函数xTaskNotifyFromISR和xTaskNotifyWait实现消息邮箱的中断方式功能。
     5.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :使用函数ulTaskNotifyTake接收定时器中断发生的消息。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 72000000 )  
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 6 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_xTaskResumeFromISR        1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 72000000 )   
        系统主频72MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 6 )
        定义可供用户使用的最大优先级数,如果这个定义的是6,那么用户可以使用的优先级号是0,1,2,3,4,5不包含6,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
27.10.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
      #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
      #definetskREADY_CHAR           ( 'R' ) 任务就绪
      #definetskDELETED_CHAR           ( 'D' )  任务删除
      #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 17 * 1024 ) )
系统栈大小分配:
27.11.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /*
  13.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  14.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  15.          系统时钟缺省配置为72MHz,如果需要更改,可以修改 system_stm32f10x.c 文件
  16.      */
  17.    
  18.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21.      bsp_InitKey();     /* 初始化按键 */
  22.      bsp_InitHardTimer(); /* 初始化TIM2定时器 */
  23. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据 */
  35.                    case KEY_DOWN_K2:
  36.                        printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息
  37. 邮箱数据\\r\\n");
  38.                        bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
  39.                        break;
  40.                   
  41.                    /* K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据 */
  42.                    case KEY_DOWN_K3:
  43.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息
  44. 邮箱数据\\r\\n");
  45.                        bsp_StartHardTimer(2 ,50000, (void *)TIM_CallBack2);
  46.                        break;
  47.                    /* 其他的键值不处理 */
  48.                    default:                    
  49.                        break;
  50.               }
  51.          }
  52.         
  53.          vTaskDelay(20);
  54.      }
  55. }
  56. /*
  57. *********************************************************************************************************
  58. *    函 数 名: vTaskLED
  59. *    功能说明: LED闪烁
  60. *    形    参: pvParameters 是在创建该任务时传递的形参
  61. *    返 回 值: 无
  62. *   优 先 级: 2
  63. *********************************************************************************************************
  64. */
  65. static void vTaskLED(void *pvParameters)
  66. {
  67.      TickType_t xLastWakeTime;
  68.      const TickType_t xFrequency = 200;
  69.      /* 获取当前的系统时间 */
  70.     xLastWakeTime = xTaskGetTickCount();
  71.    
  72.     while(1)
  73.     {
  74.          bsp_LedToggle(2);
  75.          bsp_LedToggle(3);
  76.         
  77.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  78.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  79.     }
  80. }
  81. /*
  82. *********************************************************************************************************
  83. *    函 数 名: vTaskMsgPro
  84. *    功能说明: 使用函数xTaskNotifyWait接收定时器中断发送的任务事件标志消息。
  85. *    形    参: pvParameters 是在创建该任务时传递的形参
  86. *    返 回 值: 无
  87. *   优 先 级: 3
  88. *********************************************************************************************************
  89. */
  90. static void vTaskMsgPro(void *pvParameters)
  91. {
  92.      BaseType_t xResult;
  93.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  94.      uint32_t ulValue;
  95.    
  96.     while(1)
  97.     {
  98.          /*
  99.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  100.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  101.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  102.                    的那个位就会被清零。
  103.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  104.         
  105.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  106.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  107.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  108.                    的那个位就会被清零。
  109.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  110.         
  111.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  112.          */
  113.         
  114.          xResult = xTaskNotifyWait(0x00000000,     
  115.                                       0xFFFFFFFF,     
  116.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  117.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  118.         
  119.          if( xResult == pdPASS )
  120.          {
  121.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  122.          }
  123.          else
  124.          {
  125.               /* 超时 */
  126.               bsp_LedToggle(1);
  127.               bsp_LedToggle(4);
  128.          }
  129.     }
  130. }
  131. /*
  132. *********************************************************************************************************
  133. *    函 数 名: vTaskStart
  134. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  135. *    形    参: pvParameters 是在创建该任务时传递的形参
  136. *    返 回 值: 无
  137. *   优 先 级: 4
  138. *********************************************************************************************************
  139. */
  140. static void vTaskStart(void *pvParameters)
  141. {
  142.     while(1)
  143.     {
  144.           /* 按键扫描 */
  145.          bsp_KeyScan();
  146.         vTaskDelay(10);
  147.     }
  148. }
复制代码
定时器中断回调函数中给任务发送任务消息邮箱数据:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数vTaskNotifyGiveFromISR在中断服务程序中的使用方法。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM_CallBack1和TIM_CallBack2
  4. *    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static uint32_t g_uiCount = 0; /* 设置为静态变量,方便查看数据更新 */
  10. static void TIM_CallBack1(void)
  11. {
  12.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  13.      xTaskNotifyFromISR(xHandleTaskMsgPro,     /* 目标任务 */
  14.                          g_uiCount++,           /* 发送数据 */
  15.                          eSetValueWithOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据会被覆盖 */
  16.                         &xHigherPriorityTaskWoken);
  17.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  18.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  19. }
  20. static void TIM_CallBack2(void)
  21. {
  22.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  23.    
  24.      /* 通过此函数的返回值可以判断是否发送成功,这里没有做判断 */
  25.      xTaskNotifyFromISR(xHandleTaskMsgPro,       /* 目标任务 */
  26.                       g_uiCount++,              /* 发送数据 */
  27.                       eSetValueWithoutOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据不会被覆盖 */
  28.                       &xHigherPriorityTaskWoken);
  29.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  30.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  31. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:40:13 | 显示全部楼层
27.5.2 STM32F407开发板实验


配套例子:
    V5-332_FreeRTOS实验_任务消息邮箱(中断方式)
实验目的:
    1.     学习FreeRTOS的任务通知实现任务消息邮箱(中断方式)。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送同步信号。
    3.     K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据。
    4.     本实验通过函数xTaskNotifyFromISR和xTaskNotifyWait实现消息邮箱的中断方式功能。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :使用函数ulTaskNotifyTake接收定时器中断发生的消息。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
    FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. /* Ensure stdint is only used by the compiler, and not the assembler. */
  6. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  7. #include <stdint.h>
  8. extern volatile uint32_t ulHighFrequencyTimerTicks;
  9. #endif
  10. #define configUSE_PREEMPTION         1
  11. #define configUSE_IDLE_HOOK          0
  12. #define configUSE_TICK_HOOK          0
  13. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  14. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  15. #define configMAX_PRIORITIES         ( 6 )
  16. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  17. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  18. #define configMAX_TASK_NAME_LEN      ( 16 )
  19. #define configUSE_TRACE_FACILITY      1
  20. #define configUSE_16_BIT_TICKS       0
  21. #define configIDLE_SHOULD_YIELD      1
  22. /* Run time and task stats gathering related definitions. */
  23. #define configGENERATE_RUN_TIME_STATS                1
  24. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  25. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  26. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  27. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  28. /* Co-routine definitions. */
  29. #define configUSE_CO_ROUTINES             0
  30. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  31. /* Set the following definitions to 1 to include the API function, or zero
  32. to exclude the API function. */
  33. #define INCLUDE_vTaskPrioritySet          1
  34. #define INCLUDE_uxTaskPriorityGet         1
  35. #define INCLUDE_vTaskDelete               1
  36. #define INCLUDE_vTaskCleanUpResources      0
  37. #define INCLUDE_vTaskSuspend              1
  38. #define INCLUDE_xTaskResumeFromISR        1
  39. #define INCLUDE_vTaskDelayUntil           1
  40. #define INCLUDE_vTaskDelay                1
  41. /* Cortex-M specific definitions. */
  42. #ifdef __NVIC_PRIO_BITS
  43.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  44.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  45. #else
  46.      #define configPRIO_BITS              4        /* 15 priority levels */
  47. #endif
  48. /* The lowest interrupt priority that can be used in a call to a "set priority"
  49. function. */
  50. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  51. /* The highest interrupt priority that can be used by any interrupt service
  52. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  53. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  54. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  55. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 6 )
        定义可供用户使用的最大优先级数,如果这个定义的是6,那么用户可以使用的优先级号是0,1,2,3,4,5不包含6,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
      更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
27.12.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
      #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
      #definetskREADY_CHAR           ( 'R' ) 任务就绪
      #definetskDELETED_CHAR           ( 'D' )  任务删除
      #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
      vTaskUserIF任务   :2048字节
      vTaskLED任务     :2048字节
      vTaskMsgPro任务 :2048字节
      vTaskStart任务    :2048字节
      任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
      #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
27.13.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.     /* 启动调度,开始执行任务 */
  34.     vTaskStartScheduler();
  35.      /*
  36.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  37.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  38.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  39.      */
  40.      while(1);
  41. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.      bsp_InitUart();    /* 初始化串口 */
  19.      bsp_InitKey();     /* 初始化按键变量 */
  20.      bsp_InitLed();     /* 初始LED指示灯端口 */
  21. bsp_InitHardTimer(); /* 初始化TIM2定时器 */
  22. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据 */
  35.                    case KEY_DOWN_K2:
  36.                        printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息
  37. 邮箱数据\\r\\n");
  38.                        bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
  39.                        break;
  40.                   
  41.                    /* K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据 */
  42.                    case KEY_DOWN_K3:
  43.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息
  44. 邮箱数据\\r\\n");
  45.                        bsp_StartHardTimer(2 ,50000, (void *)TIM_CallBack2);
  46.                        break;
  47.                    /* 其他的键值不处理 */
  48.                    default:                    
  49.                        break;
  50.               }
  51.          }
  52.         
  53.          vTaskDelay(20);
  54.      }
  55. }
  56. /*
  57. *********************************************************************************************************
  58. *    函 数 名: vTaskLED
  59. *    功能说明: LED闪烁
  60. *    形    参: pvParameters 是在创建该任务时传递的形参
  61. *    返 回 值: 无
  62. *   优 先 级: 2
  63. *********************************************************************************************************
  64. */
  65. static void vTaskLED(void *pvParameters)
  66. {
  67.      TickType_t xLastWakeTime;
  68.      const TickType_t xFrequency = 200;
  69.      /* 获取当前的系统时间 */
  70.     xLastWakeTime = xTaskGetTickCount();
  71.    
  72.     while(1)
  73.     {
  74.          bsp_LedToggle(2);
  75.          bsp_LedToggle(3);
  76.         
  77.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  78.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  79.     }
  80. }
  81. /*
  82. *********************************************************************************************************
  83. *    函 数 名: vTaskMsgPro
  84. *    功能说明: 使用函数xTaskNotifyWait接收定时器中断发送的任务事件标志消息。
  85. *    形    参: pvParameters 是在创建该任务时传递的形参
  86. *    返 回 值: 无
  87. *   优 先 级: 3
  88. *********************************************************************************************************
  89. */
  90. static void vTaskMsgPro(void *pvParameters)
  91. {
  92.      BaseType_t xResult;
  93.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  94.      uint32_t ulValue;
  95.    
  96.     while(1)
  97.     {
  98.          /*
  99.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  100.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  101.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  102.                    的那个位就会被清零。
  103.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  104.         
  105.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  106.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  107.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  108.                    的那个位就会被清零。
  109.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  110.         
  111.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  112.          */
  113.         
  114.          xResult = xTaskNotifyWait(0x00000000,     
  115.                                       0xFFFFFFFF,     
  116.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  117.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  118.         
  119.          if( xResult == pdPASS )
  120.          {
  121.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  122.          }
  123.          else
  124.          {
  125.               /* 超时 */
  126.               bsp_LedToggle(1);
  127.               bsp_LedToggle(4);
  128.          }
  129.     }
  130. }
  131. /*
  132. *********************************************************************************************************
  133. *    函 数 名: vTaskStart
  134. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  135. *    形    参: pvParameters 是在创建该任务时传递的形参
  136. *    返 回 值: 无
  137. *   优 先 级: 4
  138. *********************************************************************************************************
  139. */
  140. static void vTaskStart(void *pvParameters)
  141. {
  142.     while(1)
  143.     {
  144.           /* 按键扫描 */
  145.          bsp_KeyScan();
  146.         vTaskDelay(10);
  147.     }
  148. }
复制代码
定时器中断回调函数中给任务发送任务消息邮箱数据:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数vTaskNotifyGiveFromISR在中断服务程序中的使用方法。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM_CallBack1和TIM_CallBack2
  4. *    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static uint32_t g_uiCount = 0; /* 设置为静态变量,方便查看数据更新 */
  10. static void TIM_CallBack1(void)
  11. {
  12.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  13.      xTaskNotifyFromISR(xHandleTaskMsgPro,     /* 目标任务 */
  14.                          g_uiCount++,           /* 发送数据 */
  15.                          eSetValueWithOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据会被覆盖 */
  16.                         &xHigherPriorityTaskWoken);
  17.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  18.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  19. }
  20. static void TIM_CallBack2(void)
  21. {
  22.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  23.    
  24.      /* 通过此函数的返回值可以判断是否发送成功,这里没有做判断 */
  25.      xTaskNotifyFromISR(xHandleTaskMsgPro,       /* 目标任务 */
  26.                       g_uiCount++,              /* 发送数据 */
  27.                       eSetValueWithoutOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据不会被覆盖 */
  28.                       &xHigherPriorityTaskWoken);
  29.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  30.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  31. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:44:45 | 显示全部楼层
27.5.3 STM32F429开发板实验


配套例子:
    V6-332_FreeRTOS实验_任务消息邮箱(中断方式)
实验目的:
    1.     学习FreeRTOS的任务通知实现任务消息邮箱(中断方式)。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送同步信号。
    3.     K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据。
    4.     本实验通过函数xTaskNotifyFromISR和xTaskNotifyWait实现消息邮箱的中断方式功能。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :LED闪烁。
              vTaskMsgPro任务 :使用函数ulTaskNotifyTake接收定时器中断发生的消息。
              vTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
     FreeRTOSConfig.h文件中的配置如下:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. #define configUSE_PREEMPTION         1
  7. #define configUSE_IDLE_HOOK          0
  8. #define configUSE_TICK_HOOK          0
  9. #define configCPU_CLOCK_HZ           ( ( unsigned long ) 168000000 )
  10. #define configTICK_RATE_HZ           ( ( TickType_t ) 1000 )
  11. #define configMAX_PRIORITIES         ( 5 )
  12. #define configMINIMAL_STACK_SIZE     ( ( unsigned short ) 128 )
  13. #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
  14. #define configMAX_TASK_NAME_LEN      ( 16 )
  15. #define configUSE_TRACE_FACILITY      1
  16. #define configUSE_16_BIT_TICKS       0
  17. #define configIDLE_SHOULD_YIELD      1
  18. /* Run time and task stats gathering related definitions. */
  19. #define configGENERATE_RUN_TIME_STATS                1
  20. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  21. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  22. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  23. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  24. /* Co-routine definitions. */
  25. #define configUSE_CO_ROUTINES            0
  26. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  27. /* Set the following definitions to 1 to include the API function, or zero
  28. to exclude the API function. */
  29. #define INCLUDE_vTaskPrioritySet          1
  30. #define INCLUDE_uxTaskPriorityGet         1
  31. #define INCLUDE_vTaskDelete               1
  32. #define INCLUDE_vTaskCleanUpResources      0
  33. #define INCLUDE_vTaskSuspend              1
  34. #define INCLUDE_xTaskResumeFromISR        1
  35. #define INCLUDE_vTaskDelayUntil           1
  36. #define INCLUDE_vTaskDelay                1
  37. /* Cortex-M specific definitions. */
  38. #ifdef __NVIC_PRIO_BITS
  39.      /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
  40.      #define configPRIO_BITS              __NVIC_PRIO_BITS
  41. #else
  42.      #define configPRIO_BITS              4        /* 15 priority levels */
  43. #endif
  44. /* The lowest interrupt priority that can be used in a call to a "set priority"
  45. function. */
  46. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY              0x0f
  47. /* The highest interrupt priority that can be used by any interrupt service
  48. routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
  49. INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
  50. PRIORITY THAN THIS! (higher priorities are lower numeric values. */
  51. #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY         0x01
复制代码
几个重要选项说明:
1、#define configUSE_PREEMPTION        1
        使能抢占式调度器
2、#define configCPU_CLOCK_HZ      ( ( unsigned long ) 168000000 )
        系统主频168MHz。
3、#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
        系统时钟节拍1KHz,即1ms。
4、#define configMAX_PRIORITIES          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
27.14.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
程序设计:
任务栈大小分配:
    vTaskUserIF任务   :2048字节
    vTaskLED任务     :2048字节
    vTaskMsgPro任务 :2048字节
    vTaskStart任务    :2048字节
    任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
    #defineconfigTOTAL_HEAP_SIZE        ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
27.15.jpg

FreeROTS初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33. /* 创建任务通信机制 */
  34.      AppObjCreate();
  35.    
  36.     /* 启动调度,开始执行任务 */
  37.     vTaskStartScheduler();
  38.      /*
  39.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  40.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  41.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  42.      */
  43.      while(1);
  44. }
复制代码
硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  20.      bsp_InitUart();    /* 初始化串口 */
  21.      bsp_InitKey();     /* 初始化按键变量 */
  22.      bsp_InitExtIO();   /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  23.      bsp_InitLed();     /* 初始LED指示灯端口 */
  24. bsp_InitHardTimer(); /* 初始化TIM2定时器 */
  25. }
复制代码
FreeRTOS任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.     xTaskCreate( vTaskTaskUserIF,   /* 任务函数  */
  12.                  "vTaskUserIF",     /* 任务名    */
  13.                  512,               /* 任务栈大小,单位word,也就是4字节 */
  14.                  NULL,              /* 任务参数  */
  15.                  1,                 /* 任务优先级*/
  16.                  &xHandleTaskUserIF );  /* 任务句柄  */
  17.    
  18.    
  19.      xTaskCreate( vTaskLED,           /* 任务函数  */
  20.                  "vTaskLED",         /* 任务名    */
  21.                  512,                /* 任务栈大小,单位word,也就是4字节 */
  22.                  NULL,               /* 任务参数  */
  23.                  2,                  /* 任务优先级*/
  24.                  &xHandleTaskLED ); /* 任务句柄  */
  25.    
  26.      xTaskCreate( vTaskMsgPro,            /* 任务函数  */
  27.                  "vTaskMsgPro",           /* 任务名    */
  28.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  29.                  NULL,                    /* 任务参数  */
  30.                  3,                       /* 任务优先级*/
  31.                  &xHandleTaskMsgPro );  /* 任务句柄  */
  32.    
  33.    
  34.      xTaskCreate( vTaskStart,             /* 任务函数  */
  35.                  "vTaskStart",            /* 任务名    */
  36.                  512,                     /* 任务栈大小,单位word,也就是4字节 */
  37.                  NULL,                    /* 任务参数  */
  38.                  4,                       /* 任务优先级*/
  39.                  &xHandleTaskStart );   /* 任务句柄  */
  40. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据 */
  35.                    case KEY_DOWN_K2:
  36.                        printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息
  37. 邮箱数据\\r\\n");
  38.                        bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
  39.                        break;
  40.                   
  41.                    /* K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息邮箱数据 */
  42.                    case KEY_DOWN_K3:
  43.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息
  44. 邮箱数据\\r\\n");
  45.                        bsp_StartHardTimer(2 ,50000, (void *)TIM_CallBack2);
  46.                        break;
  47.                    /* 其他的键值不处理 */
  48.                    default:                    
  49.                        break;
  50.               }
  51.          }
  52.         
  53.          vTaskDelay(20);
  54.      }
  55. }
  56. /*
  57. *********************************************************************************************************
  58. *    函 数 名: vTaskLED
  59. *    功能说明: LED闪烁
  60. *    形    参: pvParameters 是在创建该任务时传递的形参
  61. *    返 回 值: 无
  62. *   优 先 级: 2
  63. *********************************************************************************************************
  64. */
  65. static void vTaskLED(void *pvParameters)
  66. {
  67.      TickType_t xLastWakeTime;
  68.      const TickType_t xFrequency = 200;
  69.      /* 获取当前的系统时间 */
  70.     xLastWakeTime = xTaskGetTickCount();
  71.    
  72.     while(1)
  73.     {
  74.          bsp_LedToggle(2);
  75.          bsp_LedToggle(3);
  76.         
  77.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  78.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  79.     }
  80. }
  81. /*
  82. *********************************************************************************************************
  83. *    函 数 名: vTaskMsgPro
  84. *    功能说明: 使用函数xTaskNotifyWait接收定时器中断发送的任务事件标志消息。
  85. *    形    参: pvParameters 是在创建该任务时传递的形参
  86. *    返 回 值: 无
  87. *   优 先 级: 3
  88. *********************************************************************************************************
  89. */
  90. static void vTaskMsgPro(void *pvParameters)
  91. {
  92.      BaseType_t xResult;
  93.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(500); /* 设置最大等待时间为500ms */
  94.      uint32_t ulValue;
  95.    
  96.     while(1)
  97.     {
  98.          /*
  99.               第一个参数 ulBitsToClearOnEntry的作用(函数执行前):
  100.                    ulNotifiedValue &= ~ulBitsToClearOnEntry
  101.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  102.                    的那个位就会被清零。
  103.                    这里ulBitsToClearOnEntry = 0x00000000就是函数执行前保留所有位。
  104.         
  105.              第二个参数 ulBitsToClearOnExit的作用(函数退出前):         
  106.                      ulNotifiedValue &= ~ulBitsToClearOnExit
  107.                    简单的说就是参数ulBitsToClearOnEntry那个位是1,那么ulNotifiedValue
  108.                    的那个位就会被清零。
  109.                      这里ulBitsToClearOnExit = 0xFFFFFFFF就是函数退出前清楚所有位。
  110.         
  111.              注:ulNotifiedValue表示任务vTaskMsgPro的任务控制块里面的变量,用来做消息邮箱数据的存取。        
  112.          */
  113.         
  114.          xResult = xTaskNotifyWait(0x00000000,     
  115.                                       0xFFFFFFFF,     
  116.                                       &ulValue,        /* 保存ulNotifiedValue到变量ulValue中 */
  117.                                       xMaxBlockTime);  /* 最大允许延迟时间 */
  118.         
  119.          if( xResult == pdPASS )
  120.          {
  121.               printf("接收到消息邮箱数据ulValue = %d\\r\\n", ulValue);
  122.          }
  123.          else
  124.          {
  125.               /* 超时 */
  126.               bsp_LedToggle(1);
  127.               bsp_LedToggle(4);
  128.          }
  129.     }
  130. }
  131. /*
  132. *********************************************************************************************************
  133. *    函 数 名: vTaskStart
  134. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  135. *    形    参: pvParameters 是在创建该任务时传递的形参
  136. *    返 回 值: 无
  137. *   优 先 级: 4
  138. *********************************************************************************************************
  139. */
  140. static void vTaskStart(void *pvParameters)
  141. {
  142.     while(1)
  143.     {
  144.           /* 按键扫描 */
  145.          bsp_KeyScan();
  146.         vTaskDelay(10);
  147.     }
  148. }
复制代码
定时器中断回调函数中给任务发送任务消息邮箱数据:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数vTaskNotifyGiveFromISR在中断服务程序中的使用方法。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TIM_CallBack1和TIM_CallBack2
  4. *    功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。                        
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static uint32_t g_uiCount = 0; /* 设置为静态变量,方便查看数据更新 */
  10. static void TIM_CallBack1(void)
  11. {
  12.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  13.      xTaskNotifyFromISR(xHandleTaskMsgPro,     /* 目标任务 */
  14.                          g_uiCount++,           /* 发送数据 */
  15.                          eSetValueWithOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据会被覆盖 */
  16.                         &xHigherPriorityTaskWoken);
  17.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  18.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  19. }
  20. static void TIM_CallBack2(void)
  21. {
  22.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  23.    
  24.      /* 通过此函数的返回值可以判断是否发送成功,这里没有做判断 */
  25.      xTaskNotifyFromISR(xHandleTaskMsgPro,       /* 目标任务 */
  26.                       g_uiCount++,              /* 发送数据 */
  27.                       eSetValueWithoutOverwrite,/* 如果目标任务上次的数据还没有处理,上次的数据不会被覆盖 */
  28.                       &xHigherPriorityTaskWoken);
  29.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  30.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  31. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-8 15:45:22 | 显示全部楼层
27.6 总结


    本章节为大家讲解了FreeRTOS的任务消息邮箱,任务消息邮箱相比消息队列(消息队列长度固定为1)的优势就是执行效率高,需要的RAM空间小。实际项目中,如果使用消息队列和任务消息邮箱都能实现相应功能,强烈建议使用任务事消息邮箱。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-30 17:03 , Processed in 0.404982 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表