# 补充
开始今天的内容之前,先补充一下上篇文章[从单片机到操作系统-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

![欢迎关注我公众号](https://img2018.cnblogs.com/blog/1834930/201910/1834930-20191015203637034-1803649238.jpg)

更多资料欢迎关注“物联网IoT开发”公众号!

从0开始学FreeRTOS-(创建任务)-2的更多相关文章

  1. 从0开始学Swift笔记整理(三)

    这是跟在上一篇博文后续内容: --Swift中相关的属性 存储属性 Swift中的属性分为存储属性和计算属性,存储属性就是Objective-C中的数据成员,计算属性不存储数据,但可以通过计算其他属性 ...

  2. 从0系统学Android--4.1探究碎片

    从0系统学Android--4.1探究碎片 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 第四章:手机平板要兼顾--探究碎片 平板电脑和手机最 ...

  3. 从0系统学Android--3.7 聊天界面编写

    从0系统学Android--3.7 聊天界面编写 本系列文章目录:更多精品文章分类 本系列持续更新中.... 3.7 编写界面的最佳实践 前面学习了那么多 UI 开发的知识,下面来进行实践,做一个美观 ...

  4. 从0系统学Android--3.6 RecyclerView

    从0系统学Android--更强大的滚动控件---RecyclerView 本系列文章目录:更多精品文章分类 本系列持续更新中.... 参考<第一行代码> 首先说明一点昨天发了一篇关于 L ...

  5. 从0系统学Android--3.5 最常用和最难用的控件---ListView

    从0系统学Android-- 3.5 最常用和最难用的控件---ListView 本系列文章目录:更多精品文章分类 本系列持续更新中.... 3.5 最常用和最难用的控件---ListView Lis ...

  6. 从0系统学Android--3.2四种基本布局

    从0系统学Android--3.2四种基本布局 本系列文章目录:更多精品文章分类 本系列持续更新中.... 3.3 系统控件不够用?创建自定义控件 上一节我们学习了 Android 中的一些常用的控件 ...

  7. 从0系统学Android--3.1编写UI界面

    从0系统学Android--3.1编写UI界面 本系列文章目录:更多精品文章分类 本系列持续更新中.... 界面设计和功能开发同样重要,界面美观的应用程序不仅可以大大增加用户粘性,还能帮我们吸引到更多 ...

  8. 从0开始学爬虫8使用requests/pymysql和beautifulsoup4爬取维基百科词条链接并存入数据库

    从0开始学爬虫8使用requests和beautifulsoup4爬取维基百科词条链接并存入数据库 Python使用requests和beautifulsoup4爬取维基百科词条链接并存入数据库 参考 ...

  9. 从0系统学Android--5.2 发送广播

    从0系统学Android--52 发送广播 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 5.3 发送自定义广播 前面已经学习了如何接受广播了 ...

  10. <-0基础学python.第一课->

    初衷:我电脑里面的歌曲很久没换了,我想听一下新的歌曲,把他们下载下来听,比如某个榜单的,但是一首一首的点击下载另存为真的很恶心 所以我想有没有办法通过程序的方式来实现,结果还真的有,而且网上已经有有人 ...

随机推荐

  1. Junit测试Controller(MockMVC使用),以及传输@RequestBody数据解决办法

    转自:http://www.importnew.com/21153.html 一.单元测试的目的 简单来说就是在我们增加或者改动一些代码以后对所有逻辑的一个检测,尤其是在我们后期修改后(不论是增加新功 ...

  2. 【Offer】[20] 【表示数值的字符串】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 请实现一个函数用来判断字符串是否表示数值(包括整数和小数).例如,字符串"+100","5e2" ...

  3. CentOS 7.3 安装 libsodium 1.0.18

    出现configure: error: The Sodium crypto library libraries not found.错误或者notfound 1.下载并解压 wget https:// ...

  4. 浅谈PHP反序列化漏洞原理

    序列化与反序列化 序列化用途:方便于对象在网络中的传输和存储 0x01 php反序列化漏洞 在PHP应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等. 常见的序列化格式: ...

  5. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试

    最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...

  6. java线上cpu、内存问题排查方法

    一.线程 查进程中占用cpu高的线程 ps -mp xxxxx -o THREAD,tid,time | sort -rn 将线程的id从10位转到16位,可以在下面jstack中找到对应线程 输出线 ...

  7. QTP8.2--安装流程

    一.安装说明: 1.进入安装文件夹,运行QTP8.2安装文件setup,进入安装向导后直接单击“QuickTest Professional 安装”选项,由于破解文件存在缺陷,所以请不要改变安装路径c ...

  8. 主动降噪技术(ANC)的前生今世--原理仿真

    一 原理: 主动降噪就是通过反相检测麦克风的声音或噪声来减弱周围环境的噪声让扬声器出来的声音听起来更清晰.主动降噪技术的目标就是通过一个自适应滤波器把不想要的噪声反相从而把噪声约束到固定的范围内.该系 ...

  9. CentOS8-网卡配置

    一. 介绍 Centos8系统更新,新的版本让人看起来感觉很舒服,这时有人会配置CentOS8系统的网卡使系统上网,就会遇到配置好的网卡不会生效,自己想想和配置CentOS7的时候一个样啊,CentO ...

  10. 为RecyclerView定制可滑动的Item

    最近项目有需要弄一个可以像手机QQ会话页一样可以滑动的小菜单,每一个Item当用户在向左滑动的时候右侧会出现一个小菜单当时就想在也不是很难心想着找个开源的使用就好呢,但是我的项目是用的Recycler ...