手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量

2023-05-16

FreeRTOS源码解析集合(全网最详细)
手把手教你FreeRTOS源码解析(一)——内存管理
手把手教你FreeRTOS源码详解(二)——任务管理
手把手教你FreeRTOS源码详解(三)——队列
手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量

信号量和互斥量是特殊的队列,关于队列的源码我们已经在前面讲解过了:
手把手教你FreeRTOS源码详解(三)——队列
本章我们来讲解一下信号量和互斥量的源码

目录:

  • 1、信号量
    • 1.1 创建二进制信号量xSemaphoreCreateBinary
    • 1.2 创建计数型信号量xSemaphoreCreateCounting
    • 1.3 释放信号量xSemaphoreGive
    • 1.4 获取信号量xSemaphoreTake
  • 2 互斥量
    • 2.1 创建互斥信号量xSemaphoreCreateMutex
    • 2.2 获取互斥信号量xSemaphoreTake
    • 2.2 释放互斥信号量xSemaphoreGive
  • 3、递归互斥量
    • 3.1 创建递归互斥量xSemaphoreCreateRecursiveMutex
    • 3.2 获取递归互斥量xSemaphoreTakeRecursive
    • 3.3 释放递归互斥量xSemaphoreGiveRecursive

1、信号量

信号量又分为二进制信号量和计数型信号量,二进制信号量相当于一个长度为一的队列,其消息数取值只能为0,1;计数型信号量创建时可以设置其队列长度与队列中的初始消息数,即其消息数的取值没有限制

1.1 创建二进制信号量xSemaphoreCreateBinary

二进制信号量的创建函数是对通用队列函数xQueueGenericCreate进行了简单的封装:

#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

利用xQueueGenericCreate函数创建的队列长度为1,队列项大小为semSEMAPHORE_QUEUE_ITEM_LENGTH,类型为二进制信号量queueQUEUE_TYPE_BINARY_SEMAPHORE

#define semSEMAPHORE_QUEUE_ITEM_LENGTH		( ( uint8_t ) 0U )

我们注意到如上宏定义,二进制信号量的队列项大小uxItemSize的实际大小为0,即二进制信号量实质上是一个队列长度为1,队列项大小为0的队列,程序实际使用的只有队列结构体,利用uxMessagesWaiting来判断二进制信号量这个“特殊的队列”中是否有信号量,当uxMessagesWaiting为0时,队列中就没有可获取的信号量,当uxMessagesWaiting为1时,队列中已经有信号量了并且无法再继续释放信号量

1.2 创建计数型信号量xSemaphoreCreateCounting

#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

创建计数型信号量的源码如上所示,入口参数有两个,uxMaxCount为最大计数值,uxInitialCount 为初始计数值,我们继续刨析其调用的函数
xQueueCreateCountingSemaphore:

xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

xQueueCreateCountingSemaphore实际调用的函数仍然是队列通用创建函数xQueueGenericCreate,最大计数值uxMaxCount作为所创建的队列长度,队列项大小仍为queueSEMAPHORE_QUEUE_ITEM_LENGTH(实际大小为0),类型为计数型信号量queueQUEUE_TYPE_COUNTING_SEMAPHORE ,最后用xHandle 来保存所创建队列的句柄

	if( xHandle != NULL )
	{
		( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;

		traceCREATE_COUNTING_SEMAPHORE();
	}

如果xHandle 不为空,即队列创建成功,则将队列中当前消息数uxMessagesWaiting 修改为初始计数值uxInitialCount

return xHandle;

最后将整个队列的起始地址作为句柄返回

1.3 释放信号量xSemaphoreGive

#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

释放信号量函数xSemaphoreGive同样也是对写队列函数xQueueGenericSend进行了简单的封装,写入的内容为NULL,即实际上不写入内容,阻塞时间为semGIVE_BLOCK_TIME(为0),即释放信号量函数不会阻塞,采用的入队形式为后向入队queueSEND_TO_BACK
信号量队列项大小uxItemSize为0,我们来看一下拷贝入队函数prvCopyDataToQueue:

uxMessagesWaiting = pxQueue->uxMessagesWaiting;
if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
	#if ( configUSE_MUTEXES == 1 )
	{
		if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
		{
			/* The mutex is no longer being held. */
			xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );
			pxQueue->pxMutexHolder = NULL;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_MUTEXES */
}
else if( xPosition == queueSEND_TO_BACK )
{
	( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. */
	pxQueue->pcWriteTo += pxQueue->uxItemSize;
	if( pxQueue->pcWriteTo >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */
	{
		pxQueue->pcWriteTo = pxQueue->pcHead;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}
else
{
	( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	pxQueue->u.pcReadFrom -= pxQueue->uxItemSize;
	if( pxQueue->u.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */
	{
		pxQueue->u.pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize );
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	if( xPosition == queueOVERWRITE )
	{
		if( uxMessagesWaiting > ( UBaseType_t ) 0 )
		{
			/* An item is not being added but overwritten, so subtract
			one from the recorded number of items in the queue so when
			one is added again below the number of recorded items remains
			correct. */
			--uxMessagesWaiting;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

当uxItemSize为0且不使用互斥锁时,该函数不会进行任何的拷贝操作

pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1;

该函数此时主要的功能就是将队列中的消息数目增1,即信号“量”增1,从而实现了释放信号量的作用

1.4 获取信号量xSemaphoreTake

#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )

获取信号量函数xSemaphoreTake是对读队列函数xQueueGenericReceive进行了简单的封装,由于信号量不会实际上向队列中写入或者读取数据,因此读队列函数的数据存储区为NULL,获取信号量与释放信号量有些许不同,获取信号量是可以阻塞的,我们通过参数xBlockTime 就能看出
我们来看一下拷贝出队函数prvCopyDataFromQueue:

	static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer )
{
	if( pxQueue->uxItemSize != ( UBaseType_t ) 0 )
	{
		pxQueue->u.pcReadFrom += pxQueue->uxItemSize;
		if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */
		{
			pxQueue->u.pcReadFrom = pxQueue->pcHead;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports.  Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. */
	}
}

该函数最开始就判断队列项大小是否不为0,但在信号量中队列项大小为0,因此获取信号量过程中也不会进行任何数据拷贝

pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;

该代码对队列中的当前消息数进行了减1操作,这段代码也是获取信号量xSemaphoreTake最为关键的,获取一次信号量,我们就对队列中的消息数uxMessagesWaiting减少1,从而实现了获取信号量的作用

2 互斥量

在信号量的使用过程中可能会出现优先级翻转,即高优先级的任务无法执行,这种情况下我们就可以采用互斥信号量,互斥信号量是一个拥有优先级继承的二进制信号量,可以降低优先级翻转带来的影响,但是不能消除优先级翻转

2.1 创建互斥信号量xSemaphoreCreateMutex

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

创建互斥信号量xSemaphoreCreateMutex实际调用的函数为xQueueCreateMutex,我们继续看xQueueCreateMutex源码:

const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );

互斥信号量队列长度uxMutexLength 为1,队列项大小uxMutexSize 为0,这点与普通的信号量相同,即互斥信号量实质上是一个队列长度为1,队列项大小为0,类型为ucQueueType 的队列

prvInitialiseMutex( pxNewQueue );

最后对互斥信号量的一些参数进行初始化,这里我们继续刨析prvInitialiseMutex源码:

		pxNewQueue->pxMutexHolder = NULL;
		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;

队列创建的时候会初始化部分结构体,但现在创建的是互斥信号量,部分与优先级继承相关的成员变量需要重新初始化,在结构体中,我们可能没有看到过pxMutexHolder 、uxQueueType ,其实它们都是在queue.c中定义的宏定义,如下:

#define pxMutexHolder					pcTail
#define uxQueueType						pcHead
#define queueQUEUE_IS_MUTEX				NULL

pxMutexHolder 实际是队列尾pcTail,uxQueueType 实际是队列头pcHead,在初始化过程中将队列头pcHead(uxQueueType )指向NULL(queueQUEUE_IS_MUTEX)

pxNewQueue->u.uxRecursiveCallCount = 0;

uxRecursiveCallCount 是在递归互斥锁中使用的,我们在这里就不进行解释了

( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );

最后向互斥信号量这个队列中释放一次信号量,即互斥信号量初始化完成时,队列中就有信号量,不需用释放信号量就能直接获取一次信号量

2.2 获取互斥信号量xSemaphoreTake

无论是普通信号量还是互斥信号量,它们获取、释放信号量的函数都是一样的
获取信号量xSemaphoreTake实际调用的函数是xQueueGenericReceive,获取互斥信号量与普通信号量的区别如下:
1:

				#if ( configUSE_MUTEXES == 1 )
				{
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
					{
						pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); 
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_MUTEXES */

用pxMutexHolder 来记录互斥锁的拥有者,这里主要是调用pvTaskIncrementMutexHeldCount函数来赋值的,该函数源码如下:

	void *pvTaskIncrementMutexHeldCount( void )
{
	/* If xSemaphoreCreateMutex() is called before any tasks have been created
	then pxCurrentTCB will be NULL. */
	if( pxCurrentTCB != NULL )
	{
		( pxCurrentTCB->uxMutexesHeld )++;
	}

	return pxCurrentTCB;
}

这个函数也十分简单,直接将当前任务的任务控制块成员变量uxMutexesHeld 增1,表明任务获取了一个互斥锁,最后返回当前任务的任务控制块,将其赋值给pxQueue->pxMutexHolder
2:

			#if ( configUSE_MUTEXES == 1 )
			{
				if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
				{
					taskENTER_CRITICAL();
					{
						vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
					}
					taskEXIT_CRITICAL();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			#endif

在前面记录了互斥锁的拥有者,在这里就调用函数vTaskPriorityInherit来判断互斥锁拥有者与当前任务的优先级大小,从而进一步判断是否需要优先级继承
vTaskPriorityInherit源码分析如下:

if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )

首先判断互斥锁拥有者的优先级是否小于当前任务的优先级,只有这样才有必要进行优先级继承

			if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
			{
				listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

当事件列表未使用时,重新调整互斥锁拥有者事件列表的值

if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )

判断需要进行优先级继承的任务是否处于就绪状态

/*从就绪列表中移除*/
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
					{
						taskRESET_READY_PRIORITY( pxTCB->uxPriority );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* 继承当前任务的优先级*/
					pxTCB->uxPriority = pxCurrentTCB->uxPriority;
					/*添加至新的就绪列表中*/
					prvAddTaskToReadyList( pxTCB );

若互斥锁拥有者处于就绪状态,则首先应该将其从就绪列表中移除,移除以后互斥锁拥有者继承当前任务的优先级,最后将互斥锁拥有者添加到新的就绪列表中

			else
			{
				/* Just inherit the priority. */
				pxTCB->uxPriority = pxCurrentTCB->uxPriority;
			}

若互斥锁拥有者不处于就绪状态,则只需要修改其优先级(继承当前任务的优先级)即可

2.2 释放互斥信号量xSemaphoreGive

释放信号量xSemaphoreGive实际调用的函数是xQueueGenericSend,释放互斥信号量与普通信号量的主要区别在拷贝入队函数prvCopyDataToQueue中,具体如下:

	if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
	#if ( configUSE_MUTEXES == 1 )
	{
		if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
		{
			/* 恢复互斥锁拥有者优先级 */
			xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );
			/*清空互斥锁拥有者*/
			pxQueue->pxMutexHolder = NULL;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_MUTEXES */
}

若队列项大小uxItemSize 为0,且使用互斥锁时,会调用函数xTaskPriorityDisinherit来恢复互斥锁拥有者的优先级,然后清空互斥锁的拥有者
xTaskPriorityDisinherit:

( pxTCB->uxMutexesHeld )--;

首先将任务的互斥锁拥有数uxMutexesHeld 减去1

if( pxTCB->uxPriority != pxTCB->uxBasePriority )

判断互斥锁拥有者当前优先级是否与原始优先级相同(原始优先级uxBasePriority 在创建任务时就会被初始化),即判断优先级是否发生了改变

if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )

若互斥锁拥有者目前不再持有任何的互斥锁

				if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
				{
					taskRESET_READY_PRIORITY( pxTCB->uxPriority );
				}

将其从就绪列表中移除

pxTCB->uxPriority = pxTCB->uxBasePriority;

恢复互斥锁拥有者的优先级

listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );

恢复互斥锁拥有者事件列表成员变量xItemValue的值

prvAddTaskToReadyList( pxTCB );

最后将互斥锁拥有者添加至新的就绪列表中

3、递归互斥量

递归互斥量可以看作一个特殊的互斥量,普通的互斥量未能实现“谁上锁只能有谁解锁”,且普通互斥量获取一次互斥量后就不能再继续获取互斥量了,而递归互斥量实现了谁上锁只能谁来解锁,且一个任务获得多少次递归量就得由该任务释放相同次数的递归量才能解锁!

3.1 创建递归互斥量xSemaphoreCreateRecursiveMutex

#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

递归互斥量的创建也是对普通互斥量的创建函数进行了简单的封装,两者几乎一模一样,只不过队列类型变成了queueQUEUE_TYPE_RECURSIVE_MUTEX
注意:虽然递归互斥量虽然可以 多次获取互斥量,但是其队列长度只有1

3.2 获取递归互斥量xSemaphoreTakeRecursive

#define xSemaphoreTakeRecursive( xMutex, xBlockTime )	xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

获取递归互斥量实际调用的函数为xQueueTakeMutexRecursive:

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;

	configASSERT( pxMutex );

	/* Comments regarding mutual exclusion as per those within
	xQueueGiveMutexRecursive(). */

	traceTAKE_MUTEX_RECURSIVE( pxMutex );
	
	/*若不是第一次获取互斥量,则判断此次获取互斥量的任务是否与之前获取互斥量的任务相同*/
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
	{
		/*若相同,则uxRecursiveCallCount增1,表明任务又一次获取递归互斥量*/
		( pxMutex->u.uxRecursiveCallCount )++;
		xReturn = pdPASS;
	}
	/*若为第一次获取互斥量*/
	else
	{
		/*调用xQueueGenericReceive获取队列中的信号量,注意:在该函数中会记录获取互斥量的任务*/
		xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );
		if( xReturn != pdFAIL )
		{
			/*获取成功,uxRecursiveCallCount加1,uxRecursiveCallCount记录递归互斥量的获取次数*/
			( pxMutex->u.uxRecursiveCallCount )++;
		}
		else
		{
			traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
		}
	}

	return xReturn;
}

由于队列长度只有1,所以只有第一次获取递归互斥量的时候才会调用xQueueGenericReceive来“获取队列消息”,参数uxRecursiveCallCount 用来记录任务获取递归互斥量的数目,当一个任务第二次及以上次数获得递归互斥量的时候,需要判断本次获取递归互斥量的任务是否与之前获取递归互斥量的任务相同,只有相同才能继续获取信号量,成功获取信号量后uxRecursiveCallCount 增一,表明又一次获取了信号量

3.3 释放递归互斥量xSemaphoreGiveRecursive

#define xSemaphoreGiveRecursive( xMutex )	xQueueGiveMutexRecursive( ( xMutex ) )

释放递归互斥量实际调用的函数为xSemaphoreGiveRecursive:

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;

	configASSERT( pxMutex );

	/* 判断释放递归互斥量的任务是否与获取互斥量的任务相同	*/
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */
	{
		traceGIVE_MUTEX_RECURSIVE( pxMutex );

		/* 参数uxRecursiveCallCount减1,表明释放了一次递归互斥量 */
		( pxMutex->u.uxRecursiveCallCount )--;

		/* 判断参数uxRecursiveCallCount是否为0,只有其为0才调用xQueueGenericSend来“入队消息” */
		if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 )
		{

			( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		xReturn = pdPASS;
	}
	else
	{
		/* The mutex cannot be given because the calling task is not the
		holder. */
		xReturn = pdFAIL;

		traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
	}

	return xReturn;
}

递归互斥量实现了“谁上锁只能由谁来解锁”,如上面源码,释放递归互斥量时,需要判断获取互斥量的任务是否与释放递归互斥量的任务相同,只有相同时才能成功释放(pxMutexHolder 中记录了互斥锁拥有者,即记录了获取互斥量任务的句柄,然后用xTaskGetCurrentTaskHandle来获取当前任务句柄,两者相同才能成功释放),成功释放以后参数uxRecursiveCallCount 减1,表明释放了一次互斥量,当参数uxRecursiveCallCount 为0时,即此次释放互斥量为最后一次释放,调用xQueueGenericSend来“入队消息”,在函数xQueueGenericSend内部会清空参数pxMutexHolder (详见2.2 释放互斥信号量xSemaphoreGive),即清空互斥锁拥有者,实现真正的“解锁”

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

手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量 的相关文章

  • FreeRTOS例程4-串口DMA收发不定长数据

    FreeRTOS例程4 串口DMA收发不定长数据 知乎 zhihu com
  • 【FreeRtos学习笔记】STM32 CubeMx——Timers(定时器)

    目录 1 软件定时器 2 示例程序 2 1 例程功能 2 2 步骤 2 3 实验结果 2 4 函数讲解 1 软件定时器 定时器是MCU常用的外设 我们在学习各种单片机时必然会学习它的硬件定时器 但是 MCU自带的硬件定时器资源是有限的 而且
  • FreeRTOS记录(七、FreeRTOS信号量、事件标志组、邮箱和消息队列、任务通知的关系)

    我们在前面单独介绍过FreeRTOS的任务通知和消息队列 但是在FreeRTOS中任务间的通讯还有信号量 邮箱 事件组标志等可以使用 这篇文章就这些成员与消息队列和任务通知的关系进行说明分析 增加邮箱部分 任务通知发送消息 Demo 202
  • 解决错误“ #error “include FreeRTOS.h“ must appear in source files before “include event_groups.““例子分享

    今天来给大家分享一下 关于之前自己在学习FreeRTOS过程中遇到的一个错误提示 话不多说 我们直接来看 错误分析 首先 我们看一下错误的提示 error 35 error directive include FreeRTOS h must
  • Freertos中vTaskDelay()是怎么用的

    1 常见的使用场景 void vLED Task void pvParameters while 1 Heartbeat LED vTaskDelay 1000 portTICK RATE MS 说明 上面这段代码的意思是 led翻转后经过
  • freertos---软定时器

    一 软件定时器介绍 freeRTOS软件定时器的时基是基于系统时钟节拍实现的 可以创建很多个 在硬件定时器资源不充足的情况下非常有用 软件定时器一般用作周期性地执行函数 在创建软件定时器时指定软件定时器的回调函数 在回调函数中实现相应的功能
  • 【FreeRTOS】队列的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例)

    记录一下一个实际项目由裸机程序改成FreeRTOS 以前产品的平台还是C8051单片机上面的程序 硬件平台改成了STM32L051 同时使用STM32CubeMX生成的工程 使用FreeRTOS系统 EEPROM数据存储读取函数修改更新 2
  • FreeRTOS:中断配置

    目录 一 Cortex M 中断 1 1中断简介 1 2中断管理简介 1 3优先级分组定义 1 4优先级设置 1 5用于中断屏蔽的特殊寄存器 1 5 1PRIMASK 和 FAULTMASK 寄存器 1 5 2BASEPRI 寄存器 二 F
  • freertos————互斥锁

    线程安全 多线程程序处于一个多变的环境 可访问的全局变量和堆数据随时可能被其他的线程改变 多个线程同时访问一个共享数据 可能造成严重的后果 出现问题的是之前移植了一个freemodbus的从站 多个任务访问全局变量保持寄存器区 导致最后读出
  • FreeRTOS之软件定时器

    FreeRTOS之软件定时器 声明 本人按照正点原子的FreeRTOS例程进行学习的 欢迎各位大佬指责和批评 谢谢 include sys h include delay h include usart h include led h in
  • 基于STM32的FreeRTOS学习之中断测试实验(五)

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

    介绍 适用于STM32F103C8的FreeRTOS STM32F103C是一种能够使用FreeRTOS的ARM Cortex M3处理器 我们直接在Arduino IDE中开始使用STM32F103C8的FreeRTOS 我们也可以使用K
  • FreeRTOS学习---“定时器”篇

    总目录 FreeRTOS学习 任务 篇 FreeRTOS学习 消息队列 篇 FreeRTOS学习 信号量 篇 FreeRTOS学习 事件组 篇 FreeRTOS学习 定时器 篇 FreeRTOS提供了一种软件定时器 用来快速实现一些周期性的
  • 单片机通信数据延迟问题排查

    1 问题说明 笔者在最近的项目中 发现系统的响应延迟较高 经过排查 排除了单片机运行卡死的问题 2 原因分析 具体排查过程这里就不细致说明了 直接给出排查后原因 任务执行周期规划不合理 导致freertos队列发送接收到的命令有延迟 为了便
  • 13-FreeRTOS任务创建与删除

    任务创建和删除API函数位于文件task c中 需要包含task h头文件 task h里面包函数任务的类型函数 例如 对xTaskCreate的调用 通过指针方式 返回一个TaskHandle t 变量 然后可将该变量用vTaskDele
  • FreeRTOS多任务调度器基础

    Cortex M4中SysTick调度器核心 Cortex M4中的中断管理 Cortex M4中影子栈指针 Cortex M4中SVC和PendSV异常 1 Cortex M4中SysTick调度器核心 systick每一次中断都会触发内
  • 如何更改 FreeRTOS 中任务的最大可用堆大小?

    我通过以下方式在任务中创建元素列表 l dllist pvPortMalloc sizeof dllist dlllist 有 32 字节大 我的嵌入式系统有 60kB SRAM 所以我希望系统可以轻松处理我的 200 个元素列表 我发现在
  • 如何将 void* 转换为函数指针?

    我在 FreeRTOS 中使用 xTaskCreate 其第四个参数 void const 是传递给新线程调用的函数的参数 void connect to foo void const task params void on connect
  • 防止GCC LTO删除函数

    我使用 GCC ARM Embedded 和 FreeRTOS FreeRTOS具有的功能vTaskSwitchContext 仅在某些情况下使用 内联汇编代码 问题是 当我使用LTO时 GCC不考虑内联汇编代码并认为该函数没有被使用 因此

随机推荐

  • LeetCode581:最短无序连续子数组

    要求 给你一个整数数组 nums xff0c 你需要找出一个 连续子数组 xff0c 如果对这个子数组进行升序排序 xff0c 那么整个数组都会变为升序排序 请你找出符合题意的 最短 子数组 xff0c 并输出它的长度 思路 我们可以假设把
  • LeetCoed617:合并二叉树

    要求 给你两棵二叉树 xff1a root1 和 root2 想象一下 xff0c 当你将其中一棵覆盖到另一棵之上时 xff0c 两棵树上的一些节点将会重叠 xff08 而另一些不会 xff09 你需要将这两棵树合并成一棵新二叉树 合并的规
  • LeetCode621:任务调度器

    要求 给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表 其中每个字母表示一种不同种类的任务 任务可以以任意顺序执行 xff0c 并且每个任务都可以在 1 个单位时间内执行完 在任何一个单位时间 xff0c CPU 可以完成
  • LeetCode647:回文子串

    要求 给你一个字符串 s xff0c 请你统计并返回这个字符串中 回文子串 的数目 回文字符串 是正着读和倒过来读一样的字符串 子字符串 是字符串中的由连续字符组成的一个序列 具有不同开始位置或结束位置的子串 xff0c 即使是由相同的字符
  • LeetCode739:每日温度

    要求 给定一个整数数组 temperatures xff0c 表示每天的温度 xff0c 返回一个数组 answer xff0c 其中 answer i 是指对于第 i 天 xff0c 下一个更高温度出现在几天后 如果气温在这之后都不会升高
  • 八大排序算法

    介绍 排序也称排序算法 Sort Algorithm xff0c 排序是将一组数据 xff0c 依指定的顺序进行排列的过程 排序分类 1 内部排序 指将需要处理的所有数据都加载到内部存储器 内存 中进行排序 2 外部排序法 数据量过大 xf
  • 时间复杂度和空间复杂度详解及排序算法复杂度

    时间复杂度 度量一个程序 算法 执行时间的两种方法 1 事前估算法 通过分析某个算法的时间复杂度来判断哪个算法更优 2 事后统计法 这种方法可行 xff0c 但是有两个问题 xff1a 一是要想对设计的算法的运行性能进行评测 xff0c 需
  • Java反编译工具JAD的安装与简单使用

    jad介绍 jad 是一个使用非常广泛的 Java 反编译工具 可以将java编译后的class文件反编译成对应的java文件 下载地址 JAD下载地址 xff0c 点击跳转https varaneckas com jad 按照自己的需求
  • python Counter() 函数

    目录 1 以统计列表中的词频为例 2 寻找出现次数最多的 k 个数 刷 leetcode 时发现了可以很方便地统计词频的 Counter 函数 xff0c 有了这个函数就不用手动的使用 for 循环来手动统计词频啦 xff01 Counte
  • C++11 新特性简介

    1 auto auto是旧关键字 xff0c 在C 43 43 11之前 xff0c auto用来声明自动变量 xff0c 表明变量存储在栈 xff0c 很少使用 在C 43 43 11中被赋予了新的含义和作用 xff0c 用于类型推断 a
  • Java反射(Reflex)机制

    反射概述 Reflection 反射 是Java被视为动态语言的关键 xff0c 反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息 xff0c 并能直接操作任意对象的内部属性及方法 加载完类之后 xff0c 在
  • 偏航角、俯仰角、滚动角

    偏航角 俯仰角 滚动角 在姿态估计中 xff0c 常常会提到三个概念 xff0c 就是偏航角 俯仰角和滚动角 姿态估计是物体在三维空间内方向的表征 通常描述物体姿态以大地作为参考系 xff08 标准坐标系 xff09 将世界坐标系 xff0
  • 定时器与计数器的区别

    曾经我在北方工业大学复试的时候 xff0c 有个老师问了我个问题 他说同学你好 xff0c 我看你前面一直都在说深度学习的问题 xff0c 请问你对单片机了解吗 xff1f xff0c 请问定时器与计数器的区别是什么 xff1f 我告诉他
  • 编译make px4_fmu-v2_default upload时,报错ERROR Board can accept larger flash images (2080768 bytes)

    编译make px4 fmu v2 default upload时 xff0c 报错ERROR Board can accept larger flash images 2080768 bytes than board config 103
  • vue中实现axios封装

    vue中实现axios封装 为什么要进行axios封装vue项目安装axios封装前的get和post请求封装后的get和post请求 为什么要进行axios封装 实际开发过程中可能有几种环境 xff0c 开发环境 xff0c 测试环境 x
  • 项目实训—场景划分(一)

    1 什么是场景 xff08 Scene xff09 场景作为电影讲故事的关键单元 xff0c 包含了演员的复杂活动及其在物理位置上的互动 识别场景的组成和语义是视觉理解复杂长视频 xff08 如电影 电视剧 娱乐节目和综艺节目 xff09
  • 手把手教你FreeRTOS源码解析(一)——内存管理

    FreeRTOS中一共有5种内存分配的方法 xff0c 分别在文件heap 1 c heap 2 c heap 3 c heap 4 c heap 5 c种 虽然标准C库中的 malloc 和 free 也可以实现动态内存管理 xff0c
  • 手把手教你FreeRTOS源码详解(二)——任务管理

    FreeRTOS源码解析集合 xff08 全网最详细 xff09 手把手教你FreeRTOS源码解析 xff08 一 xff09 内存管理 手把手教你FreeRTOS源码详解 xff08 二 xff09 任务管理 手把手教你FreeRTOS
  • 手把手教你FreeRTOS源码详解(三)——队列

    FreeRTOS源码解析集合 xff08 全网最详细 xff09 手把手教你FreeRTOS源码解析 xff08 一 xff09 内存管理 手把手教你FreeRTOS源码详解 xff08 二 xff09 任务管理 手把手教你FreeRTOS
  • 手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量

    FreeRTOS源码解析集合 xff08 全网最详细 xff09 手把手教你FreeRTOS源码解析 xff08 一 xff09 内存管理 手把手教你FreeRTOS源码详解 xff08 二 xff09 任务管理 手把手教你FreeRTOS