ucos-ii嵌入式操作系统任务调度(一)----任务调度的过程及实现原理

2023-11-11

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题,二维码如下:
在这里插入图片描述

一 概念

在单片机裸机程序中,我们以函数为最小单位来划分代码功能的,所有函数之间都存在一个先后调用的关系(不是你调用我,就是我调用你,或者你我都被他调用);但是在嵌入式操作系统中,我们可以以任务为最小单位来看待一个程序代码(当然函数仍然是每个任务的最小单位),各个任务之间没有调用关系,它们可以说是各自为营。

可以把嵌入式系统程序看成各个单片机裸机程序的集合,跑操作系统的本质原因就是因为系统程序需要实现的功能很多,我们需要合理划分每个功能,保证相互之间没有关系,从而有一个框架清晰,逻辑清晰,后期维护和查找问题更便捷的系统。

那么操作系统是如何合理调度各个任务,保证每个任务都能合理正确的运行呢?这是所有操作系统最为重要的一个目的,现代操作系统绝大部分都是多任务系统,我们先看一看ucos-ii是如何实现的。

二 ucos-ii调度策略

何为任务调度?就是终止当前正在执行的任务,转而去执行别的任务过程。

在探讨任务调度策略时,我们先假设读者对ucos-ii中任务的基本概念有了一定理解,这里也不在做介绍。任务有一个基本特性,任务优先级(存储在任务控制块结构体中,在初始化创建任务的时候会被赋值)是任务调度的重要参考,在ucos中,优先级值越低,优先级越高,越优先被系统调度。

触发任务调度的基本方式有两种,一种是任务级调度,另外一种是中断级调度。

1. 任务级调度

所谓任务级调度,就是任务本身主动或被动触发了任务调度,比如说等待信号量过程中,当前信号量还没有产生,cpu不可能一直在这里死等(当然特殊情况,也会设计成在这里死等,比如像linux中的自旋锁机制),这样会造成别的任务不能得到快速响应,还怎么能称作实时操作系统呢?
任务调度的步骤,总结来说,可以分为两步,第一步是找到当前优先级最高的并且处于就绪状态的任务;第二步就是任务切换,从当前任务切换到优先级较高的任务去。通过调用API函数OS_Sched来实现,代码如下:

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0u;
#endif
    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0u) {                          /* Schedule only if all ISRs done and ...       */
        if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
            OS_SchedNew();
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
#if OS_TASK_PROFILE_EN > 0u
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                          /* Increment context switch counter             */
#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
                OS_TLS_TaskSw();
#endif
#endif
                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }
    }
    OS_EXIT_CRITICAL();
}

我们根据宏开关的定义,去掉未使能的代码,简写OS_Sched如下:

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0u;
#endif

    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0u) {                          /* Schedule only if all ISRs done and ...       */
        if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
            OS_SchedNew();
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
                OSCtxSwCtr++;                          /* Increment context switch counter             */
                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }
    }
    OS_EXIT_CRITICAL();
}
  1. 查找优先级较高的任务
    我们先关注上面代码片段中第10行位置的OS_SchedNew,此函数实现查找优先级较高任务的目的,其代码如下:
static  void  OS_SchedNew (void)
{
#if OS_LOWEST_PRIO <= 63u                        /* See if we support up to 64 tasks                   */
    INT8U   y;


    y             = OSUnMapTbl[OSRdyGrp];
    OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
#else                                            /* We support up to 256 tasks                         */
    INT8U     y;
    OS_PRIO  *ptbl;


    if ((OSRdyGrp & 0xFFu) != 0u) {
        y = OSUnMapTbl[OSRdyGrp & 0xFFu];
    } else {
        y = OSUnMapTbl[(OS_PRIO)(OSRdyGrp >> 8u) & 0xFFu] + 8u;
    }
    ptbl = &OSRdyTbl[y];
    if ((*ptbl & 0xFFu) != 0u) {
        OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]);
    } else {
        OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u);
    }
#endif
}

同样,根据宏开关,简写OS_SchedNew如下:

static  void  OS_SchedNew (void)
{
    INT8U   y;

    y             = OSUnMapTbl[OSRdyGrp];
    OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
}

我们发现这里只有2句赋值语句,OSPrioHighRdy是ucos-ii中用来标记当前系统任务中优先级最高的任务的全局变量,那么ucos是如何找到优先级最高的任务的呢?我们重点关注一下全局数组OSUnMapTbl,定义如下:

INT8U  const  OSUnMapTbl[256] = {
    0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F                   */
    7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF                   */
};

初看这个定义,绝大部分读者可能跟我一样,晕头转向,云里雾里,什么鬼(WTF!!)?而且我可能比各位读者还要更笨一些,我可是前前后后死磕了好几遍,才看懂这边的定义,看这个数组之前,我们先来看一下ucos是如何标记任务优先级的。

ucos中,将任务用数组OsRdyTbl[8],人为划分成了8组,OsRdyTbl[0]代表任务组0,OsRdyTbl[1]代表任务组1…OsRdyTbl[7]代表任务组7;每个数组中的每一位代表一个优先级任务,比方说对于OsRdyTbl[0]的第0位代表任务优先级为0的任务,OsRdyTbl[0]的第1位代表任务优先级为1的任务,以此类推;OsRdyTbl[1]的第0位代表任务优先级为8的任务(18+0),OsRdyTbl[1]的第1位代表任务优先级为9的任务(18+1)…OsRdyTbl[7]的第0位代表任务优先级为56的任务(78+0),OsRdyTbl[7]的第1位代表任务优先级为57的任务(78+1);同时ucos用OSRdyGrp表示哪一组优先级,OSRdyGrp的第0位表示第0组优先级状态(如果该位为1,表示第0组有存在任务就绪)…第7位表示第7组优先级状态。

因此我们只要知道OSRdyGrp的值,以及OsRdyTbl[]数组的值,就可以知道当前任务就绪的状态了,比方说,如果由我来设置最初版本的ucos系统(当然现在知道这样设计是存在问题的,虽然从逻辑上来看,也能实现功能,但是从实时性上来看,是不合理的)

u8 grp, tbl;

/* 先找出属于哪一组 */
for (i = 0; i < 8; i++)
{
    temp = OSRdyGrp >> i;
    if (temp & 0x1)
        break;
}
if (OSRdyGrp)  /* 这个判断条件可以不加上,因为系统中必定会存在一个空闲任务 */
    grp = i;

/*再找出当前那一组上哪一个优先级
 *因为OsRdyTbl[0]的优先级最高,其次是OsRdyTbl[1],最后是OsRdyTbl[7]
 *所以我们采用从0开始索引查找
 */
for (j = 0; j < 8; j++)
{
    for (i = 0; i < 8; i++)
    {
        temp = OsRdyTbl[j] >> i;
        if (temp & 0x1)
            goto find_out; 
    }
}
find_out:
    tbl = i;

OSPrioHighRdy = grp << 3 + tbl;

根据我们自己所实现的查找最高优先级的代码,反过来理解ucos-ii系统中的巧妙实现方法,它是如何通过一个数组,经过查表就能找到最高优先级的呢?
我们知道OSRdyGrp代表了哪一组任务优先级,第0位代表的任务组肯定是最高优先级的,因此我们先判断OSRdyGrp最低位出现为1的那一组,那么这一组中对应的任务肯定是优先级最高的;同理,OsRdyTbl[]数组中的每一位也代表一个任务优先级,最低位出现为1的肯定是优先级最高的(为什么是这样的,因为ucos系统OSRdyGrp和OsRdyTbl[]就是这样来定义的),找到最开始出现为1的,就可以根据公式:

OSRdyGrp最低位出现为1的位数 << 3 + OsRdyTbl[OSRdyGrp最低位出现为1的位数]最低位出现为1的位数

这里两个最低位出现为1的位数不是很好理解,请读者仔细分析这段话。
因此,ucos定义的数组就是表示最低位出现为1的数,比如说

优先级的值        二进制                 低位最开始出现1的位数
0                   0000 0000      ---->       0
1                   0000 0001      ---->       1
2                   0000 0010      ---->       2
3                   0000 0011      ---->       1
4                   0000 0100      ---->       2
5                   0000 0101      ---->       1
......       
128                 1000 0000      ---->       7
......       
255                 1111 1111      ---->       0

讲到这里,各位读者应该明白了查找较高优先级任务的原理
2. 任务切换
在找到较高任务的优先级时,就需要实现任务的切换了,上面的过程中,我们只是找到了当前处于就绪状态的最高优先级任务的优先级值,如何实现任务切换呢,请看代码(仍然是OS_Sched函数中的部分片段,说明的是:在不同版本的ucos-ii上,代码的细节会有所差别,不过原理目的都是一样的)

if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
            OS_SchedNew();
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
#if OS_TASK_PROFILE_EN > 0u
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                          /* Increment context switch counter             */

#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
                OS_TLS_TaskSw();
#endif
#endif

                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }

我们一句一句分析:
A. 找到当前优先级对应的任务控制块,根据优先级的值,索引数组

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]

B. 判断最高优先级是否为当前任务,如果就是当前任务的话,不需要切换;否则需要切换;
C. 如果要切换,记录当前任务被切换的次数,以及任务总共被切换的次数

OSTCBHighRdy->OSTCBCtxSwCtr++;
OSCtxSwCtr++; 

D. 实现任务切换,调用宏OS_TASK_SW()实现,实际上是调用的汇编函数OSCtxSw,有关这个汇编函数的实现方式,我们在另外的一篇文章中会重点分析,这里我们只需要知道调用这个函数后就会实现任务切换即可

OS_TASK_SW()

2. 中断级调度

中断级调度,是调用C函数OSIntExit实现的,如下:

void  OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr = 0u;
#endif



    if (OSRunning == OS_TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0u) {                           /* Prevent OSIntNesting from wrapping       */
            OSIntNesting--;
        }
        if (OSIntNesting == 0u) {                          /* Reschedule only if all ISRs complete ... */
            if (OSLockNesting == 0u) {                     /* ... and not locked.                      */
                OS_SchedNew();
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
                if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0u
                    OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task  */
#endif
                    OSCtxSwCtr++;                          /* Keep track of the number of ctx switches */

#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
                    OS_TLS_TaskSw();
#endif
#endif

                    OSIntCtxSw();                          /* Perform interrupt level ctx switch       */
                }
            }
        }
        OS_EXIT_CRITICAL();
    }
}

根据宏开关,简写如下:

void  OSIntExit (void)
{
    OS_CPU_SR  cpu_sr = 0u;
    
    if (OSRunning == OS_TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0u) {                           /* Prevent OSIntNesting from wrapping       */
            OSIntNesting--;
        }
        if (OSIntNesting == 0u) {                          /* Reschedule only if all ISRs complete ... */
            if (OSLockNesting == 0u) {                     /* ... and not locked.                      */
                OS_SchedNew();
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
                if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy */
                    OSCtxSwCtr++;                          /* Keep track of the number of ctx switches */

                    OSIntCtxSw();                          /* Perform interrupt level ctx switch       */
                }
            }
        }
        OS_EXIT_CRITICAL();
    }
}

我们发现,这个函数的实现基本上和OS_Sched的函数实现类似,也是先找到当前优先级最高的任务,然后启动任务切换。唯一不同的就是,调用了不同的函数实现了任务切换,这里就是调用了汇编函数OSIntCtxSw()来实现的,函数如下:

OSIntCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

我们发现,这个函数也是使能了一次PendSV异常,跟汇编函数OSCtxSw的实现一样,都是软件触发了一次了PendSV异常,在PendSV异常处理函数中,实现上下文的切换,参照文章《ucos-ii嵌入式操作系统任务调度(二)----任务切换瞬间cpu做了什么》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ucos-ii嵌入式操作系统任务调度(一)----任务调度的过程及实现原理 的相关文章

  • 我发现ucos里面也是任务,任务控制块,消息队列,信号量,事件 。这些概念感觉和freertos一模一样啊

    我发现ucos里面也是任务 xff0c 任务控制块 xff0c 消息队列 xff0c 信号量 xff0c 事件 这些概念感觉和freertos一模一样啊 xff0c 感觉大家就是抄来抄去 xff1f 应该这些操作系统原理都差不多 xff0c
  • linux与freertos区别,谈谈uCOS和freeRTOS这两种实时系统的结构以及编程思想

    距离上次总结 xff0c 已经过去有差不多半年了 xff0c 最近又到了自己半年一次的总结了 首先说说自己的编程风格的变化 xff0c 在上一篇 第三篇文 中提到的数据结构 配置文件结构 预编译结构 xff0c 目前已经逐渐适应 xff0c
  • ucos-ii 的任务调度原理和实现

    ucosii 任务调度和原理 1 ucos ii 任务创建与任务调度 1 1 任务的创建 当你调用 OSTaskCreate 进行任务的创建的时候 xff0c 会初始化任务的堆栈 保存cpu的寄存器 创建任务的控制块 xff08 OS TC
  • 白话----之UCOS 信号量和邮箱

    总体理解 xff1a 两个任务需要共同访问一个共同的资源 xff0c 来切换或跳到不同的动作执行 这就产生信号量 两个任务 需要根据不同的按键选择 xff0c 来执行不同的动作 xff0c 产生邮箱 信号量和邮箱 我通过一个例子来学习的 希
  • 推荐ucos-II 3本参考书 经典

    在这里给大家推荐三本学习ucos的必看书籍 1 xff08 比较难买 xff09 嵌入式实时操作系统uc os II教程 西安电子科技大学出版 这本书对UCOS的源代码分析的非常清楚 比作者原著 在某种程度上要好 xff0c 这本书对关键的
  • uCOS-II任务间通信之信号量 [转载]

    uCOS II任务间通信之信号量 信号量是什么 xff1f 信号量有什么用 xff1f 信号量是可以用来表示一个或多个事件的发生 xff0c 还可以用来对共享资源的访问 uCOS II提供了5个对信号量进行操作的函数 如下所示 xff1a
  • uCOS/FreeRTOS任务创建的两种模式

    在我们使用uCOS FreeRTOS编写代码时 xff0c 首先要面临的一个问题是怎样创建任务并启动整个系统 一般来说 xff0c 我们会有两种不同的方式 这两种方式不仅适用于uCOS FreeRTOS xff0c 同时也适用于其它RTOS
  • 一步一步教你使用uCOS-II

    第一篇 UCOS介绍 第一篇 UCOS介绍 这个大家都知道 呵呵 考虑到咱们学习的完整性还是在这里唠叨一下 让大家再熟悉一下 高手们忍耐一下吧 xff01 uC OS II Micro Control Operation System Tw
  • freeRTOS与ucos II区别

    freeRTOS比uCOS II优胜的地方 1 内核ROM和耗费RAM都比uCOS 小 xff0c 特别是RAM 这在单片机里面是稀缺资源 xff0c UCOS至少要5K以上 xff0c 而freeOS用2 3K也可以跑的很好 xff1b
  • uCOS消息队列相关函数的理解

    OSQCreate xff1a 创建消息队列函数 有四个入口参数 xff1a 消息队列指针 xff1b 消息队列名称 xff1b 消息队列大小 xff08 不能为0 xff09 xff1b 返回错误类型 函数过程 xff1a 首先进行安全检
  • 操作系统学习笔记1--ucos

    实时操作系统 xff08 Real Time Operating System xff0c 简称RTOS xff09 一般裸机为前后台系统模式 xff0c while 1 大循环为后台 xff0c 中断为前台 RTOS操作系统比前后台系统实
  • Huawei LiteOS与freeRTOS、Ucos主流嵌入式操作内核的区别

    LiteOS与freeRTOS Ucos主流嵌入式操作内核的区别 云社区 华为云
  • UCOS-II时间管理

    uC OS II时间管理 xff1a 任务延时函数 xff0c OSTimeDly INT16U ticks 申请该服务的任务可以延时一段时间 xff0c 这段时间的长短是用时钟节拍的数目来确定的 实现这个系统服务的函数叫做 OSTimeD
  • UCOS-II任务设计

    UCOS II任务设计 任务函数的结构 xff1a 在用户任务函数中 xff0c 必须包含至少一次对操作系统服务函数的调用 xff0c 否则比其优先级低的任务将无法得到运行的机会 xff0c 这是用户任务与普通函数的明显区别 任务函数按照执
  • (转)ucos的事件 任务的通讯和同步 信号量 互斥量 消息邮箱 消息队列

    这会想了想 xff0c 在复习资料后 xff0c 最后再做个核心代码分析 ucos中使用信号量 消息邮箱 消息队列 xff0c 这些数据结构来作为通信中间媒介 这些数据结构会影响任务的程序流程 xff0c 因此也叫做事件 一 信号量 是进行
  • ucos信号量集

    事件标志组 信号量集 的使用 xff1a span class token keyword static span OS STK task testled span class token punctuation span STARTUP
  • UCOS II两个任务的模板

    芯片lm3s9b92 include lt includes h gt include 34 utils uartstdio h 34 Application tasks 优先级 define TASK2 PRIO 11 define ta
  • ucos OSTimeDly

    来源 xff1a http blog sina com cn s blog 5f9b3de40100e182 html OSTimeDly 在Task中 xff0c 一般执行一段时间之后调用OSTimeDly推迟一段时间再继续运行 xff0
  • UCOS2的文件目录

    想着闲着也是闲着 把之前学习ucos2源码的笔记整理一下 复盘一次 总结内容将其写为博客作为学习的输出 一 为什么要学RTOS或者IOTOS 我在大一时 开始进入实验室接触单片机 摸爬滚打的参加了几次比赛 也因此入了嵌入式的坑 大三时开始思
  • STM32滴答定时器与UCOS时钟系统,以及心跳和延时函数的实现.

    Systick就是一个定时器而已 只是它放在了NVIC中 主要的目的是为了给操作系统提供一个硬件上的中断 号称滴答中断 滴答中断 这里来简单地解释一下 操作系统进行运转的时候 也会有 心跳 它会根据 心跳 的节拍来工作 把整个时间段分成很多

随机推荐

  • 解决:return _compile(pattern, flags).search(string) TypeError: expected string or buffer

    今天写爬虫 爬取MM图片页面的标题时 遇到了一个问题 上图 看看我的代码 1 import urllib 2 import urllib2 3 import re 4 5 class JPMSG 6 7 def init self base
  • 全栈式部署:SpringCloud 微服务+Docker + Vue + nginx 前后端一站式部署

    markdown body line height 1 75 font weight 400 font size 16px overflow x hidden color rgba 51 51 51 1 markdown body h1 m
  • blender中旋转问题(A绕B的中心旋转)

    如果想要以正方体为中心进行对球体进行旋转 应该shift选中这两个物体 并将正方体选为父级 按下ctrl p 发现中心点变成了正方体 这时候对球体进行旋转 发现是围绕着正方体旋转 就完成了 旋转正方体 发现球体跟着正方体旋转 但我想要正方体
  • 卷积神经网络——为了毕设

    CNN笔记 通俗理解卷积神经网络 v JULY v的博客 CSDN博客 卷积神经网络通俗理解 卷积神经网络 输入层 卷积层 激活函数 池化层 全连接层 yjl9122的博客 CSDN博客 卷积神经网络输入层 CNN 卷积 对图像 不同的数据
  • 跨页面通信postMessage和onmessage

    简介 通常 对于两个不同页面的 只有当它们位于同协议 同域名 同端口时 才可以通信 而window postMessage 方法可以安全地实现跨源通信 语法 发送 otherWindow postMessage message target
  • c++虚函数有几个虚表

    怕自己忘记了 所以复制一下 尊重他人版权 http blog csdn net haoel article details 1948051 前言 C 中的虚函数的作用主要是实现了多态的机制 关于多态 简而言之就是用父类型别的指针指向其子类的
  • VUE项目开发的完整流程

    文章目录 一 VUE环境搭建 二 创建VUE项目 1 外部终端创建 2 IDea内部终端创建 3 项目介绍 4 vue 的特别之处 三 添加前端开发插件依赖 四 前端开发流程 1 创建组件与编码 2 配置组件路由 3 运行 五 前后端交互
  • S参数仿真如何添加Mask

    S参数仿真如何添加Mask 对于高速信号的仿真 一般Spec中均会对Return loss Insertion Loss提出对应的模板要求 这些模板要求多数都是通过公式进行约束 如GMSL2中对于回损的要求 这种公式约束的S参数模板 可通过
  • 数据挖掘入门指南!!!

    Datawhale 作者 王瑞楠 吴忠强 徐韬 田杨军 摘要 入门数据挖掘 必须理论结合实践 本文梳理了数据挖掘知识体系 帮助大家了解和提升在实际场景中的数据分析 特征工程 建模调参和模型融合等技能 数据分析 探索性数据分析 Explora
  • Scratch角色的绘制与中心点

    将鼠标移动到界面右下角的角色栏 可以选择 绘制一个角色 点击绘制 左侧会进入造型界面 现在来绘制一个圆形 点击左侧的圆 然后在画布上按住鼠标左键进行绘制 按下点即为圆心 默认是绘制椭圆 可以按住shift来绘制正圆 上方可以调整圆的填充和轮
  • 如何使用Python对文件进行压缩与解压缩

    前言 我们在日常工作中 除了会涉及到使用Python处理文本文件 有时候还会涉及对压缩文件的处理 通常会涉及到的压缩文件格式有 rar Windows 环境下用的比较多的压缩 比较著名的GUI工具是winrar tar Linux系统下的打
  • Mabtis设置控制台打印sql(转)

    在mybatis config xml 中加入以下配置即可
  • 一名普通本科生从无到有(暑期实习)的分享感悟

    前言 本篇帖子主要记录下自己在找实习的一个经过吧 有兴趣的佬们可以看看 帖子中可能有很多自己的看法 牛友们不喜勿喷哈 先介绍下自己吧 普通本科 没有竞赛经历 没有任何奖项 大三上用python做了一个项目 这就是2022年之前的我 在1 3
  • MySQL查看数据库性能常用命令

    MySQL查看数据库性能常用命令 一 查询服务器状态和配置 列出MySQL服务器运行各种状态值 mysql gt show global status 查询MySQL服务器配置信息语句 mysql gt show variables 二 慢
  • 在matlab中如何把两个txt文件合并到一起

    a 1 2 3 2 3 4 b 4 5 6 5 9 9 要求 c 1 2 3 2 3 4 4 5 6 5 9 9 使用命令 c a b 同时要横向合并 如产生 c 1 2 3 4 5 6
  • Excel列表名称

    一 给你一个整数 columnNumber 返回它在 Excel 表中相对应的列名称 例如 A gt 1 B gt 2 C gt 3 Z gt 26 AA gt 27 AB gt 28 示例 1 输入 columnNumber 1 输出 A
  • nginx服务器

    nginx服务器 Nginx服务器 engine X 是一种web服务器 它是一种开源的高性能HTTP和反向代理服务器 它也可提供IMAP POP3 SMTP代理服务等功能 Nginx是以多进程的方式来工作的 也支持多线程的方式 只是主流的
  • C++类的声明和类的实现分开写(.hpp和.cpp)

    1 声明和实现为什么要分开写 现在开始写项目了 你会发现我们一般都要写一个cpp 对应的还得有一个h文件 那么为什么在C 中我们要这么做 h就是声明 cpp就是实现 而所谓分离式实现就是指 声明 和 定义 分别保存在不同的文件中 声明保存在
  • ATM机测试点案例

    卡 是否是本行卡 是否是银行卡 会员卡 超市卡 是否是挂失卡 消磁 破损 锁卡 冻结 借记卡 贷记卡 本行卡是否能正常显示余额 插入银行卡不操作 是否会退卡或吞卡 密码 6位 输入正确的密码 输入错误的密码 密码长度测试 输入密码错误三次是
  • ucos-ii嵌入式操作系统任务调度(一)----任务调度的过程及实现原理

    先给自己打个广告 本人的微信公众号正式上线了 搜索 张笑生的地盘 主要关注嵌入式软件开发 股票基金定投 足球等等 希望大家多多关注 有问题可以直接留言给我 一定尽心尽力回答大家的问题 二维码如下 一 概念 在单片机裸机程序中 我们以函数为最