UCOSIII 系统(STM32任务管理)学习笔记

2023-05-16


作者:瓴
时间:2021.07.28


UCOSIII 系统(STM32任务管理)学习笔记

  • UCOSIII 系统学习笔记
    • 一、UCOSIII 任务管理
      • 1、任务状态
      • 2、任务控制块 OS_TCB
      • 3、任务堆栈 TASK_STK[]
      • 4、任务就绪表
          • (1)、优先级位映表 OSPrioTbl[]
          • (2)就绪任务列表 OSRdyList[]
      • 5、任务调度和切换
          • (1)、可剥夺型调度
          • (2)时间片轮转调度
      • 6、系统时钟函数 OSTimeTick()
      • 7、临界代码保护
    • 二、任务相关 API 函数的使用
      • 1、任务创建和删除实验
          • (1)、任务创建函数 OSTaskCreate()
          • (2)、任务删除函数 OSTaskDel()
      • 2、任务的挂起与恢复
          • (1)任务挂起函数 OSTaskSuspend()
          • (2)、任务恢复函数 OSTaskResume()
      • 3、时间片轮转调度函数
          • (1)OSSchedRoundRobinCfg()函数
          • (2)OSSchedRoundRobinYield()函数
    • 三、USCOSIII 系统内部任务
      • 1、空闲任务 OS_IdleTask()
          • (1)钩子函数 OSIdleTaskHook()
      • 2、时钟节拍任务 OS_Ticktask()
      • 3、统计任务 OS_StatTaskInit()
      • 4、定时任务 OS_TmrInit()
      • 5、中断服务管理任务 OS_IntQTask()
    • 四、UCOSIII 中断的和时间管理
      • 1、中断管理
          • (1)、UCOSIII 中断处理过程
          • (2)、直接发布和间接发布
          • (3)、时钟节拍中断
      • 2、时间管理
          • (1)、OSTimeDly()函数
          • (2)、OSTimeDlyHMSM()函数
          • (3)、其他有关时间的函数
    • 五、UCOSIII 软件定时器
      • 1、定时器工作模式
      • 2、定时器相关 API 函数
          • (1)创建定时器函数 OSTmrCreate(),函数原型如下。
          • (2)、其他定时器函数
    • 六、UCOSIII 信号量和互斥信号量
      • 1、信号量
          • (1)二进制信号量
          • (2)计数型信号量
      • 2、互斥信号量
      • 3、任务内嵌信号量
    • 七、UCOSIII 消息传递
      • 1、消息队列
      • 2、消息队列相关函数
      • 3、任务内嵌消息队列
      • 4、任务内嵌消息队列相关函数
    • 八、事件标志组
      • 1、事件标志组
      • 2、事件标志组相关函数
    • 九、同时等待多个内核对象
    • 1、同时等待多个内核对象
    • 十、存储管理
      • 1、内存管理简介
      • 2、存储区创建
      • 3、存储块的使用
          • (1)、内存申请
          • (2)、内存释放

UCOSIII 系统学习笔记

————————————————
说明:本文为个人学习UCOSIII过程中时所做的笔记,主要对UCOSIII中关键内容进行记述,所以较建议为了快速了解UCOSIII系统内容的用户阅读。对于其中的函数,仅选取了方便理解系统整体结构内容的函数进行了记录,如果需要详细使用和学习理解,建议配合正点原子的《 STM32F4 UCOS开发手册_V3.0 》进行学习,此文档可以从正点原子官网http://www.openedv.com/posts/list/38349.htm下载。

个人对UCOSIII的学习理解为,UCOSIII系统主要功能是基于链表对任务进行链接,优先级对任务进行决策和定时器、信号量、消息队列、标志组等对任务进行管理。
举个例子,比如一个机器人比赛规则为机器人需移动到不同5个位置分别射箭,如果不使用UCOSIII,那么为了保证移动过程中的精度和稳定,我们得尽量让驱动电机和读取码盘数据这两个动作时间短且来回切换,在程序中除了编写这两个动作程序,还要编写大量来回切换的程序。如果使用UCOSIII,我们仅需要两个动作的程序,然后用时间片轮转调度进行管理即可,且代码量不会随两个动作切换次数而增加。这仅是移动过程,加上射箭,取箭和其他各种辅助传感器的等动作交织在一起,动作越多越复杂,那么UCOSIII的优势将会越明显。

————————————————

一、UCOSIII 任务管理

1、任务状态

UCOSIII 支持的是单核 CPU,不支持多核 CPU,某一时刻只有一个任务会获得 CPU 使用权进入运行态,其他的任务就会进入其他状态,UCOSIII 中的任务有多个状态,其中包括休眠态,就绪态,运行态,等待态,中断服务态。

任务状态描述
休眠态休眠态就是任务只是以任务函数的方式存在,只是储存区中的一段段代码,并未用 OSTaskCreate()函数创建这个任务。
就绪态任务在就绪表中已经登记,等待获取 CPU 使用权。
运行态正在运行的任务处于运行态。
等待态正在运行的任务需要等待某一个事件,比如信号量、消息、事件标志组等,就会暂时让出 CPU 使用权,进入等待事件状态。
中断服务态一个正在执行的任务被中断打断,CPU 转而执行中断服务程序,这时这个任务就会挂起,进入中断服务态。

2、任务控制块 OS_TCB

任务控制块 OS_TCB 用来保存任务的信息,我们使用 OSTaskCreate()函数来创建任务的时候就会给任务分配一个任务控制模块。任务控制块是一个结构体。

3、任务堆栈 TASK_STK[]

任务堆栈用来切换任务和调用其他函数的时候保存现场的,每个任务都有自己的堆栈。我们使用 OSTaskCreat()函数创建任务的时候就可以把创建的堆栈传递给任务。

在 UCOSIII 中用 CPU_STK 数据类型来定义任务堆栈,其实 CPU_STK 就是CPU_INT32U,可以看出一个 CPU_STK 变量为 4 字节,因此任务的实际堆栈大小应该为我们定义的 4 倍。例如 CPU_STK TASK_STK[64]; //定义一个 TASK_STK 任务堆栈。堆栈大小为 64*4=256 字节。

EQU 和 C 语言中的宏定义#define 一样,用于定义一个宏。例如 NVIC_INT_CTRL EQU 0XE000ED04。

4、任务就绪表

UCOSIII 将已经就绪的任务放在就绪表里,包括优先级位映表 OSPrioTbl[]、就绪任务列表 OSRdyList[]。

(1)、优先级位映表 OSPrioTbl[]

优先级位映表 OSPrioTbl[]用来标记哪些任务就绪了。

当某一任务就绪后会将优先级位映射表 OSPrioTbl[]中相应的位置置 1。依次查询数组的元素,当某个元素为 0 时,就继续扫描下一元素,不为 0 就继续执行该元素对应的任务。优先级位映射表中从左到右优先级逐渐降低,同时每个 OSPrioTbl[]数组的元素最高优先级位在左边,最低优先级在右边。这样做主要是为了支持使用一条特殊的指令“计算前导零(CLZ)”,从而可以快速的找到最高优先级任务。
在这里插入图片描述

(2)就绪任务列表 OSRdyList[]

就绪任务列表 OSRdyList[]主要用来标记哪些任务就绪了。结构体定义如下

struct os_rdy_list {
OS_TCB *HeadPtr; //用于创建链表,指向链表头
OS_TCB *TailPtr; //用于创建链表,指向链表尾
OS_OBJ_QTY NbrEntries; //此优先级下的任务数量
}

UCOSIII 支持时间片轮转调度,因此同一个优先级下会有多个任务,所以 UCOSIII 的就绪任务列表 OSRdyList[]数组中每一个元素对应管理一个优先级下的所有任务,比如OSRdyList[3]就用来管理优先级 3 下的所有任务。

注:有些优先级下只能有一个任务,比如 UCOSIII 自带的 5 个系统任务:
空闲任务 OS_IdleTask()
时钟节拍任务OS_TickTask()
统计任务 OS_StatTask()
定时任务 OS_TmrTask()
中断服务管理任务 OS_IntQTask()。

同一个优先级下的所有任务是通过链表来管理的,OS_TCB 是用来构造链表的,HeadPtr 和 TailPtr 分别指向这个链表的头和尾,NbrEntries 用来记录此优先级下的任务数量,如下表示优先级 4 下 3 个任务的就绪任务列表。
在这里插入图片描述

5、任务调度和切换

任务调度和切换就是让就绪表中优先级最高的任务获得 CPU 使用权。UCOSIII 是可剥夺型、抢占式的,高优先级任务可以抢了低优先级任务的 CPU 使用权。

(1)、可剥夺型调度

任务调度是由任务调度器完成的,任务调度器有两种:任务级调度器 OSSched()和中断级调度器 OSIntExit()。

a、任务级调度器
任务级调度器函数为 OSSched(),在 OSSched()中调用 OSCtxSw()实现任务切换,所以真正执行任务切换的是宏 OS_TASK_SW(),如下:

#define OS_TASK_SW() OSCtxSw()//调用该宏和函数的满足条件就会发生任务切换
OSCtxSW()要做的就是将当前任务的 CPU 寄存器的值保存在任务堆栈中,也就是保存现场,保存完当前任务的现场后将新任务的 OS_TCB 中保存的任务堆栈指针的值加载到CPU 的堆栈指针寄存器中,最后还要从新任务的堆栈中恢复 CPU 寄存器的值。

b、中断级调度器
任务级调度器函数为 OSIntExit(),在中断级调度器中真正完成任务切换的就是中断级任务切换函数 OSIntCtxSW(),与任务级切换函数 OSCtxSW()不同的是,由于进入中断的时候现场已经保存过了,所以 OSIntCtxSW()不需要像 OSCtxSW()一样先保存当前任务现场,只需要做 OSCtxSW()的后半部分工作,也就是从将要执行的任务堆栈中恢复 CPU 寄存器的值。

(2)时间片轮转调度

UCOSIII 支持多个任务同时拥有一个优先级,要使用这个功能我们需要定义OS_CFG_SCHED_ROUND_ROBIN_EN 为 1,在 UCOSIII 中允许一个任务运行一段时间(时间片)后让出 CPU 的使用权,让拥有相同优先级的下一个任务运行,这种调度方法称为时间片轮转调度。
在这里插入图片描述
1)任务 3 运行完相应时间片,任务 3 还没运行完。
2)UCOSIII 切换到任务 1 运行完其相应时间片,任务 1 还没运行完。
3)切换到任务 2 运行完相应时间片,任务 2 还没运行完。
4)切换到任务 3 运行完相应时间片,任务 3 还没运行完。
5)切换到任务 1 运行,任务 1 在其时间片内任务运行结束,于是切换到任务 2。
6)任务 2 运行完相应时间片,切换到任务 3 运行。

当某一优先级下有多个任务时,每次任务切换后运行的都是处于就绪任务列表OSRdyList[]链表头的任务,当这个任务的时间片用完后这个任务就会被放到链表尾,然后再运行新的链表头的任务。这些任务是这样被调度和运行的。

6、系统时钟函数 OSTimeTick()

系统时钟,是处理器运行时间基准(每一条机器指令一个时钟周期),时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。

UCOSIII 需要一个系统时钟来对任务进行整个节拍的延迟,并为等待事件的任务提供超时判断。我们通过调用 OSTimeTick()函数来为系统提供时钟。

7、临界代码保护

为了保护一些代码完成运行,不被打断。这些不能被打断的代码称为临界段代码,也叫临界区。

使用宏 OS_CRITICAL_ENTER()进入临界区代码;使用宏 OS_CRITICAL_EXIT()和
OS_CRITICAL_EXIT_NO_SCHED()退出临界区代码。

在 UCOSIII 中可以通过关闭中断和任务调度器上锁两种方式来保护临界段代码。

OS_CFG_ISR_POST_DEFERRED_EN 为 0,使用关中断的方式进入临界区;
OS_CFG_ISR_POST_DEFERRED_EN 为 1,使用锁定调度器方式进入临界区。

OS_CFG_ISR_POST_DEFERRED_EN 在 os_cfg.h 文件中定义。

二、任务相关 API 函数的使用

1、任务创建和删除实验

(1)、任务创建函数 OSTaskCreate()

任务创建函数 OSTaskCreate()就是将任务控制块 0S_TCB、任务堆栈 TASK_STK[]、和我们自己编写的任务代码联系在一起,并且初始化任务控制块的相应字段。刚创建的任务就会进入就绪态,因此不能在中断服务程序中调用 OSTaskCreate()函数创建任务。创建任
务设置优先级时,不能设置为和 UCOSIII 自带系统任务相同的优先级!

(2)、任务删除函数 OSTaskDel()

任务删除函数 OSTaskDel()用于删除不需要运行的任务,删除任务只是 UCOSIII 不再管理该任务,并不是删除该任务代码。

尽量避免任务运行时删除任务,如果多个任务使用同一共享资源,任务 A 正在使用这个资源,如果此时删除了任务 A,这个共享资源就不会被释放,那么其他任务将得不到这个共享资源的使用权。

我们调用 OSTaskDel()删除一个任务后,这个任务的任务堆栈、OS_TCB 所占用的内存并没有释放掉,因此我们可以利用它们用于其他的任务,当然我们也可以使用内存管理的方法给任务堆栈和 OS_TCB 分配内存,这样当我们删除掉某个任务后我们就可以使用内存释放函数将这个任务的任务堆栈和 OS_TCB 所占用的内存空间释放掉。

2、任务的挂起与恢复

(1)任务挂起函数 OSTaskSuspend()

有时有些任务因为某些原因需要暂停运行,但是后面还要运行,因此我们就不能删除掉任务。于是我们可以使用 OSTaskSuspend()函数暂停挂起某任务,以便以后恢复再运行。

我们可以多次调用 OSTaskSuspend ()函数来挂起一个任务,同时我们也需要调用同样次数的 OSTaskResume()函数才可以恢复被挂起的任务。

(2)、任务恢复函数 OSTaskResume()

任务恢复函数 OSTaskResume()用于恢复被挂起的任务,OSTaskResume()是唯一能恢复被挂起任务的函数。

如果被挂起的任务还在等待别的内核对象,比如事件标志组、信号量、互斥信号量、消息队列等,即使使用 OSTaskResume()函数恢复了被挂起的任务,该任务也不一定能立即运行,该任务还是要等相应的内核对象,只有等到内核对象后才可以继续运行。

3、时间片轮转调度函数

UCOSIII 支持多个任务拥有相同优先级,这些任务采用时间片轮转调度方法进行任务调度 。 要 想 采 用 时 间 片 轮 转 调 度 , 就 需 将 os_cgf.h 文 件 中 的 宏OS_CFG_SCHED_ROUND_ROBIN_EN 定义为 1。这样时间片轮转调度的代码才会被编译。否则不能使用时间片轮转调度。

(1)OSSchedRoundRobinCfg()函数

OSSchedRoundRobinCfg()函数用来使能或失能 UCOSIII 的时间片轮转调度功能。

(2)OSSchedRoundRobinYield()函数

当一个任务想放弃本次时间片,OSSchedRoundRobinYield()函数用来把 CPU 使用权让给同优先级的另一任务。
调用该函数后遇到最多的错误是OS_ERR_ROUND_ROBIN_1,即当前优先级下没有就绪任务。

三、USCOSIII 系统内部任务

UCOSIII 中系统内部任务有 5 个,分别为:空闲任务 OS_IdleTask()、时钟节拍任务OS_Ticktask()、统计任务OS_StatTaskInit()、定时任务 OS_TmrInit()、终端服务管理任务 OS_IntQTask()。

1、空闲任务 OS_IdleTask()

空闲任务 OS_IdleTask()是必须创建的,不过在调用 OS_Init()初始化 UCOS 的时候就会被创建,不需要手动创建。同时空闲任务优先级是最低的。

空闲任务的作用:在所有应用任务都进入等待态的时候,CPU 可以执行空闲任务,从而一直工作。

(1)钩子函数 OSIdleTaskHook()

空闲任务中还调用了钩子函数 OSIdleTaskHook(),钩子函数主要用来对某些任务进行扩展的。要使用钩子函数需要将宏 OS_CFG_APP_HOOKS_EN 置 1。

如果我们想要在钩子函数中执行一些功能,我们可以将代码写在 App_OS_IdleTaskHook
()函数中。注:在空闲任务的钩子函数中不能调用任何可以使空闲进入等待态的代码。

原因:CPU 总是在不停的运行,需要一直工作,CPU 不能停下来,哪怕是执行一些对应用没有任何用的代码,比如简单的将一个变量加一。在 UCOS 中为了让 CPU 一直工作,在所有应用任务都进入等待态的时候 CPU 会执行空闲任务。

我们可以从空闲任务的任务函数OS_IdleTask()看出,在 OS_IdleTask()中没有任何可以让空闲任务进入等待态的代码。如果在 OS_IdleTask()中有可以让空闲任务进入等待态的代码的话,有可能会在同一时刻使得所有任务(应用任务和空闲任务)同时进入等待态,此时CPU 就会无事可做了。

UCOSIII 一共拥有 8 个钩子函数,包括空闲任务的钩子函数。

函数名功能
OSIdleTaskHook()空闲任务调用这个函数,可以用来让 CPU 进入低功耗模式。
OSInitHook()系统初始化函数 OSInit()调用此函数。
OSStatTaskHook()统计任务每秒中都会调用这个函数,此函数允许你向统计任务中添加自己的应用函数。
OSTaskCreateHook()任务创建的钩子函数。
OSTaskDelHook()任务删除的钩子函数。
OSTaskReturnHook()任务意外返回时调用的钩子函数,比如删除某个任务
OSTaskSwHook()任务切换时候调用的钩子函数。
OSTimeTickHook()滴答定时器调用的钩子函数。

2、时钟节拍任务 OS_Ticktask()

时钟节拍任务 OS_Ticktask()也是必须创建的,同样不需要手动创建,调用 OSInit()初始化函数时就会被创建。时钟节拍任务默认优先级为 1。

时钟节拍任务的作用:跟踪正在延时的任务、指定时间内等待某个内核对象的任务。

3、统计任务 OS_StatTaskInit()

统计任务 OS_StatTaskInit()默认情况下不会创建,如果要使能统计任务需要将OS_CFG_STAT_TASK_EN 置 1。OS_StatTaskInit()函数用来创建统计任务。统计任务优先级倒数第二。

统计任务的作用:用来统计 CPU 使用率、各任务的 CPU 使用率和堆栈使用情况。

如果要使用统计任务就需要在 main()函数创建的第一个也是唯一一个应用函数(如start_task()任务)中调用OSStatTaskCPUUsageInit()函数。

4、定时任务 OS_TmrInit()

UCOSIII提供软件定时器功能,如果要使能定时任务需要将宏OS_CFG_TMR_EN 置1。定时任务可选择。同样调用 OSInit()初始化函数时,OSInit()中会调用 OS_TmrInit()函数创建定时任务。定时任务默认优先级为 2。

5、中断服务管理任务 OS_IntQTask()

如果要使能中断服务管理任务需要将 OS_CFG_ISR_POST_DEFERRED_EN 置 1。OSInit()中会调用 OS_TmrInit()函数创建中断服务管理任务。中断服务管理任务的优先级为 0,是最高的。

中断服务管理任务 OS_IntQTask()的作用:该任务负责“延迟”在中断服务函数 ISR 中调用的系统 post 服务函数的行为。

调度器上锁的方式来管理临界段代码时,在中断服务函数中调用的“post”类函数就不允许操作诸如任务就绪表、等待表等系统内部数据结构。

当中断服务函数调用 post 函数时,会将要发送的数据和发送的目的地存入一个特别缓冲队列。当所有中断服务函数都执行完之后,UCOSIII 会做任务切换,运行中断服务管理任务会把刚才存入缓冲队列的信息重新发给相应任务。这样就可以减少关闭中断的时间,否则还需要把任务从等待列表中删除,然后把任务放入就绪表中等耗时操作。

四、UCOSIII 中断的和时间管理

1、中断管理

(1)、UCOSIII 中断处理过程

STM32 中是支持中断的,中断是一个硬件机制,主要用来向 CPU 通知一个异步事件
发生了。当一个中断发生时,CPU 会将当前 CPU 寄存器值存入栈中,然后去执行中断服务程序,在此期间如果有更高优先级就绪,退出中断后去执行这个更高优先级的任务。

UCOSIII 支持中断嵌套,最大支持 250 级的中断嵌套。UCOSIII 中使用 OSIntNestingCtr
记录嵌套次数,每进入一次中断加 1,退出一次减 1。

编写 UCOSIII 中断服务函数时,需要使用 OSIntEnter()和 OSIntExit()函数,这两个函数对 OSIntNestingCtr 进行相应增减处理,从而记录中断嵌套次数。中断函数如下:

void XXX_Handler(void) 
{ 
OSIntEnter(); //进入中断
/*用户自行编写的中断服务程序;*/
OSIntExit(); //触发任务切换软中断
}

XXX_Handler(void)为不同中断源的中断函数名字,可打开 startup_stm32f10x_hd.s 文件查询需要使用的中断名。

(2)、直接发布和间接发布

UCOSIII 对中断发布的信息或者信号的处理有两种模式:直接发布和延迟发布两种方式。
通过宏OS_CFG_ISR_POST_DEFERRED_EN 来选择模式,为 0 时直接发布模式,为 1 时延迟发布模式。

直接发布模式下,UCOSIII 通过关闭中断来保护临界段代码;
延迟发布模式下,UCOSIII 通过锁定任务调度来保护临界段代码。在延迟发布模式下,UCOSIII 在访问中断队列时,仍然需要关闭中断,但这个时间是非常短的。

如果应用中存在非常快速的中断源,当 UCOSIII 直接模式下中断模式关闭时间不满足要求,可以用延迟发布来降低中断关闭时间。

(3)、时钟节拍中断

UCOSIII 需要一个系统时钟节拍作为系统心跳,这个时钟我们一般都使用 MCU 的硬件定时器。Cortex-M 内核提供了一个定时器用于产生系统钟节拍 ,这个定时器就是 Systick。UCOSIII 通过时钟节拍来对任务进行整个的延迟,并为等待事件通过时钟节拍来对任务进行整个节拍的延迟,并为等待事件提供超时判断。

时钟节拍中断必须调用 OSTimeTick()函数,我们使用 Systick 来为系统提供时钟,因此在 Systick 的中断服务程序就必须调用 OSTimeTick() 。

时钟节拍中断服务程序首先会调用钩子函数 OSTimeTickHook() ,这个函数中用户可以放置一些代码。

2、时间管理

(1)、OSTimeDly()函数

OSTimeDly()函数用来对一个任务进行延时操作。

延时时间的单位为时间节拍数,如我们设置系统时间频率 OSCfg_TickRate_Hz 为 200Hz,则每个时钟节拍就是 5ms 的时间长度。

(2)、OSTimeDlyHMSM()函数

OSTimeDlyHMSM()函数来更加直观的来对某个任务延时。

延时时间单位为小时、分钟、秒、毫秒的格式。但这个延时最小单位和我们设置的时钟
节拍频率有关,比如我们设置时钟节拍频率 OSCfg_TickRate_Hz 为 200Hz 的话,那么最小延时单位就是 5ms。

补:上述延时函数执行时,会把当前任务挂起,从而任务进入中断服务态。延时一段时间之后再将任务转为就绪态。若任务 1 使用上述函数延时 1s,延时期间发生任务调度,但调度后的任务 2 执行到 1s 时还未执行完成,此时会先将任务 1 转为就绪态,但任务 2 占有CPU 使用权继续运行。任务 1 继续等待至任务 2 运行完成后,获得 CPU 使用权后继续运行。

delay_xus()延时函数是通过 us 延时函数实现,不会发生调度;ms 延时函数里面延时如果超过 OS 的最少时间周期,使用的是系统的延时函数实现,会引发调度;延时低于 OS 的最少时间周期,仍是使用 us 延时函数实现,不引起调度。delay_ms 延时程序如下。

void delay_ms(u16 nms)
{
if(delay_osrunning&&delay_osintnesting==0) //如果 OS 已经在跑了,并且不是在中
断里面(中断里面不能任务调度) 
{
if(nms>=fac_ms) //延时的时间大于 OS 的最少时间周期
{ 
 delay_ostimedly(nms/fac_ms); //OS 延时
}
nms%=fac_ms; //OS 已经无法提供这么小的延时了,采用普通方式延时
}
delay_us((u32)(nms*1000)); //普通方式延时
} 
(3)、其他有关时间的函数

a、OSTimeDlyResume()函数

一个任务可以通过调用 OSTimeDlyResume()函数来“解救”那些因为调用了 OSTimeDly()或者 OSTimeDlyHMSM()函数而进入等待态的任务。

b、OSTimeGet()和 OSTimeSet()函数

OSTimeGet()函数用来获取当前时钟节拍计数器的值。OSTimeSet()函数可以设置当前时钟节拍计数器的值,这个函数谨慎使用。

五、UCOSIII 软件定时器

UCOSIII 中提供了软件定时器,定时器的本质是计数器减到零时,可以触发某种动作的执行,这个动作通过回调函数来实现。

1、定时器工作模式

当定时器计时完成时就会自动调用回调函数。如果我们要使用定时器需要将宏OS_CFG_TMR_DEL_EN 定义为 1 。 UCOSIII 中 定 时 器 的 时 间 分 辨 率 由 一 个 宏OS_CFG_TMR_TASK_RATE_HZ 定义,默认为 100Hz。如我们定义为 200Hz,系统时钟周期为 5ms。

2、定时器相关 API 函数

(1)创建定时器函数 OSTmrCreate(),函数原型如下。
void OSTmrCreate (OS_TMR *p_tmr,
 CPU_CHAR *p_name,
 OS_TICK dly,
 OS_TICK period,
 OS_OPT opt,
 OS_TMR_CALLBACK_PTR p_callback,
void *p_callback_arg,
 OS_ERR *p_err)
/*p_tmr: 指向定时器的指针,宏 OS_TMR 是一个结构体。
p_name: 定时器名称。
dly: 初始化定时器的延迟值。
period: 重复周期。
opt: 定时器运行选项,这里有两个模式可以选择。
OS_OPT_TMR_ONE_SHOT 单次定时器
OS_OPT_TMR_PERIODIC 周期定时器
p_callback: 指向回调函数的名字。
p_callback_arg: 回调函数的参数。
p_err: 调用此函数以后返回的错误码。*/

可见定时器有单次定时器和周期定时器两种模式。单次定时器在调用 OSTmrStart()函数后开始倒计数,将 dly 减为 0 后调用回调函数,单次定时器只执行一次就停止运行。我们可以调用 OSTmrStop()函数来删除这个运行完成的定时器。其实我们也可以重新调用OSTmrStart()函数来重新触发单次定时器。

周期定时器在调用 OSTmrStart()函数后开始倒计数,将 dly 减为 0 后调用回调函数,并重置计数器重新开始计时,一直循环下去。若OSTmrCreate()创建周期定时器时的参数 dly为 0,则无初始延迟,那么定时器每个周期为 period。若参数 dly 不为 0,则有初始延迟,第一个周期就是 dly,之后的周期才为 period。
在这里插入图片描述
在这里插入图片描述

(2)、其他定时器函数
函数描述
OSTmrCreate()创建定时器。
OSTmrStart()函数启动定时器计数,无论定时器计时完成或正在运行,调用该函数都会重新触发定时器。
OSTmrStop()停止计数器倒计时。
OSTmrDel()删除定时器。
OSTmrStateGet()获取当前定时器状态。
OSTmrRemainGet()获取定时器的剩余时间。

六、UCOSIII 信号量和互斥信号量

UCOSIII 中可能会有多个任务访问共享资源,同时对这个共享资源操作时会出错,因此信号量最早是用来控制任务存取共享资源。现在信号量被用来实现任务之间的同步和 ISR 之
间的同步。

在可剥夺的内核中,当任务独占式使用共享资源时就会出现低优先级任务先于高优先任务运行的现象,这个现象称之为优先级反转现象。为了解决这个问题,引出了互斥信号量的概念。

1、信号量

信号量像是一种上锁机制,一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放,代码获得对应的钥匙继续执行。一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。
信号量分为两种:二进制信号量与计数型信号量。

在共享资源中只有任何可以使用信号量,中断服务程序则不能使用。

(1)二进制信号量

二进制信号量只能取 0 和 1 两个值。某一资源对应的信号量为 1 的时候,那么就可以使用这一资源,如果对应资源的信号量为 0,那么等待该信号量的任务就会被放进等待信号量的任务表中。同时等待信号量的任务也可设置为超时,如果超过设定的时间任务没有等到信号量的话那么该任务就会进入就绪态。

(2)计数型信号量

计数型信号量可以取不止 2 个值。我们可以同时设置多个信号量,允许有多个任务访问共享资源。比如某一个信号量初始化值为 10,那么只有前 10 个请求该信号量的任务可以使用共享资源,以后的任务需要等待前 10 个任务释放掉信号量。每当有任务请求信号量的时候,信号量的值就会减 1,直到减为 0。当有任务释放掉信号量的时候,信号量的值就会加1。

函数描述
OSSemCreate()创建一个信号量
OSSemDel()删除一个信号量
OSSemPend()等待一个信号量
OSSemPendAbort()取消等待
OSSemPost()释放或发送一个信号量
OSSemSet()强制设置一个信号量的值

2、互斥信号量

为了避免优先级反转现象,UCOSIII 支持一种特殊的二进制信号量,叫互斥信号量。

二值信号量主要用于进行共享资源的独占式访问,但是二值信号量容易产生优先级反转。
互斥信号量一般用于解决优先级反转。

原理:若某当前任务 3 拥有互斥信号量,系统会将该任务的优先级暂时提高到与最高优先级 1 任务相同的优先级,从而避免优先级反转发生。等待任务 3 完成释放互斥信号量后,又将任务 3 的优先级恢复。

注意!只有任务才能使用互斥信号量,中断服务程序则不可以。UCOSIII 允许用户嵌套使 用互斥型信号量,一旦一个任务获得了一个互斥型信号量,则该任务最多可以对该互斥型信号 量嵌套使用 250 次,当然该任务只有释放相同的次数才能真正释放这个互斥型信号量。

函数描述
OSMutexCreate()创建一个互斥信号量
OSMutexDel()删除一个互斥型信号量
OSMutexPend()等待一个互斥型信号量
OSMutexPendAbort()取消等待
OSMutexPost()释放一个互斥型信号量

3、任务内嵌信号量

UCOSIII 中每个任务都有自己的 内嵌的信号量,这种功能不仅能够简化代码,而且比
使用独立的信号量更有效。

函数描述
OSTaskSemPend()等待任务信号量
OSTaskSemPendAbort()取消等待任务信号量
OSTaskSemPost()发布任务信号量
OSTaskSemSet()强行设置任务信号量计数

七、UCOSIII 消息传递

有时候一个任务要和另外一个或者几个任务进行消息的传递,也称之为任务间通信,在UCOSIII 中消息可以通过消息队列作为中介发布给任务,也可以直接发布给任务。

1、消息队列

消息一般包含:指向数据的指针、表明数据长度的变量和记录消息发布时刻的时间戳。指针指向的可以是一块数据区或者甚至是一个函数。消息的内容必须一直保持可见性,因为发布的数据本身不产生数据拷贝,发布数据采用的引用传递是指针传递而不是值传递。

在 UCOSII 中有消息邮箱和消息队列,但是在 UCOSIII 中只有消息队列。消息队列是由用 户创建的内核对象,数量不限制。

在 UCOSIII 中对于消息队列的读取可以采用先进先出(FIFO)和后进先出(LIFO)的方式。

2、消息队列相关函数

函数描述
OSQCreate()创建一个消息队列
OSQDel()删除一个消息队列
OSQFlush()清空一个消息队列
OSQPend()等待消息队列
OSQPendAbort()取消等待消息队列
OSQPost()向消息队列发送一条消息
常用的消息队列函数有三个,创建消息队列函数 OSQCreate(),向消息队 列发送消息函数 OSQPost()和等待消息队列函数OSQPend()。

3、任务内嵌消息队列

和任务信号量一样,UCOSIII 中每个任务也都有其内建消息队列,这样的话用户就不需要使用外部的消息队列就可直接向任务发布消息,这个特性不仅简化了代码,而且比使用外部消息队列更加有效,如果要使用任务内建消息队列的话宏 OS_CFG_TASK_Q_EN 必须置1。

4、任务内嵌消息队列相关函数

函数名描述
OSTaskQPend()等待消息
OSTaskQPendAbort()取消等待消息
OSTaskQPost()向任务发送一条消息
OSTaskQFlush()清空任务的消息队列

八、事件标志组

除了使用任务信号量来完成任务同步,还有另一种任务同步的方法就是事件标志组,事件标志组用来解决一个任务和多个事件之间的同步。

1、事件标志组

有时候一个任务可能需要和多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制:“或”同步和“与”同步,当任何一个事件发生,任务都被同步的同步机制是“或”同步;需要所有的事件都发生任务才会被同步的同步机制是“与”同步。

在 UCOSIII 中事件标志组是OS_FLAG_GRP,事件标志组中也包含了一串任务,这些任务都在等待着事件标志组中的部分(或全部)事件标志被置 1 或被清零,在使用之前,必须创建事件标志组。

任务和 ISR(中断服务程序)都可以发布事件标志,但是,只有任务可以创建、删除事件标志组以及取消其他任务对事件标志组的等待。

任务可以通过调用函数 OSFlagPend()等待事件标志组中的任意个事件标志,调用函数OSFlagPend()的时候可以设置一个超时时间,如果过了超时时间请求的事件还没有被发布,那么任务就会重新进入就绪态。

我们可以设置同步机制为“或”同步还是“与”同步。

2、事件标志组相关函数

函数描述
OSFlagCreate()创建事件标志组
OSFlagDel()删除事件标志组
OSFlagPend()等待事件标志组
OSFlagPendAbort()取消等待事件标志组
OSFlagPendGetFlagsRdy()获取使任务就绪的事件标志
OSFlagPost()向事件标志组发布标志

九、同时等待多个内核对象

前面讲解的信号量、互斥信号量、消息队列和时间标志组都是任务如何等待单个对象,本部分将介绍任务如何等待多个内核对象。UCOSIII 中只支持同时等待多个信号量和消息队列,不支持持同时等待多个事件标志组和互斥信号量。

1、同时等待多个内核对象

UCOSIII 中一个任务可以同时等待任意数量的信号量或者消息队列,当只要等到其中的任意一个的时候就会导致该任务进入就绪态。

函数 OSPendMulti()用来等待多个内核对象,调用 OSPendMulti()时,如果这些对象中有多个可用,则所有可用的信号量和消息都将返回给调用者,如果没有任何对象可用,则OSPendMulti()将挂起当前任务,直到以下任一情况发生:

(1)、对象变为可用。
(2)、到达设定的超时时间。
(3)、一个或多个任务被删除或被终止。
(4)、一个或多个对象被删除。

如果一个对象变为可用,并且有多个任务在等待这个对象,则 UCOSIII 将恢复优先级最高的那个任务。

十、存储管理

作为一个操作系统,内存管理是其必备的功能,在 UCOSIII 中也有内存管理模块,使用内存管理模块可以动态的分配和释放内存,这样可以高效的使用“昂贵”的内存资源。

1、内存管理简介

内存管理是一个操作系统必备的系统模块,我们在用 VC++或者 Visual Studio 学习 C 语言的时候会使用 malloc()和 free()这两个函数来申请和释放内存。我们在使用 Keil MDK 编写 STM32 程序的时候就可以使用 malloc()和 free(),但是不建议这么用,这样的操作将原来大块内存逐渐的分割成很多个小块内存,产生大量的内存碎片,最终导致应用不能申
请到大小合适的连续内存。

UCOSIII 提供了自己的动态内存方案,UCOIII 将存储空间分成区和块,一个存储区有数个固定大小的库组成。
在这里插入图片描述
一般存储区是固定的,在程序中可以用数组来表示一个存储区,比如 u8 buffer[20][10]就表示一个有 20 个存储块,每个存储块 10 字节的存储区。如果我们定义的存储区在程序运行期间都不会被删除掉,一直有效,那么存储区内存也可以使用 malloc()来分配。在创建存储区以后应用程序就可以获得固定大小的存储块。

在实际使用中我们可以根据应用程序对内存需求的不同建立多个存储区,每个存储区中有不同大小、不同数量的存储块,应用程序可以根据所需内存不同从不同的存储区中申请内存使用,使用完以后在释放到相应的存储区中。

2、存储区创建

在使用内存管理之前首先要创建存储区,创建存储区之前我们需要了解一个重要的结构体,存储区控制块:OS_MEM。创建存储区使用函数 OSMemCreate()。
在这里插入图片描述

3、存储块的使用

调用函数 OSMemCreate()创建好存储区以后我们就可以使用创建好的存储块了。

(1)、内存申请

使用函数 OSMemGet()来获取存储块,从指定的存储区中获取存储块供给应用使用。

UCOSIII 自带的内存管理函数的局限性,每次申请内存的时候用户要先估计所申请的内存是否会超过存储区中存储块的大小。比如我们创建了一个有 10 个存储块,每个存储块大小为 100 字节的存储区 buffer。这时我们应用程序需要申请一个 10 字节的内存,那么就可以使用函数 OSMemGet()从存储区 buffer 中申请一个存储块。但是每个存储块有 100 个字节,但是应用程序只使用其中的 10 个字节,剩余的 90 个字节就浪费掉了,为了减少浪费我们可以创建一个每个存储块为 10 字节的存储区,这样就不会有内费了。但是,问题又来了,假设我们在程序的其他地方需要申请一个 150 字节的内存,但是存储区 buffer 的每个存储块只有 100 字节,显然存储区 buffer 不能满足程序的需求。有读者就会问可不可以在存储区中连续申请两个 100 字节的存储块,这样就有 200 字节的内存供应用程序使用了?想法是好想法,但是通过阅读函数 OSMemGet()发现并没有提供这样的功能,OSMemGet()函数在申请内存的时候每次只取指定存储区的一个存储块!如果想申请 150 字节的内存就必须再新建一个每个存储块至少有 150 字节的存储区。

可以看出 UCOSIII 的内存管理很粗糙,不灵活,并不能申请指定大小的内存块。使用过 ALIENTEK 的 STM32 开发板的用户就会知道, ALIENTEK 实现了内存的动态使用,可以申请任意大小的内存空间,使用起来十分方便。

(2)、内存释放

在 UCOSIII 中内存释放可以使用函数OSMemPut()来完成,将申请到的存储块还给指定的存储区。

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

UCOSIII 系统(STM32任务管理)学习笔记 的相关文章

  • 向Github上传文件或项目

    准备工作 xff1a 具有 Github 账号 xff0c 电脑已安装 Git Bash 一 在 Github 上创建新的仓库 1 登录到 Github 主页创建一个新的仓库 xff08 两种方法都可以 xff09 2 配置新建仓库的属性
  • Github分支创建、管理、下载与上传

    当我们想进行文件备份 文件分类 版本更新 分工合作等工作时 xff0c 对github仓库进行分支就变得非常重要 一 创建分支 在Github仓库创建时会生成默认的主分支 xff0c 一般名称为 main xff0c 我们可以以主分支为基础
  • 删除Github仓库中的指定的文件或文件夹

    在Github 仓库中是无法通过鼠标操作直接删除文件和文件夹的 xff0c 那只能通过 git 命令来执行删除操作 xff0c 当我们想删除 First 分支中的 folder 文件夹及其内部的文件 和 file c 文件 时 一 获取当前
  • 如何判断栈、堆的增长方向?

    如何判断栈的增长方向 xff1f 对于一个用惯了i386系列机器的人来说 xff0c 这似乎是一个无聊的问题 xff0c 因为栈就是从高地址向低地址增长 不过 xff0c 显然这不是这个问题的目的 xff0c 既然把这个问题拿出来 xff0
  • 向文件最后一行添加字符串

    当我们想向某个文件中添加指定的字符串时 xff0c 可以使用相关的指令 一 echo指令 使用echo追加重定向可以直接向文件中添加一行字符串 echo 34 add string 34 gt gt test txt 可以看到echo会在文
  • KernelShark分析内核任务执行过程

    一 KernelShark简介 KernelShark是一个非常实用的工具 xff0c 其可以搭配 trace cmd 使用 xff0c 将内核的任务执行过程以直观的形式展现出来 下面的文档中详细的介绍了KernelShark的使用方法和功
  • 关于Linux中断的相关查询

    1 linux 内核 proc interrupts 在 proc interrupts 文件中记录了 Linux 内核的中断信息 xff0c 我们可以通过命令查看 sudo cat proc interrupts 文件中以表格的形式列举出
  • Latex中编译IEEE sensors journal 模板中遇到的种种问题

    总的来说 xff0c 这个期刊的模板跟之前那个TIE的模板不太一样 xff0c 因为目前只接触了这两个 xff08 本人水平比较菜 xff09 1 编译左上角的Logo xff0c 一直在报错 xff0c 并没有显示成功 xff0c 一直显
  • python笔记:argparse模块

    用途 python用于解析命令行参数和选项的标准模块 xff0c 内置于python xff0c 不需要安装 使用步骤 引入模块 span class token keyword import span argparse 创建一个解析器 使
  • Hadoop伪分布部署

    Hadoop伪分布部署 一 任务描述二 任务目标三 任务环境四 任务分析五 任务实施步骤1 解压Hadoop压缩包步骤2 配置Hadoop文件 六 任务测试 原创申明 xff1a 未经许可 xff0c 禁止以任何形式转载 xff0c 若要引
  • ubuntu下共存多个python版本,切换python版本

    ubuntu下切换默认python版本 知乎 zhihu com 1 以 root 身份登录 xff0c 首先罗列出所有可用的python 替代版本信息 update alternatives list python 这一步可能会报错upd
  • Deformable DETR进行目标检测,解决size mismatch问题

    问题描述 xff1a strict 61 False 但还是size mismatch for copying a param with shape from checkpoint the shape in cur 接着 6条消息 Defo
  • CubeMX 配置PWM使用DMA,生成Dshot600的协议

    看到电调支持Dshot125 600的协议 xff0c 想自己做一个支持Dshot协议的驱动 xff0c 所以研究了一下 xff0c 如何利用精确的PWM产生Dshot协议 先看结果 xff01 图中为油门值为1500的时候的输出的Dsho
  • Linux驱动开发——串口设备驱动

    Linux驱动开发 串口设备驱动 一 串口简介 串口全称叫做串行接口 xff0c 通常也叫做 COM 接口 xff0c 串行接口指的是数据一个一个的顺序传输 xff0c 通信线路简单 使用两条线即可实现双向通信 xff0c 一条用于发送 x
  • 1.开始在leetCode中刷题的一些感受

    最近项目上没有什么事情 xff0c 鉴于自己的基础比较差 xff0c 正好最近也开始学习了Python xff0c 就想在leetCode中使用Python语言刷刷题 xff0c 加强一下自己的算法的能力 下面就来谈谈最近刷题的一些感受 x
  • mathtype公式经常闪现,然后公式删除变成了字母c

    解决方案 百度 关闭金山词霸中的 划译 功能就OK啦 xff0c 具体情况具体分析 xff0c 大家可以试一下这种方法 个人经历 上一次是 Ctrl 43 C Ctrl 43 V 无法正常使用 xff0c 最后关闭有道的划译功能就完美解决了
  • RT-Thread学习入门之RT-Thread Studio的使用

    本科生因做比赛需要 xff0c 使用RT Thread Studio进行stm32f407ZGT6的程序开发 xff0c 文章仅以用来记录过程 系列文章目录 第一篇 RT Thread学习入门之RT Thread Studio的使用 第二篇
  • 【CSDN三周年纪念日】我的创作纪念日

    2019年09 月 17 日 xff0c 一个看似平凡的一天 在这么平凡的一天里 xff0c 我发表了第一篇博客 在这平凡的一天 xff0c 赋予了它不平凡的意义 也许是立志成为一名专业 IT 作者 也许是记录一段刚实践的经验 但在那一刻
  • 自定义串口通讯协议

    通信协议 1 读操作 主机发送设备地址0x0A 读命令字 数据长度 xff08 数据长度不包括CRC xff09 xff0c 当主机接收完数据和CRC后 xff0c 需要进行数据校验 xff0c 并和从机返回的CRC进行对比 数据校验方式为
  • MySql批量插入指定位数的随机数

    需求 xff1a 向mysql中批量插入指定位数的随机数 sql脚本 xff1a 第一步 xff1a 创建一个函数用于生成指定位数的随机数 span class token comment 创建一个函数用于生成随机字符串 span span

随机推荐

  • 路由器不开机——维修更换MT7621AT CPU

    故障类别 xff1a 不开机 故障现象 xff1a 210mA横流不开机 故障描述 xff1a 发现CPU异常发烫不开机 xff0c 其它地方未有发热现象 附件 xff1a 原因分析 xff1a 开机测量各路电压 xff0c 发现均有电压
  • 路由器5G WiFi不工作维修分析

    故障类别 xff1a WiFi异常 故障现象 xff1a WiFi指示灯不亮 故障描述 xff1a 开机正常 xff0c 但是5G WiFi不工作指示灯不亮 xff0c 2 4G工作正常 xff0c 其他工作正常 附件 xff1a 原因分析
  • Ubuntu18.04安装ROS Melodic(详细,亲测安装完成,有清晰的截图步骤)

    这也是我在Ubuntu里面安装ROS的第N次 xff0c 以前每次安装过程都忘记总结了 xff0c 导致每次安装ROS都浪费了很多的时间用来解决各种问题 为了避免自己以后出现问题需要再安装 xff0c 所以写这篇博客总结一下 xff0c 这
  • 04Git从入门到入土之码云的使用及代码迁移

    1 国内代码托管中心 码云 众所周知 xff0c GitHub 服务器在国外 xff0c 使用 GitHub 作为项目托管网站 xff0c 如果网速不好的话 xff0c 严重影响使用体验 xff0c 甚至会出现登录不上的情况 针对这个情况
  • 操作系统部分习题

    操作系统部分题目 第一章 操作系统引论第二章 进程的描述与控制第三章 处理机调度与死锁第四章 存储器管理第五章 虚拟存储器第六章 输入输出系统第七章 文件管理第八章 磁盘处理器的管理 习题书籍 xff1a 计算机操作系统 xff08 第四版
  • 基于自适应反步法的三自由度直升机(3 DOF Helicopter)轨迹跟踪

    文章目录 前言一 3 DOF Helicopter实验装置二 3 DOF Helicopter模型建立及简化2 1模型建立2 1 1 俯仰轴建模2 1 2 横侧轴建模2 1 3 旋转轴建模 2 2 模型简化 三 控制器设计四 实验验证4 1
  • rabbitmq安装(rpm方式)

    rabbitmq 1 准备资料2 安装3 常用操作命令 1 准备资料 1 erlang 23 0 2 1 el7 x86 64 2 rabbitmq server 3 8 4 1 el7 noarch 3 centos7 RabbitMQ的
  • Docker入门教程

    目录 一 Docker介绍 为什么需要沙箱机制 xff1f 什么是沙箱机制 xff1f 二 Docker的优点 三 安装Docker 四 Docker的组成 问题 xff1a 是否不理解容器与镜像 xff1f 五 启动Docker 六 安装
  • ubuntu下录屏软件kazam及使用问题

    Ubuntu下视频录制工具kazam及问题 视频录制工具 在Ubuntu或linux下录制视频应该是每个用linux的朋友都会用到的 xff0c 这里笔者使用了Kazam 功能挺多的 xff0c 包括截屏 xff08 虽然ubuntu有自带
  • NUC安装Ubuntu18.04系统

    NUC安装Ubuntu18 04系统 基本信息前言安装过程配置Ubuntu18 04系统镜像安装系统解决系统字体过大 分辨率低的问题测试 其他软件安装 基本信息 Time xff1a 2021 3 9使用NUC版本 xff1a 猎豹峡谷NU
  • 11代i5 NUC使用记录

    11代i5 NUC使用记录 NUC信息NUC选型配置过程内存条固态安装安装windows安装Ubuntu18 04使用ROS2GO 其他学习总结 NUC信息 NUC基本信息 xff1a NUC xff1a Next Unit of Comp
  • ROS rosdep initupdate报错解决方法

    ROS rosdep init update报错解决方法 在安装ROS的过程中 xff0c 很多同学在执行上述指令时会提示以下错误 xff1a ERROR cannot download default sources list from
  • Ubuntu18.04下使用D435i跑ROS包和ORB-SLAM2

    Ubuntu18 04下使用D435i跑ROS包和ORB SLAM2相关问题 前言主要问题及解决方法汇总一 realsense包版本问题二 无法定位安装包问题三 警告问题四 如何检测是否发布五 利用D435i跑ORB SLAM2一点注意 后
  • 小觅双目sdk地址

    https mynt eye d sdk readthedocs io zh CN latest sdk install ubuntu src html
  • FreeRTOS移植STM32F4

    64 TOC FreeRTOS FreeRTOS移植到STM32F4上 本文章讲解一下如何将FreeRTOS移植到STM32F4开发板上 xff0c 如有不对之处 xff0c 欢迎指正 xff0c 多多交流 一 创建工程文件及代码调试 我们
  • stm32使用串口进行通讯之发送数据

    前提准备 xff1a 1 库函数基础模板 2 stlink下载器 USB TTL下载器 单片机最小开发板stm32F103C8T6 3 面包板及相关接线 4 vscode与keil的联合开发更流畅 5 串口软件 xff0c 这个下面视频有
  • 什么是事件流(总结),ECMAScript6(总结)

    1 xff0c 事件流 xff08 1 xff09 事件流是指页面 接收事件的顺序 xff08 2 xff09 假设页面中的元素都具备相同的事件 并且这些个元素之间是相互嵌套的 关系 xff08 3 xff09 那么在触发一个元素的事件时候
  • rabbitmq消息可靠性分析

    通过firehose插件跟踪消息过程 开发测试阶段用于分析消息消费的过程 xff0c 生产关闭 xff0c 以免影响整体性能 rabbitmq tracing 插件相当于Firehose 的GUI 版本 xff0c 能跟踪RabbitMQ
  • vscode拉取git代码到本地提示 在签出前,请清理存储库工作树

    最近使用vscode拉取git代码到本地提示 在签出前 请清理存储库工作树 xff0c 具体如图 xff1a 一 原因分析 原因很简单 xff0c 就是因为本地代码和git远程库代码存在冲突 xff0c 也就是你改的文件 xff0c 别人也
  • UCOSIII 系统(STM32任务管理)学习笔记

    作者 xff1a 瓴 时间 xff1a 2021 07 28 UCOSIII 系统 xff08 STM32任务管理 xff09 学习笔记 UCOSIII 系统学习笔记一 UCOSIII 任务管理1 任务状态2 任务控制块 OS TCB3 任