从0开始学FreeRTOS-(创建任务)-2
# 补充
开始今天的内容之前,先补充一下上篇文章[从单片机到操作系统-1](https://jiejietop.gitee.io/freertos-1/)的一点点遗漏的知识点。
```js
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pvCreatedTask
);
```
创建任务中的堆栈大小问题,在task.h中有这样子的描述:
```js
/**
* @param usStackDepth The size of the task stack specified as the number of variables the stack * can hold - not the number of bytes. For example, if the stack is 16 bits wide and
* usStackDepth is defined as 100, 200 byteswill be allocated for stack storage.
*/
```
当任务创建时,内核会分为每个任务分配属于任务自己的唯一堆栈。usStackDepth 值用于告诉内核为它应该分配多大的栈空间。
这个值指定的是栈空间可以保存多少个字(word) ,而不是多少个字节(byte)。
文档也有说明,如果是16位宽度的话,假如usStackDepth = 100;那么就是200个字节(byte)。
当然,我用的是stm32,32位宽度的, usStackDepth=100;那么就是400个字节(byte)。
好啦,补充完毕。下面正式开始我们今天的主题。
---
我自己学的是应用层的东西,很多底层的东西我也不懂,水平有限,出错了还请多多包涵。
其实我自己写文章的时候也去跟着火哥的书看着底层的东西啦,但是本身自己也是不懂,不敢乱写。所以,这个《从单片机到操作系统》系列的文章,我会讲一点底层,更多的是应用层,主要是用的方面。
按照一般的写代码的习惯,在main函数里面各类初始化完毕了,并且创建任务成功了,那么,可以开启任务调度了。
```js
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
Delay_Init(); //延时函数初始化
Uart_Init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init();
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
```
来大概看看分析一下创建任务的过程,虽然说会用就行,但是也是要知道了解一下的。
注意:下面说的创建任务均为xTaskCreate(动态创建)而非静态创建。
```js
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
/*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxStack != NULL )
{
/* Allocate space for the TCB. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
/*lint !e961 MISRA exception as the casts are only redundant for some paths. */
if( pxNewTCB != NULL )
{
/* Store the stack location in the TCB. */
pxNewTCB->pxStack = pxStack;
}
else
{
/* The stack cannot be used as the TCB was not created. Free
it again. */
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
```
首先是利用`pvPortMalloc`给任务的堆栈分配空间,`if( pxStack != NULL )`如果内存申请成功,就接着给任务控制块申请内存。`pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );`同样是使用`pvPortMalloc();`如果任务控制块内存申请失败则释放 之前已经申请成功的任务堆栈`的内存vPortFree( pxStack );`
然后就初始化任务相关的东西,并且将新初始化的任务控制块添加到列表中`prvAddNewTaskToReadyList( pxNewTCB );`
最后返回任务的状态,如果是成功了就是`pdPASS`,假如失败了就是返回`errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;`
```js
prvInitialiseNewTask( pxTaskCode,
pcName,
( uint32_t ) usStackDepth,
pvParameters,
uxPriority,
pxCreatedTask,
pxNewTCB,
NULL );
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
// 相关宏定义
#define pdPASS ( pdTRUE )
#define pdTRUE ( ( BaseType_t ) 1 )
/* FreeRTOS error definitions. */
#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 )
```
具体的`static void prvInitialiseNewTask()`实现请参考`FreeRTOS`的`tasks.c`文件的`767`行代码。具体的`static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )`实现请参考`FreeRTOS`的`tasks.c`文件的`963`行代码。
因为这些是`tasks.c`中的`静态的函数`,仅供xTaskCreate创建任务内部调用的,我们无需理会这些函数的实现过程,当然如果需要请自行了解。
创建完任务就开启任务调度了:
```js
vTaskStartScheduler(); //开启任务调度
```
在任务调度里面,会创建一个空闲任务(我们将的都是动态创建任务,静态创建其实一样的)
```js
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
/*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
}
相关宏定义:
#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U )
#ifndef portPRIVILEGE_BIT
#define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 )
#endif
#define configUSE_TIMERS 1
//为1时启用软件定时器
```
从上面的代码我们可以看出,空闲任务的优先级是tskIDLE_PRIORITY为0,也就是说空闲任务的优先级最低。当CPU没事干的时候才执行空闲任务,以待随时切换优先级更高的任务。
如果使用了软件定时器的话,我们还需要创建定时器任务,创建的函数是:
```js
#if ( configUSE_TIMERS == 1 )
BaseType_t xTimerCreateTimerTask( void )
```
然后还要把中断关一下
```js
portDISABLE_INTERRUPTS();
```
至于为什么关中断,也有说明:
```js
/* Interrupts are turned off here, toensure a tick does not occur
before or during the call toxPortStartScheduler(). The stacks of
the created tasks contain a status wordwith interrupts switched on
so interrupts will automatically getre-enabled when the first task
starts to run. */
```
中断在这里被关闭,以确保不会发生滴答在调用xPortStartScheduler()之前或期间。堆栈创建的任务包含一个打开中断的状态字因此中断将在第一个任务时自动重新启用开始运行。
那么如何打开中断呢????这是个很重要的问题
别担心,我们在SVC中断服务函数里面就会打开中断的
看代码:
```js
__asm void vPortSVCHandler( void )
{
PRESERVE8
ldr r3, =pxCurrentTCB /* Restore the context. */
ldrr1, [r3] /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */
ldrr0, [r1] /* Thefirst item in pxCurrentTCB is the task top of stack. */
ldmiar0!, {r4-r11} /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */
msrpsp, r0 /*Restore the task stack pointer. */
isb
movr0, #0
msr basepri, r0
orrr14, #0xd
bxr14
}
```
```js
msr basepri, r0
```
就是它把中断打开的。看不懂没所谓,我也不懂汇编,看得懂知道就好啦。
```js
xSchedulerRunning = pdTRUE;
```
任务调度开始运行
```js
/* If configGENERATE_RUN_TIME_STATS isdefined then the following
macro must be defined to configure thetimer/counter used to generate
the run time counter time base. */
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
```
如果`configGENERATE_RUN_TIME_STATS`使用时间统计功能,这个宏为`1`,那么用户必须实现一个宏`portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();`用来配置一个定时器或者计数器。
来到我们的重点了,开启任务调度,那么任务到这了就不会返回了。
```js
if( xPortStartScheduler() != pdFALSE )
{
/*Should not reach here as if the scheduler is running the
functionwill not return. */
}
```
然后就能开启第一个任务了,感觉好难是吧,我一开始也是觉得的,但是写了这篇文章,觉得还行吧,也不算太难,可能也是在查看代码跟别人的书籍吧,写东西其实还是蛮好的,能加深理解,写过文章的人就知道,懂了不一定能写出来,所以,我还是很希望朋友们能投稿的。杰杰随时欢迎。。。
开始任务就按照套路模板添加自己的代码就好啦,很简单的。
先创建任务:
```js
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
```
创建完任务就开启任务调度:
```js
1vTaskStartScheduler(); //开启任务调度
```
然后具体实现任务函数:
```js
//LED0任务函数
void led0_task(void *pvParameters)
{
while(1)
{
LED0=~LED0;
vTaskDelay(500);
}
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
```
好啦,今天的介绍到这了为止,后面还会持续更新,敬请期待哦~
欢迎大家一起来讨论操作系统的知识
我们的群号是:783234154

更多资料欢迎关注“物联网IoT开发”公众号!
从0开始学FreeRTOS-(创建任务)-2的更多相关文章
- 从0开始学Swift笔记整理(三)
这是跟在上一篇博文后续内容: --Swift中相关的属性 存储属性 Swift中的属性分为存储属性和计算属性,存储属性就是Objective-C中的数据成员,计算属性不存储数据,但可以通过计算其他属性 ...
- 从0系统学Android--4.1探究碎片
从0系统学Android--4.1探究碎片 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 第四章:手机平板要兼顾--探究碎片 平板电脑和手机最 ...
- 从0系统学Android--3.7 聊天界面编写
从0系统学Android--3.7 聊天界面编写 本系列文章目录:更多精品文章分类 本系列持续更新中.... 3.7 编写界面的最佳实践 前面学习了那么多 UI 开发的知识,下面来进行实践,做一个美观 ...
- 从0系统学Android--3.6 RecyclerView
从0系统学Android--更强大的滚动控件---RecyclerView 本系列文章目录:更多精品文章分类 本系列持续更新中.... 参考<第一行代码> 首先说明一点昨天发了一篇关于 L ...
- 从0系统学Android--3.5 最常用和最难用的控件---ListView
从0系统学Android-- 3.5 最常用和最难用的控件---ListView 本系列文章目录:更多精品文章分类 本系列持续更新中.... 3.5 最常用和最难用的控件---ListView Lis ...
- 从0系统学Android--3.2四种基本布局
从0系统学Android--3.2四种基本布局 本系列文章目录:更多精品文章分类 本系列持续更新中.... 3.3 系统控件不够用?创建自定义控件 上一节我们学习了 Android 中的一些常用的控件 ...
- 从0系统学Android--3.1编写UI界面
从0系统学Android--3.1编写UI界面 本系列文章目录:更多精品文章分类 本系列持续更新中.... 界面设计和功能开发同样重要,界面美观的应用程序不仅可以大大增加用户粘性,还能帮我们吸引到更多 ...
- 从0开始学爬虫8使用requests/pymysql和beautifulsoup4爬取维基百科词条链接并存入数据库
从0开始学爬虫8使用requests和beautifulsoup4爬取维基百科词条链接并存入数据库 Python使用requests和beautifulsoup4爬取维基百科词条链接并存入数据库 参考 ...
- 从0系统学Android--5.2 发送广播
从0系统学Android--52 发送广播 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 5.3 发送自定义广播 前面已经学习了如何接受广播了 ...
- <-0基础学python.第一课->
初衷:我电脑里面的歌曲很久没换了,我想听一下新的歌曲,把他们下载下来听,比如某个榜单的,但是一首一首的点击下载另存为真的很恶心 所以我想有没有办法通过程序的方式来实现,结果还真的有,而且网上已经有有人 ...
随机推荐
- yzoj P1948 取数字问题
题意 sb题目,不多说,爆搜就能过. 代码 #include<bits/stdc++.h> using namespace std; int n,m,ans=1<<30,a[1 ...
- github 授权登录教程与如何设计第三方授权登录的用户表
需求:在网站上想评论一篇文章,而评论文章是要用户注册与登录的,那么怎么免去这麻烦的步骤呢?答案是通过第三方授权登录.本文讲解的就是 github 授权登录的教程. 效果体验地址:http://biao ...
- webstrom 内存溢出,软件崩溃卡死解决的方法
今天用gulp搭建了一个工程,准备做一个体育h5的项目,其中需要用到sass代码压缩,加版本号等功能. gulpfile.js和package.json都是已经写好的.我用CMD命令窗口cnpm安装n ...
- Git使用(二)版本库创建及文件修改
一.创建版本库 1.安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功! 安装完成后,还需要最后一步设置,在命令行输入: $ gi ...
- 分库分表之后,id 主键如何处理?
其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来支持.所以这都是你实际生产环境中必须考虑的 ...
- Redis小白入门系列
一.从NoSQL说起 NoSQL 是 Not only SQL 的缩写,大意为"不只是SQL",说明这项技术是传统关系型数据库的补充而非替代.在整个NoSQL技术栈中 MemCac ...
- Git服务端下载
链接:http://pan.baidu.com/s/1kVshpQ3提取密码:4g36
- H5当弹出弹窗遮罩时如何阻止滚动穿透(使用css方式)
最近的一个H5活动中有一个是点击[分享]弹窗指引遮罩弹窗引导用户进行分享,但突然发现弹出弹窗的时候下层仍然可以进行滑动,这个问题是个H5经久不衰讨论的问题,重点是我这个页面在安卓系统上有明显的滑动闪烁 ...
- Pandas对缺失值的处理
Pandas使用这些函数处理缺失值: isnull和notnull:检测是否是空值,可用于df和series dropna:丢弃.删除缺失值 axis : 删除行还是列,{0 or 'index', ...
- Promise核心原理解析
作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要 ...