FreeRTOS笔记篇:第六章 - (二进制信号量 && 计数信号量&& 队列下)使用中断管理

测试环境如下

stm32F103C8T6
MDK keil5r
stm32cube + FreeRTOS

概述

嵌入式实时系统必须对来自环境的事件采取行动。 例如,到达以太网外围设备(事件)的数据包可能需要传递到TCP/IP堆栈进行处理(操作)。 非平凡系统将不得不服务来自多个源的事件,所有这些事件都将具有不同的处理开销和响应时间要求。 在每种情况下,必须对最佳事件处理实施策略作出判断.

  • 事件应该如何检测? 中断通常被使用,但输入也可以被轮询
  • 当使用中断时,应该在中断服务例程(ISR)中执行多少处理,在外部执行多少处理? 通常最好保持每个ISR尽可能短
  • 如何将事件传达给主(非ISR)代码,以及如何构造此代码以最佳地适应潜在异步事件的处理.

必须区分任务的优先级和中断的优先级:

  • 任务是与Free RTOS运行的硬件无关的软件特性。 任务的优先级由应用程序编写器在软件中分配,软件算法(调度器)决定哪个任务将处于运行状态。
  • 虽然是用软件编写的,但中断服务例程是硬件特性,因为硬件控制中断服务例程将运行的硬件,以及它将运行的时间。 任务只有在没有ISR运行的情况下才会运行,因此最低优先级中断会中断最高优先级的任务,并且任务没有办法预先阻止ISR

概况

本章旨在让读者对:有一个很好的认识:

  • 哪些免费RTOSAPI函数可以从中断服务例程中使用。
  • 将中断处理推迟到任务的方法。
  • 如何创建和使用二进制信号量和计数信号量。
  • 二进制和计数信号量的区别。
  • 如何使用队列将数据输入和输出中断服务例程。
  • 一些空闲RTOS端口可用的中断嵌套模型。

中断对RTOS的影响

Interrupt Service Routines(中断服务程序)
无效操作:

  • 如果从ISR(中断)调用API函数,则不会从任务调用API函数,因此可以放置到阻塞状态的调用任务
  • RTOS通过提供两个版本的一些API函数来解决这个问题:一个版本用于任务,一个版本用于ISR
  • 中断在执行完成后,任务会进行调度,会执行任务优先级更高的任务。

在任务执行任何处理之前,中断可以执行不止一次。 例如,考虑一个场景,其中任务处理由中断驱动的UART接收的字符串;每次接收字符时,UART ISR切换到任务将是浪费的,因为任务只有在接收到完整字符串后才能执行处理。

中断请求任务切换的宏

  • portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
  • portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

xHigherPriorityTaskWoken参数说明:从中断安全API函数中传递出来的r可以直接用作调用中的参数

  • xHigherPriorityTaskWoken假如是pdFALSE(0),那么则不切换任务。
  • xHigherPriorityTaskWoken参数不是pd false,然后请求切换任务,并且运行状态中的任务可能会更改。 中断将始终返回到运行状态下的任务,即使在中断执行时,运行状态下的任务发生了变化。

延迟中断处理

通常认为最好的做法是尽量缩短ISR。 原因包括:

  • 即使任务被分配了非常高的优先级,它们也只能在没有中断被硬件服务的情况下运行。
  • 中断可以破坏任务的开始时间和执行时间(添加“抖动
  • 根据Free RTOS运行的体系结构,在执行中断时,可能不可能接受任何新的中断,或者至少一个新中断的子集
  • 应用程序编写器需要考虑任务和ISR同时访问的变量、外围设备和内存缓冲区等资源的后果并加以防范
  • 一些自由RTOS端口允许中断嵌套,但中断嵌套可以增加复杂性并降低可预测性。 中断越短,越不可能嵌套

中断服务例程必须记录中断的原因并清除中断。 中断所需要的任何其他处理通常可以在任务中执行,从而允许中断服务例程尽可能快地退出。 这被称为“延迟中断处理”,因为中断所需的处理从ISR“延迟”到任务。

如果中断处理被推迟的任务的优先级高于任何其他任务的优先级,那么处理将立即执行,就像处理是在ISR本身中执行的一样。 此场景如图48所示,其中Task1是正常的应用任务,Task2是中断处理被推迟的任务
在这里插入图片描述
中断处理从时间T2开始,有效地在时间T4结束,但只有时间T2和T3之间的周期在ISR中度过。 如果没有使用延迟中断处理,那么T2和T4之间的整个期间将在ISR中度过。

对于什么时候最好执行ISR中中断所需要的所有处理以及什么时候最好将部分处理推迟到任务上,没有绝对的规则。 将处理延迟到任务是最有用的

  • 中断所需要的处理并非微不足道。 例如,如果中断只是存储模拟到数字转换的结果,那么几乎可以肯定这是最好的在ISR中执行的,但是如果转换的结果也必须通过软件过滤器传递,那么在任务中执行过滤器可能是最好的。
  • 中断处理可以方便地执行不能在ISR中执行的操作,例如写入控制台或分配内存
  • 中断处理不是确定性的-这意味着不知道处理需要多长时间。

二进制信号量

二进制信号量API的中断安全版本可用于在每次发生特定中断时解除任务阻塞,有效地使任务与中断同步。 这允许大多数中断事件处理在同步任务中实现,只有非常快和短的部分直接保留在ISR中。 如上一节所述,二进制信号量用于将中断处理推迟到任务

如果中断处理特别关键,那么可以设置延迟处理任务的优先级,以确保任务始终抢占系统中的其他任务。 然后可以实现ISR,包括对portYIELD_FROM_ISR()的调用,确保ISR直接返回到中断处理被延迟的任务。 这样做的效果是确保整个事件处理在时间上连续执行(没有中断),就像它都是在ISR本身中实现的一样。 图49重复了图48中所示的场景,但是更新了文本以描述如何使用信号量控制延迟处理任务的执行
在这里插入图片描述
延迟处理任务使用对信号量的阻塞“接受”调用作为进入阻塞状态以等待事件发生的手段在同一信号量上的**“give”**操作,以解除任务的阻塞,以便所需的事件处理可以继续进行

“取一个信号量”和“给出一个信号量”是不同含义的概念,取决于它们的使用场景。在这个中断同步场景中,二进制信号量在概念上可以被认为是一个长度为1的队列。 队列可以在任何时候包含最多一个项,因此总是空的或满的(因此是二进制的)。 通过调用xSemaphoreTake(),中断处理被延迟的任务有效地尝试从队列中读取阻塞时间,如果队列为空,则导致任务进入阻塞状态。

当事件发生时,ISR使用 **xSemaphoreGiveFromISR()**函数将令牌(信号量)放入队列,使队列满。 这将导致任务退出阻塞状态并删除令牌,使队列再次为空。 当任务完成处理后,它再次尝试从队列中读取,并在发现队列为空时,重新输入阻塞状态以等待下一个事件。 这个序列如图50所示

显示中断“给出”信号量,即使它没有先“拿走”它,任务“拿走”信号量,但永远不会还给它。 这就是为什么这个场景被描述为在概念上类似于对队列的写入和读取。 它通常会引起混乱,因为它不遵循与其他信号量使用场景相同的规则,其中使用信号量的任务必须始终将其返回-例如第7章“资源管理”中描述的场景。
在这里插入图片描述
解释:信号量失效、所以信号量任务会阻塞。
在这里插入图片描述
这个时候中断来了,我们将信号量给信号量任务
在这里插入图片描述
这个时候,信号量任务收到信号,解除阻塞状态,可运行。

xSemaphoreCreateBinary()

所有类型的空闲RTOS信号量都存储在SemaphoreHandle_t类型的变量中
在这里插入图片描述
使用:SemaphoreHandle_t xSemaphoreCreateBinary( void );
返回:如果返回NULL,则无法创建信号量,因为Free RTOS没有足够的堆内存来分配信号量数据结构。

返回的非空值表示信号量已成功创建。 返回的值应该作为创建的信号量的句柄存储。

xSemaphoreTake()

“获取”信号量意味着“获取”或“接收”信号量。 信号量只有在可用时才能被取走
所有类型的 FreeRTOS信号量,除了递归互斥,都可以使用xSemaphoreTake()函数“取。
注意!!!不得从中断服务例程中使用()

参数说明:

  • xSemaphore:信号量由SemaphoreHandle_t类型的变量引用。 必须显式地创建它才能使用它
  • xTicksToWait:–
  • Returned value : 已经解释

xSemaphoreGiveFromISR() &&xSemaphoreGive()

二进制和计数信号量可以使用xSemaphoreGiveFromISR()

xSemaphoreGiveFromISR()相比于xSemaphoreGive()是中断安全版本。
pxHigherPriorityTaskWoken
在这里插入图片描述
参数说明:
在这里插入图片描述

  • xSemaphore:不解释
  • pxHigherPriorityTaskWoken : 有可能单个信号量将有一个或多个任务被阻塞,等待信号量可用,调用xSemaphoreGiveFromISR()可以使信号量可用,因此导致等待信号量离开阻塞状态的任务
    如果调用xSemaphoreGiveFromISR()会导致任务离开阻塞状态,并且未阻塞的任务具有高于当前执行任务(被中断的任务)的优先级,那么在内部,xSemaphoreGiveFromISR()将will
    set *pxHigherPriorityTaskWoken to pdTRUE.
    如果xSemaphoreGiveFromISR()将此值设置为pd true,则通常应在中断退出之前执行任务切换。 这将确保中断直接返回到最高优先级Ready状态任务。

案例测试1-二进制信号量中断

stm32F103C8T6
MDK keil5r
stm32cube + FreeRTOS

使用
在这里插入图片描述
由于STM32比较垃圾,所以这边就直接简单hua
PB14给控制硬
PB15输入中断做信号
中断函数:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	BaseType_t xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;
	xSemaphoreGiveFromISR( myBinarySem01Handle, &xHigherPriorityTaskWoken );
	if(GPIO_Pin==NVIC_PB15_Pin)
	{
		LED1_TOGGLE;
		LED2_TOGGLE;
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

任务一:

void TaskLed1Func(void const * argument)
{

  /* USER CODE BEGIN TaskLed1Func */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1000);
		printf("Periodic task - About to generate an interrupt.\r\n");
		PB14_TOGGLE;
		printf("Periodic task - Interrupt generated.\r\n\r\n\r\n");
  }
  /* USER CODE END TaskLed1Func */
}

任务二:

void TaskLed2Func(void const * argument)
{
  /* USER CODE BEGIN TaskLed2Func */
  /* Infinite loop */
  for(;;)
  {
		xSemaphoreTake(myBinarySem01Handle,10000);
		printf("LED2 is running !!! \r\n");
  }
  /* USER CODE END TaskLed2Func */
}

结果:
在这里插入图片描述
在这里插入图片描述
程序执行流程如下:

  • 中断发生了
  • 中断服务函数执行并“给出”信号量以解除tas的阻塞
  • 任务二在中断服务函数之后立即执行,并“获取”信号量
  • 任务二处理事件,然后尝试再次“获取”信号量-进入阻塞状态,因为信号量尚未可用(另一个中断尚未发生)。

频繁中断处理(未完成)

只有当中断发生在相对较低的频率时,示例16中使用的任务的结构才是足够的。 为了理解为什么,考虑如果在任务完成第一个中断的处理之前发生了第二个中断,然后发生了第三个中断,会发生什么:

  • 当第二个ISR执行时,信号量将为空,因此ISR将给出信号量,任务将在完成处理第一个事件后立即处理第二个事件。 该场景如图53所示。
  • 当第三个ISR执行时,信号量已经可用,从而阻止ISR再次给出信号量,因此任务将不知道第三个事件已经发生。 该场景如图54所示。

计数信号量

正如二进制信号量可以被认为是长度为1的队列一样,计数信号量也可以被认为是长度大于1的队列。 任务对存储在队列中的数据不感兴趣-只是队列中的项数。 configUSE_COUNTING_SEMAPHORES必须在FreeRTOSConfig.h中设置为1,以便计数信号量。

计数信号量通常用于两件事:

  • 计数事件
    在这种情况下,事件处理程序将在每次事件发生时“给予”一个信号量-导致信号量的计数值在每个“给予”上增加’。 任务每次处理一个事件时都会“获取”一个信号量,从而使信号量的计数值在每个“获取”时减少’。 计数值是已发生事件的数量与已处理的数量之间的差额。 此机制如图55所示。
  • 资源管理
    在此场景中,计数值指示可用资源的数量。 要获得对资源的控制,任务必须首先获得信号量-递减信号量的计数值。 当计数值达到零时,没有空闲资源。 当任务完成资源时,它会“返回”信号量-增加信号量的计数值
    创建用于管理资源的计数信号量,使其初始计数值等于可用资源的数量。 第7章涉及使用信号量来管理资源。
    在这里插入图片描述
    在这里插入图片描述

xSemaphoreCreateCounting()

在这里插入图片描述

xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 );

参数说明:

  • uxMaxCount:
    信号量将计数到的最大值。 为了继续队列类比,uxMaxCount最大计数值实际上是队列的长度。 当信号量用于计数或锁存事件时,uxMaxCount是可以锁存的事件的最大数量。 当要使用信号量来管理对资源集合的访问时,UxMaxCount应该设置为可用的资源总数。
  • uxInitialCount:
    信号量创建后的初始计数值。 当信号量用于计数或锁定事件时,uxInitialCount应该设置为0-大概是,当创建信号量时,还没有发生任何事件。 当要使用信号量来管理对资源集合的访问时,UX初始计数应该设置为等于UX计数-大概,当创建信号量时,所有资源都是可用的

案例测试2-计数信号量使用-高频中断

通过使用计数信号量代替二进制信号量。
为了模拟高频发生的多个事件,中断服务例程被更改为“给予”每个中断不止一次的信号量。 每个事件都锁在信号量的计数值中。 修改后的中断服务例程如清单99所示。

修改部分:
在这里插入图片描述

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	BaseType_t xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;
	
	xSemaphoreGiveFromISR( myCountingSem01Handle, &xHigherPriorityTaskWoken );
	xSemaphoreGiveFromISR( myCountingSem01Handle, &xHigherPriorityTaskWoken );
	xSemaphoreGiveFromISR( myCountingSem01Handle, &xHigherPriorityTaskWoken );
	
	if(GPIO_Pin==NVIC_PB15_Pin)
	{
		LED1_TOGGLE;
		LED2_TOGGLE;
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

结果:
在这里插入图片描述

集中式延迟中断处理

集中式延迟中断处理的优点包括:

  • 降低资源使用率.它消除了为每个延迟中断创建单独任务的需要。
  • 简化用户模型延迟中断处理函数是一个标准的C函数。 集中式延迟中断处理的缺点包括:
  • 较少的灵活性.不可能单独设置每个延迟中断处理任务的优先级。 每个延迟中断处理函数都以守护进程任务的优先级执行。 如第5章所述,守护进程任务的优先级由Free RTOSConfig.h中的configTIMER_TASK_PRIORITY编译时间配置常量设置。

xTimerPendFunctionCallFromISR()

xTimerPendFunctionCallFromISR() 是中断安全版本的xTimerPendFunctionCall()这两个API函数都允许应用程序编写器提供的函数被执行,因此在上下文中RTOS守护进程任务。 要执行的函数和函数输入参数的值都被发送到计时器命令队列上的守护进程任务。 因此,当函数实际执行时,依赖于守护进程任务相对于应用程序中其他任务的优先级。

参数说明:

BaseType_t xTimerPendFunctionCallFromISR( 
PendedFunction_t xFunctionToPend, 
void *pvParameter1, 
uint32_t ulParameter2, 
BaseType_t *pxHigherPriorityTaskWoken )
  • xFunctionToPend:
    指向将在守护进程任务中执行的函数的指针(实际上,只是函数名)。 函数的原型必须与清单101所示相同。
  • pvParameter1:
    将作为函数的pvParameter1参数传递给由守护进程任务执行的函数的值。 该参数有一个void类型,允许它用于传递任何数据类型。 例如,整数类型可以直接转换为void,或者void*可以用来指向结构
  • ulParameter2:
    将作为函数的ulParameter2参数传递给守护进程任务执行的函数的值
  • pxHigherPriorityTaskWoken

案例测试3-集中式延迟中断处理-高频中断

示例18提供了与示例16相似的功能,但不使用信号量,也不创建特定于执行中断所需处理的任务。 相反,处理由RTOS守护进程任务执行。

所示。 它调用来自ISR的xTimerPendFunctionCallFromISR()将指针传递到名为vDeferredHandlingFunction()到守护进程任务。 延迟中断处理由e vDeferredHandlingFunction() f函数执行。

中断服务例程每次执行时都会增加一个名为ulParameterValue的变量。

修改部分:

static void vDeferredHandlingFunction( void *pvParameter1, uint32_t ulParameter2 )
{ 
 /* Process the event - in this case just print out a message and the value of 
 ulParameter2. pvParameter1 is not used in this example. */
 printf( "Handler function - Processing event = %d\r\n", ulParameter2 );
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	static uint32_t ulParameterValue = 0;
	
	xTimerPendFunctionCallFromISR(vDeferredHandlingFunction,
																NULL,
																ulParameterValue,
																&xHigherPriorityTaskWoken
																);
	ulParameterValue++;
	
	if(GPIO_Pin==NVIC_PB15_Pin)
	{
		LED1_TOGGLE;
		LED2_TOGGLE;
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

结果:
在这里插入图片描述
原理说明:
在这里插入图片描述

  • 空闲任务大部分时间都在运行。 每500ms它就会被周期任务预先加密。
  • 周期任务打印它的第一个消息,然后强制中断。 中断服务例程立即执行。
  • -中断调用来自s xTimerPendFunctionCallFromISR(),该函数写入计时器命令队列,导致守护进程任务解除阻塞。 中断服务例程然后直接返回到守护进程任务,因为守护进程任务是最高优先级的就绪状态任务。 守护进程任务打印出它的消息,包括增量参数值,然后返回到阻塞状态,等待另一条消息到达计时器命令队列,或者软件计时器过期
  • -4-周期任务再次是最高优先级任务-它在再次进入阻塞状态以等待下一个时间段之前打印出第二条消息。 这只剩下空闲任务可以运行

在中断服务例程中使用队列

二进制和计数信号量用于通信事件。 队列用于通信事件和传输数据。

xQueueSendToFrontFromISR()

在这里插入图片描述
参数说明:

  • xQueue
    数据被发送到的队列的句柄(写入)。 队列句柄将从调用返回到用于创建队列的xQueue Create()。
  • pvItemToQueue:
    指向将复制到队列中的数据的指针。 当创建队列时,将设置队列可以容纳的每个项的大小,因此将从pv Item到队列的许多字节复制到队列存储区域
  • pxHigherPriorityTaskWoken

使用ISR队列时的考虑

  • 队列提供了一种简单方便的方式将数据从中断传递到任务,但如果数据到达高频,则使用队列是无效的。
  • FreeRTOS下载中的许多演示应用程序包括一个简单的UART驱动程序,它使用队列将字符从UART的接收ISR中传递出去在这些演示中,使用队列有两个原因:演示从ISR使用队列,以及故意加载系统以测试Free RTOS端口。 以这种方式使用队列的ISR肯定不打算表示有效的设计,除非数据到达速度减慢,建议生产代码不复制该技术。 更有效的技术,适合生产代码。
  • 使用直接内存访问(DMA)硬件接收和缓冲字符。 这种方法实际上没有软件开销。 然后,可以使用直接指向任务通知1来解除对任务的阻塞,该任务只有在检测到传输中断后才会处理缓冲区。
  • 将每个接收到的字符复制到线程安全RAMBuffer2中。 同样,直接到任务通知可以用来解除在接收到完整消息后或在检测到传输中断后处理缓冲区的任务

xQueueSendToBackFromISR() && xQueueReceiveFromISR()

在这里插入图片描述
参数说明:

  • xQueue
    数据被发送到的队列的句柄(写入)。 队列句柄将从调用返回到用于创建队列的xQueue Create()。
  • pvItemToQueue:
    指向将复制到队列中的数据的指针。 当创建队列时,将设置队列可以容纳的每个项的大小,因此将从pv Item到队列中复制许多字节到队列中
  • pxHigherPriorityTaskWoken

案例测试4-中断服务例程中使用队列

此示例演示xQueueSendToBackFromISR()以及在同一中断中使用 xQueueReceiveFromISR()队列。 和以前一样,为了方便起见,中断是由软件生成的。

创建一个周期任务,每200毫秒向队列发送五个数字。 只有在发送了所有五个值之后,它才会生成一个软件中断。 任务实现情况显示

队列定义

osThreadId TaskLed1Handle;
osThreadId TaskLed2Handle;
osMessageQId xIntegerQueueHandle;
osMessageQId xStringQueueHandle;
osSemaphoreId myCountingSem01Handle;

  osMessageQDef(xIntegerQueue, 10, uint16_t);
  xIntegerQueueHandle = osMessageCreate(osMessageQ(xIntegerQueue), NULL);

  /* definition and creation of xStringQueue */
  osMessageQDef(xStringQueue, 10, uint16_t);
  xStringQueueHandle = osMessageCreate(osMessageQ(xStringQueue), NULL);

任务一:发送5个整数给整数队列

void TaskLed1Func(void const * argument)
{
  /* USER CODE BEGIN TaskLed1Func */
	TickType_t xLastExecutionTime;
	uint32_t ulValueToSend = 0;
	int i;
	
	xLastExecutionTime = xTaskGetTickCount();
  /* Infinite loop */
  for(;;)
  {
   vTaskDelayUntil( &xLastExecutionTime, pdMS_TO_TICKS( 200 ) );
		
		for( i = 0; i < 5; i++ )
	 {//·¢Å¶5¸öÕûÊý¸ø¶ÓÁУ¬È»ºó½øÖжÏ
	 xQueueSendToBack( xIntegerQueueHandle, &ulValueToSend, 0 );
	 ulValueToSend++;
	 }
		
		printf("Periodic task - About to generate an interrupt.\r\n");
		PB14_TOGGLE;
		printf("Periodic task - Interrupt generated.\r\n\r\n\r\n");
  }
  /* USER CODE END TaskLed1Func */
}

任务二:判断字符队列是否有数据,有数据就打印数据

void TaskLed2Func(void const * argument)
{
  /* USER CODE BEGIN TaskLed2Func */
	char *pcString;
  /* Infinite loop */
  for(;;)
  {
		xQueueReceive( xStringQueueHandle, &pcString, portMAX_DELAY );
		printf("task is  = %x !!! \r\n",pcString);
  }
  /* USER CODE END TaskLed2Func */
}

中断:使用收到整数队列判断,并将字符队列填充,让字符打印任务工作。

static void vDeferredHandlingFunction( void *pvParameter1, uint32_t ulParameter2 )
{ 
 /* Process the event - in this case just print out a message and the value of 
 ulParameter2. pvParameter1 is not used in this example. */
 printf( "Handler function - Processing event = %d\r\n", ulParameter2 );
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	uint32_t ulReceivedNumber;
	
static const char *pcStrings[] =
{
 "String 0\r\n",
 "String 1\r\n",
 "String 2\r\n",
 "String 3\r\n"
};
	while( xQueueReceiveFromISR( xIntegerQueueHandle, 
															 &ulReceivedNumber, 
															 &xHigherPriorityTaskWoken ) != errQUEUE_EMPTY )
 {
 /* Truncate the received value to the last two bits (values 0 to 3
 inclusive), then use the truncated value as an index into the pcStrings[]
 array to select a string (char *) to send on the other queue. */
		 ulReceivedNumber &= 0x03;
		 xQueueSendToBackFromISR( xStringQueueHandle, 
		 &pcStrings[ ulReceivedNumber ], 
		 &xHigherPriorityTaskWoken );
 }
	
	if(GPIO_Pin==NVIC_PB15_Pin)
	{
		LED1_TOGGLE;
		LED2_TOGGLE;
	}
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

结果图:
在这里插入图片描述
代码流程图:
在这里插入图片描述
详细说明如下:

  • 1-空闲任务大部分时间都在运行。 每200ms它就会被整数生成器任务抢占
  • 2-整数生成器将5个值写入队列,然后强制中断。
  • 3中断服务例程既从队列中读取,又写入队列,为从另一个队列接收的每个整数向一个队列写入字符串。 将字符串写入队列将解禁String Printer任务。
  • 4-String Printer任务是最高优先级的任务,因此在中断服务例程之后立即运行。 它打印出它在队列上接收到的每个字符串-当队列为空时,它进入阻塞状态,允许较低优先级的IntegerGenerator任务再次运行。
  • Integer Generator任务是一个周期性的任务,因此需要等待下一个时间段。 最后一次开始执行整个序列重复后200ms

中断嵌套(未完成)

任务优先级和中断优先级之间出现混淆是常见的。 本节讨论中断优先级,这是中断服务例程(ISR)相对于彼此执行的优先级。 分配给任务的优先级与分配给中断的优先级没有任何关系。 硬件决定ISR何时执行,而软件决定任务何时执行。 响应硬件中断而执行的ISR将中断任务,但任务不能抢占ISR。

支持中断嵌套的端口需要在FreeRTOSConfig.h中定义表39中详细列出的一个或两个常量。 configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY都定义了相同的属性。 旧的免费RTOS端口使用configMAX_SYSCALL_INTERRUPT_PRIORITY,新的免费RTOS端口使用configMAX_API_CALL_INTERRUPT_PRIORITY。

  • configMAX_SYSCALL_INTERRUPT_PRIORITY or
    configMAX_API_CALL_INTERRUPT_PRIORITY
    设置可以调用中断安全的FreeRTOSAPI函数的最高中断优先级
  • configKERNEL_INTERRUPT_PRIORITY
    设置滴答中断使用的中断优先级,并且必须始终设置为尽可能低的中断优先级。 如果正在使用的Free RTOS端口也不使用configMAX_SYSCALL_INTERRUPT_PRIORITY常数,那么任何使用中断安全的Free RTOSAPI函数的中断也必须以configKERNEL_INTERRUPT_PRIORITY定义的优先级执行。

数字优先级 && 逻辑优先级

数字优先级只是分配给中断优先级的数字。 例如,如果给中断分配了7的优先级,那么它的数字优先级是7。 同样,如果一个中断被分配了200的优先级,那么它的数字优先级是200。

中断的逻辑优先级描述了中断相对于其他中断的优先级。 如果两个优先级不同的中断同时发生,那么处理器将执行ISR,因为这两个中断中的任何一个具有较高的逻辑优先级,然后执行ISR,因为这两个中断中的任何一个具有较低的逻辑优先级。 中断可以中断(nest with)任何具有较低逻辑优先级的中断,但中断不能中断(nest with)任何具有相等或较高逻辑优先级的中断

中断的数字优先级和逻辑优先级之间的关系取决于处理器体系结构;在某些处理器上,分配给中断的数字优先级越高,中断的逻辑优先级就越高,而在其他处理器体系结构中,分配给中断的数字优先级越高,中断的逻辑优先级就越低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值