1.硬件初始化

本章创建的任务需要用到开发板上的 LED,所以先要将 LED 相关的函数初始化好, 为了方便以后统一管理板级外设的初始化,我们在 main.c 文件中创建一个 BSP_Init()函数, 专门用于存放板级外设初始化函数,


/************************************************************************ @ 函数名  : BSP_Init* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面* @ 参数    :   * @ 返回值  : 无*********************************************************************/
static void BSP_Init(void)
{/** STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,* 都统一用这个优先级分组,千万不要再分组,切忌。*/NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* LED 初始化 */LED_GPIO_Config();/* 串口初始化  */USART_Config();}

执行到 BSP_Init()函数的时候,操作系统完全都还没有涉及到,即 BSP_Init()函数所做 的工作跟我们以前编写的裸机工程里面的硬件初始化工作是一模一样的。运行完 BSP_Init () 函数,接下来才慢慢启动操作系统,最后运行创建好的任务。有时候任务创建好,整个系 统跑起来了,可想要的实验现象就是出不来,比如 LED 不会亮,串口没有输出,LCD 没 有显示等等。如果是初学者,这个时候就会心急如焚,四处求救,那怎么办?这个时候如 何排除是硬件的问题还是系统的问题,这里面有个小小的技巧,即在硬件初始化好之后, 顺便测试下硬件,测试方法跟裸机编程一样,


/************************************************************************ @ 函数名  : BSP_Init* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面* @ 参数    :   * @ 返回值  : 无*********************************************************************/
static void BSP_Init(void)
{/** STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,* 都统一用这个优先级分组,千万不要再分组,切忌。*/NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* LED 初始化 */LED_GPIO_Config();/* 测试硬件是否正常工作 */ LED1_ON;/* 让程序停在这里,不再继续往下执行 */while(1);/* 串口初始化   */USART_Config();}

2.创建单任务—SRAM静态内存

这里,我们创建一个单任务,任务使用的栈和任务控制块都使用静态内存,即预先定 义好的全局变量,这些预先定义好的全局变量都存在内部的 SRAM 中。

2.1定义任务函数

任务实际上就是一个无限循环且不带返回值的 C 函数。目前,我们创建一个这样的任 务,让开发板上面的 LED 灯以 500ms 的频率闪烁,


/*********************************************************************** @ 函数名  : LED_Task* @ 功能说明: LED_Task任务主体* @ 参数    :   * @ 返回值  : 无********************************************************************/
static void LED_Task(void* parameter)
{   while (1){LED1_ON;vTaskDelay(500);   /* 延时500个tick */printf("LED_Task Running,LED1_ON\r\n");LED1_OFF;     vTaskDelay(500);   /* 延时500个tick */             printf("LED_Task Running,LED1_OFF\r\n");}
}

2.2空闲任务与定时器任务堆栈函数实现

当我们使用了静态创建任务的时候,configSUPPORT_STATIC_ALLOCATION 这个宏 定 义 必 须为 1 (在 FreeRTOSConfig.h 文 件 中 ) , 并且 我 们需 要 实 现两 个 函数 : vApplicationGetIdleTaskMemory()与 vApplicationGetTimerTaskMemory(),这两个函数是用 户设定的空闲(Idle)任务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而 不能是动态分配,

/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;/************************************************************************* @brief  获取空闲任务的任务堆栈和任务控制块内存*                 ppxTimerTaskTCBBuffer   :       任务控制块内存*                    ppxTimerTaskStackBuffer :   任务堆栈内存*                 pulTimerTaskStackSize   :       任务堆栈大小* @date    2018-xx-xx***********************************************************************/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */*ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */
}/************************************************************************ @brief  获取定时器任务的任务堆栈和任务控制块内存*                   ppxTimerTaskTCBBuffer   :       任务控制块内存*                    ppxTimerTaskStackBuffer :   任务堆栈内存*                 pulTimerTaskStackSize   :       任务堆栈大小***********************************************************************/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize)
{*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}

2.3 定义任务栈

目前我们只创建了一个任务,当任务进入延时的时候,因为没有另外就绪的用户任务, 那么系统就会进入空闲任务,空闲任务是 FreeRTOS 系统自己启动的一个任务,优先级最 低。当整个系统都没有就绪任务的时候,系统必须保证有一个任务在运行,空闲任务就是 为这个设计的。当用户任务延时到期,又会从空闲任务切换回用户任务。 在 FreeRTOS 系统中,每一个任务都是独立的,他们的运行环境都单独的保存在他们 的栈空间当中。那么在定义好任务函数之后,我们还要为任务定义一个栈,目前我们使用 的是静态内存,所以任务栈是一个独立的全局变量,具体见代码清单 14-5。任务的栈占用 的是 MCU 内部的 RAM,当任务越多的时候,需要使用的栈空间就越大,即需要使用的 RAM 空间就越多。一个 MCU 能够支持多少任务,就得看你的 RAM 空间有多少。

/* AppTaskCreate任务任务堆栈 */
static StackType_t AppTaskCreate_Stack[128];
/* LED任务堆栈 */
static StackType_t LED_Task_Stack[128];

2.4定义任务控制块

定义好任务函数和任务栈之后,我们还需要为任务定义一个任务控制块,通常我们称 这个任务控制块为任务的身份证。在 C代码上,任务控制块就是一个结构体,里面有非常 多的成员,这些成员共同描述了任务的全部信息,

/* AppTaskCreate 任务控制块 */
static StaticTask_t AppTaskCreate_TCB;
/* AppTaskCreate 任务控制块 */
static StaticTask_t LED_Task_TCB;

2.5 静态创建任务

一个任务的三要素是任务主体函数,任务栈,任务控制块,那么怎么样把这三个要素 联合在一起?FreeRTOS 里面有一个叫静态任务创建函数 xTaskCreateStatic(),它就是干这 个活的。它将任务主体函数,任务栈(静态的)和任务控制块(静态的)这三者联系在一 起,让任务可以随时被系统启动,

   /* 创建 AppTaskCreate 任务 */AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t    )AppTaskCreate,     //任务函数(const char*  )"AppTaskCreate",     //任务名称(uint32_t         )128,   //任务堆栈大小(void*          )NULL,              //传递给任务函数的参数(UBaseType_t    )3,     //任务优先级(StackType_t*   )AppTaskCreate_Stack,    //任务堆栈(StaticTask_t*  )&AppTaskCreate_TCB); //任务控制块   if(NULL != AppTaskCreate_Handle)/* 创建成功 */vTaskStartScheduler();   /* 启动任务,开启调度 */

2.6 启动任务

当任务创建好后,是处于任务就绪(Ready),在就绪态的任务可以参与操作系统的调度。 但是此时任务仅仅是创建了,还未开启任务调度器,也没创建空闲任务与定时器任务(如果使 能了 configUSE_TIMERS 这个宏定义),那这两个任务就是在启动任务调度器中实现,每个操 作系统,任务调度器只启动一次,之后就不会再次执行了,FreeRTOS 中启动任务调度器的函 数是 vTaskStartScheduler(),并且启动任务调度器的时候就不会返回,从此任务管理都由 FreeRTOS管理,此时才是真正进入实时操作系统中的第一步,

vTaskStartScheduler();   /* 启动任务,开启调度 */

2.7 main.c文件内容全貌

现在我们把任务主体,任务栈,任务控制块这三部分代码统一放到 main.c 中,我们在 main.c 文件中创建一个 AppTaskCreate 任务,这个任务是用于创建用户任务,为了方便管 理,我们的所有的任务创建都统一放在这个函数中,在这个函数中创建成功的任务就可以 直接参与任务调度了,

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
static void AppTaskCreate(void);/* 用于创建任务 */static void LED_Task(void* pvParameters);/* LED_Task任务实现 */static void BSP_Init(void);/* 用于初始化板载相关资源 */
/**************************** 任务句柄 ********************************/
/* * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么* 这个句柄可以为NULL。*//* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle;
/* LED任务句柄 */
static TaskHandle_t LED_Task_Handle;        /********************************** 内核对象句柄 *********************************/
/** 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我* 们就可以通过这个句柄操作这些内核对象。** 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数* 来完成的* *//******************************* 全局变量声明 ************************************/
/** 当我们在写应用程序的时候,可能需要用到一些全局变量。*/
/* AppTaskCreate任务任务堆栈 */
static StackType_t AppTaskCreate_Stack[128];
/* LED任务堆栈 */
static StackType_t LED_Task_Stack[128];/* AppTaskCreate 任务控制块 */
static StaticTask_t AppTaskCreate_TCB;
/* AppTaskCreate 任务控制块 */
static StaticTask_t LED_Task_TCB;/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;/*** 使用了静态分配内存,以下这两个函数是由用户实现,函数在task.c文件中有引用*  当且仅当 configSUPPORT_STATIC_ALLOCATION 这个宏定义为 1 的时候才有效*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize);void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize);/****************************************************************** @brief  主函数* @param  无* @retval 无* @note   第一步:开发板硬件初始化 第二步:创建APP应用任务第三步:启动FreeRTOS,开始多任务调度****************************************************************/
int main(void)
{   /* 开发板硬件初始化 */BSP_Init();printf("这是一个[野火]-STM32全系列开发板-FreeRTOS-静态创建单任务!\r\n");/* 创建 AppTaskCreate 任务 */AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t  )AppTaskCreate,     //任务函数(const char*  )"AppTaskCreate",     //任务名称(uint32_t         )128,   //任务堆栈大小(void*          )NULL,              //传递给任务函数的参数(UBaseType_t    )3,     //任务优先级(StackType_t*   )AppTaskCreate_Stack,    //任务堆栈(StaticTask_t*  )&AppTaskCreate_TCB); //任务控制块   if(NULL != AppTaskCreate_Handle)/* 创建成功 */vTaskStartScheduler();   /* 启动任务,开启调度 */while(1);   /* 正常不会执行到这里 */
}/************************************************************************ @ 函数名  : AppTaskCreate* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面* @ 参数    : 无  * @ 返回值  : 无**********************************************************************/
static void AppTaskCreate(void)
{taskENTER_CRITICAL();           //进入临界区/* 创建LED_Task任务 */LED_Task_Handle = xTaskCreateStatic((TaskFunction_t  )LED_Task,      //任务函数(const char*  )"LED_Task",      //任务名称(uint32_t         )128,                   //任务堆栈大小(void*          )NULL,              //传递给任务函数的参数(UBaseType_t    )4,                 //任务优先级(StackType_t*   )LED_Task_Stack, //任务堆栈(StaticTask_t*  )&LED_Task_TCB);  //任务控制块   if(NULL != LED_Task_Handle)/* 创建成功 */printf("LED_Task任务创建成功!\n");elseprintf("LED_Task任务创建失败!\n");vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务taskEXIT_CRITICAL();            //退出临界区
}/*********************************************************************** @ 函数名  : LED_Task* @ 功能说明: LED_Task任务主体* @ 参数    :   * @ 返回值  : 无********************************************************************/
static void LED_Task(void* parameter)
{   while (1){LED1_ON;vTaskDelay(500);   /* 延时500个tick */printf("LED_Task Running,LED1_ON\r\n");LED1_OFF;     vTaskDelay(500);   /* 延时500个tick */             printf("LED_Task Running,LED1_OFF\r\n");}
}/************************************************************************ @ 函数名  : BSP_Init* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面* @ 参数    :   * @ 返回值  : 无*********************************************************************/
static void BSP_Init(void)
{/** STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,* 都统一用这个优先级分组,千万不要再分组,切忌。*/NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* LED 初始化 */LED_GPIO_Config();/* 测试硬件是否正常工作 */ LED1_ON;/* 让程序停在这里,不再继续往下执行 */while(1);/* 串口初始化   */USART_Config();}/************************************************************************* @brief  获取空闲任务的任务堆栈和任务控制块内存*                  ppxTimerTaskTCBBuffer   :       任务控制块内存*                    ppxTimerTaskStackBuffer :   任务堆栈内存*                 pulTimerTaskStackSize   :       任务堆栈大小* @date    2018-xx-xx***********************************************************************/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */*ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */
}/************************************************************************ @brief  获取定时器任务的任务堆栈和任务控制块内存*                   ppxTimerTaskTCBBuffer   :       任务控制块内存*                    ppxTimerTaskStackBuffer :   任务堆栈内存*                 pulTimerTaskStackSize   :       任务堆栈大小***********************************************************************/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize)
{*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}/********************************END OF FILE****************************/

注意:在使用静态创建任务的时候必须要将 FreeRTOSConfig.h 中 的 configSUPPORT_STATIC_ALLOCATION 宏配置为 1。

下载后,可以看到板子上面的 LED 灯已经在 闪烁,说明我们创建的单任务(使用静态内存)已经跑起来了。

本篇内容摘自野火《FreeRTOS内核实现与应用开发》

FreeRTOS基础教程第一章创建任务相关推荐

  1. excel工具栏隐藏了怎么办_Office2016基础教程第一章:认识Excel #excel #职场 #办公技巧...

    今日详讲Excel基础 教程 第一章:认识Excel 1. 版本兼容性 2. Excel2016的独特之处 3. 安装Excel2016 4. 启动Excel2016 5. 退出Excel2016 6 ...

  2. 村田噪声抑制基础教程-第一章 需要EMI静噪滤波器的原因

    1-1. 简介 EMI静噪滤波器 (EMIFIL®) 是为电子设备提供电磁噪声抑制的电子元件,配合屏蔽罩和其他保护装置一起使用.这种滤波器仅从通过连线传导的电流中提取并移除引起电磁噪声的元件.第1章说 ...

  3. SQL基础教程|第一章:数据库和SQL

    前言: 1.本书在知识点提炼时侧重于实践效果,所以有些理论部分的知识点不会涉及或者几笔带过. 2.在语法实践时将会带入更多的例子和问题以便于理解数据库语法. 3.在所有关于SQL基础教程的文章中的语句 ...

  4. Linux操作系统基础教程 第一章 绪论

    第一章 绪论 一. Unix 家族中的一员 ① UNIX , 1971 年由 AT&T 发布, 73 年重写, 70 年代末, AT&T 成立了 Unix 系统实验室( Unix Sy ...

  5. sql 拼接int类型的字段_SQL 基础教程—第一章:4. 表的创建

    第四节:表的创建 1.1 数据库的创建 创建表之前,需要先创建一个用来存储表的数据库,使用: create 比如我们要创建一个名称为 shop 的数据库,则 create database shop; ...

  6. 精简版Abp开发教程 - 第一章: 创建解决方案

    前言 基于官方教程,前前后后走了N遍.好多地方不清楚,为什么会有这效果,有一堆的问号.哪些是必须的?哪些是不必须的? 经过N长时间的研究,自己也搭建多无数个精简的Abp解决方案.在这过程中,出现一堆莫 ...

  7. 北大青鸟c语言课后答案,北大青鸟C语言教程--第一章 C语言基础.ppt

    <北大青鸟C语言教程--第一章 C语言基础.ppt>由会员分享,可在线阅读,更多相关<北大青鸟C语言教程--第一章 C语言基础.ppt(20页珍藏版)>请在人人文库网上搜索. ...

  8. 软考 程序员教程-第一章 计算机系统基础知识

    软考 程序员教程-第一章 计算机系统基础知识 为了督促自己学习,告别懒惰,在此先给自己定个小目标,请大家监督哟! 目标:一个月内过一遍<程序员教程>,下一个月开始上真题. 简单看了下,我在 ...

  9. 乐行学院RabbitMQ学习教程 第一章 RabbitMQ介绍(可供技术选型时使用)

    乐行学院RabbitMQ学习教程 第一章 RabbitMQ介绍 RabbitMQ介绍 1.RabbitMQ技术简介 2.RabbitMQ其他扩展插件 2.1监控工具rabbitmq-managemen ...

  10. javascript进阶教程第一章案例实战

    javascript进阶教程第一章案例实战 一.学习任务 通过几个案例练习回顾学过的知识 通过练习积累JS的使用技巧 二.实例 练习1:删除确认提示框 实例描述: 防止用户小心单击了"删除& ...

最新文章

  1. HTTP缓存——304与200 from cache
  2. HTML Viewer展示不同字体
  3. BZOJ 2288 贪心 +链表
  4. HTML--三种样式插入方法--链接---表格---列表
  5. 网站服务器打开新页面,什么网页适合新窗口打开?哪些网页又适合当前窗口打开?...
  6. SSH连接两台虚拟机、秘钥免密登录
  7. netty系列之:channelHandlerContext详解
  8. 关于字符串流的学习(c++)
  9. 【C++深度剖析教程7】C++之类中的函数重载
  10. 引入Vant-UI全部组件的代码 - (备份)
  11. 网络通信 netstat
  12. Failed to connect to GitHub to update the CocoaPods/Specs specs repo 问题
  13. Kotlin实战【二】Kotlin基本要素
  14. 通讯录 C语言分类,C语言 通讯录
  15. ubuntu文件名乱码(转载)
  16. redis基于Lettuce客户端实现读写分离
  17. 互联网思维之简约思维
  18. 一文读懂天翼物联网平台(AIoT)
  19. Java 常用工具类 - 校验身份证 IdCardUtils
  20. Linux- 网络配置

热门文章

  1. mysql中存储过程和函数区别
  2. 下载 Chrome插件 crx的教程
  3. 使用MATLAB的trainNetwork设计一个简单的LSTM神经网络
  4. 各大浏览器兼容性报告
  5. IOS平台hosts修改
  6. node.js读取JSON文件
  7. 工控领域组态软件开发感触
  8. 斗鱼弹幕服务器未响应,斗鱼看不到弹幕的解决方法步骤
  9. 风车IM即时通讯聊天系统源码
  10. python怎么过滤停用词_第6天:文本处理流程——停用词的过滤、正则化操作