RTOS原理与实现02:基本任务切换实现

2023-05-16

目录

1. 任务定义与切换原理

1.1 任务是什么

1.1.1 任务的外观

1.1.2 任务的内在

1.2 任务切换原理

1.2.1 任务切换的本质

1.2.2 要保存哪些任务运行状态

1.2.3 任务运行状态保存方案

1.3 设计实现

1.3.1 类型定义

1.3.2 任务定义与初始化

2. 任务切换的实现

2.1 设计目标

2.2 任务切换原理

2.2.1 如何启动初始任务

2.2.2 如何实现任务切换

2.3 设计实现

2.3.1 设置任务初始栈

2.3.2 启动初始任务

2.3.3 申请任务调度

2.3.4 任务切换

3. 双任务时间片运行原理

3.1 设计目标

3.2 时间片切换原理

3.2.1 时间片实现

3.2.2 SytTick定时器简介

3.3 设计实现

3.3.1 SysTick设置

3.3.2 SysTick_Handler

3.3.3 任务函数

4. 双任务延时原理与空闲任务

4.1 设计目标

4.2 任务延时原理

4.2.1 任务延时

4.2.2 软件计时器

4.2.3 延时精度问题

4.2.4 空闲任务

4.3 设计实现

4.3.1 增设软件计时器

4.3.2 tTaskDelay函数

4.3.3 空闲任务的实现

4.3.4 SysTick_Handler

4.3.5 任务调度函数


1. 任务定义与切换原理

1.1 任务是什么

1.1.1 任务的外观

任务的外观:一个永不返回的函数

说明:使用void *类型形参,确保可以传入任意类型的参数

1.1.2 任务的内在

任务的内在:一个函数的执行

① 代码段和数据区由编译器在编译代码时自动分配与控制

② 堆的分配和使用由程序员控制

③ C代码中一般不会显式使用栈,将由编译器完成;在汇编代码中,程序员可以设置栈的位置并使用

④ C代码也不会显示使用寄存器,也是由编译器完成

个人:代码区 + 数据区 + 栈 + 堆可以理解为任务的实体 + 运行环境

1.2 任务切换原理

1.2.1 任务切换的本质

任务切换的本质:保存前一任务(prev)的当前运行状态,恢复后一任务(next)之前的运行状态,并切换到该任务运行

1.2.2 要保存哪些任务运行状态

① 代码区 & 数据区:由编译器自动分配,各个任务相互独立,并不冲突

② 堆:由程序员定义并使用,对堆操作的句柄保存在栈中

③ 栈:内核硬件只支持两个栈空间(MSP & PSP),而不同任务的栈不能共用,所以需要给每个任务设置自己的栈空间

④ 寄存器:编译器会在某些时刻将寄存器保存到栈中(e.g. 函数调用,异常处理),如果其他寄存器也会被破坏,则需要程序员自己保存

⑤ 其他需要保存的状态数据

1.2.3 任务运行状态保存方案

解决方案:为每个任务分配独立的栈,用于保存该任务的所有状态数据

在进行任务切换时,

① 将前一(prev)任务的当前状态保存在该任务的栈中

② 找到下一(next)任务的栈,然后从该栈中恢复任务状态

1.3 设计实现

1.3.1 类型定义

① 栈类型

Cortex-M中对栈操作的单位为4B,所以使用uint32_t类型

typedef uint32_t tTaskStack;

② 任务类型

typedef struct _tTask {
    // 任务栈指针
    tTaskStack *stack;
} tTask;

说明:每个任务的栈地址,均保存在任务结构体中,即TCB中

1.3.2 任务定义与初始化

tTask tTask1;
tTask tTask2;

// 任务栈
tTaskStack task1Env[1024];
tTaskStack task2Env[1024];

// 任务函数
void task1(void *param)
{
    for (;;) {
        
    }
}

void task2(void *param)
{
    for (;;) {
        
    }
}

// 任务初始化函数
// 当前仅初始化任务栈指针
void tTaskInit(tTask *task, void (*entry)(void *), void *param,
               tTaskStack *stack)
{
	task->stack = stack;
}

// 调用任务初始化函数
// 由于默认使用满减栈,所以调用时传递任务栈高地址,即栈顶 
tTaskInit(&tTask1, task1, (void *)0x11111111, task1Env + 1024);
tTaskInit(&tTask2, task2, (void *)0x22222222, task2Env + 1024);

2. 任务切换的实现

2.1 设计目标

说明:在本节实现中,任务切换由任务主动调用tTaskSched函数实现

2.2 任务切换原理

2.2.1 如何启动初始任务

① 上电启动后,系统进入特权级线程模式(线程模式 + MSP),而任务一般运行在用户级线程模式(线程模式 + PSP)

② 设置任务初始栈

③ 触发任务切换,使用任务初始栈内容填充任务运行状态

说明1:由于启动初始任务的步骤是先设置任务初始栈,然后触发任务调度,用初始栈的内容设置当前运行环境。所以任务调度函数中需要识别出这种场景

说明2:在设置任务初始栈时,会将任务函数入口地址(task entry)填入PC寄存器对应的位置,这样在执行任务切换后,CPU即可运行任务函数

2.2.2 如何实现任务切换

① 将当前任务运行状态保存到当前任务栈中

说明:此处的任务运行状态保存分为2部分,

a. 硬件自动保存部分(进入pendSV异常时硬件自动保存),硬件保存的数据也是保存在系统当前使用的栈中,也就是当前任务的栈中

b. 程序员自行保存部分

② 找到下一任务,并用下一任务的任务栈恢复该任务运行环境

说明:与任务状态的保存类似,任务状态的恢复也分为,

a. 硬件自动恢复部分

b. 程序员自行恢复部分

在任务切换函数中,只需要完成程序员自行恢复部分即可

2.3 设计实现

2.3.1 设置任务初始栈

void tTaskInit(tTask *task, void (*entry)(void *), void *param, 
                tTaskStack *stack)
{
	// 硬件自动保存状态
	// 在设置初始栈状态时,必须与硬件操作顺序一致
	*(--stack) = (unsigned int)(1 << 24);  // xPSR,进入thumb状态
	*(--stack) = (unsigned int)entry;      // PC,任务函数入口
	*(--stack) = (unsigned int)0x14;       // R14(LR),任务不会通过return结束
	*(--stack) = (unsigned int)0x12;       // R12
	*(--stack) = (unsigned int)0x3;        // R3
	*(--stack) = (unsigned int)0x2;        // R2
	*(--stack) = (unsigned int)0x1;        // R1
	*(--stack) = (unsigned int)param;      // R0 = param,根据ATPCS规则传参
	
	// 程序员自行保存状态
	*(--stack) = (unsigned int)0x11;       // R11
	*(--stack) = (unsigned int)0x10;       // R10
	*(--stack) = (unsigned int)0x9;        // R9
	*(--stack) = (unsigned int)0x8;        // R8
	*(--stack) = (unsigned int)0x7;        // R7
	*(--stack) = (unsigned int)0x6;        // R6
	*(--stack) = (unsigned int)0x5;        // R5
	*(--stack) = (unsigned int)0x4;        // R4
	
	// 任务栈指针
	// 切换任务时的R13(SP)从该字段获取
	task->stack = stack;
}

说明1:任务初始化栈,是任务首次运行时的环境。由于是任务首次运行,软硬件均为设置过该任务的栈,所以此时同时初始化了由硬件保存的部分和由程序员保存的部分

说明2:任务初始化栈设置注意事项

① 硬件自动保存状态部分必须与硬件操作顺序一致

② 程序员自行保存状态部分要遵循高编号寄存器对应高地址的规则(STRM & LDRM指令要求的规则)

说明:其实硬件自动保存状态部分也遵循了该规则

2.3.2 启动初始任务

void tTaskRunFirst(void)
{
    // 将PSP寄存器设置为0,标识系统中第1个任务运行
    __set_PSP(0);
    
    // 设置pendSV为最低优先级
    MEM8(NVIC_SYSPRI2) = NVIC_PENDSV_PRI;
    
    // 设置挂起pendSV位
    MEM32(NVIC_INT_CTRL) = NVIC_PENDSVSET;
}

说明:触发pendSV异常后,系统进入pendSV ISR运行,由于此处将PSP寄存器置为0,在进行任务切换时据此可识别出此时为启动初始任务,无需保存前一任务的运行状态(因为此时还木有这个"前一任务")

2.3.3 申请任务调度

void tTaskSched(void)
{
    if (currentTask == taskTable[0]) {
        nextTask = taskTable[1];
    } else {
        nextTask = taskTable[0];
    }
    
    // 设置挂起pendSV位
    MEM32(NVIC_INT_CTRL) = NVIC_PENDSVSET;
}

说明:此处调度实现非常简单,就是在2个任务之间相互切换

2.3.4 任务切换

__asm void PendSV_Handler(void)
{
    // 在汇编中引入C变量
	IMPORT currentTask;
	IMPORT nextTask;
	
	// 根据PSP判断是否是启动初始任务
    // 如果是启动初始任务,则无需保存前一任务状态
    // 关于MRS R0, PSP说明2点:
    // 1. 使用MRS获取PSP是因为在Handler模式下只能使用MSP
    // 2. 可以覆盖R0是因为进入ISR时硬件自动保存了R0 ~ R3
    MRS R0, PSP
	CBZ R0, PendSVHandler_nosave
	
	// 保存前一任务状态(即当前任务)
   // 此处仅保存程序员自主保存部分,硬件自动保存部分此时已经入栈(PSP)
	STMDB R0!, {R4-R11}
	
	// 更新前一任务栈指针
	LDR R1, =currentTask
	LDR R1, [R1]
	STR R0, [R1]
	
PendSVHandler_nosave
	// 将nextTask设置为currentTask
	LDR R0, =currentTask
	LDR R1, =nextTask
	LDR R2, [R1]
	STR R2, [R0]
	
	// 恢复后一任务状态
   // 此处仅恢复程序员自主恢复部分,硬件自动恢复部分将由BX LR指令启动
	LDR R0, [R2]
	LDMIA R0!, {R4-R11}
	
	// 设置PSP为后一任务栈指针
	MSR PSP, R0
	
	// 异常返回用户级线程模式
	ORR LR, LR, #0x04
	
	BX LR
}

说明1:任务切换流程图如下,

说明2:修改LR的目的

在进入异常处理流程后,LR会被修改为EXC_RETURN,在硬件自动处理的过程中,异常结束后会返回触发异常前的状态(e.g. 从特权级线程模式进入异常后,将返回特权级线程模式;从用户级线程模式进入异常后,将返回用户级线程模式)

在启动初始任务时,系统处于特权级线程模式,但是结束ISR进入任务时,我们希望系统处于用户级线程模式,所以在此处修改了LR,将bit 2置为1,即进入用户级

说明3:切换任务栈是通过用任务栈的地址设置PSP寄存器实现的,即

MSR PSP, R0

说明4:启动初始任务后,首次进入PendSV_Handler时,硬件保存的寄存器值是保存在MSP指向的栈中,因为Cortex-M3启动后默认进入thread + MSP模式

3. 双任务时间片运行原理

3.1 设计目标

说明:在上节实现中,任务切换由任务主动调用tTaskSched函数实现;在本节实现中,将在SysTick ISR中调用tTaskSched函数进行任务切换,进而实现基于时间片的任务调度

3.2 时间片切换原理

3.2.1 时间片实现

核心:利用SysTick的周期性中断,在该中断的ISR中选择下一任务,并触发pendSV异常进行任务切换

3.2.2 SytTick定时器简介

SysTick定时器是Cortex-M3内核内置的24位递减定时器,当递减到0时,将从RELEAD寄存器中自动重载定时初值到CURRENT寄存器,如此反复

说明1:SysTick控制与状态寄存器

① bit [1:0]置位后,SysTick定时器将开始工作,并在计数值为0时触发SysTick异常,因此需要注意使能SysTick的时机,基于时间片的调度即以此处触发的SysTick异常为依据

② 示例中使用内核时钟作为SysTick计数所用时钟,同时需要修改工程中的system_ARMCM3.c,设置__XTAL & __SYSTEM_CLOCK的值

此处设置晶振频率为12MHz,且系统时钟与晶振频率相同

说明2:SysTick重装值寄存器

① RELOAD初始值可以是1到0x00FFFFFF之间的任何值

② 作为一个连拍式(multi-shot)定时器,SysTick每N + 1个时钟脉冲就触发一次SysTick异常(e.g. 如果希望每100个时钟脉冲触发一次异常,则需要将RELOAD值设置为99)

③ 如果每次SysTick异常后都写入一个新值,那么可以作为单拍式(single-shot)定时器,此时必须写入时机的倒计数值(e.g. 如果希望在100个时钟脉冲后触发一次异常,则需要将RELOAD值设置为100)

3.3 设计实现

3.3.1 SysTick设置

void tSetSysTickPeriod(uint32_t ms)
{
	SysTick->LOAD = ms * SystemCoreClock / 1000 - 1; // RELOAD值
	NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
	SysTick->VAL = 0;
	SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
	                SysTick_CTRL_TICKINT_Msk   |
	                SysTick_CTRL_ENABLE_Msk;
}

说明1:将SysTick优先级设置为最低

此处__NVIC_PRIO_BITS宏为使用多少个bit位标识中断优先级,因此 (1 << __NVIC_PRIO_BITS) - 1就是设置最低优先级(有效位均为1)

说明2:tSetSysTickPeriod函数调用时机

一旦调用tSetSysTickPeriod函数SysTick计时器即开始工作,因此我们在启动的初始任务中调用该函数,以避免初始任务尚未启动即进入SysTick_Handler,从而导致调度错误

个人:另一种处理方式则是先关闭SysTick的异常中断使能位,这样可以在初始化阶段就设置系统时钟,直到准备就绪时再使能该中断

3.3.2 SysTick_Handler

void SysTick_Handler(void)
{
    tTaskSched();
}

说明:使用SysTick_Handler这个函数名也是因为在Keil初始化环境中将SysTick的异常向量设置为SysTick_Handler

3.3.3 任务函数

int task1Flag = 0;
void task1Entry(void *param)
{
    // 启动SysTick计时器,周期为10ms
	tSetSysTickPeriod(10);
	for (;;) {
         task1Flag = 1;
         delay(100);
         task1Flag = 0;
         delay(100);
	}
}

int task2Flag = 0;
void task2Entry(void *param)
{
	for (;;) {
		task2Flag = 1;
		delay(100);
		task2Flag = 0;
		delay(100);
	}
}

说明:由于在SysTick_Handler中进行任务切换,所以在任务函数中无需再调用tTaskSched函数

4. 双任务延时原理与空闲任务

4.1 设计目标

说明:delay函数的延时通过在循环中递减计数值实现,属于忙等操作,在延时过程中持续占用CPU,因此降低了CPU使用率

本节设计的任务延时接口的要点就是在延时过程中释放CPU

4.2 任务延时原理

4.2.1 任务延时

目标:在任务延时过程中,暂停当前任务运行,释放CPU控制权给其他任务

步骤:

① 开始延时,启动计时器,暂停当前任务

② 延时结束,关闭计时器,恢复任务运行

说明:

① 启动任务计时器时,通过调用任务切换函数释放CPU

② 结束任务计时器时,也会调用任务切换函数,恢复之前暂停的任务(当然,此处还涉及多任务优先级的调度问题)

4.2.2 软件计时器

说明1:由于任务数量不限,而硬件计时器资源有限,所以任务计时器一般使用软件计时器实现

说明2:软件计时器是基于硬件计时器工作的,从本质上说,软件计时器是一个计数值,每当硬件SysTick触发时,在SysTick ISR中维护软件计时器计数值

说明3:由于软件计时器基于硬件计时器工作,所以软件定时时间必须是硬件定时器周期的倍数

相当于硬件计时器提供了最小的计时分辨率,如果需要更高精度的延时则只能使用硬件定时器

4.2.3 延时精度问题

说明:由于使用软件计时器,加之延时处理需要时间,所以延时精度有限,使用时需要注意场景。在上图中,

① 开始延时的位置(在一个时间片的中间开始延时)

② 中断抢占(更高优先级中断抢占SysTick中断)

③ 任务优先级调度(多个任务延时完成,调度优先级最高的任务运行)

均会影响软件计时精度(准确说是任务实际睡眠的时间)

4.2.4 空闲任务

4.2.4.1 引入背景

如果所有任务都进入延时(或者当前没有任务可运行),那么RTOS应该运行什么 ?(毕竟CPU不能闲着呀~)

4.2.4.2 引入空闲任务的好处

① 简化RTOS代码结构

引入空闲任务后,CPU总有任务可以运行

② 可在空闲任务中添加自定义功能

e.g. 在空闲任务中添加低功耗运行指令或者进行CPU统计任务

4.3 设计实现

4.3.1 增设软件计时器

typedef struct _tTask {
    // 任务栈指针
    tTaskStack *stack;
    
    // 任务延时计数器(SysTick个数)
    uint32_t delayTick;
} tTask;

说明:在任务初始化函数tTaskInit函数中需要对应地将delayTick字段初始化为0

4.3.2 tTaskDelay函数

void tTaskDelay(uint32_t delay)
{
    // 设置当前任务延时计数器值
    currentTask->delayTick = delay;
    
    // 触发任务切换
    tTaskSched();
}

说明1:tTaskDelay函数的参数为延时时间对应的SysTick个数

说明2:任务调用tTaskDelay函数设置延时后,需要释放CPU控制权,该动作通过调用tTaskSched函数实现

4.3.3 空闲任务的实现

tTask tTaskIdle;
tTaskStack idleTaskEnv[1024];
tTask *idleTask;

void idleTaskEntry(void *param)
{
    for (;;) {
        // 空闲任务
    }
}

// 在main函数中创建空闲任务
tTaskInit(&tTaskIdle, idleTaskEntry, (void *)0, idleTaskEnv + 1024);
idleTask = &tTaskIdle;

4.3.4 SysTick_Handler

void SysTick_Handler(void)
{
	int i = 0;
	
	for (i = 0; i < 2; ++i) {
		if (taskTable[i]->delayTick > 0)
			--taskTable[i]->delayTick;
	}
	
	tTaskSched();
}

说明:在SysTick ISR中,首先遍历所有任务的延时计数值,如果非零则递减,说明完成一个SysTick的延时;然后调用tTaskSched函数触发任务切换,这是原先基于时间片调度就需要完成的操作

4.3.5 任务调度函数

void tTaskSched(void)
{
	// 当前为空闲任务
	if (currentTask == idleTask) {
		// 如果有延时结束的任务则切换到该任务
		// 否则继续执行空闲任务
		if (taskTable[0]->delayTick == 0) {
			nextTask = taskTable[0];
		} else if (taskTable[1]->delayTick ==0) {
			nextTask = taskTable[1];
		} else {
			return; // 不切换
		}
	}
	// 当前不是空闲任务
	else {
		// 如果其他任务延时结束则切换到该任务
		// 否则判断当前任务是否需要进入延时状态,如果需要则切换到空闲任务
		// 否则不进行切换
		if (currentTask == taskTable[0]) {
			if (taskTable[1]->delayTick == 0) {
				nextTask = taskTable[1];
			} else if (currentTask->delayTick != 0) {
				nextTask = idleTask;
			} else {
				return; // 不切换
			}
		} else {
			if (taskTable[0]->delayTick == 0) {
				nextTask = taskTable[0];
			} else if (currentTask->delayTick != 0) {
				nextTask = idleTask;
			} else {
				return; // 不切换
			}
		}
	}
	
	// 设置挂起pendSV位
	MEM32(NVIC_INT_CTRL) = NVIC_PENDSVSET;
}

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

RTOS原理与实现02:基本任务切换实现 的相关文章

  • 基于STM32 HAL库的自定义USB HID设备通信

    最近有个项目需要用到STM32的高速通信 xff0c 准备调试一下STM32的USB功能 xff0c 但是发现F103C8T6只有全速USB xff0c 因此作为HID设备一帧只能发送64字节 xff0c 最小间隔是1ms xff0c 显然
  • PX4-Autopilot安装

    Ubuntu 18 04 43 ROS Melodic 1 PX4源码下载 1 先mkdir一个文件夹 xff0c 在该文件夹下进行源码下载 xff08 因为在github下载很慢 xff0c 所以后续到PX4 Autopilot目录下再单
  • MAVROS(1)offboard模式(手动和roslaunch启动)

    官方教程 xff1a https docs px4 io master en ros mavros offboard html 1 编写功能包 参考 xff1a https blog csdn net weixin 44917390 art
  • VINS-Fusion安装

    1 安装Ceres Solver 官方教程 xff1a http ceres solver org installation html 参考教程 xff1a https blog csdn net qq 27251141 article d
  • APM-MAVROS连接

    1 运行mavros roslaunch mavros apm launch fcu url 61 34 dev ttyUSB0 921600 34 2 读取topic之前先运行以下命令 xff0c 修改飞控广播频率 rosservice
  • PX4 Avoidance(3DVFH*)

    Github地址 xff1a https github com PX4 PX4 Avoidance readme PX4用户指南 xff1a computer vision obstacle avoidance PX4 Avoidance下
  • FreeRTOS源码分析与应用开发04:消息队列

    目录 1 队列结构 2 创建队列 2 1 动态创建队列 2 1 1 xQueueCreate函数 2 1 2 xQueueGenericCreate函数 2 1 3 xQueueGenericReset函数 2 2 静态创建队列 2 2 1
  • APM-SITL Gazebo MAVROS 仿真

    1 配置APM SITL环境 1 下载安装Ardupilot 参考链接 xff1a 官方教程 注意 xff1a 文件install prereqs ubuntu sh路径在 ardupilot Tools environment insta
  • Ego-planner-swarm安装及报错解决

    项目地址 xff1a https github com ZJU FAST Lab ego planner swarmhttps github com ZJU FAST Lab ego planner swarm https github c
  • 单片机和嵌入式系统的区别

    单片机和嵌入式系统的区别 嵌入式和单片机并不是一对相对的概念 xff0c 嵌入式系统包括硬件和软件部分 xff0c 而单片机是单片微型计算机 Single Chip Microcomputer 的简称 xff0c 即微控制单元 Microc
  • 程序员的5个级别,你属于哪一个等级?

    码农和程序员虽说是调侃 xff0c 但是实质上还真的是不一样 还别说 xff0c 程序员还是有分等级的 比如有技术专家 xff0c 初级专员等 程序员的级别不同 xff0c 薪水也是有着天壤之别 免费领取Python学习资料可以加小编的微信
  • FutureTask的使用示例

    今天看书 xff0c 有关于 FutureTask 的介绍 xff0c 感觉还蛮有意思的 xff0c 可以用它来做一些比较花时间的事情 下面打个通俗的比方来说明一下它的用处 xff1a 比如 xff0c 早上一大早的去公交站台等公交 xff
  • 用脚本创建快捷方式

    64 echo off set shortCutPath 61 C Documents and Settings administrator 桌面 set shortCutName 61 报表 set StartPath 61 起始位置 s
  • activemq配置wss协议

    wss是加密协议 xff0c 必须配置https证书 span class token operator lt span sslContext span class token operator gt span span class tok
  • MATLAB 初学者 课堂笔记

    1 预定义变量 xff1a pi i j inf xff1a 无穷大 eps a xff1a a 与大于 a 的最小的浮点数之间的距离 xff0c 距离越小表示精度越高 默认a 61 1 2 矩阵运算 xff1a 左除 C 61 A B 6
  • 备赛笔记:Opencv学习:颜色识别

    OpenCV颜色识别一般要以下步骤 xff1a 1 颜色空间转换 xff0c 将BGR转换为HSV xff0c 用色调区分颜色 2 按照阈值滤出所识别的颜色 3 消除噪点 xff0c 平滑边界 3 提取连续域 xff0c 提取要识别的颜色
  • 树莓派buster安装ROS完整记录

    我敢说这一教程全网找不到相同的 我今天在安装ROS时查了各方教程 xff1a 官方Wiki xff0c Google xff0c CSDN都各查了不下10篇 xff0c 终于综合各教程内容花了大半天下好了 使用硬件及软件 xff1a 树莓派
  • Linux操作系统原理与应用06:系统调用

    目录 1 Linux中的各种接口 1 1 LSB标准 1 2 Linux API 1 2 1 概述 1 2 2 Linux内核系统调用接口 1 2 3 C标准库 1 3 Linux ABI 1 4 内核API 1 5 系统调用与各种接口的关
  • 将csdn博客转换成makedown形式的文件并保存

    span class token comment 作者 Rain span span class token keyword import span re span class token keyword import span parse
  • VIBE:3D人体姿态预测项目复现笔记

    VIBE是一个的3D人体姿态预测开源项目 xff0c 需要基于该项目作一些开发 xff0c 首先需要能够搭建和是的环境成功复现它 不过 xff0c 这个项目的复现的 xff0c 真的不是一星半点的艰难 1 系统选择 之前一直用的Window

随机推荐

  • geometry_msgs设计的几个消息类型,定义的数据类型详解

    Point 点 float64 x xff0c float64 y xff0c float64 z Point32 float32 x xff0c float32 y xff0c float32 z 一般使用Point xff0c 大规模点
  • 分享个好用的开源录屏工具 Captura

    百度 或 点击 Captura 8 0 Download 进入官网下载 安装后是 如果折起来是这样 xff1a 红色框框那个是折叠按钮 红色圆形按钮是 开始录制 结束录制 和其他的录制按钮样式 xff0c 都差不多 xff0c 自行探索吧
  • 使用 aptitude解决ubuntu下apt-get install g++依赖问题

    问题描述 xff1a ubuntu下运行C 43 43 程序 xff0c 给出了如下错误提示 程序 g 43 43 尚未安装 使用以下命令安装 xff1a sudo apt get install g 43 43 执行 得出如下错误 正在读
  • 学习笔记-Raspberry Pi Zero W-4:串口(UART)的配置和使用

    4 1 开启UART 据官方所言 xff08 https www raspberrypi org documentation configuration uart md xff09 xff1a 树莓派CPU内部有两个串口 xff0c 一个P
  • CAAnimation——基本动画,关键帧动画和贝塞尔路径

    概述 在做对于图层的动画效果时 xff0c 往往直接改变属性或者使用隐式动画是不能满足我们的需求的 xff0c 所以我们就用到了显式动画 xff0c CAAnimation 它可以管理重复动画 准确的控制时间和步调 xff0c 并且能设定图
  • IOS详解TableView——性能优化及手工绘制UITableViewCell

    提高表视图的性能 UITableView作为应用中最常用的视图 xff0c 它的性能优化问题几乎是经常提及 下面对在非网络访问情况下的表视图性能优化进行了主要的几点说明 xff1a 1 自定义类或XIB文件时 在系统提供的样式不能满足我们的
  • IOS详解TableView——实现九宫格效果

    根据需求九宫格的效果可以有很多种 九宫格效果应用比较广泛 xff0c 实现也多种多样 xff0c 比如选项抽屉效果 这里写了一个在UITableView上显示九宫格效果的Demo 思路 xff1a 在Cell上初始化自定义按钮 xff0c
  • IOS详解TableView——内置刷新,EGO,以及搜索显示控制器

    这几天因为住的地方的网出了一点问题 xff0c 除了能上Q xff0c 上微博以外其他的网页全都无法登陆 博客也就没有跟进 今天恢复了 xff0c 所以继续更新博客 也希望大家能继续评论或私自给我一些建议或者交流 今天找到了以前一个Tabl
  • Linux设备驱动基础01:Linux设备驱动概述

    目录 1 设备驱动的作用 2 有无操作系统时的设备驱动 2 1 无操作系统 2 1 1 硬件 驱动和应用程序的关系 2 1 2 单任务软件典型架构 2 2 有操作系统 2 2 1 硬件 驱动 操作系统和应用软件的关系 3 Linux设备分类
  • IOS回调机制——代理,通知中心以及Block

    Xcode5 0正式版 IOS7和Xcode5正式版在昨天正式可以下载 IOS7不多说了 xff0c 交互设计 xff0c 界面风格 xff0c 操作的简化程度都属于比较领先的水平 这里来说说Xcode5正式版 xff0c 和以前的Xcod
  • IOS飞机大战OC版

    前一阵子看到了很多版本的打飞机游戏 xff0c 有Java版的C 43 43 版本的还有C语言版的 这几天闲着的时候写了一个OC版的 xff0c 也正好是因为答应朋友写这个游戏来把飞机都换成他照片 没有用Cocos2d框架 xff0c 用的
  • Swift的可选链,类型转换和扩展

    可选链 Optional Chaining 可选链是一种请求或调用属性 xff0c 方法 xff0c 子脚本的过程 可选性体现于请求或调用的目标当前可能为nil 若不为nil则成功调用 xff0c 否则返回nil并将链失效 调用可选链的返回
  • iOS小米遥控器的手势监听及UI实现

    这篇文章通过实例实现了一个类似小米手势遥控器的功能页面 效果图如下所示 xff1a 触摸事件的响应通过对系统的触摸实践监听来进行 通过一个数组来对点的集合进行缓存和分析 void touchesBegan NSSet touches wit
  • 博客搬家至Github

    为了使用Markdown写作更方便一些 xff0c 以后将使用github pages来管理博客 地址 xff1a Rannie s Page 欢迎来访
  • C++使用http向服务器发送json数据

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • 如何使用Git将Github项目拉到本地

    如何使用Git将Github项目拉到本地 前言 因为国内访问GIthub速度比较慢 xff0c 复制粘贴代码又慢效率也低 xff0c 所以建议下载Git工具 xff0c 直接把Github的项目整个下载到本地的文件夹 安装配置git 步骤如
  • 笔记本 - 数据分析百宝箱

    Numpy 一 基本操作 xff1a 属性 xff1a improt numpy as np 生成数组 xff1a array 61 np array 1 2 3 2 3 4 xff0c dtype 61 np int float arra
  • Faiss(5):IndexIVFPQ原理

    说明 原本想尝试自己从头写 xff0c 但看了下网上的各位前辈的博客后 xff0c 感觉自己还是才疏学浅 xff0c 没有理解透彻 xff0c 所以在这里做个搬运工 xff0c 偶尔加些个人的理解在里面 原文链接 xff1a https b
  • cmake(3):编译库和链接可执行文件

    1 说明 在实际开发的过程当中 xff0c 我们会经常需要将部分程序编译成静态或动态库的形式 xff0c 供其他应用程序调用而不是将所有文件一次编译为一个可执行文件 这篇笔记就记录使用cmake编译动态和静态库以及将库链接到可执行文件中的过
  • RTOS原理与实现02:基本任务切换实现

    目录 1 任务定义与切换原理 1 1 任务是什么 1 1 1 任务的外观 1 1 2 任务的内在 1 2 任务切换原理 1 2 1 任务切换的本质 1 2 2 要保存哪些任务运行状态 1 2 3 任务运行状态保存方案 1 3 设计实现 1