【FreeRTOS源码阅读】<2> task.c (1) 任务创建以及TCB、List的结构

2023-05-16

上篇讲述了list.c关于链表操作的源码阅读,此片文章将开始阅读task.c

task.h相关结构体


/* 由eTaskGetState返回的任务状态 */
typedef enum
{
	eRunning = 0,	/* 一个任务查询自己的状态,必定是运行状态 */
	eReady,			/* 被查询的任务处于Ready状态 */
	eBlocked,		/* 被查询的任务处于被阻塞的状态 */
	eSuspended,		/* 被查询的任务处于被挂起的状态 */
	eDeleted		/* 被查询的任务已经被删除了,但是他的TCB还没有被释放 */
} eTaskState;

/* 当调用vTaskNotify()函数是会有什么动作发生  */
typedef enum
{
	eNoAction = 0,				/* 不通过更新任务的 notify value 而通知任务 */
	eSetBits,					/* 设置任务的 notification value对应的bit */
	eIncrement,					/* 增加 notification value. */
	eSetValueWithOverwrite,		/* 直接覆盖任务的notification value. */
	eSetValueWithoutOverwrite	/* 当任务读取完notification value后再设置notification value. */
} eNotifyAction;

/* Used with the uxTaskGetSystemState() function to return the state of each task
in the system. */
typedef struct xTASK_STATUS
{
	TaskHandle_t xHandle;			/* The handle of the task to which the rest of the information in the structure relates. */
	const char *pcTaskName;			/* A pointer to the task's name.  This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
	UBaseType_t xTaskNumber;		/* A number unique to the task. */
	eTaskState eCurrentState;		/* The state in which the task existed when the structure was populated. */
	UBaseType_t uxCurrentPriority;	/* The priority at which the task was running (may be inherited) when the structure was populated. */
	UBaseType_t uxBasePriority;		/* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex.  Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
	uint32_t ulRunTimeCounter;		/* The total run time allocated to the task so far, as defined by the run time stats clock.  See http://www.freertos.org/rtos-run-time-stats.html.  Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
	uint16_t usStackHighWaterMark;	/* The minimum amount of stack space that has remained for the task since the task was created.  The closer this value is to zero the closer the task has come to overflowing its stack. */
} TaskStatus_t;

/* Possible return values for eTaskConfirmSleepModeStatus(). */
typedef enum
{
	eAbortSleep = 0,		/* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */
	eStandardSleep,			/* Enter a sleep mode that will not last any longer than the expected idle time. */
	eNoTasksWaitingTimeout	/* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */
} eSleepModeStatus;

task control block 结构体定义:


/*
 * 任务控制块,每个任务都会单独申请一个任务块,任务块存储任务的状态信息,其中包括任务的上下文信息(包括寄存器数值在内的任务运行时环境)
 */
typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;	/*< 指向任务栈的最后一个元素的地址.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */

	ListItem_t			xGenericListItem;	/*< 被用于表示任务状态链表中的项目 (Ready, Blocked, Suspended ). */
	ListItem_t			xEventListItem;		/*< 被用于表示事件的链表中的项目 */
	UBaseType_t			uxPriority;			/*< 任务的优先级,0是最低优先级 */
	StackType_t			*pxStack;			/*< 指向任务栈的起始地址 */
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*< 描述被创建任务的名称,调试时用到. */

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
		UBaseType_t 	uxCriticalNesting; 	/*< 用于那些无法使用嵌套调用的平台上保持嵌套层数,arm-cortex-m3不需要 */
	#endif

    UBaseType_t		uxTCBNumber;		/*< 每次TCB被创建都会增加的数. */
    UBaseType_t  	uxTaskNumber;		/*< 第三方使用 */

	#if ( configUSE_MUTEXES == 1 )
		UBaseType_t 	uxBasePriority;		/*< 最后分配给任务的优先级-由优先级继承机制使用。 */
		UBaseType_t 	uxMutexesHeld;
	#endif



    volatile uint32_t ulNotifiedValue;
    volatile eNotifyValue eNotifyState;

} tskTCB;

task.c用到的内部全局变量:

static volatile UBaseType_t uxCurrentNumberOfTasks 	= ( UBaseType_t ) 0U;//当前的任务数量
static volatile TickType_t xTickCount 				= ( TickType_t ) 0U;//时钟计数
static volatile UBaseType_t uxTopReadyPriority 		= tskIDLE_PRIORITY;//最高的ready任务的优先级
static volatile BaseType_t xSchedulerRunning 		= pdFALSE;//调度器是否在运行
static volatile UBaseType_t uxPendedTicks 			= ( UBaseType_t ) 0U;//调度器被挂起的次数
static volatile BaseType_t xYieldPending 			= pdFALSE;//空闲任务被挂起,不允许切换到yield任务
static volatile BaseType_t xNumOfOverflows 			= ( BaseType_t ) 0;
static UBaseType_t uxTaskNumber 					= ( UBaseType_t ) 0U;//任务变更次数
static volatile TickType_t xNextTaskUnblockTime		= portMAX_DELAY;

BaseType_t xTaskGenericCreate( \
                    TaskFunction_t pxTaskCode, \
                    const char * const pcName, \
                    const uint16_t usStackDepth, \
                    void * const pvParameters, \
                    UBaseType_t uxPriority, \
                    TaskHandle_t * const pxCreatedTask, \
                    StackType_t * const puxStackBuffer, \
                    const MemoryRegion_t * const xRegions )
{
BaseType_t xReturn;
TCB_t * pxNewTCB;
StackType_t *pxTopOfStack;

	/* 申请TCB 和 新任务的栈 所需要的内存,并检查是否申请成功 */
	pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer );

	if( pxNewTCB != NULL )
	{
		/* 计算栈顶的地址,这个依赖平台的栈生长方向(arm-cortex-m3 增长方向为向下增长,高地址到低地址)*/
        
        pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - ( uint16_t ) 1 );
        //arm-cortex-m3 四字节对齐
        pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK  ) ); 

		/* Setup the newly allocated TCB with the initial state of the task. */
		prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth );

		/* Initialize the TCB stack to look as if the task was already running,
		but had been interrupted by the scheduler.  The return address is set
		to the start of the task function. Once the stack has been initialised
		the    top of stack variable is updated. */

		pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );

		if( ( void * ) pxCreatedTask != NULL )
		{
			/* Pass the TCB out - in an anonymous way.  The calling function/
			task can use this as a handle to delete the task later if
			required.*/
			*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* Ensure interrupts don't access the task lists while they are being
		updated. */
		taskENTER_CRITICAL();
		{
			uxCurrentNumberOfTasks++;
			if( pxCurrentTCB == NULL )
			{
				/* There are no other tasks, or all the other tasks are in
				the suspended state - make this the current task. */
				pxCurrentTCB =  pxNewTCB;

				if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
				{
					/* This is the first task to be created so do the preliminary
					initialisation required.  We will not recover if this call
					fails, but we will report the failure. */
					prvInitialiseTaskLists();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				/* If the scheduler is not already running, make this task the
				current task if it is the highest priority task to be created
				so far. */
				if( xSchedulerRunning == pdFALSE )
				{
					if( pxCurrentTCB->uxPriority <= uxPriority )
					{
						pxCurrentTCB = pxNewTCB;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}

			uxTaskNumber++;

			#if ( configUSE_TRACE_FACILITY == 1 )
			{
				/* Add a counter into the TCB for tracing only. */
				pxNewTCB->uxTCBNumber = uxTaskNumber;
			}
			#endif /* configUSE_TRACE_FACILITY */
			traceTASK_CREATE( pxNewTCB );

			prvAddTaskToReadyList( pxNewTCB );

			xReturn = pdPASS;
			portSETUP_TCB( pxNewTCB );
		}
		taskEXIT_CRITICAL();
	}
	else
	{
		xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
		traceTASK_CREATE_FAILED();
	}

	if( xReturn == pdPASS )
	{
		if( xSchedulerRunning != pdFALSE )
		{
			/* If the created task is of a higher priority than the current task
			then it should run now. */
			if( pxCurrentTCB->uxPriority < uxPriority )
			{
				taskYIELD_IF_USING_PREEMPTION();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	return xReturn;
}

xTaskGenericCreate函数创建任务的流程如下图:

 


static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer )
{
TCB_t *pxNewTCB;

	StackType_t *pxStack;

		/* 调用portMalloc给TCB的栈申请 usStackDepth * 32 Byte的空间(4Byte 对齐) */
		pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); 


		if( pxStack != NULL )
		{
			/* 给TCB 申请内存 */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

			if( pxNewTCB != NULL )
			{
				/* Store the stack location in the TCB. */
				pxNewTCB->pxStack = pxStack;
			}
			else
			{
				/* 如果TCB的空间申请失败则释放掉栈的内存空间 */
				vPortFree( pxStack );
			}
		}
		else
		{
			pxNewTCB = NULL;
		}
·

	return pxNewTCB;
}

上面是在创建任务时给TCB和任务的栈分配内存空间的函数。


static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
UBaseType_t x;

	/* 存储任务描述名称 */
	for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
	{
		pxTCB->pcTaskName[ x ] = pcName[ x ];

		if( pcName[ x ] == 0x00 )
		{
			break;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	/* 确保任务描述名称在允许的最大长度截至 */
	pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

	/* 如果添加的任务的优先级已经超过系统允许的最大优先级则将该任务的优先级设置为系统允许的最大优先级 */
	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
	{
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	pxTCB->uxPriority = uxPriority;
	#if ( configUSE_MUTEXES == 1 )
	{
		pxTCB->uxBasePriority = uxPriority;
		pxTCB->uxMutexesHeld = 0;
	}
	#endif /* configUSE_MUTEXES */

    /*< 初始化TCB中状态链表项和事件链表项 >*/
	vListInitialiseItem( &( pxTCB->xGenericListItem ) );
	vListInitialiseItem( &( pxTCB->xEventListItem ) );

	/* 更新链表项的所属关系 */
	listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB );

	/* 事件链表总是按任务的优先级排序 */
	listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB );

}

上面的代码是将新申请的TCB初始化。包含:任务的描述名称更新、任务优先级更新、任务状态链表和事件链表的初始化。

在FreeRTOS中任务优先级是降序排列(即优先级为10的任务优先级比优先级为1的任务高),在事件链表中记录时却是取反进行记录的(见listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );),因为链表插入排序是使用升序排列的(即数字小的在链表前端,数字大的在链表后端)。

在记录最高优先级的准备链表uxTopReadyPriority是是通过优先级通过移位记录portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ),然后通过portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __clz( ( uxReadyPriorities ) ) ) __clz读取最高非零位然后得到优先级,然后再去指定优先级的ReadyList中读取任务的TCB进行调度。


StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	/* 模拟由中断引起的上下文切换,arm-cortex-m3进入中断时的入栈顺序 */
	pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) pxCode;	/* PC */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */

	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

	return pxTopOfStack;
}

初始化新建任务的栈,模拟因为中断到来引起的上下文切换,这个可以查询Cortex-M3权威手册,内容如下:

由此来看,经过任务创建函数之后,新任务的任务控制块如下图所示:

FreeRTOS创建第一个任务后的链表结构如下:

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

【FreeRTOS源码阅读】<2> task.c (1) 任务创建以及TCB、List的结构 的相关文章

  • 如何在 Haskell 中向右或向左移动列表的 1 个元素?

    嗨 我一直在寻找答案 但找不到 假设我们有一个像这样的列表 1 10 4 5 3 我怎样才能将 5 向左移动 使这个列表变成 1 10 5 4 3 我尝试过了swapElementsAt通过找到该元素的索引 但它看起来非常不足 swapEl
  • 当我尝试从列表中删除元素时,如何忽略 ValueError?

    如果我打电话 如何忽略 不在列表中 错误消息a remove x when x不在列表中a 这是我的情况 gt gt gt a range 10 gt gt gt a 0 1 2 3 4 5 6 7 8 9 gt gt gt a remov
  • Python range() 和 zip() 对象类型

    我了解功能如何range and zip 可以在 for 循环中使用 然而我期望range 输出一个列表 很像seq在 Unix shell 中 如果我运行以下代码 a range 10 print a 输出是range 10 表明它不是一
  • python中的列表列表的集合

    我有一个列表列表 mat 1 2 3 4 5 6 1 2 3 7 8 9 4 5 6 我想转换成set即删除重复列表并从中创建一个新列表 其中仅包含unique lists 在上述情况下 所需的答案将是 1 2 3 4 5 6 7 8 9
  • 如何在 Java 中获得列表的反向列表视图?

    我想在列表上有一个反向列表视图 与List sublist提供列表上的子列表视图 是否有一些函数可以提供此功能 我不想复制该列表 也不想修改该列表 在这种情况下 如果我能在列表上至少获得一个反向迭代器就足够了 另外 我知道如何自己实现这一点
  • 检查子字符串是否在字符串列表中?

    我之前已经找到了这个问题的一些答案 但它们对于当前的Python版本来说似乎已经过时了 或者至少它们对我不起作用 我想检查字符串列表中是否包含子字符串 我只需要布尔结果 我找到了这个解决方案 word to check or wordlis
  • Django查询:如何过滤对象以排除列表中的id?

    如何在查询中进行过滤 以便结果排除 ID 属于列表的任何对象实例 可以说我有 object id list 1 5 345 MyObject objects filter Q time gte datetime now Q what to
  • 按多个键分组并对字典列表的值进行汇总/平均值

    在Python中按多个键进行分组并对字典列表进行汇总 平均值的最Pythonic方法是什么 假设我有一个字典列表 如下所示 input dept 001 sku foo transId uniqueId1 qty 100 dept 001
  • R:ifelse 中的字符串列表

    我正在寻找与 MySQL 中的 where var in 语句类似的东西 我的代码如下 data lt data frame id 10001 10030 cc1 rep c a b c 10 attach data data new lt
  • 使用字符串中的变量名称访问变量值,R

    Intro 一个数据集有大量的age year变量 age 1990 age 1991 etc 我有一个字符串值数组length age years 表示这些变量 使得age years 1 回报 age 1990 etc Need 我想搜
  • Scala 中的随机列表[重复]

    这个问题在这里已经有答案了 我对 scala 中的随机播放列表有疑问 使用scala util Random 例如我有 val a cyan val b magenta val c yellow val d key val color Ra
  • 在 Django 模板中使用 forloop.counter 值作为列表索引

    在我的 Django 1 1 1 应用程序中 我在视图中有一个函数 它向模板返回一系列数字和项目列表列表 例如 data item1 item2 item3 item4 item5 item6 item7 item8 item9 retur
  • .join() 方法到底是做什么的?

    我对 Python 还很陌生 并且完全困惑 join 我读过的是连接字符串的首选方法 I tried strid repr 595 print array array c random sample string ascii letters
  • PYTHON 从嵌套列表中删除元素

    我有一个像这样的数组 dataSet 387230 296163 323434 311472 323412 166282 410119 我想删除元素 311472 但不知道如何删除 我努力了 for set in dataSet for i
  • 使用 pandas 单元格中列表的长度选择行[重复]

    这个问题在这里已经有答案了 我有一张表 df a b c 1 x y x 2 x z c d 3 x t e f g 只是想知道如何使用 c 列的长度选择行 such as df loc len df c gt 1 我知道这是不对的 正确的
  • 在Python中按属性获取对象列表中的索引

    我有具有属性 id 的对象列表 我想找到具有特定 id 的对象的索引 我写了这样的东西 index 1 for i in range len my list if my list i id specific id index i break
  • 类型错误:“float”对象不可下标

    PizzaChange float input What would you like the new price for all standard pizzas to be PriceList 0 1 2 3 4 5 6 PizzaCha
  • 对 numpy 数组中存在的元组中的列进行切片

    我已将一个文本文件导入到 numpy 数组中 如下所示 data np genfromtxt f dtype None delimiter names None 其中 f 包含我的 csv 文件的路径 现在数据包含以下内容 array 53
  • 如何在 Python 中创建多个(但单独的)空列表?

    我编写了一个脚本 该脚本有时会生成一堆空列表 应用具有以下结构的代码 A B C D 产生输出 A B C D 现在的情况是 每次使用不同的数据集作为输入时 我都必须手动修改字母 我希望能够实现自动化 我想过这样做 FieldList A
  • 如何使用foldr为列表创建显示实例?

    我想为我的数据类型 我的列表 编写自己的显示实例 到目前为止 我的方法是有效的 但我总是在末尾有一个逗号 我已经尝试用最后一个元素启动折叠并将其从列表中删除 但它很麻烦而且不起作用 有没有更简单的方法来获得正确的解决方案 实际 1 2 3

随机推荐

  • nuScenes、Culane数据集、Camera选型及内参标定、TX2外接双目相机ZED

    1 xff1a nuScenes数据集 https zhuanlan zhihu com p 295549692 nuScenes mini data 点云数据集 针对cuda10 0 spconv版本 安装cu100版本 torch 1
  • 指针指向0的问题

    问 xff1a 定义指针变量int a 61 0 是指针变量指向地址为零的数据吗 xff1f 答 xff1a 指针赋0 xff0c 就是不指向任何对象 xff0c 相当于NULL
  • pip install basicsr安装时在Preparing metadata (setup.py)卡住

    pip install basicsr安装时在Preparing metadata setup py 卡住 如下图pip安装basicsr时卡在Preparing metadata 查了很多资料也没解决 xff0c 最后从pypi下载包通过
  • Ros 图(节点,消息,话题)命令

    1 启动守护进程 xff1a roscore xff1a ros 43 core这条命令会启动ros的核心模块 xff0c 我感觉这个是一个守护进程 xff0c 会在11311端口 xff08 可能会有不同的配置 xff09 启动一个soc
  • Android 串口通信(二)

    前言 上一篇中主要介绍了串口基础内容和基于谷歌官方android serialport api库 对Android设备进行的串口操作 xff0c 这篇文章主要介绍基于Usb转串口驱动库usb serial for android 的相关内容
  • DeepMind带来了大魔王阿尔法扣,程序员还有前途吗?

    最近 著名的编程竞赛网站C odeforces 发布了一篇名为 AlphaCode DeepMind Solves Programming Problems on CodeForce 的文章 xff0c 将阿尔法扣 xff08 AlphaC
  • 最优控制理论 四、线性二次型最优控制和LQR

    在前面两节最优控制理论 二 哈密尔顿函数法 xff0c 我们利用Hamilton函数法讨论了终端等式约束的非线性控制系统的最优控制 xff0c 它所解决的是 x
  • 多源传感器GNSS INS 视觉 LiDAR 组合导航与SLAM开源项目总结

    多源传感器GNSS INS 视觉 LiDAR 组合导航与SLAM开源项目总结 本文基于 吴桐wutong 微信公众号文章完善而来 开源代码总览 名称传感器类型组合类型滤波方法备注RTKLIBG KFGAMP rtklibexplorer h
  • Integer类超详解

    目录 xff08 点击传送 x1f680 x1f680 xff09 前言 Integer的内部结构 1 MAX VALUE 2 MIN VALUE 3 SIZE 4 TYPE 构造方法 1 Integer xff08 int number
  • PPM信号介绍

    原文地址 xff1a http nicekwell net blog 20161223 ppmxin hao jie shao html 我们知道PWM信号可以控制舵机 xff0c 一般信号周期是20ms xff0c 脉宽在500us 25
  • 算法建模语言比较:C的优势(相比于Matlab)

    数据结构定义 structure 方便组织相关数据 union 方便多角度访问数据 xff08 软件角度 硬件角度等 xff09 bit field 方便硬件角度访问数据 内存分配方式 malloc 预先 灵活申请内存空间 xff0c 拓展
  • 学会使用CMakeLists.txt在VScode中搭建C++工程

    目录 一 Cmake 简介 二 常用命令 1 指定 cmake 的最小版本 2 设置项目名称 3 设置变量 3 1 set 直接设置变量的值 3 2 set 追加设置变量的值 3 3 list 追加或者删除变量的值 4 添加第三方库或链接其
  • WIN10源码编译安装QGC-V3.4

    目录 写在前面 环境 安装VS2015 xff08 采用的是社区版 xff09 安装Git xff08 见GIT安装教程 xff09 并克隆QGC源码 安装Qt xff0c 并用Qt进行编译 运行 安装注意事项 写在前面 最近想起来之前有过
  • 飞控固件烧录方法

    目录 写在前面 方法一 方法二 写在前面 整理两个烧录飞控固件的方法 方法一 1 xff09 waf targets bin ardusub upload 这时两个过程 xff0c 第一个过程target 会产生一个 px4 文件 xff0
  • Ubuntu 20.04 LTS 安装qt4 library

    How to Install Qt4 Libraries in Ubuntu 20 04 LTS July 9 2020 3 Comments The Qt4 framework has been removed from Ubuntu 2
  • STM32接入ONENET-实现数据上传和命令下发

    前言 xff1a 使用ONENET平台进行远程传输数据和远程控制开发板是相对简单的事 xff0c 但由于ONENET官方给的代码只对他家的开发板比较友好 xff0c 对于初学者来说修改这些代码相对麻烦 xff0c 所以我就分享一份STM32
  • 从x86到ARM,代码移植指北

    最近ARM架构的处理器从云到端全面开花 xff0c 比如苹果MAC上的ARM架构处理器M1 MAX就堪称王炸 xff0c 华为的鲲鹏系列ARM处理器也已经稳定服务了很长时间 xff0c 目前业内有口皆碑 xff0c 因此基于x86环境编写的
  • AGV车载控制系统搭建(初学者入门)

    本文转载 xff1a 博主 robinvista的http blog csdn net robinvista article details 78349627 目的 本文介绍 AGV 车载控制系统的实现过程 xff0c 可以分为硬件搭建和软
  • 激光无轨导航AGV,未来智能工厂的必需品

    这篇文章结合一家激光无轨导航AGV公司 xff0c 开启了进入AGV领域的学习与总结 随着国家政策大力支持 xff0c 智能制造 工业4 0 人工智能等等成为了风口 xff0c 热门话题 智能制造中重要的一环 xff0c 物流搬运 分拣和智
  • 【FreeRTOS源码阅读】<2> task.c (1) 任务创建以及TCB、List的结构

    上篇讲述了list c关于链表操作的源码阅读 xff0c 此片文章将开始阅读task c task h相关结构体 由eTaskGetState返回的任务状态 typedef enum eRunning 61 0 一个任务查询自己的状态 xf