硬汉嵌入式论坛

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

[FreeRTOS教程] 第23章 FreeRTOS互斥信号量

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-9-3 15:38:34 | 显示全部楼层 |阅读模式



第23章      FreeRTOS互斥信号量



      本章节讲解FreeRTOS重要的资源共享机制---互斥信号量(Mutex,即MutualExclusion的缩写)。注意,建议初学者学习完前两个章节的信号量后再学习本章节的互斥信号量
       FreeRTOS中互斥信号量的源码实现是基于消息队列实现的。
      本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
23.1 互斥信号量
23.2 互斥信号量API函数
23.3 实验例程说明
23.4      总结



23.1 互斥信号量


23.1.1  互斥信号量的概念及其作用

      互斥信号量的主要作用是对资源实现互斥访问,使用二值信号量也可以实现互斥访问的功能,不过互斥信号量与二值信号量有区别。下面我们先举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识,进而引出要讲解的互斥信号量。
运行条件:
        1、让两个任务Task1Task2都运行串口打印函数printf,这里我们就通过二值信号量实现对函数printf的互斥访问。如果不对函数printf进行互斥访问,串口打印容易出现乱码。
        2、用计数信号量实现二值信号量只需将计数信号量的初始值设置为1即可。
代码实现:
创建二值信号量
  1. static SemaphoreHandle_t  xSemaphore = NULL;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: AppObjCreate
  5. *    功能说明: 创建任务通信机制
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static void AppObjCreate (void)
  11. {
  12.      /* 创建二值信号量,首次创建信号量计数值是0 */
  13.      xSemaphore = xSemaphoreCreateBinary();
  14.    
  15.      if(xSemaphore == NULL)
  16.     {
  17.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  18.     }
  19.    
  20.      /* 先释放一次,将初始值改为1,利用二值信号量实现互斥功能 */
  21.      xSemaphoreGive(xSemaphore);
  22. }
复制代码
通过二值信号量实现对printf函数互斥访问的两个任务
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskLED
  4. *    功能说明: 实现对串口的互斥访问
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 2
  8. *********************************************************************************************************
  9. */
  10. static void vTaskLED(void *pvParameters)
  11. {
  12.      TickType_t xLastWakeTime;
  13.      const TickType_t xFrequency = 300;
  14.      /* 获取当前的系统时间 */
  15.     xLastWakeTime = xTaskGetTickCount();
  16.    
  17.     while(1)
  18.     {
  19.          /* 通过二值信号量实现资源互斥访问,永久等待直到资源可用 */
  20.          xSemaphoreTake(xSemaphore, portMAX_DELAY);
  21.         
  22.          printf("任务vTaskLED在运行\r\n");
  23.          bsp_LedToggle(1);
  24.          bsp_LedToggle(4);
  25.         
  26.          xSemaphoreGive(xSemaphore);
  27.         
  28.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  29.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  30.     }
  31. }
  32. /*
  33. *********************************************************************************************************
  34. *    函 数 名: vTaskMsgPro
  35. *    功能说明: 实现对串口的互斥访问
  36. *    形    参: pvParameters 是在创建该任务时传递的形参
  37. *    返 回 值: 无
  38. *   优 先 级: 3
  39. *********************************************************************************************************
  40. */
  41. static void vTaskMsgPro(void *pvParameters)
  42. {
  43.      TickType_t xLastWakeTime;
  44.      const TickType_t xFrequency = 300;
  45.      /* 获取当前的系统时间 */
  46.     xLastWakeTime = xTaskGetTickCount();
  47.    
  48.     while(1)
  49.     {
  50.          /* 通过二值信号量实现资源互斥访问,永久等待直到资源可用 */
  51.          xSemaphoreTake(xSemaphore, portMAX_DELAY);
  52.         
  53.          printf("任务vTaskMsgPro在运行\r\n");
  54.          bsp_LedToggle(2);
  55.          bsp_LedToggle(3);
  56.         
  57.          xSemaphoreGive(xSemaphore);
  58.         
  59.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  60.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  61.     }
  62. }
复制代码
    有了上面二值信号量的认识之后,互斥信号量与二值信号量又有什么区别呢?互斥信号量可以防止优先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。

23.1.2 优先级翻转问题

    下面我们通过如下的框图来说明一下优先级翻转的问题,让大家有一个形象的认识。
23.1.JPG

运行条件:
1、创建3个任务Task1Task2Task3,优先级分别为321。也就是Task1的优先级最高。
2、任务Task1Task3互斥访问串口打印printf,采用二值信号实现互斥访问。
3、起初Task3通过二值信号量正在调用printf,被任务Task1抢占,开始执行任务Task1,也就是上图的起始位置。
运行过程描述如下:
    (1)任务Task1运行的过程需要调用函数printf,发现任务Task3正在调用,任务Task1会被挂起,等待Task3释放函数printf
    (2)在调度器的作用下,任务Task3得到运行,Task3运行的过程中,由于任务Task2就绪,抢占了Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务Task1需要等待Task2执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务Task1等待低优先级任务Task2完成。所以这种情况被称之为优先级翻转问题
    (3)任务Task2执行完毕后,任务Task3恢复执行,Task3释放互斥资源后,任务Task1得到互斥资源,从而可以继续执行。
    上面就是一个产生优先级翻转问题的现象。

23.1.3 FreeRTOS互斥信号量的实现

    FreeRTOS互斥信号量是怎么实现的呢?其实相对于二值信号量,互斥信号量就是解决了一下优先级翻转的问题。下面我们通过如下的框图来说明一下FreeRTOS互斥信号量的实现,让大家有一个形象的认识。
23.2.JPG

运行条件:
    1、创建2个任务Task1Task2,优先级分别为13,也就是任务Task2的优先级最高。
    2、任务Task1Task2互斥访问串口打印printf
    3、使用FreeRTOS的互斥信号量实现串口打印printf的互斥访问。
运行过程描述如下:
    (1)低优先级任务Task1执行过程中先获得互斥资源printf的执行。此时任务Task2抢占了任务Task1的执行,任务Task1被挂起。任务Task2得到执行。
    (2)任务Task2执行过程中也需要调用互斥资源,但是发现任务Task1正在访问,此时任务Task1的优先级会被提升到与Task2同一个优先级,也就是优先级3这个就是所谓的优先级继承(Priorityinheritance),这样就有效地防止了优先级翻转问题。任务Task2被挂起,任务Task1有新的优先级继续执行。
    (3)任务Task1执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务Task2获得互斥资源后开始执行。
  
    上面就是一个简单的FreeRTOS互斥信号量的实现过程。

23.1.4 FreeRTOS中断方式互斥信号量的实现

    互斥信号量仅支持用在FreeRTOS的任务中,中断函数中不可使用。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-3 15:49:40 | 显示全部楼层
23.2 互斥信号量API函数


    使用如下18个函数可以实现FreeRTOS的信号量(含计数信号量,二值信号量和互斥信号):
(1)     xSemaphoreCreateBinary()
(2)    xSemaphoreCreateBinaryStatic()
(3)    vSemaphoreCreateBinary()
(4)    xSemaphoreCreateCounting()
(5)    xSemaphoreCreateCountingStatic()
(6)    xSemaphoreCreateMutex()
(7)    xSemaphoreCreateMutexStatic()
(8)    xSem'CreateRecursiveMutex()
(9)    xSem'CreateRecursiveMutexStatic()
(10)    vSemaphoreDelete()
(11)    xSemaphoreGetMutexHolder()
(12)    uxSemaphoreGetCount()
(13)    xSemaphoreTake()
(14)    xSemaphoreTakeFromISR()
(15)    xSemaphoreTakeRecursive()
(16)    xSemaphoreGive()
(17)    xSemaphoreGiveRecursive()
(18)    xSemaphoreGiveFromISR()
    关于这18个函数的讲解及其使用方法可以看FreeRTOS在线版手册:
23.3.jpg

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

23.2.1 函数xSemaphoreCreateMutex


函数原型:
  1. SemaphoreHandle_t xSemaphoreCreateMutex( void )
复制代码
函数描述:
函数xSemaphoreCreateMutex用于创建互斥信号量。
    (1)返回值,如果创建成功会返回互斥信号量的句柄,如果由于FreeRTOSConfig.h文件中heap大小不足,无法为此互斥信号量提供所需的空间会返回NULL
使用这个函数要注意以下问题:
1.     此函数是基于函数xQueueCreateMutex实现的:
        #definexSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
        函数xQueueCreateMutex的实现是基于消息队列函数xQueueGenericCreate实现的。
2.     使用此函数要在FreeRTOSConfig.h文件中使能宏定义:
        #defineconfigUSE_MUTEXES   1
使用举例:
  1. static SemaphoreHandle_t  xMutex = NULL;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: AppObjCreate
  5. *    功能说明: 创建任务通信机制
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static void AppObjCreate (void)
  11. {
  12.      /* 创建互斥信号量 */
  13. xMutex = xSemaphoreCreateMutex();
  14.      if(xSemaphore == NULL)
  15.     {
  16.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  17.     }
  18. }
复制代码

23.2.2 函数xSemaphoreGive


函数原型:
  1. xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 信号量句柄 */
复制代码
函数描述:
函数xSemaphoreGive用于在任务代码中释放信号量。
    (1)第1个参数是信号量句柄。
    (2)返回值,如果信号量释放成功返回pdTRUE,否则返回pdFALSE,因为信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。
使用这个函数要注意以下问题:
1.     此函数是基于消息队列函数xQueueGenericSend实现的:
      #define xSemaphoreGive( xSemaphore )                                               \\
      xQueueGenericSend(( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, \\
                               queueSEND_TO_BACK )
2.     此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是xSemaphoreGiveFromISR
3.     使用此函数前,一定要保证用函数xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者 xSemaphoreCreateCounting()创建了信号量。
4.     此函数不支持使用xSemaphoreCreateRecursiveMutex()创建的信号量。
使用举例:
  1. static SemaphoreHandle_t  xMutex = NULL;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: vTaskLED
  5. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  6. *    形    参: pvParameters 是在创建该任务时传递的形参
  7. *    返 回 值: 无
  8. *   优 先 级: 2
  9. *********************************************************************************************************
  10. */
  11. static void vTaskLED(void *pvParameters)
  12. {
  13.      TickType_t xLastWakeTime;
  14.      const TickType_t xFrequency = 200;
  15.      /* 获取当前的系统时间 */
  16.     xLastWakeTime = xTaskGetTickCount();
  17.    
  18.     while(1)
  19.     {
  20.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  21.          xSemaphoreTake(xMutex, portMAX_DELAY);
  22.         
  23.          printf("任务vTaskLED在运行\\r\\n");
  24.          bsp_LedToggle(2);
  25.          bsp_LedToggle(3);
  26.         
  27.          xSemaphoreGive(xMutex);
  28.         
  29.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  30.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  31.     }
  32. }
复制代码

23.2.3 函数xSemaphoreTake


函数原型:
  1. xSemaphoreTake( SemaphoreHandle_t xSemaphore, /* 信号量句柄 */
  2.                  TickType_t xTicksToWait );   /* 等待信号量可用的最大等待时间 */
复制代码
函数描述:
函数xSemaphoreTake用于在任务代码中获取信号量。
    (1)第1个参数是信号量句柄。
    (2)第2个参数是没有信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。
    (3)返回值,如果创建成功会获取信号量返回pdTRUE,否则返回pdFALSE
使用这个函数要注意以下问题:
1.     此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是xSemaphoreTakeFromISR。
2.     如果消息队列为空且第2个参数为0,那么此函数会立即返回。
3.     如果用户将FreeRTOSConfig.h文件中的宏定义INCLUDE_vTaskSuspend配置为1且第2个参数配置为portMAX_DELAY,那么此函数会永久等待直到信号量可用。
使用举例:
  1. static SemaphoreHandle_t  xMutex = NULL;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: vTaskLED
  5. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  6. *    形    参: pvParameters 是在创建该任务时传递的形参
  7. *    返 回 值: 无
  8. *   优 先 级: 2
  9. *********************************************************************************************************
  10. */
  11. static void vTaskLED(void *pvParameters)
  12. {
  13.      TickType_t xLastWakeTime;
  14.      const TickType_t xFrequency = 200;
  15.      /* 获取当前的系统时间 */
  16.     xLastWakeTime = xTaskGetTickCount();
  17.    
  18.     while(1)
  19.     {
  20.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  21.          xSemaphoreTake(xMutex, portMAX_DELAY);
  22.         
  23.          printf("任务vTaskLED在运行\\r\\n");
  24.          bsp_LedToggle(2);
  25.          bsp_LedToggle(3);
  26.         
  27.          xSemaphoreGive(xMutex);
  28.         
  29.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  30.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  31.     }
  32. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-3 16:00:32 | 显示全部楼层
23.3 实验例程说明



23.3.1 STM32F103开发板实验


配套例子:
      V4-323_FreeRTOS实验_互斥信号量
实验目的:
      1.     学习FreeRTOS的互斥信号量。
实验内容:
      1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
      2.     K2键按下,直接发送同步信号给任务vTaskMsgPro。
      3.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码。
              vTaskMsgPro任务:实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码。
              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. #define configUSE_MUTEXES            1
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              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          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
       定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、#define configUSE_MUTEXES                     1
       使能互斥信号量。
7、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
23.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 ) )
系统栈大小分配:
23.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.      /* 创建互斥信号量 */
  12.     xMutex = xSemaphoreCreateMutex();
  13.    
  14.      if(xMutex == NULL)
  15.     {
  16.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  17.     }
  18. }
复制代码

四个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.    
  15.     while(1)
  16.     {
  17.          ucKeyCode = bsp_GetKey();
  18.         
  19.          if (ucKeyCode != KEY_NONE)
  20.          {
  21.               switch (ucKeyCode)
  22.               {
  23.                    /* K1键按下 打印任务执行情况 */
  24.                    case KEY_DOWN_K1:
  25.                        /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  26.                        xSemaphoreTake(xMutex, portMAX_DELAY);                     
  27.                        printf("=================================================\\r\\n");
  28.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  29.                        vTaskList((char *)&pcWriteBuffer);
  30.                        printf("%s\\r\\n", pcWriteBuffer);
  31.                   
  32.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  33.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  34.                        printf("%s\\r\\n", pcWriteBuffer);
  35.                        xSemaphoreGive(xMutex);
  36.                        break;
  37.                   
  38.                    /* 其他的键值不处理 */
  39.                    default:                    
  40.                        break;
  41.               }
  42.          }
  43.         
  44.          vTaskDelay(20);
  45.      }
  46. }
  47. /*
  48. *********************************************************************************************************
  49. *    函 数 名: vTaskLED
  50. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  51. *    形    参: pvParameters 是在创建该任务时传递的形参
  52. *    返 回 值: 无
  53. *   优 先 级: 2
  54. *********************************************************************************************************
  55. */
  56. static void vTaskLED(void *pvParameters)
  57. {
  58.      TickType_t xLastWakeTime;
  59.      const TickType_t xFrequency = 200;
  60.      /* 获取当前的系统时间 */
  61.     xLastWakeTime = xTaskGetTickCount();
  62.    
  63.     while(1)
  64.     {
  65.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  66.          xSemaphoreTake(xMutex, portMAX_DELAY);
  67.         
  68.          printf("任务vTaskLED在运行\\r\\n");
  69.          bsp_LedToggle(2);
  70.          bsp_LedToggle(3);
  71.         
  72.          xSemaphoreGive(xMutex);
  73.         
  74.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  75.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  76.     }
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: vTaskMsgPro
  81. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  82. *    形    参: pvParameters 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84. *   优 先 级: 3
  85. *********************************************************************************************************
  86. */
  87. static void vTaskMsgPro(void *pvParameters)
  88. {
  89.      TickType_t xLastWakeTime;
  90.      const TickType_t xFrequency = 300;
  91.      /* 获取当前的系统时间 */
  92.     xLastWakeTime = xTaskGetTickCount();
  93.    
  94.     while(1)
  95.     {
  96.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  97.          xSemaphoreTake(xMutex, portMAX_DELAY);
  98.         
  99.          printf("任务vTaskMsgPro在运行\\r\\n");
  100.          bsp_LedToggle(1);
  101.          bsp_LedToggle(4);
  102.         
  103.          xSemaphoreGive(xMutex);
  104.         
  105.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  106.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  107.     }
  108. }
  109. /*
  110. *********************************************************************************************************
  111. *    函 数 名: vTaskStart
  112. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  113. *    形    参: pvParameters 是在创建该任务时传递的形参
  114. *    返 回 值: 无
  115. *   优 先 级: 4
  116. *********************************************************************************************************
  117. */
  118. static void vTaskStart(void *pvParameters)
  119. {
  120.     while(1)
  121.     {
  122.          /* 按键扫描 */
  123.          bsp_KeyScan();
  124.         vTaskDelay(10);
  125.     }
  126. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-3 16:08:56 | 显示全部楼层
23.3.2 STM32F407开发板实验


配套例子:
    V5-323_FreeRTOS实验_互斥信号量
实验目的:
    1.     学习FreeRTOS的互斥信号量。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,直接发送同步信号给任务vTaskMsgPro。
    3.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码。
              vTaskMsgPro任务:实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码。
              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. #define configUSE_MUTEXES            1
  23. /* Run time and task stats gathering related definitions. */
  24. #define configGENERATE_RUN_TIME_STATS                1
  25. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  26. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  27. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  28. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  29. /* Co-routine definitions. */
  30. #define configUSE_CO_ROUTINES             0
  31. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  32. /* Set the following definitions to 1 to include the API function, or zero
  33. to exclude the API function. */
  34. #define INCLUDE_vTaskPrioritySet     1
  35. #define INCLUDE_uxTaskPriorityGet         1
  36. #define INCLUDE_vTaskDelete               1
  37. #define INCLUDE_vTaskCleanUpResources 0
  38. #define INCLUDE_vTaskSuspend              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          ( 5 )
        定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
5、#define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 30 * 1024 ) )
        定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
6、#define configUSE_MUTEXES                     1
       使能互斥信号量。
7、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
    更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
23.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 ) )
系统栈大小分配:
23.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.      /* 创建互斥信号量 */
  12.     xMutex = xSemaphoreCreateMutex();
  13.    
  14.      if(xMutex == NULL)
  15.     {
  16.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  17.     }
  18. }
复制代码
四个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.    
  15.     while(1)
  16.     {
  17.          ucKeyCode = bsp_GetKey();
  18.         
  19.          if (ucKeyCode != KEY_NONE)
  20.          {
  21.               switch (ucKeyCode)
  22.               {
  23.                    /* K1键按下 打印任务执行情况 */
  24.                    case KEY_DOWN_K1:
  25.                        /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  26.                        xSemaphoreTake(xMutex, portMAX_DELAY);                     
  27.                        printf("=================================================\\r\\n");
  28.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  29.                        vTaskList((char *)&pcWriteBuffer);
  30.                        printf("%s\\r\\n", pcWriteBuffer);
  31.                   
  32.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  33.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  34.                        printf("%s\\r\\n", pcWriteBuffer);
  35.                        xSemaphoreGive(xMutex);
  36.                        break;
  37.                   
  38.                    /* 其他的键值不处理 */
  39.                    default:                    
  40.                        break;
  41.               }
  42.          }
  43.         
  44.          vTaskDelay(20);
  45.      }
  46. }
  47. /*
  48. *********************************************************************************************************
  49. *    函 数 名: vTaskLED
  50. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  51. *    形    参: pvParameters 是在创建该任务时传递的形参
  52. *    返 回 值: 无
  53. *   优 先 级: 2
  54. *********************************************************************************************************
  55. */
  56. static void vTaskLED(void *pvParameters)
  57. {
  58.      TickType_t xLastWakeTime;
  59.      const TickType_t xFrequency = 200;
  60.      /* 获取当前的系统时间 */
  61.     xLastWakeTime = xTaskGetTickCount();
  62.    
  63.     while(1)
  64.     {
  65.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  66.          xSemaphoreTake(xMutex, portMAX_DELAY);
  67.         
  68.          printf("任务vTaskLED在运行\\r\\n");
  69.          bsp_LedToggle(2);
  70.          bsp_LedToggle(3);
  71.         
  72.          xSemaphoreGive(xMutex);
  73.         
  74.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  75.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  76.     }
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: vTaskMsgPro
  81. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  82. *    形    参: pvParameters 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84. *   优 先 级: 3
  85. *********************************************************************************************************
  86. */
  87. static void vTaskMsgPro(void *pvParameters)
  88. {
  89.      TickType_t xLastWakeTime;
  90.      const TickType_t xFrequency = 300;
  91.      /* 获取当前的系统时间 */
  92.     xLastWakeTime = xTaskGetTickCount();
  93.    
  94.     while(1)
  95.     {
  96.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  97.          xSemaphoreTake(xMutex, portMAX_DELAY);
  98.         
  99.          printf("任务vTaskMsgPro在运行\\r\\n");
  100.          bsp_LedToggle(1);
  101.          bsp_LedToggle(4);
  102.         
  103.          xSemaphoreGive(xMutex);
  104.         
  105.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  106.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  107.     }
  108. }
  109. /*
  110. *********************************************************************************************************
  111. *    函 数 名: vTaskStart
  112. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  113. *    形    参: pvParameters 是在创建该任务时传递的形参
  114. *    返 回 值: 无
  115. *   优 先 级: 4
  116. *********************************************************************************************************
  117. */
  118. static void vTaskStart(void *pvParameters)
  119. {
  120.     while(1)
  121.     {
  122.          /* 按键扫描 */
  123.          bsp_KeyScan();
  124.         vTaskDelay(10);
  125.     }
  126. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-3 16:13:42 | 显示全部楼层
23.3.3   STM32F429开发板实验


配套例子:
     V6-323_FreeRTOS实验_互斥信号量
实验目的:
     1.     学习FreeRTOS的互斥信号量。
实验内容:
    1.     K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
    2.     K2键按下,直接发送同步信号给任务vTaskMsgPro。
     3.     各个任务实现的功能如下:
              vTaskUserIF任务 :按键消息处理。
              vTaskLED任务    :实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码。
              vTaskMsgPro任务:实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码。
              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. #define configUSE_MUTEXES            1
  19. /* Run time and task stats gathering related definitions. */
  20. #define configGENERATE_RUN_TIME_STATS                1
  21. #define configUSE_STATS_FORMATTING_FUNCTIONS         1
  22. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul)
  23. #define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
  24. //#define portALT_GET_RUN_TIME_COUNTER_VALUE           1
  25. /* Co-routine definitions. */
  26. #define configUSE_CO_ROUTINES            0
  27. #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
  28. /* Set the following definitions to 1 to include the API function, or zero
  29. to exclude the API function. */
  30. #define INCLUDE_vTaskPrioritySet          1
  31. #define INCLUDE_uxTaskPriorityGet         1
  32. #define INCLUDE_vTaskDelete               1
  33. #define INCLUDE_vTaskCleanUpResources      0
  34. #define INCLUDE_vTaskSuspend              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、#define configUSE_MUTEXES                     1
       使能互斥信号量。
7、configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY          0x01
        定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
    (1)使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
    (2)对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4 = 16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为015,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
    (3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY0x01表示用户可以在抢占式优先级为115的中断里面调用FreeRTOSAPI函数,抢占式优先级为0的中断里面是不允许调用的。
     更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
23.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 ) )
系统栈大小分配:
23.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.      /* 创建互斥信号量 */
  12.     xMutex = xSemaphoreCreateMutex();
  13.    
  14.      if(xMutex == NULL)
  15.     {
  16.         /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
  17.     }
  18. }
复制代码
四个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.    
  15.     while(1)
  16.     {
  17.          ucKeyCode = bsp_GetKey();
  18.         
  19.          if (ucKeyCode != KEY_NONE)
  20.          {
  21.               switch (ucKeyCode)
  22.               {
  23.                    /* K1键按下 打印任务执行情况 */
  24.                    case KEY_DOWN_K1:
  25.                        /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  26.                        xSemaphoreTake(xMutex, portMAX_DELAY);                     
  27.                        printf("=================================================\\r\\n");
  28.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  29.                        vTaskList((char *)&pcWriteBuffer);
  30.                        printf("%s\\r\\n", pcWriteBuffer);
  31.                   
  32.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  33.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  34.                        printf("%s\\r\\n", pcWriteBuffer);
  35.                        xSemaphoreGive(xMutex);
  36.                        break;
  37.                   
  38.                    /* 其他的键值不处理 */
  39.                    default:                    
  40.                        break;
  41.               }
  42.          }
  43.         
  44.          vTaskDelay(20);
  45.      }
  46. }
  47. /*
  48. *********************************************************************************************************
  49. *    函 数 名: vTaskLED
  50. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  51. *    形    参: pvParameters 是在创建该任务时传递的形参
  52. *    返 回 值: 无
  53. *   优 先 级: 2
  54. *********************************************************************************************************
  55. */
  56. static void vTaskLED(void *pvParameters)
  57. {
  58.      TickType_t xLastWakeTime;
  59.      const TickType_t xFrequency = 200;
  60.      /* 获取当前的系统时间 */
  61.     xLastWakeTime = xTaskGetTickCount();
  62.    
  63.     while(1)
  64.     {
  65.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  66.          xSemaphoreTake(xMutex, portMAX_DELAY);
  67.         
  68.          printf("任务vTaskLED在运行\\r\\n");
  69.          bsp_LedToggle(2);
  70.          bsp_LedToggle(3);
  71.         
  72.          xSemaphoreGive(xMutex);
  73.         
  74.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  75.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  76.     }
  77. }
  78. /*
  79. *********************************************************************************************************
  80. *    函 数 名: vTaskMsgPro
  81. *    功能说明: 实现串口的互斥访问,防止多个任务同时访问造成串口打印乱码
  82. *    形    参: pvParameters 是在创建该任务时传递的形参
  83. *    返 回 值: 无
  84. *   优 先 级: 3
  85. *********************************************************************************************************
  86. */
  87. static void vTaskMsgPro(void *pvParameters)
  88. {
  89.      TickType_t xLastWakeTime;
  90.      const TickType_t xFrequency = 300;
  91.      /* 获取当前的系统时间 */
  92.     xLastWakeTime = xTaskGetTickCount();
  93.    
  94.     while(1)
  95.     {
  96.          /* 互斥信号量,xSemaphoreTake和xSemaphoreGive一定要成对的调用 */
  97.          xSemaphoreTake(xMutex, portMAX_DELAY);
  98.         
  99.          printf("任务vTaskMsgPro在运行\\r\\n");
  100.          bsp_LedToggle(1);
  101.          bsp_LedToggle(4);
  102.         
  103.          xSemaphoreGive(xMutex);
  104.         
  105.          /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
  106.         vTaskDelayUntil(&xLastWakeTime, xFrequency);
  107.     }
  108. }
  109. /*
  110. *********************************************************************************************************
  111. *    函 数 名: vTaskStart
  112. *    功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
  113. *    形    参: pvParameters 是在创建该任务时传递的形参
  114. *    返 回 值: 无
  115. *   优 先 级: 4
  116. *********************************************************************************************************
  117. */
  118. static void vTaskStart(void *pvParameters)
  119. {
  120.     while(1)
  121.     {
  122.          /* 按键扫描 */
  123.          bsp_KeyScan();
  124.         vTaskDelay(10);
  125.     }
  126. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-9-3 16:14:11 | 显示全部楼层
23.4 总结

    本章节主要为大家讲解了另一个重要的资源共享机制-互斥信号量,其中优先级翻转是互斥信号量中一个比较重要的概念,初学者要花些时间去掌握这个知识点。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-1 03:25 , Processed in 0.357495 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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