从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.第一课->
初衷:我电脑里面的歌曲很久没换了,我想听一下新的歌曲,把他们下载下来听,比如某个榜单的,但是一首一首的点击下载另存为真的很恶心 所以我想有没有办法通过程序的方式来实现,结果还真的有,而且网上已经有有人 ...
随机推荐
- SpringBoot整合ActiveMQ,看这篇就够了
ActiveMQ是Apache提供的一个开源的消息系统,完全采用Java来实现,因此它能很好地支持JMS(Java Message Service,即Java消息服务)规范:本文将详细介绍下Activ ...
- 卸载VMware
最近使用ubuntu的时候操作不当直接卡死了,然后强制关闭VMware软件,之后再打开时出现本文中的 “Vmware启动ubuntu 出现错误 ”这个情况,具体请看链接:https://www.cnb ...
- IDEA中输出syso的快捷键设置
1. 2. 3. 4. 5.上图中的第三步会出现警告,那个红色的字,点击Define,选择Java 6.之后点击Apply和OK即可
- MySQL二进制日志挖掘器BinlogMiner 1.0发布了。
MySQL从2014年开始超越SQL Server, 占据DB-Engines数据库流行度排行榜第二名, 是一种非常流行的关系型数据库, 特别是在互联网领域, 是一种应该掌握的数据库系统.最近在学My ...
- STL中nth_element的用法
nth_element函数原型有四个,详细我就不一一累赘了,我们就用最普通的用法寻找第k位置的元素. 函数用法为:nth_element(first,kth,end). first,last 第一个和 ...
- 安装完oracle11g_2x64位数据库后使用PL Developer链接oracle报错“请确认是否安装了32位oracle和TNS错误”解决方案
解决使用PL Developer登录oracle报错没有配置TNS错误.首先安装好oracle11g数据库,安装PL developer32位 1,下载“instantclient-basic-win ...
- 我用数据结构花了一夜给女朋友写了个h5走迷宫小游戏
目录 起因 分析 画线(棋盘) 画迷宫 方块移动 结语 @(文章目录) 先看效果图(在线电脑尝试地址http://biggsai.com/maze.html): 起因 又到深夜了,我按照以往在公众号写 ...
- Android静态注册广播无法接收的问题(8.0+版本)
如果你静态注册的广播无法接收到消息,请先检查下:你的安卓版本是不是8.0+ * 前言** Google官方声明:Beginning with Android 8.0 (API level 26), t ...
- Junit测试Service类方法教程
Junit测试是很方便的,本博客记录一下Junit测试一些Service接口的方法,这样可以不运行项目,在@Test注解的方法里直接测试 Maven引入jar包: <properties> ...
- Ubuntu+docker+gitlab安装和使用
以前自己写的代码都是在本地,因为都是自己一个人维护,现在交给团队维护了,所以想着搭建一个gitlab 1,拉镜像 安装非常简单 docker search gitlab 搜索镜像 docker pu ...