FreeRTOS-信号量

2023-05-16

FreeRTOS-信号量

  • 信号量其实就是队列的一种应用,信号量的各种操作都是在队列的基础上建立起来的。那么既然是在队列的基础上建立的,信号量一定具有和队列相同的属性。因此信号量也是为任务和任务、任务和中断之间通信做准备的,但是信号量一般用来进行资源管理和任务同步。因为信号量是一种共享资源,当它被创建之后,系统中所有任务和中断都能对信号量进行访问。同时也可以进行任务同步,即在一个任务(或中断)中告诉另一个任务它所等待的事件发生了,等到发生任务调度的时候,再切换到相应任务中,执行该事件发生的相关处理。FreeRTOS中的信号量有二值信号量、互斥信号量、计数信号量、递归互斥信号量,我们这里只讲述前三个。

相关API

  • 信号量使用的API函数最核心的就是xQueueGenericCreate()、xQueueGenericSend()、xQueueGenericReceive()这三个函数,看到这三个函数是不是有点眼熟?没错,这就是上一章中队列所使用的三个函数,只不过在信号量中对其做了一些封装而已,庆幸的是我们上一章已经着重分析过这些函数了,所以本章将不会再长篇大论的去分析这些源码了。下面来看一下信号量中的函数是如何对其进行封装的。代码定义如下。
#define semSEMAPHORE_QUEUE_ITEM_LENGTH		( ( uint8_t ) 0U )
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

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

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

  • xSemaphoreCreateBinary()是创建二值信号量的函数,从定义可见,该函数调用了xQueueGenericCreate(),只不过定义的队列长度是1,队列项大小为0。那么这样问题来了,队列项大小为0的队列怎么入队和出队呢?这个无需着急,接下来我们会详细分析。
  • xSemaphoreGive() 是发送信号量,只要是要发送信号量,不管是二值信号量、计数信号量、互斥信号量还是递归互斥信号量都是调用的这个函数。该函数调用了xQueueGenericSend()函数,从中可见,这时候入队的队列项是NULL,相应的将不会占用空间,所以上面在创建信号量的时候为其分配的空间是0也就能解释一部分了,但是问题还没有解决,既然队列始终是空的,那么如何来判断是否有信号发送呢?其实是根据uxMessagesWaiting这个变量进行判断的,因为即使我们入队的是一个空队列项,那么如果说这是候满足uxMessagesWaiting<uxLength这个条件,依旧会进行入队操作,既然会进行入队操作,那么根据我们上一章讲述的,uxMessagesWaiting必然会+1,如果这时候我们规定uxLength=1,那么uxMessagesWaiting只能在0到1之间取值,所以就相当于该信号量只有0、1两种状态,所以叫二值信号量。如果我们规定的uxLength>1,那么uxMessagesWaiting最大计数值就为uxLength,所以这时候就是计数信号量。
  • xSemaphoreTake()是接收信号量,和发送信号量一样,任何类型的信号量接收都会调用这个函数。该函数调用了xQueueGenericReceive()函数,输入参数为信号量句柄和阻塞时间,最后一个输入参数为pdFALSE,设置接收后是否要删除信号量中的信号,这里设置为删除。同样我们可以看到,接收的信号量的时候传入的接收数组也是NULL,对照上面的发送信号量可以知道,接收信号量其实也是操作uxMessagesWaiting的,因为这部分代码上章节中没有分析,这里简要分析一下源码。
...
//非主要源码忽略
...
if( uxMessagesWaiting > ( UBaseType_t ) 0 )---1
			{

				pcOriginalReadPosition = pxQueue->u.pcReadFrom;

				prvCopyDataFromQueue( pxQueue, pvBuffer );---2

				if( xJustPeeking == pdFALSE )---3
				{
					traceQUEUE_RECEIVE( pxQueue );

					/* Actually removing data, not just peeking. */
					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;----4
    		 ...
		     //互斥信号量相关的暂时忽略
		     ...
		     
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )----5
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
						{
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else//xJustPeeking == pdTRUE    ----6
				{
					traceQUEUE_PEEK( pxQueue );

					pxQueue->u.pcReadFrom = pcOriginalReadPosition;

					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* The task waiting has a higher priority than this task. */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				taskEXIT_CRITICAL();
				return pdPASS;----7
			}
else----8
{
...
//这一部分就是设置阻塞时间
...
}
  1. 队列不为空
  2. 将读取队列项,注意读取的时候是不会自动进行uxMessagesWaiting -1操作的
  3. 判断是否读取后要移除队列项
  4. 因为读取后要移除队列项,那么队列中队列项的数量-1
  5. 执行移除队列项操作
  6. 如果读取后不溢出队列项,那么相应的队列中队列项的数量不变
  7. 如果队列是空的,那么就没有办法读取队列项,所以设置阻塞时间,详细的代码上章节已经分析过了,这里不再分析了
  • 从上面的分析中可以看出,当要进行信号量读取的时候,本质上还是根据uxMessagesWaiting来操作的。
  • 因为计数信号量和互斥信号量创建的封装过程要比二值信号量稍微复杂一点,所以没在上面分析,下面我们就来分析这两种信号量的创建。
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
	
	QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
	{
	QueueHandle_t xHandle;

		configASSERT( uxMaxCount != 0 );
		configASSERT( uxInitialCount <= uxMaxCount );

		xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

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

			traceCREATE_COUNTING_SEMAPHORE();
		}
		else
		{
			traceCREATE_COUNTING_SEMAPHORE_FAILED();
		}

		return xHandle;
	}
  • xSemaphoreCreateCounting()其实是调用了xQueueCreateCountingSemaphore()函数,输入参数为最大计数量和初始计数值。在xQueueCreateCountingSemaphore()中我们可以看出,其实最终创建计数信号量还是调用了xQueueGenericCreate()函数,只不过创建的队列长度为uxMaxCount,初始时候uxMessagesWaiting 为uxInitialCount而已,其他和创建二值信号量并没有什么本质区别。下面再分析互斥信号量创建过程。
  • 互斥信号量创建的时候调用的函数是xQueueCreateMutex()函数源码如下。
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
	{
	Queue_t *pxNewQueue;
	const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;

		pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );
		prvInitialiseMutex( pxNewQueue );

		return pxNewQueue;
	}
  • 从上面可以看出,创建互斥信号量依旧调用了xQueueGenericCreate()函数,并且对垒的定义与二值信号量相似,队列长度均为1。但是后面紧接着调用了函数prvInitialiseMutex()来初始化互斥信号量。下面来看一下该函数定义。
static void prvInitialiseMutex( Queue_t *pxNewQueue )
	{
		if( pxNewQueue != NULL )
		{

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

			/* In case this is a recursive mutex. */
			pxNewQueue->u.uxRecursiveCallCount = 0;----3

			traceCREATE_MUTEX( pxNewQueue );

			/* Start with the semaphore in the expected state. */
			( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );----4
		}
		else
		{
			traceCREATE_MUTEX_FAILED();
		}
	}
  • 注意,在Queue_t 结构体中并没有发现pxMutexHolder 、uxQueueType 这两个成员,那么为什么这里会有这两个成员变量呢,这里我们在quue.c文件中找到其相关定义如下。
#define pxMutexHolder					pcTail
#define uxQueueType						pcHead
#define queueQUEUE_IS_MUTEX				NULL
  • 从上面定义可以看出,其实就是对Queue_t 结构体中的一些成员进行了一个重命名,这样增强代码可读性。
  1. 初始化互斥信号量队列为指针
  2. 初始化队列的类型为互斥信号量
  3. 重置互斥信号量回调次数为0
  4. 调用信号量发送函数,表示初始时互斥信号量是有效的

  • 到这里我们就将这些相关的API介绍完了,从中可以看出,信号量和队列是水乳交融,密不可分的,信号量其实就是队列的一种应用,下面我们来讲述一下信号量的如何来使用。

二值信号量

实验目标:在中断和任务中分别实现二值信号量的发送和接收,
在任务中采用按键实现信号量的发送,在另一个任务中等待接
收信号量。在中断中,PC发送给串口信息,串口接收到信息后
将信号量发送给任务。
  • 创建二值信号量代码如下
 Binary_Handler = xSemaphoreCreateBinary();	
	 if (Binary_Handler == NULL)
	 {
		 printf("Semaphore Binary Creat Failed!!!\r\n");
	 }
  • 创建按键任务,当按键按下时发送信号量。函数定义如下
void key_task(void* pvParameters)
{
    BaseType_t err;
	while(1)
	{
		if (Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
		{
			err = xSemaphoreGive(Binary_Handler);//发送新信号量
			if (err != pdTRUE)//判断信号量是否发送成功
			{
				printf("信号量发送失败!\r\n");
			}
		}
		vTaskDelay(10);
	}
}
  • 创建信号量接收函数,接收按键任务和中断发送的信号量。定义如下
void task2_task(void* pvParameters)
{
	u8 count = 0;
	BaseType_t err;
	while(1)
	{
		count++;//记录信号量接收次数
		if (Binary_Handler != NULL)//判断句柄有效性
		{
			err = xSemaphoreTake(Binary_Handler,portMAX_DELAY);//接收信号量
			if (err != pdTRUE)//判断是否接收成功
			{
				printf("接收信号量失败!!\r\n");
			}
			else//LED翻转 并且输出接收到的值
			{
				LED1 = ~LED1;
				printf("count = %d   Receive %s\r\n",count,USART_RX_BUF);
				memset(USART_RX_BUF,0,USART_REC_LEN);
				USART_RX_STA = 0;
			}
		}
		else
		{
			vTaskDelay(10);
		}

	}
}
  • 在串口中断服务函数中我们做以下更改。代码如下。
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
	BaseType_t  HigherPriorityTaskWoken=pdFALSE;//用以判断是否需要进行任务切换
	BaseType_t 	err;
	
	
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 
		
	if ( (Binary_Handler!=NULL) && (USART_RX_STA&0x8000) )//判断是否接收完成
	{
		err = xSemaphoreGiveFromISR(Binary_Handler,&HigherPriorityTaskWoken);//从中断中发送信号量
		if (err != pdTRUE)//判断是否出错
		{
			printf("Semaphore Give Failed!!!\r\n");
		}
		portYIELD_FROM_ISR(HigherPriorityTaskWoken);//根据HigherPriorityTaskWoken判断是否进行任务切换
		
	}

} 

优先级翻转

  • 上面使用二值信号量似乎没什么问题,但是在某些情况下,二值信号量可能会导致任务的优先级发生翻转,什么叫任务优先级翻转?假设我们这里有三个任务,分别为low_task、middle_task、high_task,且优先级依次升高,这里有一种可能,就是当高优先级的任务请求信号量进入阻塞状态,而低优先级任务一直控制着信号量不发出,这时,中等优先级的任务恰巧进入了准备态,所以中等优先级的任务就会打断低优先级任务,使得中等优先级的任务先执行,待其执行完毕后,再回到低优先级中,直到低优先级任务释放信号量后,高优先级的任务才能收到信号量而解除阻塞,这中间有一段时间导致中等优先级的任务优先级高于高优先级的任务优先级。如下图所示
    在这里插入图片描述
  • 接下来就用代码测试一下优先级翻转。首先定义三个任务,且优先级依次从小到达,定义如下
#define LOW_TASK_PRIO  2
#define LOW_STACK_SIZE 50
TaskHandle_t LowTask_Handler;
void low_task(void *pvParameters);

#define MIDDLE_TASK_PRIO  3
#define MIDDLE_STACK_SIZE 50
TaskHandle_t MiddleTask_Handler;
void middle_task(void *pvParameters);

#define HIGH_TASK_PRIO  4
#define HIGH_STACK_SIZE 50
TaskHandle_t HighTask_Handler;
void high_task(void *pvParameters);

void  low_task(void* pvParameters)
{
	long i;
	while(1)
	{
		printf("low task running\r\n");
		xSemaphoreTake(Binary_Handler,portMAX_DELAY);
		for (i=0; i<22222222; i++);
		xSemaphoreGive(Binary_Handler);
		printf("low task give semaphore\r\n");
		vTaskDelay(1000);
	}
}

void  middle_task(void* pvParameters)
{
	while(1)
	{
		printf("middle task running\r\n");
		vTaskDelay(1000);
	}
}

void  high_task(void* pvParameters)
{
	while(1)
	{
		printf("high task ask the semaphore\r\n");
		xSemaphoreTake(Binary_Handler,portMAX_DELAY);
		printf("high task running\r\n");
		xSemaphoreGive(Binary_Handler);
		vTaskDelay(1000);
	}
}
  • 接下来用在start_task任务中创建信号量和任务
void start_task(void *pvParameters)
{				

   Binary_Handler = xSemaphoreCreateBinary();	
	
	 if (Binary_Handler == NULL)
	 {
		 printf("Semaphore Binary Creat Failed!!!\r\n");
	 }
	  xSemaphoreGive(Binary_Handler);

	 xTaskCreate((TaskFunction_t )high_task,            //任务函数
                (const char*    )"high_task",          //任务名称
                (uint16_t       )HIGH_STACK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )HIGH_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&HighTask_Handler);   //任务句柄 
	 
	 xTaskCreate((TaskFunction_t )middle_task,            //任务函数
                (const char*    )"middle_task",          //任务名称
                (uint16_t       )MIDDLE_STACK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )MIDDLE_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&MiddleTask_Handler);   //任务句柄  
								
   xTaskCreate((TaskFunction_t )low_task,            //任务函数
                (const char*    )"low_task",          //任务名称
                (uint16_t       )LOW_STACK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )LOW_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&LowTask_Handler);   //任务句柄  	 

	 vTaskDelete(StartTask_Handler);
}

  • 核心代码就这些,下面来看一下运行结果
    在这里插入图片描述
  • 从结果可以看出,其中有一段时间高优先级的任务一直在请求信号量而进入阻塞,这时中等优先级的任务会打断低优先级的任务而率先执行,高优先级的任务一直等待低优先级的任务释放信号量,相当于高优先级的任务优先级低了。

互斥信号量

  • 从上面可以看出,如果发生优先级翻转了,那么将不能保证高优先级的任务总是被率先执行,从而严重影响了系统的实时性,所以为了解决这个问题,FreeRTOS又提供了互斥信号量。互斥信号量相比于二值信号量,很大的改善了优先级翻转的问题,当高优先级任务等待低优先级的任务是释放信号量而进入阻塞状态时,会将低优先级任务的任务优先级提高到与高优先级一样,确保低优先级任务先执行,从而尽早释放信号量,释放信号量后,再将低优先级任务的优先级复原。这就是互斥信号量降低优先级翻转发生概率的原理。那么接下来看一下使用互斥信号量是否能有效地解决优先级翻转问题。
  • 同上面代码不变,这里仅将start_task中的信号量创建该为互斥信号量,代码如下。
void start_task(void *pvParameters)
{				

   Mutex_Handler = xSemaphoreCreateMutex();	
	
	 if (Mutex_Handler == NULL)
	 {
		 printf("Semaphore Binary Creat Failed!!!\r\n");
	 }
	  xSemaphoreGive(Mutex_Handler);

	 xTaskCreate((TaskFunction_t )high_task,            //任务函数
                (const char*    )"high_task",          //任务名称
                (uint16_t       )HIGH_STACK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )HIGH_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&HighTask_Handler);   //任务句柄 
	 
	 xTaskCreate((TaskFunction_t )middle_task,            //任务函数
                (const char*    )"middle_task",          //任务名称
                (uint16_t       )MIDDLE_STACK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )MIDDLE_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&MiddleTask_Handler);   //任务句柄  
								
   xTaskCreate((TaskFunction_t )low_task,            //任务函数
                (const char*    )"low_task",          //任务名称
                (uint16_t       )LOW_STACK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )LOW_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&LowTask_Handler);   //任务句柄  	   	

	 vTaskDelete(StartTask_Handler);
}

  • 运行结果如下
    在这里插入图片描述
  • 从上图可以看出,使用互斥信号量能够解决优先级翻转问题,保证高优先级任务最先执行

计数信号量

  • 所谓计数型信号量,就是当有任务发送信号量的时候,uxMessagesWaiting +1(但必须满足uxMessagesWaiting <uxLength),当有任务请求接收信号量的时候,uxMessagesWaiting -1。这就是计数型信号量,但是要注意计数型信号量的计数值必须在0-uxLength之间。下面是计数型信号量的测试代码(通过按键发送信号量,另一个任务定时获取信号量)。

  • 按键发送信号量


void key_task(void* pvParameters)
{
	
	while(1)
	{
		if (Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
		{
			xSemaphoreGive(Count_Handler);
			//count = uxSemaphoreGetCount(Count_Handler);
			//printf("count=%ld\r\n",count);
		}
		
		if (Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON)
		{
		   xSemaphoreTake(Count_Handler,10);
		}
		vTaskDelay(10);
	}
}
  • 任务定时接收信号量
void task2_task(void* pvParameters)
{
	
	//BaseType_t err;
	while(1)
	{
		count = uxSemaphoreGetCount(Count_Handler);
		printf("count=%ld\r\n",count);
		if (Count_Handler != NULL)
		{
			if (count > 5)
			{
				LED1 = 0;
			}
			else
			{
				LED1 = 1;
			}
		}
		vTaskDelay(100);
	}
}
  • 具体代码就不详细说明了,用法比较简单。

  • 到这里就将FreeRTOS中的信号量讨论完了,主要讨论了二值信号量、互斥信号量和计数信号量,这几种信号量使用方法大同小异,其中互斥信号量能够很大的弥补二值信号量可能导致的优先级翻转问题。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS-信号量 的相关文章

  • 【FreeRtos学习笔记】STM32 CubeMx——Timers(定时器)

    目录 1 软件定时器 2 示例程序 2 1 例程功能 2 2 步骤 2 3 实验结果 2 4 函数讲解 1 软件定时器 定时器是MCU常用的外设 我们在学习各种单片机时必然会学习它的硬件定时器 但是 MCU自带的硬件定时器资源是有限的 而且
  • FreeRTOS内核配置说明---FreeRTOS Kernel V10.2.1

    FreeRTOS内核是高度可定制的 使用配置文件FreeRTOSConfig h进行定制 每个FreeRTOS应用都必须包含这个头文件 用户根据实际应用来裁剪定制FreeRTOS内核 这个配置文件是针对用户程序的 而非内核 因此配置文件一般
  • 【FreeRTOS 信号量】互斥信号量

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

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 FreeRTOS延时函数有两个 分别是 vTaskDelay vTaskDelayUntil 1 vTaskDelay 任务相对延时 函数原型
  • 【FreeRTOS】队列的使用

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

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

    文章目录 任务状态 任务挂起 vTaskSuspend 取消任务挂起 vTaskResume 挂起任务调度器 vTaskSuspendAll 取消挂起任务调度器 xTaskResumeAll 代码示例 任务挂起 取消任务挂起 代码示例 挂起
  • Error: L6218E: Undefined symbol vApplicationGetIdleTaskMemory (referred from tasks.o).

    我用的是F103ZET6的板子 移植成功后 编译出现两个错误是关于stm32f10x it c 里 void SVC Handler void void PendSV Handler void 两个函数的占用问题 随后编译出现以下两个问题
  • FreeRTOS:中断配置

    目录 一 Cortex M 中断 1 1中断简介 1 2中断管理简介 1 3优先级分组定义 1 4优先级设置 1 5用于中断屏蔽的特殊寄存器 1 5 1PRIMASK 和 FAULTMASK 寄存器 1 5 2BASEPRI 寄存器 二 F
  • FreeRTOS,串口中断接收中使用xQueueOverwriteFromISR()函数,程序卡死在configASSERT

    原因 UART的中断优先级设置的太高 高于了configMAX SYSCALL INTERRUPT PRIORITY宏定义的安全中断等级 UART的中断等级小于等于宏定义的优先等级即可
  • FreeRTOS之事件

    FreeRTOS之事件 声明 本人按照正点原子的FreeRTOS例程进行学习的 欢迎各位大佬指责和批评 谢谢 一 事件定义 事件 事件集 与高数上的集合意义差不多 事件啊 其实是实现任务间通信的机制 主要用于实现多任务间的同步 但是事件类型
  • Arduino IDE将FreeRTOS用于STM32

    介绍 适用于STM32F103C8的FreeRTOS STM32F103C是一种能够使用FreeRTOS的ARM Cortex M3处理器 我们直接在Arduino IDE中开始使用STM32F103C8的FreeRTOS 我们也可以使用K
  • freeRTOS出现任务卡死的情况。

    最近在做一个产品二代升级的项目 代码是上一任工程师留下的 很多BUG 而且融合了HAL库和LL库 以及github上下载的GSM源码 很不好用 我这边是将2G模块换成了4G 且添加了单独的BLE模块 因此只在源码的基础上 去除2G和BLE代
  • FreeRTOS临界段

    1 临界段 在访问共享资源时不希望被其他任务或者中断打断的代码 这段要执行的代码称为临界段代码 2 设置临界段的目的 保护共享资源 例如 全局变量 公共函数 不可重入函数 函数里面使用 了一些静态全局变量 malloc 等 保护外设的实时性
  • 使用 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 匈牙利表示法 [重复]

    这个问题在这里已经有答案了 我是 RTOS 和 C 编程的新手 而且我仍在习惯 C 的良好实践 因此 我打开了一个使用 FreeRTOS 的项目 我注意到操作系统文件使用匈牙利表示法 我知道一点符号 但面临一些新的 标准 FreeRTOS
  • 有可用的 FreeRTOS 解释语言库吗?

    我在一家公司工作 该公司使用 FreeRTOS 为多个设备创建固件 最近 我们对新功能的要求已经超出了我们固件工程师的工作能力 但我们现在也无力雇用任何新人 即使进行微小的更改 也需要固件人员在非常低的级别上进行修改 我一直在为 FreeR
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d
  • 有关 CMake 错误的问题:没有为目标提供源

    我正在尝试使用 cmake 和 eclipse 将 FreeRtos 添加到我的项目中 但出现错误 我运行的是 debian 10 我的 cmake 版本是 3 13 4 cmake 的文件可以在以下位置找到这个 git 仓库 https

随机推荐

  • FreeRtos嵌入式操作系统学习1--操作系统原理初探

    这里由于是第一篇文章 xff0c 不讲复杂的数据机构 xff0c 也不进行代码分析 xff0c 只讲嵌入式操作系统原理 先看下面一个简单的程序 xff1a void task1 while 1 Led1 1 xff08 1 xff09 de
  • 初学四旋翼之定高

    本项目使用US 100超声波模块测高 xff0c 与飞控的通讯方式为UART 硬件连接应注意 xff1a 通常飞控的发送管脚连超声波的接收管脚 xff0c 飞控的接收管脚连超声波的发送管脚 xff08 即tx rx xff1b rx tx
  • 初学四旋翼之光流定点

    本项目使用px4flow模块测速 xff0c 与飞控的通讯方式为I2C 安装时因注意光流模块与飞控的方向 xff08 一 xff09 为什么使用光流模块 xff1f 在悬停时 xff0c 若采用开环控制 xff0c 由于一些不可控的外界因素
  • 初学JetsonTX2之部署YOLO

    本人准备使用 YOLO进行人脸检测 xff0c 硬件设备为 Jetson TX2 查阅 YOLO 官网 xff0c 要部署 YOLO xff0c 首先要安装 CUDA CUDNN OPENCV xff0c 然后部署 Darknet xff0
  • C语言,超过10位数的字符串转整型函数

    include lt stdio h gt static long str2int const char str long temp 61 0 const char p 61 str if str 61 61 NULL return 0 i
  • C语言去掉MAC地址中的冒号

    include lt stdio h gt include lt string h gt void strdel char s char del x char p char q for p 61 s q 61 s p 61 39 0 39
  • Jetson Xavier NX 套件将系统装到SSD

    目录 第一步 xff1a 虚拟机 第二步 xff1a 装SDK Manager 第三步 xff1a 将系统装到eMMC 第四步 xff1a 将系统装到SSD内 xff0c 我以新买的500G硬盘为例 第五步 xff1a 装各种库 解决问题时
  • MySQL使用.ibd文件恢复或者迁移数据库

    使用86的Alice数据库的 ibd文件备份 恢复到76数据库 xff0c 该数据库版本为8 0 17 1 创建一个表确认与原始表结构一致 将86数据库的表结构导出 xff0c 在76上执行 xff08 注 xff1a 在5 5 26版本需
  • 学习ARM反汇编工具objdump和一个简单实例

    学习ARM反汇编工具objdump和一个简单实例 参考朱有鹏ARM裸机编程 1 反汇编的原理 amp 为什么需要反汇编 arm linux objdump D led elf gt led elf dis objdump是gcc工具链中的反
  • 从零开始学习UCOSII操作系统1--UCOSII的基础知识

    从零开始学习UCOSII操作系统1 UCOSII的基础知识 前言 xff1a 首先比较主流的操作系统有UCOSII FREERTOS LINUX等 xff0c UCOSII的资料相对比其余的两个操作系统的资料是多很多的 更重要的原因是自己本
  • 从零开始学习UCOSII操作系统2--UCOSII的内核实现

    从零开始学习UCOSII操作系统2 UCOSII的内核实现 参考书籍 xff1a 嵌入式实时操作系统 COS II原理及应用 嵌入式实时操作系统uCOS II 邵贝贝 第二版 1 任务的结构 任务控制块 首先这个任务控制块是非常的大的 xf
  • 从零开始学习UCOSII操作系统4--任务管理

    从零开始学习UCOSII操作系统4 任务管理 1 重讲任务 1 任务可以是一个无限的循环 xff0c 也可以在一次执行完毕后被删除 这里需要注意的是 xff0c 任务的代码并不是真正的删除了 xff0c 而是UCOSII不再理会该任务代码
  • 从零开始学习UCOSII操作系统7--信号量

    从零开始学习UCOSII操作系统7 信号量 参考博客 xff1a 64 http blog csdn net gatiemehttps blog csdn net gatieme article details 21071379 前言 xf
  • 从零开始学习UCOSII操作系统15--总结篇

    从零开始学习UCOSII操作系统15 总结篇 前言 xff1a 在大学的时候 xff0c 我们班级上面都有很多人觉得学习UCOSII 包括UCOSIII 是没什么厉害的 xff0c 因为很多人都喜欢去学习Linux操作系统 xff0c 但是
  • 手把手教你搭建TFTP服务器

    手把手教你搭建TFTP服务器 前言 xff0c 东西来自于网络 xff0c 但是根据自己的理解写了一下建议 xff0c 记录下来 xff0c 让下次不要在网络上面浪费时间搜索 1 保证自己的虚拟机能够上网 测试方法 xff1a 里面一般都有
  • 从零开始写一个单向不循环链表

    从零开始写一个单向不循环链表 总结 xff1a 郝斌数据结构与算法课程 数据结构概述 xff1a 定义 xff1a 我们如何把现实中大量的而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器 xff08 内存 xff09 中 xff0
  • STM32-CAN通信协议

    STM32 CAN通讯协议 CAN协议简述 CAN Controller Area Network xff08 控制器局域网 xff09 xff0c 由Bosch开发的一种面向汽车的通信协议 这是目前应用最广泛的通信协议 xff0c 更是尤
  • FreeRTOS-任务运行时间统计

    FreeRTOS 任务运行时间统计 引入 上一章节中我们讲述了任务信息获取 xff0c 我们已经能够获取绝大部分任务信息了 xff0c 但是任务还有一个很重要的信息 xff0c 那就是运行时间 如果我们知道了每个任务的运行时间和占比我们就可
  • 【Linux】解决Nvidia Jetson Xavier NX开发套件开机启动时间过长问题

    环境 硬件 xff1a Jetson Xavier NX 套件 系统 xff1a Ubuntu 20 04 解决 0 现象 在使用Nvidia 的Jetson Xavier NX套件 xff0c 开发产品 xff0c 准备发布时 xff0c
  • FreeRTOS-信号量

    FreeRTOS 信号量 信号量其实就是队列的一种应用 xff0c 信号量的各种操作都是在队列的基础上建立起来的 那么既然是在队列的基础上建立的 xff0c 信号量一定具有和队列相同的属性 因此信号量也是为任务和任务 任务和中断之间通信做准