FreeRTOS_第7章_任务的定义与任务切换的实现_任务调度部分程序详解

2023-05-16

目录

简介:

启动任务调度的流程图:

第一个任务调度启动相关程序注释:

主任务循环,启动调度器顶级封装

指定TCB

修改PendSV 和 SysTick 的中断优先级为最低

呼叫系统调用(手动触发SVC)

调用第一个任务的核心操作(使用SVC中断来实现)

 

任务启动后关于任务调度切换的相关程序注释: 

主循环中的任务结束后切换任务的顶级封装

使用手动触发PendSV中断

多任务调度的核心操作(使用PendSV中断来实现)


简介:

本文是 [野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》 这本书第7章任务的定义与任务切换的实现的补充

启动任务调度的流程图:

虚线表示,这个线程程序完,触发的中断服务和中断服务好返回的线程程序位置。

第一个任务调度启动相关程序注释:

主任务循环,启动调度器顶级封装

vTaskStartScheduler() (main.c)

int main(void)
{	
    /* 硬件初始化 */
	/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */
    
    /* 初始化与任务相关的列表开始,如就绪列表 */
    /* 初始化与任务相关的列表结束,如就绪列表 */
                                      
    /* 启动调度器,开始多任务调度,启动成功则不返回 */
    vTaskStartScheduler();                                      
    
    for(;;)
	{
		/* 系统启动成功不会到达这里 */
	}
}

指定TCB

 void vTaskStartScheduler( void ) (task.c)

void vTaskStartScheduler( void )
{
    /* 手动指定第一个运行的任务 */
    pxCurrentTCB = &Task1TCB;
    
    /* 启动调度器 */
    if( xPortStartScheduler() != pdFALSE )
    {
        /* 调度器启动成功,则不会返回,即不会来到这里 */
    }
}

修改PendSV 和 SysTick 的中断优先级为最低

BaseType_t xPortStartScheduler( void ) (port.c)

BaseType_t xPortStartScheduler( void )
{
    /* 配置PendSV 和 SysTick 的中断优先级为最低 */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

	/* 启动第一个任务,不再返回 */
	prvStartFirstTask();

	/* 不应该运行到这里 */
	return 0;
}

呼叫系统调用(手动触发SVC)

__asm void prvStartFirstTask( void ) (port.c)

__asm void prvStartFirstTask( void )
{
	//8字节对齐
	//12345678 87654321
	//12345678 00000000
	PRESERVE8

	/* 在Cortex-M中,0xE000ED08是SCB_VTOR这个寄存器的地址,
       里面存放的是向量表的起始地址,即MSP的地址 */
    //1)立即数	->	寄存器地址
	//2)寄存器的值	->	向量表起始地址
	//3) 向量表起始地址第一个值	->	MSP的值
	ldr r0, =0xE000ED08		
	ldr r0, [r0]
	ldr r0, [r0]

	/* 设置主堆栈指针msp的值 */
	//MSR{条件}   程序状态寄存器(CPSR或SPSR)_<域>,操作数
	//给把r0的值赋值给msp寄存器
	msr msp, r0
    
	/* 使能全局中断 ,这个可以用作原语*/
//	中断开关指令
//	CPSID CPSIE 用于快速的开关中断
//	CPSID I PRIMASK=1 关中断
//	CPSIE I PRIMASK=0 开中断
//	CPSID F FAULTMASK=1 关异常
//	CPSIE F FAULTMASK=0 开异常
//	隔离指令
//	ISB 指令同步隔离,最严格:证所有它前面的指令都执行完毕之后,才执行它后面的指令
//	DSB 数据同步隔离,比DMB 严格: 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访问操作——译者注)
//	DMB 数据存储隔离,DMB 指令保证仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。
	cpsie i
	cpsie f
	dsb
	isb
	
    /* 调用SVC去启动第一个任务 */
//	调用SVC的0号服务
//	svc_handler			0号服务
//	svc_handler_1		1号服务
	svc 0  
	nop
	nop
}

调用第一个任务的核心操作(使用SVC中断来实现)

 __asm vPortSVCHandler(void)(port.c )

__asm void vPortSVCHandler( void )
{
	//更新系统栈为pxCurrentTCB
	//这边用于第一次启动任务的时候,将MSP切到PSP
	
    extern pxCurrentTCB;
    
    PRESERVE8
	//R3是当前TCB指针的地址,R1是当前TCB指针的值,R0是TCB指针指向第一个成员的值
	ldr	r3, =pxCurrentTCB	/* 加载pxCurrentTCB的地址到r3 */
	ldr r1, [r3]			/* 加载pxCurrentTCB到r1 */
	ldr r0, [r1]			/* 加载pxCurrentTCB指向的值到r0,目前r0的值等于第一个任务堆栈的栈顶 */

	//将自建任务堆的R4-R11压入系统栈中,剩下其他寄存器的在函数返回自动压入,psp指向r0
	ldmia r0!, {r4-r11}		/* 以r0为基地址,将栈里面的内容加载到r4~r11寄存器,同时r0会递增 */
	msr psp, r0				/* 将r0的值,即任务的栈指针更新到psp */
	isb

//	设置 basepri 寄存器的值为 0,即打开所有中断。basepri 是一个中断屏蔽寄存器,大于等于此寄存器值的中断都将被屏蔽。
	mov r0, #0              /* 设置r0的值为0 */
	msr	basepri, r0         /* 设置basepri寄存器的值为0,即所有的中断都没有被屏蔽 */

//		在进入异常服务程序后,将自动更新LR的值为特殊的EXC_RETURN。
//		这是一个高28位全为1的值,只有[3:0]的值有特殊含义,如表9.3所示。
//		当异常服务例程把这个值送往PC时,就会启动处理器的中断返回序列。
//		因为LR的值是由CM3自动设置的,所以只要没有特殊需求,就不要改动它。
//
//		表9.3 EXC_RETURN位段详解位段 含义
//		[31:4] 	EXC_RETURN的标识:必须全为1 
//		3      	0=返回后进入Handler模式
//				1=返回后进入线程模式
//		2 		0=从主堆栈中做出栈操作,返回后使用MSP,
//				1=从进程堆栈中做出栈操作,返回后使用PSP
//		1 		保留,必须为0 
//		
//		0 		0=返回ARM状态。
//				1=返回Thumb状态。在CM3中必须为1
//	
//		表9.4 合法的EXC_RETURN值及其功能
//		数值			功能
//		0xFFFF_FFF1 返回handler模式
//		0xFFFF_FFF9 返回线程模式,并使用主堆栈(SP=MSP)
//		0xFFFF_FFFD 返回线程模式,并使用线程堆栈(SP=PSP)
//
//		使得硬件在退出时使用进程堆栈指针 PSP 完成出栈操作并返回后进入任务模式、返回 Thumb 状态。
//		在 SVC 中断服务里面,使用的是 MSP 堆栈指针,是处在 ARM 状态。
//		当 r14 为 0xFFFFFFFX,执行是中断返回指令,cortext-m3 的做法,X 的 bit0 为 1 表示返回 thumb 状态,
//		bit1 和 bit2 分别表示返回后 sp 用 msp 还是 psp、以及返回到特权模式还是用户模式
//
//		这个是第一次任务是MSP进来的所以进入异常服务程序后,LR的为 0xFFFF_FFF9
	orr r14, #0xd           /* 当从SVC中断服务退出前,通过向r14寄存器最后4位按位或上0x0D,
                               使得硬件在退出时使用进程堆栈指针PSP完成出栈操作并返回后进入线程模式、返回Thumb状态 */
                               
    //压入剩下的,并返回线程指针
    //R13变为PSP,指向xPSR的上一格子
	bx r14                  /* 异常返回,这个时候栈中的剩下内容将会自动加载到CPU寄存器:
                               xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
                               同时PSP的值也将更新,即指向任务栈的栈顶 */
}

 

任务启动后关于任务调度切换的相关程序注释: 

主循环中的任务结束后切换任务的顶级封装

 taskYIELD() (main.c)

/* 软件延时 */
void delay (uint32_t count)
{
	for(; count!=0; count--);
}
/* 任务1 */
void Task1_Entry( void *p_arg )
{
	for( ;; )
	{
		flag1 = 1;
		delay( 100 );		
		flag1 = 0;
		delay( 100 );
		
		/* 任务切换,这里是手动切换 */
        taskYIELD();
	}
}

/* 任务2 */
void Task2_Entry( void *p_arg )
{
	for( ;; )
	{
		flag2 = 1;
		delay( 100 );		
		flag2 = 0;
		delay( 100 );
		
		/* 任务切换,这里是手动切换 */
        taskYIELD();
	}
}

使用手动触发PendSV中断

  taskYIELD()( porrmacro.h)

#define taskYIELD()			portYIELD()

#define portYIELD()																\
{																				\
	/* 触发PendSV,产生上下文切换 */								                \
	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;								\
	__dsb( portSY_FULL_READ_WRITE );											\
	__isb( portSY_FULL_READ_WRITE );											\
}

 

多任务调度的核心操作(使用PendSV中断来实现)

__asm void xPortPendSVHandler( void )(port.c)

void vTaskSwitchContext( void )
{    
    /* 两个任务轮流切换 */
    if( pxCurrentTCB == &Task1TCB )
    {
        pxCurrentTCB = &Task2TCB;
    }
    else
    {
        pxCurrentTCB = &Task1TCB;
    }
}

__asm void xPortPendSVHandler( void )
{
	//申明变量和函数
	extern pxCurrentTCB;
	extern vTaskSwitchContext;
	//8字节对齐
	PRESERVE8

    /* 当进入PendSVC Handler时,上一个任务运行的环境即:
       xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
       这些CPU寄存器的值会自动保存到任务的栈中,剩下的r4~r11需要手动保存 */
       
    /* 获取任务栈指针到r0 */
    //MRS访问特殊寄存器
	mrs r0, psp
	isb

	//r3是TCB这个指针的地址,r2是TCB指针的值
	ldr	r3, =pxCurrentTCB		/* 加载pxCurrentTCB的地址到r3 */
	ldr	r2, [r3]                /* 加载pxCurrentTCB到r2 */

//	弹出需要手动保存的r4-r11保存在当前tcb中
	stmdb r0!, {r4-r11}			/* 将CPU寄存器r4~r11的值存储到r0指向的地址 */
	
//	STR 把一个寄存器按字存储到存储器中
//	STRH 把一个寄存器存器的低半字存储到存储器中
//	STRB 把一个寄存器的低字节存储到存储器中
//	r0是CB地址第一个变量的地址的值,即栈顶指针
//	上面r0是psp指向R0的起始指针,这个r0是指向r4的起始指针
	str r0, [r2]                /* 将任务栈的新的栈顶指针存储到当前任务TCB的第一个成员,即栈顶指针 */				
                               
	//r3是存TCB指针的地址,后面切任务,指针会变,指针地址不变
	//r14是存这个异常任务的返回值EXC_RETURN=0xFFFF_FFFD
    //sp是压入系统栈中
	stmdb sp!, {r3, r14}        /* 将R3和R14临时压入堆栈,因为即将调用函数vTaskSwitchContext,
                                  调用函数时,返回地址自动保存到R14中,所以一旦调用发生,R14的值会被覆盖,因此需要入栈保护;
                                  R3保存的当前激活的任务TCB指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护 */
	//屏蔽11以上编号的中断,systick即以下的函数中断,就是屏蔽systick和svc                                 
	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY    /* 进入临界段 */
	msr basepri, r0
	dsb
	isb

	//这边切函数了,之前函数的xPSR,PC,R14,R12,R3,R2,R1,R0自动保存;
	//切换进程任务
	bl vTaskSwitchContext       /* 调用函数vTaskSwitchContext,寻找新的任务运行,通过使变量pxCurrentTCB指向新的任务来实现任务切换 */ 

	//开启所有中断
	mov r0, #0                  /* 退出临界段 */
	msr basepri, r0

	//切换新任务了,r3切回当前TCB的地址(调用切换函数前,当前TCB地址存放的值是上一个任务的指针,调用切换函数后,存放的值是下一个任务的指针)
    //R14是当前异常函数需要返回的值
	ldmia sp!, {r3, r14}        /* 恢复r3和r14 */

	//r3是上一个任务的TCB地址,r1是上一个任务TCB地址第一个变量的地址,r0是上一个任务TCB地址第一个变量的地址的值,即栈顶指针
	ldr r1, [r3]
	ldr r0, [r1] 				/* 当前激活的任务TCB第一项保存了任务堆栈的栈顶,现在栈顶值存入R0*/
	ldmia r0!, {r4-r11}			/* 出栈 */
	msr psp, r0
	isb
	bx r14                      /* 异常发生时,R14中保存异常返回标志,包括返回后进入线程模式还是处理器模式、
                                   使用PSP堆栈指针还是MSP堆栈指针,当调用 bx r14指令后,硬件会知道要从异常返回,
                                   然后出栈,这个时候堆栈指针PSP已经指向了新任务堆栈的正确位置,
                                   当新任务的运行地址被出栈到PC寄存器后,新的任务也会被执行。*/
	nop
}

 

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

FreeRTOS_第7章_任务的定义与任务切换的实现_任务调度部分程序详解 的相关文章

  • FreeRTOS系列

    1 多任务系统 1 1 前后台系统 单片机裸机开发时 一般都是在main函数里面用while 1 做一个大循环来完成所有的处理 循环中调用相应的函数完成所需的处理 有时也需要在中断中完成一些处理 相对于多任务系统而言 这就是单人单任务系统也
  • 【FreeRTOS 信号量】互斥信号量

    互斥信号量与二值信号量类似 但是互斥信号量可以解决二值信号量出现的优先级翻转问题 解决办法就是优先级继承 普通互斥信号量创建及运行 参阅安富莱电子demo 互斥信号量句柄 static SemaphoreHandle t xMutex NU
  • STM32CubeMX+FreeRTOS学习笔记(一)

    嵌入式实时操作系统FreeRTOS 基本概述 在嵌入式领域当中 实时操作系统的应用越来越广泛了 目前嵌入式操作系统种类很多 例如 Clinux C OS II C OS III FreeRTOS RT Thread等等 这篇文章所记录的就是
  • FreeRTOS系列

    1 RTOS简介 RTOS全称为 Real Time Operation System 即实时操作系统 RTOS强调的是实时性 又分为硬实时和软实时 硬实时要求在规定的时间内必须完成操作 不允许超时 而软实时里对处理过程超时的要求则没有很严
  • 【FreeRTOS】队列的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • FreeRTOS学习笔记<中断>

    中断概念 Cortex M的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽中断 NMI 1个Systick 滴答定时器 定时器中断和多个系统异常 Cortex M处理器有多个用于管中断和异常的可编程寄存器 这些寄存器大多数都在 NV
  • FreeRTOS ------- 任务(task)

    在学习RTOS的时候 个人觉得带着问题去学习 会了解到更多 1 什么是任务 在FreeRTOS中 每个执行线程都被称为 任务 每个任务都是在自己权限范围内的一个小程序 其具有程序入口每个任务都是在自己权限范围内的一个小程序 其具有程序入口通
  • FreeRTOS打印任务对CPU的占有率

    1 配置RTOS 1 打开RTOS Config Parameter 找到Run Time And Task States gathering related definitions 使能GENERATE RUN TIME STATS US
  • FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例)

    记录一下一个实际项目由裸机程序改成FreeRTOS 以前产品的平台还是C8051单片机上面的程序 硬件平台改成了STM32L051 同时使用STM32CubeMX生成的工程 使用FreeRTOS系统 EEPROM数据存储读取函数修改更新 2
  • 基于HAL库的FREERTOS-----------三.队列

    一 队列简介 在实际的应用中 常常会遇到一个任务或者中断服务需要和另外一个任务进行 沟通交流 这个 沟通交流 的过程其实就是消息传递的过程 在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式 但是如果在使用操作系统的应用中用
  • FreeRTOS临界段和开关中断

    http blog sina com cn s blog 98ee3a930102wg5u html 本章教程为大家讲解两个重要的概念 FreeRTOS的临界段和开关中断 本章教程配套的例子含Cortex M3内核的STM32F103和Co
  • STM32F103移植FreeRTOS必须搞明白的系列知识---2(FreeRTOS任务优先级)

    STM32F103移植FreeRTOS必须搞明白的系列知识 1 Cortex CM3中断优先级 STM32F103移植FreeRTOS必须搞明白的系列知识 2 FreeRTOS任务优先级 STM32F103移植FreeRTOS必须搞明白的系
  • 基于STM32的FreeRTOS学习之中断测试实验(五)

    记录一下 方便以后翻阅 本章内容是接着上一章节进行的实际演练 1 实验目的 FreeRTOS可以屏蔽优先级低于configMAX SYSCALL INTERRUPT PRIORITY的中断 不会屏蔽高于其的中断 本次实验就是验证这个说法 本
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • FreeRTOS临界段

    1 临界段 在访问共享资源时不希望被其他任务或者中断打断的代码 这段要执行的代码称为临界段代码 2 设置临界段的目的 保护共享资源 例如 全局变量 公共函数 不可重入函数 函数里面使用 了一些静态全局变量 malloc 等 保护外设的实时性
  • 再论FreeRTOS中的configTOTAL_HEAP_SIZE

    关于任务栈和系统栈的基础知识 可以参考之前的随笔 FreeRTOS 任务栈大小确定及其溢出检测 这里再次说明 define configTOTAL HEAP SIZE size t 17 1024 这个宏 官方文档解释 configTOTA
  • 使用 GCC 编译器的 ARM 内核的堆栈回溯(当存在 MSP 到 PSP 切换时)

    核心 ARM Cortex M4 编译器 GCC 5 3 0 ARM EABI 操作系统 免费 RTOS 我正在使用 gcc 库函数 Unwind Reason Code Unwind Backtrace Unwind Trace Fn v
  • 当一个任务写入变量而其他任务读取该变量时,我们是否需要信号量?

    我正在研究 freeRtos 并且我有一个名为 x 的变量 现在 每秒只有一个任务正在写入该变量 而其他任务正在读取该变量值 我需要用互斥锁来保护变量吗 如果变量为 32 位或更小 并且其值是独立的并且不与任何其他变量一起解释 则不需要互斥
  • FreeRTOS 匈牙利表示法 [重复]

    这个问题在这里已经有答案了 我是 RTOS 和 C 编程的新手 而且我仍在习惯 C 的良好实践 因此 我打开了一个使用 FreeRTOS 的项目 我注意到操作系统文件使用匈牙利表示法 我知道一点符号 但面临一些新的 标准 FreeRTOS
  • GNU Arm Cortex m4 上的 C++ 异常处理程序与 freertos

    2016 年 12 月更新现在还有一个关于此行为的最小示例 https community nxp com message 862676 https community nxp com message 862676 我正在使用带有 free

随机推荐

  • ros学习(2-2):ros节点创建(python)

    参考 Python创建简单的ROS节点 xff1a 用Python实现ROS节点 xff08 这里也说明了用Python写ROS功能包也是需要CMakelists txt的 xff09 https blog csdn net sinat 1
  • Ubuntu下使用眼动仪Tobii Eye Tracker 4C

    01 安装 amp 使用 安装 使用过程参考 xff1a https github com Eitol tobii eye tracker linux installer 下面过程主要是github中自述文件的翻译 下载 在新终端 xff0
  • 深度学习之卷积

    01 卷积 卷积是指在滑动中提取特征的过程 xff0c 可以形象地理解为用放大镜把每步都放大并且拍下来 xff0c 再把拍下来的图片拼接成一个新的大图片的过程 2D卷积是一个相当简单的操作 xff1a 我们先从一个小小的权重矩阵 xff0c
  • Ubuntu 18.04 安装网卡驱动(有线连接)

    之前一直用小米的驱动 xff0c 但是感觉太慢了 xff0c 所以还是决定使用有线连接 但是之前进入Linux系统后 xff0c 有线连接没有 xff0c 所以需要安装驱动 01 查询网卡类型 xff08 Windows xff09 进入w
  • realsense d435i获取某像素点三维坐标(计算深度和点云两种方法)

    这篇写的比较详细但也比较乱 xff0c 另一篇思路更清晰和简介一点 xff0c 可供参考 xff1a Ubutntu下使用realsense d435i xff08 二 xff09 xff1a 获取某二维像素点的三维坐标 00 说明 代码1
  • Ubuntu18配置与ROS兼容的深度学习环境(Anaconda3+PyTorch1.10+python3.8+cuda10.2)

    之前在Window下安装了Anaconda xff0c 熟悉了一下安装过程 xff0c Ubuntu下最难的应该就是和ROS的兼容问题 ROS是基于Python2 7的 xff0c 而Anaconda3则是python3 看有的博文说需要先
  • Ubutntu下使用realsense d435i(三):使用yolo v5测量目标物中心点三维坐标

    01 参考 本文下述使用参考的的工程均来自于下面的两个github yolo v5参考的代码 xff1a https github com ultralytics yolov5本文参考的代码 xff08 仅获得深度 xff09 xff1a
  • 使用IAR embedded workbench for MCS-51编写的一个示例程序

    新下了一个IAR xff0c 不知道该IDE是否好用 xff0c 使用了一个程序测试了一下 xff0c 并烧到89c51上 include 34 ioAT89C51 h 34 void delayms unsigned int number
  • 如何查看ROS话题的频率

    可以使用rqt工具查看 rosrun rqt topic rqt topic 在话题名称前面选中就可以查看话题的type 频率以及发布的信息 感觉这个要比直接在命令行rostopic查看方便一点
  • UR+RealSense手眼标定(eye-to-hand)

    01 手眼标定的原理 基坐标系 xff08 base tree xff09 和相机 xff08 camera tree xff09 两个坐标系属于不同的tree xff0c 通过将标签贴到手上 xff0c 相机识别出标签的position和
  • Ubuntu18.04安装手控器

    参考 xff1a 官网教程 xff1a https s3 amazonaws com dl 3dsystems com binaries Sensable Linux Touch Device Drivers 2019 2 15 Linux
  • ros tf坐标

    参考 xff1a 讲解 xff1a https www bilibili com video BV1zt411G7Vn p 61 18 amp vd source 61 3a1ad336af3eaae4fcced56c75d309d1ROS
  • 使用代理时网页访问正常,git无法clone

    明明已经使用了vpn xff0c 命令行还git clone一直被拒绝 fatal unable to access span class token string 39 https github com pytorch vision 39
  • rosbag修复

    span class token comment 等待修复 xff0c 按照录制包的大小时间可能长也可能短 span rosbag reindex xxx bag active xff1b span class token comment
  • 【HNU-CSEE jetson nano 番外篇】jetson nano与显示器的连接

    前言 首先需要搞清楚VGA信号和HDMI信号的大致区别 二者都是主机与显示器之间传输图像的协议 xff0c 但HDMI传输的是图像数字信号 xff0c VGA传输的是图像模拟信号 xff0c 因而两者之间 xff0c 如果要进行切换的话 x
  • 【教程】简单5步教你手机制作寸照、证件照、照片回执

    我们日常生活中需要用到证件照的地方有很多 xff0c 求职入学 考试报名 办理证件 出国旅行等场合 xff0c 都离不开要用到电子证件照或纸质证件照 不想一遍遍跑照相馆的 xff0c 那就来学一学如何用手机直接生成证件照吧 xff0c 操作
  • Android5.0+ Camera专栏终结

    本文均属自己阅读源码的点滴总结 xff0c 转账请注明出处谢谢 欢迎和大家交流 qq 1037701636 email gzzaigcn2009 64 163 com Software xff1a 系统源码Android5 1 寥寥几笔写在
  • 写给我的2013

    前沿 xff1a 代码看的累了 xff0c 在新的一年终于可以找点时间来回忆我的2013 想着要写点什么 xff0c 可是又没有什么可以写 因为回忆无非就是夹杂着些许痛苦与欢乐 写给我的2013 家 生活 xff1a 2013年 xff0c
  • 自动控制原理(4)——传递函数、典型环节的传递函数

    自动控制原理 xff08 4 xff09 传递函数 典型环节的传递函数 微分方程模型 优点 xff1a 是时间域的数学模型 xff0c 比较直观 xff0c 它用时间域的方式 xff0c 描述系统输入和输出变量之间的关系 在给定初始条件和输
  • FreeRTOS_第7章_任务的定义与任务切换的实现_任务调度部分程序详解

    目录 简介 xff1a 启动任务调度的流程图 xff1a 第一个任务调度启动相关程序注释 xff1a 主任务循环 xff0c 启动调度器顶级封装 指定TCB 修改PendSV 和 SysTick 的中断优先级为最低 呼叫系统调用 xff08