|

楼主 |
发表于 2016-8-27 16:58:15
|
显示全部楼层
18.4.2 STM32F407开发板实验
配套例子:
V5-313_FreeRTOS实验_事件标志组(中断方式)
实验目的:
1. 学习FreeRTOS的事件标志组(中断方式)。
实验内容:
1. K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
2. K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件标志,设置bit0。
3. K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件标志,设置bit1。
4. 任务vTaskMsgPro只有接收到bit0和bit1都被设置了才执行串口打印消息。
5. 各个任务实现的功能如下:
vTaskUserIF任务 :按键消息处理。
vTaskLED任务 :LED闪烁。
vTaskMsgPro任务 : 消息处理,使用函数xEventGroupWaitBits接收定时器中断的事件标志。
vTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
FreeRTOSConfig.h文件中的配置如下:
- /* Ensure stdint is only used by the compiler, and not the assembler. */
- #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
- #include <stdint.h>
- extern volatile uint32_t ulHighFrequencyTimerTicks;
-
- /* Ensure stdint is only used by the compiler, and not the assembler. */
- #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
- #include <stdint.h>
- extern volatile uint32_t ulHighFrequencyTimerTicks;
- #endif
-
- #define configUSE_PREEMPTION 1
- #define configUSE_IDLE_HOOK 0
- #define configUSE_TICK_HOOK 0
- #define configCPU_CLOCK_HZ ( ( unsigned long ) 168000000 )
- #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
- #define configMAX_PRIORITIES ( 6 )
- #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
- #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 30 * 1024 ) )
- #define configMAX_TASK_NAME_LEN ( 16 )
- #define configUSE_TRACE_FACILITY 1
- #define configUSE_16_BIT_TICKS 0
- #define configIDLE_SHOULD_YIELD 1
-
- /* Run time and task stats gathering related definitions. */
- #define configGENERATE_RUN_TIME_STATS 1
- #define configUSE_STATS_FORMATTING_FUNCTIONS 1
- #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (ulHighFrequencyTimerTicks = 0ul)
- #define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks
- //#define portALT_GET_RUN_TIME_COUNTER_VALUE 1
-
- /* Co-routine definitions. */
- #define configUSE_CO_ROUTINES 0
- #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
-
- /* Set the following definitions to 1 to include the API function, or zero
- to exclude the API function. */
-
- #define INCLUDE_vTaskPrioritySet 1
- #define INCLUDE_uxTaskPriorityGet 1
- #define INCLUDE_vTaskDelete 1
- #define INCLUDE_vTaskCleanUpResources 0
- #define INCLUDE_vTaskSuspend 1
- #define INCLUDE_xTaskResumeFromISR 1
- #define INCLUDE_vTaskDelayUntil 1
- #define INCLUDE_vTaskDelay 1
- #define INCLUDE_xEventGroupSetBitFromISR 1
- #define INCLUDE_xTimerPendFunctionCall 1
-
- /* Software timer definitions. */
- #define configUSE_TIMERS 1
- #define configTIMER_TASK_PRIORITY ( 5 )
- #define configTIMER_QUEUE_LENGTH 20
- #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
-
-
- /* Cortex-M specific definitions. */
- #ifdef __NVIC_PRIO_BITS
- /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
- #define configPRIO_BITS __NVIC_PRIO_BITS
- #else
- #define configPRIO_BITS 4 /* 15 priority levels */
- #endif
-
- /* The lowest interrupt priority that can be used in a call to a "set priority"
- function. */
- #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f
-
- /* The highest interrupt priority that can be used by any interrupt service
- routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
- INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
- PRIORITY THAN THIS! (higher priorities are lower numeric values. */
- #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、使用事件标志组中的函数xEventGroupSetBitsFromISR,务必使能以下三个宏定义
(1)#define INCLUDE_xEventGroupSetBitFromISR 1
(2)#define INCLUDE_xTimerPendFunctionCall 1
(3)#define configUSE_TIMERS 1
因为使能了FreeRTOS的定时器组任务,定时器组的其它宏定义也做一下配置,配置如下:
(1)#define configTIMER_TASK_PRIORITY ( 5 )
(2)#define configTIMER_QUEUE_LENGTH 20
(3)#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
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的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
(3)这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
#definetskBLOCKED_CHAR ( 'B' ) 任务阻塞
#definetskREADY_CHAR ( 'R' ) 任务就绪
#definetskDELETED_CHAR ( 'D' ) 任务删除
#definetskSUSPENDED_CHAR ( 'S' ) 任务挂起
使用事件标志组要包含的头文件:
使用FreeRTOS的事件标志组要包含头文件#include "event_groups.h",这个头文件在includes.h文件中进行了包含,具体如下:
- /*
- *********************************************************************************************************
- * OS
- *********************************************************************************************************
- */
- #include "FreeRTOS.h"
- #include "task.h"
- #include "queue.h"
- #include "croutine.h"
- #include "semphr.h"
- #include "event_groups.h"
复制代码 程序设计:
任务栈大小分配:
vTaskUserIF任务 :2048字节
vTaskLED任务 :2048字节
vTaskMsgPro任务 :2048字节
vTaskStart任务 :2048字节
任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
#defineconfigTOTAL_HEAP_SIZE ( ( size_t )( 30 * 1024 ) )
系统栈大小分配:
FreeROTS初始化:
- /*
- *********************************************************************************************************
- * 函 数 名: main
- * 功能说明: 标准c程序入口。
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- int main(void)
- {
- /*
- 在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
- 这样做的好处是:
- 1. 防止执行的中断服务程序中有FreeRTOS的API函数。
- 2. 保证系统正常启动,不受别的中断影响。
- 3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
- 在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
- 和cpsie i是等效的。
- */
- __set_PRIMASK(1);
-
- /* 硬件初始化 */
- bsp_Init();
-
- /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
- 目中不要使用,因为这个功能比较影响系统实时性。
- 2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
- */
- vSetupSysInfoTest();
-
- /* 创建任务 */
- AppTaskCreate();
-
- /* 创建任务通信机制 */
- AppObjCreate();
-
- /* 启动调度,开始执行任务 */
- vTaskStartScheduler();
-
- /*
- 如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
- heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
- #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
- */
- while(1);
- }
复制代码
硬件外设初始化
硬件外设的初始化是在bsp.c文件实现:
- /*
- *********************************************************************************************************
- * 函 数 名: bsp_Init
- * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_Init(void)
- {
- /*
- 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
- 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
-
- 系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
- */
- /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
-
- bsp_InitUart(); /* 初始化串口 */
- bsp_InitKey(); /* 初始化按键变量 */
- bsp_InitLed(); /* 初始LED指示灯端口 */
-
- bsp_InitHardTimer(); /* 初始化TIM2定时器 */
- }
复制代码 FreeRTOS任务创建:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskCreate
- * 功能说明: 创建应用任务
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppTaskCreate (void)
- {
- xTaskCreate( vTaskTaskUserIF, /* 任务函数 */
- "vTaskUserIF", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 1, /* 任务优先级*/
- &xHandleTaskUserIF ); /* 任务句柄 */
-
-
- xTaskCreate( vTaskLED, /* 任务函数 */
- "vTaskLED", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 2, /* 任务优先级*/
- &xHandleTaskLED ); /* 任务句柄 */
-
- xTaskCreate( vTaskMsgPro, /* 任务函数 */
- "vTaskMsgPro", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 3, /* 任务优先级*/
- &xHandleTaskMsgPro ); /* 任务句柄 */
-
-
- xTaskCreate( vTaskStart, /* 任务函数 */
- "vTaskStart", /* 任务名 */
- 512, /* 任务栈大小,单位word,也就是4字节 */
- NULL, /* 任务参数 */
- 4, /* 任务优先级*/
- &xHandleTaskStart ); /* 任务句柄 */
- }
复制代码 FreeRTOS事件标志组创建:
- /*
- *********************************************************************************************************
- * 函 数 名: AppObjCreate
- * 功能说明: 创建任务通信机制
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppObjCreate (void)
- {
- /* 创建事件标志组 */
- xCreatedEventGroup = xEventGroupCreate();
-
- if(xCreatedEventGroup == NULL)
- {
- /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
- }
- }
复制代码 四个FreeRTOS任务的实现:
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskTaskUserIF
- * 功能说明: 接口消息处理。
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- static void vTaskTaskUserIF(void *pvParameters)
- {
- uint8_t ucKeyCode;
- uint8_t pcWriteBuffer[500];
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下 打印任务执行情况 */
- case KEY_DOWN_K1:
- printf("=================================================\\r\\n");
- printf("任务名 任务状态 优先级 剩余栈 任务序号\\r\\n");
- vTaskList((char *)&pcWriteBuffer);
- printf("%s\\r\\n", pcWriteBuffer);
-
- printf("\\r\\n任务名 运行计数 使用率\\r\\n");
- vTaskGetRunTimeStats((char *)&pcWriteBuffer);
- printf("%s\\r\\n", pcWriteBuffer);
- break;
-
- /* K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件标志 */
- case KEY_DOWN_K2:
- printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件
- 标志\\r\\n");
- bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
- break;
-
- /* K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件标志 */
- case KEY_DOWN_K3:
- printf("K3键按下,启动单次定时器中断,50ms后在定时器中断给任务vTaskMsgPro发送事件
- 标志\\r\\n");
- bsp_StartHardTimer(2 ,50000, (void *)TIM_CallBack2);
- break;
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- vTaskDelay(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskLED
- * 功能说明: LED闪烁
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- static void vTaskLED(void *pvParameters)
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 200;
-
- /* 获取当前的系统时间 */
- xLastWakeTime = xTaskGetTickCount();
-
- while(1)
- {
- bsp_LedToggle(2);
-
- /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
- vTaskDelayUntil(&xLastWakeTime, xFrequency);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskMsgPro
- * 功能说明: 消息处理,使用函数xEventGroupWaitBits接收定时器中断发送的事件标志
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- static void vTaskMsgPro(void *pvParameters)
- {
- EventBits_t uxBits;
- const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS; /* 最大延迟100ms */
-
- while(1)
- {
- /* 等K2按键按下设置bit0和K3按键按下设置bit1 */
- uxBits = xEventGroupWaitBits(xCreatedEventGroup, /* 事件标志组句柄 */
- BIT_ALL, /* 等待bit0和bit1被设置 */
- pdTRUE, /* 退出前bit0和bit1被清除 */
- pdTRUE, /* 设置为pdTRUE表示等待bit1和bit0都被设置*/
- xTicksToWait); /* 等待延迟时间 */
-
- if((uxBits & BIT_ALL) == BIT_ALL)
- {
- /* 接收到bit1和bit0都被设置的消息 */
- printf("接收到bit0和bit1都被设置的消息\\r\\n");
- }
- else
- {
- /* 超时,另外注意仅接收到一个按键按下的消息时,变量uxBits的相应bit也是被设置的 */
- bsp_LedToggle(3);
- }
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: vTaskStart
- * 功能说明: 启动任务,也就是最高优先级任务,这里用作按键扫描。
- * 形 参: pvParameters 是在创建该任务时传递的形参
- * 返 回 值: 无
- * 优 先 级: 4
- *********************************************************************************************************
- */
- static void vTaskStart(void *pvParameters)
- {
- while(1)
- {
- /* 按键扫描 */
- bsp_KeyScan();
- vTaskDelay(10);
- }
- }
复制代码 定时器中断回调函数中给任务发送事件标志组设置消息:
定时器中断的初始化和中断函数在bsp_timer.c 文件中实现,这个不是教程的重点,故不作介绍。 这里主要关心函数xEventGroupSetBitsFromISR在中断服务程序中的使用方法。
- /*
- *********************************************************************************************************
- * 函 数 名: TIM_CallBack1和TIM_CallBack2
- * 功能说明: 定时器中断的回调函数,此函数被bsp_StartHardTimer所调用。
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void TIM_CallBack1(void)
- {
- BaseType_t xResult;
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-
- /* 向任务vTaskMsgPro发送事件标志 */
- xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */
- BIT_0 , /* 设置bit0 */
- &xHigherPriorityTaskWoken );
-
- /* 消息被成功发出 */
- if( xResult != pdFAIL )
- {
- /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
- portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
- }
- }
-
- static void TIM_CallBack2(void)
- {
- BaseType_t xResult;
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-
- /* 向任务vTaskMsgPro发送事件标志 */
- xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, /* 事件标志组句柄 */
- BIT_1, /* 设置bit1 */
- &xHigherPriorityTaskWoken );
-
- /* 消息被成功发出 */
- if( xResult != pdFAIL )
- {
- /* 如果xHigherPriorityTaskWoken = pdTRUE,那么退出中断后切到当前最高优先级任务执行 */
- portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
- }
- }
复制代码 |
|