硬汉嵌入式论坛

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

[FreeRTOS教程] 第20章 FreeRTOS消息队列

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-8-31 10:40:43 | 显示全部楼层 |阅读模式



第20章      FreeRTOS消息队列


    本章节为大家讲解FreeRTOS的一个重要的通信机制----消息队列,初学者要熟练掌握,因为消息队列在实际项目中应用较多。
    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
20.1 消息队列
20.2 消息队列API函数
20.3 实验例程说明(任务间通信)
20.4 实验例程说明(中断方式通信)
20.5      总结



20.1 消息队列

20.1.1  消息队列的概念及其作用

      消息队列就是通过RTOS内核提供的服务,任务或中断服务子程序可以将一个消息(注意,FreeRTOS消息队列传递的是实际数据,并不是数据地址,RTX,uCOS-II和uCOS-III是传递的地址)放入到队列。同样,一个或者多个任务可以通过RTOS内核服务从队列中得到消息。通常,先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入到消息队列的消息,即先进先出的原则(FIFO),FreeRTOS的消息队列支持FIFO和LIFO两种数据存取方式。
      也许有不理解的初学者会问采用消息队列多麻烦,搞个全局数组不是更简单,其实不然。在裸机编程时,使用全局数组的确比较方便,但是在加上RTOS后就是另一种情况了。相比消息队列,使用全局数组主要有如下四个问题:
    1、使用消息队列可以让RTOS内核有效地管理任务,而全局数组是无法做到的,任务的超时等机制需要用户自己去实现。
    2、使用了全局数组就要防止多任务的访问冲突,而使用消息队列则处理好了这个问题,用户无需担心。
    3、使用消息队列可以有效地解决中断服务程序与任务之间消息传递的问题。
    4、FIFO机制更有利于数据的处理。

20.1.2 FreeRTOS任务间消息队列的实现

      任务间消息队列的实现是指各个任务之间使用消息队列实现任务间的通信。下面我们通过如下的框图来说明一下FreeRTOS消息队列的实现,让大家有一个形象的认识。
20.1.JPG

运行条件:
1、创建消息队列,可以存放10个消息。
2、创建2个任务Task1Task2,任务Task1向消息队列放数据,任务Task2从消息队列取数据。
3、FreeRTOS的消息存取采用FIFO方式。
运行过程主要有以下两种情况:
    (1)任务Task1 向消息队列放数据,任务Task2从消息队列取数据,如果放数据的速度快于取数据的速度,那么会出现消息队列存放满的情况,FreeRTOS的消息存放函数xQueueSend支持超时等待,用户可以设置超时等待,直到有空间可以存放消息或者设置的超时时间溢出。
    (2)任务Task1 向消息队列放数据,任务Task2从消息队列取数据,如果放数据的速度慢于取数据的速度,那么会出现消息队列为空的情况,FreeRTOS的消息获取函数xQueueReceive支持超时等待,用户可以设置超时等待,直到消息队列中有消息或者设置的超时时间溢出。

上面就是一个简单的FreeRTOS任务间消息队列通信过程,FIFO方式数据存取过程的动态演示看官方地址:http://www.freertos.org/Embedded-RTOS-Queues.html 里面的GIF图片。

20.1.3 FreeRTOS中断方式消息队列的实现

      FreeRTOS中断方式消息队列的实现是指中断函数和FreeRTOS任务之间使用消息队列。下面我们通过如下的框图来说明一下FreeRTOS消息队列的实现,让大家有一个形象的认识。
20.2.JPG

运行条件:
1、创建消息队列,可以存放10个消息。
2、创建1个任务Task1和一个串口接收中断。
3、FreeRTOS的消息存取采用FIFO方式。
运行过程主要有以下两种情况:
    (1)中断服务程序向消息队列放数据,任务Task1从消息队列取数据,如果放数据的速度快于取数据的速度,那么会出现消息队列存放满的情况。由于中断服务程序里面的消息队列发送函数xQueueSendFromISR不支持超时设置,所以发送前要通过函数xQueueIsQueueFullFromISR检测消息队列是否满。
    (2)中断服务程序向消息队列放数据,任务Task1从消息队列取数据,如果放数据的速度慢于取数据的速度,那么会出现消息队列存为空的情况。在FreeRTOS的任务中可以通过函数xQueueReceive获取消息,因为此函数可以设置超时等待,直到消息队列中有消息存放或者设置的超时时间溢出。

上面就是一个简单的FreeRTOS中断方式消息队列通信过程。实际应用中,中断方式的消息机制要注意以下四个问题:
    1、中断函数的执行时间越短越好,防止其它低于这个中断优先级的异常不能得到及时响应。
    2、实际应用中,建议不要在中断中实现消息处理,用户可以在中断服务程序里面发送消息通知任务,在任务中实现消息处理,这样可以有效地保证中断服务程序的实时响应。同时此任务也需要设置为高优先级,以便退出中断函数后任务可以得到及时执行。
    3、中断服务程序中一定要调用专用于中断的消息队列函数,即以FromISR结尾的函数。
    4、在操作系统中实现中断服务程序与裸机编程的区别。
        (1)如果FreeRTOS工程的中断函数中没有调用FreeRTOS的消息队列API函数,与裸机编程是一样的。
        (2)如果FreeRTOS工程的中断函数中调用了FreeRTOS的消息队列的API函数,退出的时候要检测是否有高优先级任务就绪,如果有就绪的,需要在退出中断后进行任务切换,这点与裸机编程稍有区别,详见20.4小节实验例程说明(中断方式):
        (3)另外强烈推荐用户将Cortex-M3内核的STM32F103Cortex-M4内核的STM32F407F429NVIC优先级分组设置为4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);这样中断优先级的管理将非常方便。
        (4)用户要在FreeRTOS多任务开启前就设置好优先级分组,一旦设置好切记不可再修改。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 14:00:51 | 显示全部楼层
20.2 消息队列API函数

    使用如下23个函数可以实现FreeRTOS的消息队列:
(1)    xQueueCreateStatic()
(2)    vQueueDelete()
(3)    xQueueSend()
(4)    xQueueSendFromISR()
(5)    xQueueSendToBack()
(6)    xQueueSendToBackFromISR()
(7)    xQueueSendToFront()
(8)    xQueueSendToFrontFromISR()
(9)    xQueueReceive()
(10)    xQueueReceiveFromISR()
(11)    uxQueueMessagesWaiting()
(12)    uxQueueMessagesWaitingFromISR()
(13)    uxQueueSpacesAvailable()
(14)    xQueueReset()
(15)    xQueueOverwrite()
(16)    xQueueOverwriteFromISR()
(17)    xQueuePeek()
(18)    xQueuePeekFromISR()
(19)    vQueueAddToRegistry()
(20)    vQueueUnregisterQueue()
(21)    pcQueueGetName()
(22)    xQueueIsQueueFullFromISR()
(23)    xQueueIsQueueEmptyFromISR()
    关于这23个函数的讲解及其使用方法可以看FreeRTOS在线版手册:
20.3.jpg

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

20.2.1 函数xQueueCreate

函数原型:
  1. QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, /* 消息个数 */
  2.                              UBaseType_t uxItemSize ); /* 每个消息大小,单位字节 */
复制代码
函数描述:
函数xQueueCreate用于创建消息队列。
    (1)第1个参数是消息队列支持的消息个数。
    (2)第2个参数是每个消息的大小,单位字节。
    (3)返回值,如果创建成功会返回消息队列的句柄,如果由于FreeRTOSConfig.h文件中heap大小不足,无法为此消息队列提供所需的空间会返回NULL

使用这个函数要注意以下问题:
1.     FreeRTOS的消息传递是数据的复制,而不是传递的数据地址,这点要特别注意。每一次传递都是uxItemSize个字节。
使用举例:
  1. static QueueHandle_t xQueue1 = NULL;
  2. static QueueHandle_t xQueue2 = NULL;
  3. /*
  4. *********************************************************************************************************
  5. *    函 数 名: AppObjCreate
  6. *    功能说明: 创建任务通信机制
  7. *    形    参: 无
  8. *    返 回 值: 无
  9. *********************************************************************************************************
  10. */
  11. static void AppObjCreate (void)
  12. {
  13.      /* 创建10个uint8_t型消息队列 */
  14.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  15.     if( xQueue1 == 0 )
  16.     {
  17.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  18.     }
  19.    
  20.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  21.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  22.     if( xQueue2 == 0 )
  23.     {
  24.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  25.     }
  26. }
复制代码

20.2.2 函数xQueueSend

函数原型:
  1. BaseType_t xQueueSend(
  2.                           QueueHandle_t xQueue,       /* 消息队列句柄 */
  3.                           const void * pvItemToQueue, /* 要传递数据地址 */
  4.                           TickType_t xTicksToWait     /* 等待消息队列有空间的最大等待时间 */
  5.                          );
复制代码
函数描述:
函数xQueueSend用于任务中消息发送。
    (1)第1个参数是消息队列句柄。
    (2)第2个参数要传递数据地址,每次发送都是将消息队列创建函数xQueueCreate所指定的单个消息大小复制到消息队列空间中。
    (3)第3个参数是当消息队列已经满时,等待消息队列有空间时的最大等待时间,单位系统时钟节拍。
    (4)返回值,如果消息成功发送返回pdTRUE,否则返回errQUEUE_FULL

使用这个函数要注意以下问题:
1.     FreeRTOS的消息传递是数据的复制,而不是传递的数据地址。
2.     此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xQueueSendFromISR
3.     如果消息队列已经满且第三个参数为0,那么此函数会立即返回。
4.     如果用户将FreeRTOSConfig.h文件中的宏定义INCLUDE_vTaskSuspend配置为1且第三个参数配置为portMAX_DELAY,那么此发送函数会永久等待直到消息队列有空间可以使用。
5.     消息队列还有两个函数xQueueSendToBack和xQueueSendToFront,函数xQueueSendToBack实现的是FIFO方式的存取,函数xQueueSendToFront实现的是LIFO方式的读写。我们这里说的函数xQueueSend等效于xQueueSendToBack,即实现的是FIFO方式的存取。
使用举例:
  1. static QueueHandle_t xQueue1 = NULL;
  2. static QueueHandle_t xQueue2 = NULL;
  3. typedef struct Msg
  4. {
  5.      uint8_t  ucMessageID;
  6.      uint16_t usData[2];
  7.      uint32_t ulData[2];
  8. }MSG_T;
  9. MSG_T   g_tMsg; /* 定义一个结构体用于消息队列 */
  10. /*
  11. *********************************************************************************************************
  12. *    函 数 名: vTaskTaskUserIF
  13. *    功能说明: 接口消息处理。
  14. *    形    参: pvParameters 是在创建该任务时传递的形参
  15. *    返 回 值: 无
  16. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  17. *********************************************************************************************************
  18. */
  19. static void vTaskTaskUserIF(void *pvParameters)
  20. {
  21.      MSG_T   *ptMsg;
  22.      uint8_t ucCount = 0;
  23.      uint8_t ucKeyCode;
  24.    
  25.      /* 初始化结构体指针 */
  26.      ptMsg = &g_tMsg;
  27.    
  28.      /* 初始化数组 */
  29.      ptMsg->ucMessageID = 0;
  30.      ptMsg->ulData[0] = 0;
  31.      ptMsg->usData[0] = 0;
  32.     while(1)
  33.     {
  34.          ucKeyCode = bsp_GetKey();
  35.         
  36.          if (ucKeyCode != KEY_NONE)
  37.          {
  38.               switch (ucKeyCode)
  39.               {                 
  40.                    /* K2键按下,向xQueue1发送数据 */
  41.                    case KEY_DOWN_K2:
  42.                        ucCount++;
  43.                   
  44.                        /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  45.                        if( xQueueSend(xQueue1,
  46.                                         (void *) &ucCount,
  47.                                         (TickType_t)10) != pdPASS )
  48.                        {
  49.                             /* 发送失败,即使等待了10个时钟节拍 */
  50.                             printf("K2键按下,向xQueue1发送数据失败,即使等待了10个时钟节拍\\r\\n");
  51.                        }
  52.                        else
  53.                        {
  54.                             /* 发送成功 */
  55.                             printf("K2键按下,向xQueue1发送数据成功\\r\\n");                          
  56.                        }
  57.                        break;
  58.                   
  59.                    /* K3键按下,向xQueue2发送数据 */
  60.                    case KEY_DOWN_K3:
  61.                        ptMsg->ucMessageID++;
  62.                        ptMsg->ulData[0]++;;
  63.                        ptMsg->usData[0]++;
  64.                      
  65.                         /* 使用消息队列实现指针变量的传递 */
  66.                        if(xQueueSend(xQueue2,                  /* 消息队列句柄 */
  67.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  68.                                       (TickType_t)10) != pdPASS )
  69.                        {
  70.                             /* 发送失败,即使等待了10个时钟节拍 */
  71.                             printf("K3键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  72.                        }
  73.                        else
  74.                        {
  75.                             /* 发送成功 */
  76.                             printf("K3键按下,向xQueue2发送数据成功\\r\\n");                          
  77.                        }
  78.                   
  79.                    /* 其他的键值不处理 */
  80.                    default:                    
  81.                        break;
  82.               }
  83.          }
  84.         
  85.          vTaskDelay(20);
  86.      }
  87. }
复制代码

20.2.3 函数xQueueSendFromISR

函数原型:
  1. BaseType_t xQueueSendFromISR
  2.            (
  3.                QueueHandle_t xQueue,                  /* 消息队列句柄 */
  4.                const void *pvItemToQueue,             /* 要传递数据地址 */
  5.                BaseType_t *pxHigherPriorityTaskWoken  /* 高优先级任务是否被唤醒的状态保存 */
  6.            );
复制代码
函数描述:
函数xQueueSendFromISR用于中断服务程序中消息发送。
    (1)第1个参数是消息队列句柄。
    (2)第2个参数要传递数据地址,每次发送都是将消息队列创建函数xQueueCreate所指定的单个消息大小复制到消息队列空间中。
    (3)第3个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是pdTRUE,说明有高优先级任务要执行,否则没有。
    (4)返回值,如果消息成功发送返回pdTRUE,否则返回errQUEUE_FULL

使用这个函数要注意以下问题:
1.     FreeRTOS的消息传递是数据的复制,而不是传递的数据地址。正因为这个原因,用户在创建消息队列时单个消息大小不可太大,因为一定程度上面会增加中断服务程序的执行时间。
2.     此函数是用于中断服务程序中调用的,故不可以在任务代码中调用此函数,任务代码中使用的是xQueueSend。
3.     消息队列还有两个函数xQueueSendToBackFromISR和xQueueSendToFrontFromISR,函数xQueueSendToBackFromISR实现的是FIFO方式的存取,函数xQueueSendToFrontFromISR实现的是LIFO方式的读写。我们这里说的函数xQueueSendFromISR等效于xQueueSendToBackFromISR,即实现的是FIFO方式的存取。
使用举例:
  1. static QueueHandle_t xQueue1 = NULL;
  2. static QueueHandle_t xQueue2 = NULL;
  3. typedef struct Msg
  4. {
  5.      uint8_t  ucMessageID;
  6.      uint16_t usData[2];
  7.      uint32_t ulData[2];
  8. }MSG_T;
  9. MSG_T   g_tMsg; /* 定义一个结构体用于消息队列 */
  10. /*
  11. *********************************************************************************************************
  12. *    函 数 名: TIM1_IRQHandler
  13. *    功能说明: 定时器中断。                     
  14. *    形    参: 无
  15. *    返 回 值: 无
  16. *********************************************************************************************************
  17. */
  18. static uint32_t g_uiCount = 0; /* 设置为全局静态变量,方便数据更新 */
  19. static void TIM1_IRQHandler(void)
  20. {
  21.      BaseType_t xResult;
  22.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  23.      /* 中断消息处理,此处省略 */
  24.      ……
  25.    
  26.      g_uiCount++;
  27.    
  28.      /* 向消息队列发数据 */
  29.      xQueueSendFromISR(xQueue1,
  30.                          (void *)&g_uiCount,
  31.                          &xHigherPriorityTaskWoken);
  32.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  33.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  34. }
  35. /*
  36. *********************************************************************************************************
  37. *    函 数 名: TIM2_IRQHandler
  38. *    功能说明: 定时器中断。                     
  39. *    形    参: 无
  40. *    返 回 值: 无
  41. *********************************************************************************************************
  42. */
  43. static void TIM2_IRQHandler (void)
  44. {
  45.      MSG_T   *ptMsg;
  46.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  47.      /* 中断消息处理,此处省略 */
  48.      ……
  49.    
  50.      /* 初始化结构体指针 */
  51.      ptMsg = &g_tMsg;
  52.    
  53.      /* 初始化数组 */
  54.      ptMsg->ucMessageID++;
  55.      ptMsg->ulData[0]++;
  56.      ptMsg->usData[0]++;
  57.      /* 向消息队列发数据 */
  58.      xQueueSendFromISR(xQueue2,
  59.                          (void *)&ptMsg,
  60.                           &xHigherPriorityTaskWoken);
  61.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  62.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  63. }
复制代码

20.2.4 函数xQueueReceive

函数原型:
  1. BaseType_t xQueueReceive(
  2.                                QueueHandle_t xQueue,    /* 消息队列句柄 */
  3.                                void *pvBuffer,          /* 接收消息队列数据的缓冲地址 */
  4.                                TickType_t xTicksToWait  /* 等待消息队列有数据的最大等待时间 */
  5.                             );
复制代码
函数描述:
函数xQueueReceive用于接收消息队列中的数据。
    (1)第1个参数是消息队列句柄。
    (2)第2个参数是从消息队列中复制出数据后所储存的缓冲地址,缓冲区空间要大于等于消息队列创建函数xQueueCreate所指定的单个消息大小,否则取出的数据无法全部存储到缓冲区,从而造成内存溢出。
    (3)第3个参数是消息队列为空时,等待消息队列有数据的最大等待时间,单位系统时钟节拍。
    (4)返回值,如果接到到消息返回pdTRUE,否则返回pdFALSE

使用这个函数要注意以下问题:
1.     此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是xQueueReceiveFromISR。
2.     如果消息队列为空且第三个参数为0,那么此函数会立即返回。
3.     如果用户将FreeRTOSConfig.h文件中的宏定义INCLUDE_vTaskSuspend配置为1且第三个参数配置为portMAX_DELAY,那么此函数会永久等待直到消息队列有数据。
使用举例:
  1. static QueueHandle_t xQueue1 = NULL;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: vTaskMsgPro
  5. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  6. *    形    参: pvParameters 是在创建该任务时传递的形参
  7. *    返 回 值: 无
  8. *   优 先 级: 3
  9. *********************************************************************************************************
  10. */
  11. static void vTaskMsgPro(void *pvParameters)
  12. {
  13.      BaseType_t xResult;
  14.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  15.      uint8_t ucQueueMsgValue;
  16.    
  17.     while(1)
  18.     {
  19.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  20.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  21.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  22.         
  23.          if(xResult == pdPASS)
  24.          {
  25.               /* 成功接收,并通过串口将数据打印出来 */
  26.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  27.          }
  28.          else
  29.          {
  30.               /* 超时 */
  31.               bsp_LedToggle(1);
  32.               bsp_LedToggle(4);
  33.          }
  34.     }
  35. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 14:21:09 | 显示全部楼层
20.3 实验例程说明(任务间通信)



20.3.1 STM32F103开发板实验


配套例子:
      V4-315_FreeRTOS实验_消息队列
实验目的:
      1.     学习FreeRTOS的消息队列。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,向消息队列xQueue1发送数据。
    3.     K3键按下,向消息队列xQueue2发送数据。
    4.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue2)
              vTaskMsgPro任务:使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue1)
              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按键,串口打印):
20.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 ) )
系统栈大小分配:
20.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.      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. */
  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. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {
  11.      /* 创建10个uint8_t型消息队列 */
  12.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17.    
  18.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  19.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  20.     if( xQueue2 == 0 )
  21.     {
  22.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  23.     }
  24. }
复制代码

四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      MSG_T   *ptMsg;
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.    
  17.      /* 初始化结构体指针 */
  18.      ptMsg = &g_tMsg;
  19.    
  20.      /* 初始化数组 */
  21.      ptMsg->ucMessageID = 0;
  22.      ptMsg->ulData[0] = 0;
  23.      ptMsg->usData[0] = 0;
  24.     while(1)
  25.     {
  26.          ucKeyCode = bsp_GetKey();
  27.         
  28.          if (ucKeyCode != KEY_NONE)
  29.          {
  30.               switch (ucKeyCode)
  31.               {
  32.                    /* K1键按下 打印任务执行情况 */
  33.                    case KEY_DOWN_K1:         
  34.                        printf("=================================================\\r\\n");
  35.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  36.                        vTaskList((char *)&pcWriteBuffer);
  37.                        printf("%s\\r\\n", pcWriteBuffer);
  38.                   
  39.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  40.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  41.                        printf("%s\\r\\n", pcWriteBuffer);
  42.                        break;
  43.                   
  44.                    /* K2键按下,向xQueue1发送数据 */
  45.                    case KEY_DOWN_K2:
  46.                        ucCount++;
  47.                   
  48.                        /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  49.                        if( xQueueSend(xQueue1,
  50.                                         (void *) &ucCount,
  51.                                         (TickType_t)10) != pdPASS )
  52.                        {
  53.                             /* 发送失败,即使等待了10个时钟节拍 */
  54.                             printf("K2键按下,向xQueue1发送数据失败,即使等待了10个时钟节拍\\r\\n");
  55.                        }
  56.                        else
  57.                        {
  58.                             /* 发送成功 */
  59.                             printf("K2键按下,向xQueue1发送数据成功\\r\\n");                          
  60.                        }
  61.                        break;
  62.                   
  63.                    /* K3键按下,向xQueue2发送数据 */
  64.                    case KEY_DOWN_K3:
  65.                        ptMsg->ucMessageID++;
  66.                        ptMsg->ulData[0]++;;
  67.                        ptMsg->usData[0]++;
  68.                      
  69.                        /* 使用消息队列实现指针变量的传递 */
  70.                        if(xQueueSend(xQueue2,                  /* 消息队列句柄 */
  71.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  72.                                       (TickType_t)10) != pdPASS )
  73.                        {
  74.                             /* 发送失败,即使等待了10个时钟节拍 */
  75.                             printf("K3键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  76.                        }
  77.                        else
  78.                        {
  79.                             /* 发送成功 */
  80.                             printf("K3键按下,向xQueue2发送数据成功\\r\\n");                          
  81.                        }
  82.                   
  83.                    /* 其他的键值不处理 */
  84.                    default:                    
  85.                        break;
  86.               }
  87.          }
  88.         
  89.          vTaskDelay(20);
  90.      }
  91. }
  92. /*
  93. *********************************************************************************************************
  94. *    函 数 名: vTaskLED
  95. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue2)
  96. *    形    参: pvParameters 是在创建该任务时传递的形参
  97. *    返 回 值: 无
  98. *   优 先 级: 2
  99. *********************************************************************************************************
  100. */
  101. static void vTaskLED(void *pvParameters)
  102. {
  103.      MSG_T *ptMsg;
  104.      BaseType_t xResult;
  105.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  106.    
  107.     while(1)
  108.     {
  109.          xResult = xQueueReceive(xQueue2,                   /* 消息队列句柄 */
  110.                                  (void *)&ptMsg,            /* 这里获取的是结构体的地址 */
  111.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  112.         
  113.         
  114.          if(xResult == pdPASS)
  115.          {
  116.               /* 成功接收,并通过串口将数据打印出来 */
  117.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  118.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  119.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  120.          }
  121.          else
  122.          {
  123.               /* 超时 */
  124.               bsp_LedToggle(2);
  125.               bsp_LedToggle(3);
  126.          }
  127.     }
  128. }
  129. /*
  130. *********************************************************************************************************
  131. *    函 数 名: vTaskMsgPro
  132. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  133. *    形    参: pvParameters 是在创建该任务时传递的形参
  134. *    返 回 值: 无
  135. *   优 先 级: 3
  136. *********************************************************************************************************
  137. */
  138. static void vTaskMsgPro(void *pvParameters)
  139. {
  140.      BaseType_t xResult;
  141.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  142.      uint8_t ucQueueMsgValue;
  143.    
  144.     while(1)
  145.     {
  146.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  147.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  148.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  149.         
  150.          if(xResult == pdPASS)
  151.          {
  152.               /* 成功接收,并通过串口将数据打印出来 */
  153.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  154.          }
  155.          else
  156.          {
  157.               /* 超时 */
  158.               bsp_LedToggle(1);
  159.               bsp_LedToggle(4);
  160.          }
  161.     }
  162. }
  163. /*
  164. *********************************************************************************************************
  165. *    函 数 名: vTaskStart
  166. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  167. *    形    参: pvParameters 是在创建该任务时传递的形参
  168. *    返 回 值: 无
  169. *   优 先 级: 4
  170. *********************************************************************************************************
  171. */
  172. static void vTaskStart(void *pvParameters)
  173. {
  174.     while(1)
  175.     {
  176.          /* 按键扫描 */
  177.          bsp_KeyScan();
  178.         vTaskDelay(10);
  179.     }
  180. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 14:26:11 | 显示全部楼层
20.3.2   STM32F407开发板实验


配套例子:
     V5-315_FreeRTOS实验_消息队列
实验目的:
    1.     学习FreeRTOS的消息队列。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,向消息队列xQueue1发送数据。
    3.     K3键按下,向消息队列xQueue2发送数据。
    4.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue2)
              vTaskMsgPro任务:使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue1)
              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按键,串口打印):
20.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 ) )
系统栈大小分配:
20.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.      AppObjCreate();
  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.      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. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {
  11.      /* 创建10个uint8_t型消息队列 */
  12.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17.    
  18.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  19.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  20.     if( xQueue2 == 0 )
  21.     {
  22.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  23.     }
  24. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      MSG_T   *ptMsg;
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.    
  17.      /* 初始化结构体指针 */
  18.      ptMsg = &g_tMsg;
  19.    
  20.      /* 初始化数组 */
  21.      ptMsg->ucMessageID = 0;
  22.      ptMsg->ulData[0] = 0;
  23.      ptMsg->usData[0] = 0;
  24.     while(1)
  25.     {
  26.          ucKeyCode = bsp_GetKey();
  27.         
  28.          if (ucKeyCode != KEY_NONE)
  29.          {
  30.               switch (ucKeyCode)
  31.               {
  32.                    /* K1键按下 打印任务执行情况 */
  33.                    case KEY_DOWN_K1:         
  34.                        printf("=================================================\\r\\n");
  35.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  36.                        vTaskList((char *)&pcWriteBuffer);
  37.                        printf("%s\\r\\n", pcWriteBuffer);
  38.                   
  39.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  40.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  41.                        printf("%s\\r\\n", pcWriteBuffer);
  42.                        break;
  43.                   
  44.                    /* K2键按下,向xQueue1发送数据 */
  45.                    case KEY_DOWN_K2:
  46.                        ucCount++;
  47.                   
  48.                        /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  49.                        if( xQueueSend(xQueue1,
  50.                                         (void *) &ucCount,
  51.                                         (TickType_t)10) != pdPASS )
  52.                        {
  53.                             /* 发送失败,即使等待了10个时钟节拍 */
  54.                             printf("K2键按下,向xQueue1发送数据失败,即使等待了10个时钟节拍\\r\\n");
  55.                        }
  56.                        else
  57.                        {
  58.                             /* 发送成功 */
  59.                             printf("K2键按下,向xQueue1发送数据成功\\r\\n");                          
  60.                        }
  61.                        break;
  62.                   
  63.                    /* K3键按下,向xQueue2发送数据 */
  64.                    case KEY_DOWN_K3:
  65.                        ptMsg->ucMessageID++;
  66.                        ptMsg->ulData[0]++;;
  67.                        ptMsg->usData[0]++;
  68.                      
  69.                        /* 使用消息队列实现指针变量的传递 */
  70.                        if(xQueueSend(xQueue2,                  /* 消息队列句柄 */
  71.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  72.                                       (TickType_t)10) != pdPASS )
  73.                        {
  74.                             /* 发送失败,即使等待了10个时钟节拍 */
  75.                             printf("K3键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  76.                        }
  77.                        else
  78.                        {
  79.                             /* 发送成功 */
  80.                             printf("K3键按下,向xQueue2发送数据成功\\r\\n");                          
  81.                        }
  82.                  
  83.                    /* 其他的键值不处理 */
  84.                    default:                    
  85.                        break;
  86.               }
  87.          }
  88.         
  89.          vTaskDelay(20);
  90.      }
  91. }
  92. /*
  93. *********************************************************************************************************
  94. *    函 数 名: vTaskLED
  95. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue2)
  96. *    形    参: pvParameters 是在创建该任务时传递的形参
  97. *    返 回 值: 无
  98. *   优 先 级: 2
  99. *********************************************************************************************************
  100. */
  101. static void vTaskLED(void *pvParameters)
  102. {
  103.      MSG_T *ptMsg;
  104.      BaseType_t xResult;
  105.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  106.    
  107.     while(1)
  108.     {
  109.          xResult = xQueueReceive(xQueue2,                   /* 消息队列句柄 */
  110.                                  (void *)&ptMsg,            /* 这里获取的是结构体的地址 */
  111.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  112.         
  113.         
  114.          if(xResult == pdPASS)
  115.          {
  116.               /* 成功接收,并通过串口将数据打印出来 */
  117.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  118.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  119.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  120.          }
  121.          else
  122.          {
  123.               /* 超时 */
  124.               bsp_LedToggle(2);
  125.               bsp_LedToggle(3);
  126.          }
  127.     }
  128. }
  129. /*
  130. *********************************************************************************************************
  131. *    函 数 名: vTaskMsgPro
  132. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  133. *    形    参: pvParameters 是在创建该任务时传递的形参
  134. *    返 回 值: 无
  135. *   优 先 级: 3
  136. *********************************************************************************************************
  137. */
  138. static void vTaskMsgPro(void *pvParameters)
  139. {
  140.      BaseType_t xResult;
  141.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  142.      uint8_t ucQueueMsgValue;
  143.    
  144.     while(1)
  145.     {
  146.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  147.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  148.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  149.         
  150.          if(xResult == pdPASS)
  151.          {
  152.               /* 成功接收,并通过串口将数据打印出来 */
  153.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  154.          }
  155.          else
  156.          {
  157.               /* 超时 */
  158.               bsp_LedToggle(1);
  159.               bsp_LedToggle(4);
  160.          }
  161.     }
  162. }
  163. /*
  164. *********************************************************************************************************
  165. *    函 数 名: vTaskStart
  166. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  167. *    形    参: pvParameters 是在创建该任务时传递的形参
  168. *    返 回 值: 无
  169. *   优 先 级: 4
  170. *********************************************************************************************************
  171. */
  172. static void vTaskStart(void *pvParameters)
  173. {
  174.     while(1)
  175.     {
  176.          /* 按键扫描 */
  177.          bsp_KeyScan();
  178.         vTaskDelay(10);
  179.     }
  180. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 14:32:02 | 显示全部楼层
20.3.3 STM32F429开发板实验


配套例子:
    V6-315_FreeRTOS实验_消息队列
实验目的:
    1.     学习FreeRTOS的消息队列。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,向消息队列xQueue1发送数据。
    3.     K3键按下,向消息队列xQueue2发送数据。
    4.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue2)
              vTaskMsgPro任务:使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数(xQueue1)
              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、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按键,串口打印):
20.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 ) )
系统栈大小分配:
20.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.      AppObjCreate();
  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. }
复制代码
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. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {
  11.      /* 创建10个uint8_t型消息队列 */
  12.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17.    
  18.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  19.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  20.     if( xQueue2 == 0 )
  21.     {
  22.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  23.     }
  24. }
复制代码
四个FreeRTOS任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      MSG_T   *ptMsg;
  13.      uint8_t ucCount = 0;
  14.      uint8_t ucKeyCode;
  15.      uint8_t pcWriteBuffer[500];
  16.    
  17.      /* 初始化结构体指针 */
  18.      ptMsg = &g_tMsg;
  19.    
  20.      /* 初始化数组 */
  21.      ptMsg->ucMessageID = 0;
  22.      ptMsg->ulData[0] = 0;
  23.      ptMsg->usData[0] = 0;
  24.     while(1)
  25.     {
  26.          ucKeyCode = bsp_GetKey();
  27.         
  28.          if (ucKeyCode != KEY_NONE)
  29.          {
  30.               switch (ucKeyCode)
  31.               {
  32.                    /* K1键按下 打印任务执行情况 */
  33.                    case KEY_DOWN_K1:         
  34.                        printf("=================================================\\r\\n");
  35.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  36.                        vTaskList((char *)&pcWriteBuffer);
  37.                        printf("%s\\r\\n", pcWriteBuffer);
  38.                   
  39.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  40.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  41.                        printf("%s\\r\\n", pcWriteBuffer);
  42.                        break;
  43.                   
  44.                    /* K2键按下,向xQueue1发送数据 */
  45.                    case KEY_DOWN_K2:
  46.                        ucCount++;
  47.                   
  48.                        /* 向消息队列发数据,如果消息队列满了,等待10个时钟节拍 */
  49.                        if( xQueueSend(xQueue1,
  50.                                         (void *) &ucCount,
  51.                                         (TickType_t)10) != pdPASS )
  52.                        {
  53.                             /* 发送失败,即使等待了10个时钟节拍 */
  54.                             printf("K2键按下,向xQueue1发送数据失败,即使等待了10个时钟节拍\\r\\n");
  55.                        }
  56.                        else
  57.                        {
  58.                             /* 发送成功 */
  59.                             printf("K2键按下,向xQueue1发送数据成功\\r\\n");                          
  60.                        }
  61.                        break;
  62.                   
  63.                    /* K3键按下,向xQueue2发送数据 */
  64.                    case KEY_DOWN_K3:
  65.                        ptMsg->ucMessageID++;
  66.                        ptMsg->ulData[0]++;;
  67.                        ptMsg->usData[0]++;
  68.                      
  69.                        /* 使用消息队列实现指针变量的传递 */
  70.                        if(xQueueSend(xQueue2,                  /* 消息队列句柄 */
  71.                                       (void *) &ptMsg,           /* 发送结构体指针变量ptMsg的地址 */
  72.                                       (TickType_t)10) != pdPASS )
  73.                        {
  74.                             /* 发送失败,即使等待了10个时钟节拍 */
  75.                             printf("K3键按下,向xQueue2发送数据失败,即使等待了10个时钟节拍\\r\\n");
  76.                        }
  77.                        else
  78.                        {
  79.                             /* 发送成功 */
  80.                             printf("K3键按下,向xQueue2发送数据成功\\r\\n");                          
  81.                        }
  82.                   
  83.                    /* 其他的键值不处理 */
  84.                    default:                    
  85.                        break;
  86.               }
  87.          }
  88.         
  89.          vTaskDelay(20);
  90.      }
  91. }
  92. /*
  93. *********************************************************************************************************
  94. *    函 数 名: vTaskLED
  95. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue2)
  96. *    形    参: pvParameters 是在创建该任务时传递的形参
  97. *    返 回 值: 无
  98. *   优 先 级: 2
  99. *********************************************************************************************************
  100. */
  101. static void vTaskLED(void *pvParameters)
  102. {
  103.      MSG_T *ptMsg;
  104.      BaseType_t xResult;
  105.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  106.    
  107.     while(1)
  108.     {
  109.          xResult = xQueueReceive(xQueue2,                   /* 消息队列句柄 */
  110.                                  (void *)&ptMsg,            /* 这里获取的是结构体的地址 */
  111.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  112.         
  113.         
  114.          if(xResult == pdPASS)
  115.          {
  116.               /* 成功接收,并通过串口将数据打印出来 */
  117.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  118.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  119.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  120.          }
  121.          else
  122.          {
  123.               /* 超时 */
  124.               bsp_LedToggle(2);
  125.               bsp_LedToggle(3);
  126.          }
  127.     }
  128. }
  129. /*
  130. *********************************************************************************************************
  131. *    函 数 名: vTaskMsgPro
  132. *    功能说明: 使用函数xQueueReceive接收任务vTaskTaskUserIF发送的消息队列数据(xQueue1)
  133. *    形    参: pvParameters 是在创建该任务时传递的形参
  134. *    返 回 值: 无
  135. *   优 先 级: 3
  136. *********************************************************************************************************
  137. */
  138. static void vTaskMsgPro(void *pvParameters)
  139. {
  140.      BaseType_t xResult;
  141.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  142.      uint8_t ucQueueMsgValue;
  143.    
  144.     while(1)
  145.     {
  146.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  147.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  148.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  149.         
  150.          if(xResult == pdPASS)
  151.          {
  152.               /* 成功接收,并通过串口将数据打印出来 */
  153.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  154.          }
  155.          else
  156.          {
  157.               /* 超时 */
  158.               bsp_LedToggle(1);
  159.               bsp_LedToggle(4);
  160.          }
  161.     }
  162. }
  163. /*
  164. *********************************************************************************************************
  165. *    函 数 名: vTaskStart
  166. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  167. *    形    参: pvParameters 是在创建该任务时传递的形参
  168. *    返 回 值: 无
  169. *   优 先 级: 4
  170. *********************************************************************************************************
  171. */
  172. static void vTaskStart(void *pvParameters)
  173. {
  174.     while(1)
  175.     {
  176.          /* 按键扫描 */
  177.          bsp_KeyScan();
  178.         vTaskDelay(10);
  179.     }
  180. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 14:53:36 | 显示全部楼层
20.4 实验例程说明(中断方式)



20.4.1 STM32F103开发板实验


配套例子:
    V4-316_FreeRTOS实验_消息队列(中断方式)
实验目的:
    1.     学习FreeRTOS的消息队列(中断方式)。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息。
    3.     K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskLED发送消息。
    4.     任务vTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印消息。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :使用函数xQueueReceive接定时器中断发送的消息队列数据。
              vTaskMsgPro任务 :使用函数xQueueReceive接定时器中断发送的消息队列数据。
              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按键,串口打印):
20.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 ) )
系统栈大小分配:
20.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.      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. */
  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. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {
  11.      /* 创建10个uint8_t型消息队列 */
  12.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17.    
  18.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  19.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  20.     if( xQueue2 == 0 )
  21.     {
  22.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  23.     }
  24. }
复制代码
四个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后在定时器中断给任务vTaskLED发送消息 */
  42.                    case KEY_DOWN_K3:
  43.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskLED发送消息
  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. *    功能说明: 使用函数xQueueReceive接定时器中断发送的消息队列数据  
  60. *    形    参: pvParameters 是在创建该任务时传递的形参
  61. *    返 回 值: 无
  62. *   优 先 级: 2
  63. *********************************************************************************************************
  64. */
  65. static void vTaskLED(void *pvParameters)
  66. {
  67.      MSG_T *ptMsg;
  68.      BaseType_t xResult;
  69.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  70.    
  71.    
  72.     while(1)
  73.     {
  74.          xResult = xQueueReceive(xQueue2,                   /* 消息队列句柄 */
  75.                                  (void *)&ptMsg,           /* 这里获取的是结构体的地址 */
  76.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  77.         
  78.         
  79.          if(xResult == pdPASS)
  80.          {
  81.               /* 成功接收,并通过串口将数据打印出来 */
  82.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  83.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  84.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  85.          }
  86.          else
  87.          {
  88.               /* 超时 */
  89.               bsp_LedToggle(2);
  90.               bsp_LedToggle(3);
  91.          }
  92.     }
  93. }
  94. /*
  95. *********************************************************************************************************
  96. *    函 数 名: vTaskMsgPro
  97. *    功能说明: 使用函数xQueueReceive接定时器中断发送的消息队列数据
  98. *    形    参: pvParameters 是在创建该任务时传递的形参
  99. *    返 回 值: 无
  100. *   优 先 级: 3
  101. *********************************************************************************************************
  102. */
  103. static void vTaskMsgPro(void *pvParameters)
  104. {
  105.      BaseType_t xResult;
  106.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  107.      uint8_t ucQueueMsgValue;
  108.    
  109.     while(1)
  110.     {
  111.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  112.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  113.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  114.         
  115.          if(xResult == pdPASS)
  116.          {
  117.               /* 成功接收,并通过串口将数据打印出来 */
  118.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  119.          }
  120.          else
  121.          {
  122.               /* 超时 */
  123.               bsp_LedToggle(1);
  124.               bsp_LedToggle(4);
  125.          }
  126.     }
  127. }
  128. /*
  129. *********************************************************************************************************
  130. *    函 数 名: vTaskStart
  131. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  132. *    形    参: pvParameters 是在创建该任务时传递的形参
  133. *    返 回 值: 无
  134. *   优 先 级: 4
  135. *********************************************************************************************************
  136. */
  137. static void vTaskStart(void *pvParameters)
  138. {
  139.     while(1)
  140.     {
  141.          /* 按键扫描 */
  142.          bsp_KeyScan();
  143.         vTaskDelay(10);
  144.     }
  145. }
复制代码
定时器中断回调函数中给任务发送消息队列数据:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数xQueueSendFromISR在中断服务程序中的使用方法。
  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.    
  14.      g_uiCount++;
  15.    
  16.      /* 向消息队列发数据 */
  17.      xQueueSendFromISR(xQueue1,
  18.                          (void *)&g_uiCount,
  19.                          &xHigherPriorityTaskWoken);
  20.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  21.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  22. }
  23. static void TIM_CallBack2(void)
  24. {
  25.      MSG_T   *ptMsg;
  26.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  27.    
  28.      /* 初始化结构体指针 */
  29.      ptMsg = &g_tMsg;
  30.    
  31.      /* 初始化数组 */
  32.      ptMsg->ucMessageID++;
  33.      ptMsg->ulData[0]++;
  34.      ptMsg->usData[0]++;
  35.      /* 向消息队列发数据 */
  36.      xQueueSendFromISR(xQueue2,
  37.                          (void *)&ptMsg,
  38.                           &xHigherPriorityTaskWoken);
  39.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  40.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  41. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 14:59:40 | 显示全部楼层
20.4.2 STM32F407开发板实验


配套例子:
     V5-316_FreeRTOS实验_消息队列(中断方式)
实验目的:
     1.     学习FreeRTOS的消息队列(中断方式)。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息。
    3.     K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskLED发送消息。
    4.     任务vTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印消息。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :使用函数xQueueReceive接定时器中断发送的消息队列数据。
              vTaskMsgPro任务 :使用函数xQueueReceive接定时器中断发送的消息队列数据。
              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按键,串口打印):
20.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 ) )
系统栈大小分配:
20.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.      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.      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. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {
  11.      /* 创建10个uint8_t型消息队列 */
  12.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17.    
  18.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  19.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  20.     if( xQueue2 == 0 )
  21.     {
  22.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  23.     }
  24. }
复制代码
四个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后在定时器中断给任务vTaskLED发送消息 */
  42.                    case KEY_DOWN_K3:
  43.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskLED发送消息
  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. *    功能说明: 使用函数xQueueReceive接定时器中断发送的消息队列数据  
  60. *    形    参: pvParameters 是在创建该任务时传递的形参
  61. *    返 回 值: 无
  62. *   优 先 级: 2
  63. *********************************************************************************************************
  64. */
  65. static void vTaskLED(void *pvParameters)
  66. {
  67.      MSG_T *ptMsg;
  68.      BaseType_t xResult;
  69.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  70.    
  71.    
  72.     while(1)
  73.     {
  74.          xResult = xQueueReceive(xQueue2,                   /* 消息队列句柄 */
  75.                                  (void *)&ptMsg,           /* 这里获取的是结构体的地址 */
  76.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  77.         
  78.         
  79.          if(xResult == pdPASS)
  80.          {
  81.               /* 成功接收,并通过串口将数据打印出来 */
  82.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  83.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  84.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  85.          }
  86.          else
  87.          {
  88.               /* 超时 */
  89.               bsp_LedToggle(2);
  90.               bsp_LedToggle(3);
  91.          }
  92.     }
  93. }
  94. /*
  95. *********************************************************************************************************
  96. *    函 数 名: vTaskMsgPro
  97. *    功能说明: 使用函数xQueueReceive接定时器中断发送的消息队列数据
  98. *    形    参: pvParameters 是在创建该任务时传递的形参
  99. *    返 回 值: 无
  100. *   优 先 级: 3
  101. *********************************************************************************************************
  102. */
  103. static void vTaskMsgPro(void *pvParameters)
  104. {
  105.      BaseType_t xResult;
  106.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  107.      uint8_t ucQueueMsgValue;
  108.    
  109.     while(1)
  110.     {
  111.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  112.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  113.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  114.         
  115.          if(xResult == pdPASS)
  116.          {
  117.               /* 成功接收,并通过串口将数据打印出来 */
  118.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  119.          }
  120.          else
  121.          {
  122.               /* 超时 */
  123.               bsp_LedToggle(1);
  124.               bsp_LedToggle(4);
  125.          }
  126.     }
  127. }
  128. /*
  129. *********************************************************************************************************
  130. *    函 数 名: vTaskStart
  131. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  132. *    形    参: pvParameters 是在创建该任务时传递的形参
  133. *    返 回 值: 无
  134. *   优 先 级: 4
  135. *********************************************************************************************************
  136. */
  137. static void vTaskStart(void *pvParameters)
  138. {
  139.     while(1)
  140.     {
  141.          /* 按键扫描 */
  142.          bsp_KeyScan();
  143.         vTaskDelay(10);
  144.     }
  145. }
复制代码
定时器中断回调函数中给任务发送消息队列数据:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数xQueueSendFromISR在中断服务程序中的使用方法。
  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.    
  14.      g_uiCount++;
  15.    
  16.      /* 向消息队列发数据 */
  17.      xQueueSendFromISR(xQueue1,
  18.                          (void *)&g_uiCount,
  19.                          &xHigherPriorityTaskWoken);
  20.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  21.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  22. }
  23. static void TIM_CallBack2(void)
  24. {
  25.      MSG_T   *ptMsg;
  26.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  27.    
  28.      /* 初始化结构体指针 */
  29.      ptMsg = &g_tMsg;
  30.    
  31.      /* 初始化数组 */
  32.      ptMsg->ucMessageID++;
  33.      ptMsg->ulData[0]++;
  34.      ptMsg->usData[0]++;
  35.      /* 向消息队列发数据 */
  36.      xQueueSendFromISR(xQueue2,
  37.                          (void *)&ptMsg,
  38.                           &xHigherPriorityTaskWoken);
  39.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  40.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  41. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 15:05:24 | 显示全部楼层
20.4.3 STM32F429开发板实验


配套例子:
    V4-316_FreeRTOS实验_消息队列(中断方式)
实验目的:
   1.     学习FreeRTOS的消息队列(中断方式)。
实验内容:
     1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送消息。
    3.     K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskLED发送消息。
    4.     任务vTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印消息。
    5.     各个任务实现的功能如下:
              vTaskUserIF任务   :按键消息处理。
              vTaskLED任务     :使用函数xQueueReceive接定时器中断发送的消息队列数据。
              vTaskMsgPro任务 :使用函数xQueueReceive接定时器中断发送的消息队列数据。
              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按键,串口打印):
20.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 ) )
系统栈大小分配:
20.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. *    函 数 名: AppObjCreate
  4. *    功能说明: 创建任务通信机制
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppObjCreate (void)
  10. {
  11.      /* 创建10个uint8_t型消息队列 */
  12.      xQueue1 = xQueueCreate(10, sizeof(uint8_t));
  13.     if( xQueue1 == 0 )
  14.     {
  15.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  16.     }
  17.    
  18.      /* 创建10个存储指针变量的消息队列,由于CM3/CM4内核是32位机,一个指针变量占用4个字节 */
  19.      xQueue2 = xQueueCreate(10, sizeof(struct Msg *));
  20.     if( xQueue2 == 0 )
  21.     {
  22.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  23.     }
  24. }
复制代码
四个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后在定时器中断给任务vTaskLED发送消息 */
  42.                    case KEY_DOWN_K3:
  43.                        printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskLED发送消息
  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. *    功能说明: 使用函数xQueueReceive接定时器中断发送的消息队列数据  
  60. *    形    参: pvParameters 是在创建该任务时传递的形参
  61. *    返 回 值: 无
  62. *   优 先 级: 2
  63. *********************************************************************************************************
  64. */
  65. static void vTaskLED(void *pvParameters)
  66. {
  67.      MSG_T *ptMsg;
  68.      BaseType_t xResult;
  69.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(200); /* 设置最大等待时间为200ms */
  70.    
  71.    
  72.     while(1)
  73.     {
  74.          xResult = xQueueReceive(xQueue2,                   /* 消息队列句柄 */
  75.                                  (void *)&ptMsg,           /* 这里获取的是结构体的地址 */
  76.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  77.         
  78.         
  79.          if(xResult == pdPASS)
  80.          {
  81.               /* 成功接收,并通过串口将数据打印出来 */
  82.               printf("接收到消息队列数据ptMsg->ucMessageID = %d\\r\\n", ptMsg->ucMessageID);
  83.               printf("接收到消息队列数据ptMsg->ulData[0] = %d\\r\\n", ptMsg->ulData[0]);
  84.               printf("接收到消息队列数据ptMsg->usData[0] = %d\\r\\n", ptMsg->usData[0]);
  85.          }
  86.          else
  87.          {
  88.               /* 超时 */
  89.               bsp_LedToggle(2);
  90.               bsp_LedToggle(3);
  91.          }
  92.     }
  93. }
  94. /*
  95. *********************************************************************************************************
  96. *    函 数 名: vTaskMsgPro
  97. *    功能说明: 使用函数xQueueReceive接定时器中断发送的消息队列数据
  98. *    形    参: pvParameters 是在创建该任务时传递的形参
  99. *    返 回 值: 无
  100. *   优 先 级: 3
  101. *********************************************************************************************************
  102. */
  103. static void vTaskMsgPro(void *pvParameters)
  104. {
  105.      BaseType_t xResult;
  106.      const TickType_t xMaxBlockTime = pdMS_TO_TICKS(300); /* 设置最大等待时间为300ms */
  107.      uint8_t ucQueueMsgValue;
  108.    
  109.     while(1)
  110.     {
  111.          xResult = xQueueReceive(xQueue1,                   /* 消息队列句柄 */
  112.                                  (void *)&ucQueueMsgValue,  /* 存储接收到的数据到变量ucQueueMsgValue中 */
  113.                                  (TickType_t)xMaxBlockTime);/* 设置阻塞时间 */
  114.         
  115.          if(xResult == pdPASS)
  116.          {
  117.               /* 成功接收,并通过串口将数据打印出来 */
  118.               printf("接收到消息队列数据ucQueueMsgValue = %d\\r\\n", ucQueueMsgValue);
  119.          }
  120.          else
  121.          {
  122.               /* 超时 */
  123.               bsp_LedToggle(1);
  124.               bsp_LedToggle(4);
  125.          }
  126.     }
  127. }
  128. /*
  129. *********************************************************************************************************
  130. *    函 数 名: vTaskStart
  131. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  132. *    形    参: pvParameters 是在创建该任务时传递的形参
  133. *    返 回 值: 无
  134. *   优 先 级: 4
  135. *********************************************************************************************************
  136. */
  137. static void vTaskStart(void *pvParameters)
  138. {
  139.     while(1)
  140.     {
  141.          /* 按键扫描 */
  142.          bsp_KeyScan();
  143.         vTaskDelay(10);
  144.     }
  145. }
复制代码
定时器中断回调函数中给任务发送消息队列数据:
    定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数xQueueSendFromISR在中断服务程序中的使用方法。
  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.    
  14.      g_uiCount++;
  15.    
  16.      /* 向消息队列发数据 */
  17.      xQueueSendFromISR(xQueue1,
  18.                          (void *)&g_uiCount,
  19.                          &xHigherPriorityTaskWoken);
  20.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  21.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  22. }
  23. static void TIM_CallBack2(void)
  24. {
  25.      MSG_T   *ptMsg;
  26.      BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  27.    
  28.      /* 初始化结构体指针 */
  29.      ptMsg = &g_tMsg;
  30.    
  31.      /* 初始化数组 */
  32.      ptMsg->ucMessageID++;
  33.      ptMsg->ulData[0]++;
  34.      ptMsg->usData[0]++;
  35.      /* 向消息队列发数据 */
  36.      xQueueSendFromISR(xQueue2,
  37.                          (void *)&ptMsg,
  38.                           &xHigherPriorityTaskWoken);
  39.      /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
  40.      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  41. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-31 15:07:31 | 显示全部楼层
20.5 总结

    本章节就为大家讲解了消息队列的使用,在实际项目中,消息队列用到的地方非常多,希望初学者认真学习并掌握。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-1 01:15 , Processed in 0.372339 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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