FreeRTOS学习笔记5——Interrupt
xHigherPriorityTaskWoken
portYIELD_FROM_ISR() and portEND_SWITCHING_ISR()
延迟中断处理Deferred Interrupt Processing
Binary Semaphores Used for Synchronization
The xSemaphoreCreateBinary() API Function
The xSemaphoreTake() API Function
The xSemaphoreGiveFromISR() API Function
Examples
Example Improve
Counting Semaphores
xSemaphoreCreateCounting() API
example
Deferring Work to the RTOS Daemon Task
xTimerPendFunctionCallFromISR()
examples
Using Queues within an Interrupt Service Routine
The xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() API Function
Considerations When Using a Queue From an ISR
examples
Interrupt Nesting
example
ARM Cortex-M1和ARM GIC注意点
FreeRTOS Interrupt Management
FreeRTOS在任务和中断中使用两个版本API,以必免造成阻塞,中断使用的API以
FromISR
结尾
xHigherPriorityTaskWoken
- 如果在中断中应用上下文切换(
xHigherPriorityTaskWoken=pdTRUE
),中断将中断一个进程,而退出中断后切换到另一个进程中去。 - 如果被唤醒的进程的优先级大于当前运行的进程时,将切换至优先级高的进程,
抢占策略
- 线程安全API(以FromISR结尾的API),有一个指针参数
pxHigherPriorityTaskWoken
,用以指示当前中断是否需要进行上下文切换- 当需要进行上下文切换时,API(FromISR)会将
*pxHigherPriorityTaskWoken=pdTRUE
, 在使用API(FromISR)之前,必需将pxHigherPriorityTaskWoken 初使化为pdFALSE - 若ISR中调用多个API(FromISR),时,必须在调用每个API之前将
pxHigherPriorityTaskWoken
初使化为pdFALSE
- 当需要进行上下文切换时,API(FromISR)会将
- pxHigherPriorityTaskWoken 是可选的,当不用时可以将pxHigherPriorityTaskWoken 设为NULL。
portYIELD_FROM_ISR() and portEND_SWITCHING_ISR()
taskYIELD()
是宏 用于在task中请求context切换- portYIELD_FROM_ISR(), portEND_SWITCHING_ISR()用法相同,都是
taskYIELD()
的 safe version.
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
The xHigherPriorityTaskWoken parameter passed out of an interrupt safe API function can be
used directly as the parameter in a call to portYIELD_FROM_ISR()
- 大部分FreeRTOS ports允许在ISR中的任何位置调用
portYIELD_FROM_ISR()
- 有一小部分 ports 只允许在ISR尾部调用
portYIELD_FROM_ISR()
延迟中断处理Deferred Interrupt Processing
- 中断处理要尽可能短
An interrupt service routine must record the cause of the interrupt, and clear the interrupt. Any other processing necessitated by the interrupt can often be performed in a task, allowing the interrupt service routine to exit as quickly as is practical. This is called deferred interrupt processing
, because the processing necessitated by the interrupt is deferred
from the ISR to a task
Binary Semaphores Used for Synchronization
二进制信号量用于同步
- Binary Semaphores 通常用于将中断处理程序延迟至task中处理的目的。
the binary semaphore is used to ‘defer’ interrupt processing
to a task1. - 若ISR处理程序,时间非常紧急,可以将
defered task
优先级设置为最高,并且调用portYIELD_FROM_ISR(),确保ISR退出时,直接回到defered task
中去执行程序。就好像所有的处理都是在ISR中完成的一样。
if the interrupt processing is particularly time critical, then the priority of the deferred processing task can be set to ensure the task always
preempts
the other tasks in the system. The ISR can then be implemented to include a call toportYIELD_FROM_ISR()
, ensuring the ISR returns directly to the task to which interrupt processing is beingdeferred
. This has the effect of ensuring the entire event processing executes contiguously(without a break)
in time, just as if it had all been implemented within the ISR itself. Figure 49 repeats the scenario shown in Figure 48, but with the text updated to describe how the execution of the deferred processing task can be controlled using a semaphore.
Taking a semaphore
和giving a semaphore
根据使用环境的不同具有不同的意义,在中断同步的场景下,binary semaphore
可以设想为长度为1的队列Taking a semaphore
andgiving a semaphore
are concepts that have different meanings depending on their usage scenario. In this interrupt synchronization scenario, the binary semaphore can be considered conceptually as a queue with a length of one.- Using a binary semaphore to synchronize a task with an interrupt
The xSemaphoreCreateBinary() API Function
FreeRTOS V9.0.0 提供了API
xSemaphoreCreateBinaryStatic()
,用于在编译时静态声明一个binary semaphore的内存。
- API原形
SemaphoreHandle_t xSemaphoreCreateBinary( void );
The xSemaphoreTake() API Function
- API原形
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
- 返回值
pdPASS,pdFALSE
The xSemaphoreGiveFromISR() API Function
xSemaphoreGiveFromISR()
is the interrupt safe version ofxSemaphoreGive()
,BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore)
- API原形
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken );
- 返回值
pdPASS,pdFALSE
Examples
- 周期性产生软件中断
/* The number of the software interrupt used in this example. The code shown is from
the Windows project, where numbers 0 to 2 are used by the FreeRTOS Windows port
itself, so 3 is the first number available to the application. */
#define mainINTERRUPT_NUMBER 3
static void vPeriodicTask( void *pvParameters )
{
const TickType_t xDelay500ms = pdMS_TO_TICKS( 500UL );
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Block until it is time to generate the software interrupt again. */
vTaskDelay( xDelay500ms );
/* Generate the interrupt, printing a message both before and after
the interrupt has been generated, so the sequence of execution is evident
from the output.
The syntax used to generate a software interrupt is dependent on the
FreeRTOS port being used. The syntax used below can only be used with
the FreeRTOS Windows port, in which such interrupts are only simulated. */
vPrintString( "Periodic task - About to generate an interrupt.\r\n" );
vPortGenerateSimulatedInterrupt( mainINTERRUPT_NUMBER );
vPrintString( "Periodic task - Interrupt generated.\r\n\r\n\r\n" );
}
}
- 中断延时处理的task
下面的task虽然处理软件产生的中断是可以的,但是对于硬件产生的中断,还需要调整
static void vHandlerTask( void *pvParameters )
{
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Use the semaphore to wait for the event. The semaphore was created
before the scheduler was started, so before this task ran for the first
time. The task blocks indefinitely, meaning this function call will only
return once the semaphore has been successfully obtained - so there is
no need to check the value returned by xSemaphoreTake(). */
xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );
/* To get here the event must have occurred. Process the event (in this
Case, just print out a message). */
vPrintString( "Handler task - Processing event.\r\n" );
}
}
- ISR
static uint32_t ulExampleInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken;
/* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as
it will get set to pdTRUE inside the interrupt safe API function if a
context switch is required. */
xHigherPriorityTaskWoken = pdFALSE;
/* 'Give' the semaphore to unblock the task, passing in the address of
xHigherPriorityTaskWoken as the interrupt safe API function's
pxHigherPriorityTaskWoken parameter. */
xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );
/* Pass the xHigherPriorityTaskWoken value into portYIELD_FROM_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR()
then calling portYIELD_FROM_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling
portYIELD_FROM_ISR() will have no effect. Unlike most FreeRTOS ports, the
Windows port requires the ISR to return a value - the return statement
is inside the Windows version of portYIELD_FROM_ISR(). */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
- main
中断回调函数的注册语法,根据ports会有不同,要查阅具体的ports规定
int main( void )
{
/* Before a semaphore is used it must be explicitly created. In this example
a binary semaphore is created. */
xBinarySemaphore = xSemaphoreCreateBinary();
/* Check the semaphore was created successfully. */
if( xBinarySemaphore != NULL )
{
/* Create the 'handler' task, which is the task to which interrupt
processing is deferred. This is the task that will be synchronized with
the interrupt. The handler task is created with a high priority to ensure
it runs immediately after the interrupt exits. In this case a priority of
3 is chosen. */
xTaskCreate( vHandlerTask, "Handler", 1000, NULL, 3, NULL );
/* Create the task that will periodically generate a software interrupt.
This is created with a priority below the handler task to ensure it will
get preempted each time the handler task exits the Blocked state. */
xTaskCreate( vPeriodicTask, "Periodic", 1000, NULL, 1, NULL );
/* Install the handler for the software interrupt. The syntax necessary
to do this is dependent on the FreeRTOS port being used. The syntax
shown here can only be used with the FreeRTOS windows port, where such
interrupts are only simulated. */
vPortSetInterruptHandler( mainINTERRUPT_NUMBER, ulExampleInterruptHandler );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}
/* As normal, the following line should never be reached. */
for( ;; );
}
Example Improve
上面的例程,只适用于中断频率比较低的情况,若中断频率较快,会出现中断丢失的现象。
无限等待事件发生,经常出现在例程中,是因为这样简单一些,但在实际项目中这样做,会出现错误,而又无法从错误中恢复的情况
static void vUARTReceiveHandlerTask( void *pvParameters )
{
/* xMaxExpectedBlockTime holds the maximum time expected between two interrupts. */
const TickType_t xMaxExpectedBlockTime = pdMS_TO_TICKS( 500 );
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* The semaphore is 'given' by the UART's receive (Rx) interrupt.
maximum of xMaxExpectedBlockTime ticks for the next interrupt. */ | Wait a |
if( xSemaphoreTake( xBinarySemaphore, xMaxExpectedBlockTime ) == pdPASS )
{
/* The semaphore was obtained. Process ALL pending Rx events before
calling xSemaphoreTake() again. Each Rx event will have placed a
character in the UART’s receive FIFO, and UART_RxCount() is assumed to
return the number of characters in the FIFO. */
while( UART_RxCount() > 0 )
{
/* UART_ProcessNextRxEvent() is assumed to process one Rx character,
reducing the number of characters in the FIFO by 1\. */
UART_ProcessNextRxEvent();
}
/* No more Rx events are pending (there are no more characters in the
FIFO), so loop back and call xSemaphoreTake() to wait for the next
interrupt. Any interrupts occurring between this point in the code and
the call to xSemaphoreTake() will be latched in the semaphore, so will
not be lost. */
}
else
{
/* An event was not received within the expected time. Check for, and if
necessary clear, any error conditions in the UART that might be
preventing the UART from generating any more interrupts. */
UART_ClearErrors();
}
}
}
Counting Semaphores
- 就像二进制信号量()可以看成长度为1的队列,Counting Semaphores 可以看成长度大于1的队列
- 使用
Counting Semaphores
,需要在头文件FreeRTOSConfig.h中设置configUSE_COUNTING_SEMAPHORES
为1- 应该场景scenario
- 事件记数
- 资源管理
xSemaphoreCreateCounting() API
FreeRTOS V9.0.0 提供了API
xSemaphoreCreateCountingStatic()
,用于在编译时静态声明一个counting semaphore。
处理FreeRTOS所有semaphore类型,都存在类型为SemaphoreHandle_t
变量中
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount );
- uxMaxCount
- 当用于事件捕获时,
uxMaxCount
应设置为能事件捕获的最大数值 - 当用于资源管理时,
uxMaxCount
应设置为资源的最大数量
- 当用于事件捕获时,
- uxInitialCount
- 当用于事件捕获时,
uxInitialCount
应设置为0,假设创建时还没有事件发生 - 当用于资源管理时,
uxInitialCount
应设置为uxMaxCount
一样的数值,因为资源还没有被使用。
- 当用于事件捕获时,
example
/* Before a semaphore is used it must be explicitly created. In this example a
counting semaphore is created. The semaphore is created to have a maximum count
value of 10, and an initial count value of 0\. */
xCountingSemaphore = xSemaphoreCreateCounting( 10, 0 );
static uint32_t ulExampleInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken;
/* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as it
will get set to pdTRUE inside the interrupt safe API function if a context switch
is required. */
xHigherPriorityTaskWoken = pdFALSE;
/* 'Give' the semaphore multiple times. The first will unblock the deferred
interrupt handling task, the following 'gives' are to demonstrate that the
semaphore latches the events to allow the task to which interrupts are deferred
to process them in turn, without events getting lost. This simulates multiple
interrupts being received by the processor, even though in this case the events
are simulated within a single interrupt occurrence. */
xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );
xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );
xSemaphoreGiveFromISR( xCountingSemaphore, &xHigherPriorityTaskWoken );
/* Pass the xHigherPriorityTaskWoken value into portYIELD_FROM_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR() then
calling portYIELD_FROM_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling portYIELD_FROM_ISR() will
have no effect. Unlike most FreeRTOS ports, the Windows port requires the ISR to
return a value - the return statement is inside the Windows version of
portYIELD_FROM_ISR(). */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
Deferring Work to the RTOS Daemon Task
守护进程: Daemon Task
集中化延迟中断处理:centralized deferred interrupt processing
API xTimerPendFunctionCallFromISR()
可以延迟中断处理至守护进程中去执行, 而不再需要为每一个中断创建一个延迟处理task
xTimerPendFunctionCallFromISR()
和 xTimerPendFunctionCall
利用timer command 队列,发送执行函数execute function
至 Daemon Task
。被发送至守护进程的函数,是在daemon task 上下文context中执行的
- 集中化延迟中断处理的优点
- 降低资源消耗 Lower resource usage
- 简化用户模型 Simplified user model
- 集中化延迟中断处理的缺点
- 不灵活 Less flexibility,因为dameon task的优先级在编译时由
configTIMER_TASK_PRIORITY
确定, - 不确定 Less determinism,damon task会先执行
execute function
之前的timer queue command.
- 不灵活 Less flexibility,因为dameon task的优先级在编译时由
不同中断有不同的时间约束(timing constraints),所以在同一个APP中,两种延迟处理程序都会用到。
xTimerPendFunctionCallFromISR()
- xTimerPendFunctionCallFromISR() API 函数模型
xFunctionToPend
, 函数指针,也就是函数名pvParameter1
,类型为void * 可以传递任何数据类型,如int, 结构体等ulParameter2
,在damon task中执行的函数的第二个参数pxHigherPriorityTaskWoken
,当daemon task优先级小于当前被中断的任务时,pxHigherPriorityTaskWoken 将设置为pdFALSE
, 当daemon task高于被中断的任务时,pxHigherPriorityTaskWoken 将被设置为pdTRUE
。
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend,
void *pvParameter1,
uint32_t ulParameter2,
BaseType_t *pxHigherPriorityTaskWoken );
- xFunctionToPend 必须遵守的函数模型
void vPendableFunction( void *pvParameter1, uint32_t ulParameter2 );
examples
static uint32_t ulExampleInterruptHandler( void )
{
static uint32_t ulParameterValue = 0;
BaseType_t xHigherPriorityTaskWoken;
/* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as it will
get set to pdTRUE inside the interrupt safe API function if a context switch is
required. */
xHigherPriorityTaskWoken = pdFALSE;
/* Send a pointer to the interrupt's deferred handling function to the daemon task.
The deferred handling function's pvParameter1 parameter is not used so just set to
NULL. The deferred handling function's ulParameter2 parameter is used to pass a
number that is incremented by one each time this interrupt handler executes. */
xTimerPendFunctionCallFromISR( vDeferredHandlingFunction, /* Function to execute. */
NULL, /* Not used. */
ulParameterValue, /* Incrementing value. */
&xHigherPriorityTaskWoken );
ulParameterValue++;
/* Pass the xHigherPriorityTaskWoken value into portYIELD_FROM_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside xTimerPendFunctionCallFromISR() then
calling portYIELD_FROM_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling portYIELD_FROM_ISR() will have
no effect. Unlike most FreeRTOS ports, the Windows port requires the ISR to return a
value - the return statement is inside the Windows version of portYIELD_FROM_ISR(). */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
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. */
vPrintStringAndNumber( "Handler function - Processing event ", ulParameter2 );
}
int main( void )
{
/* The task that generates the software interrupt is created at a priority below the
priority of the daemon task. The priority of the daemon task is set by the
configTIMER_TASK_PRIORITY compile time configuration constant in FreeRTOSConfig.h. */
const UBaseType_t ulPeriodicTaskPriority = configTIMER_TASK_PRIORITY - 1;
/* Create the task that will periodically generate a software interrupt. */
xTaskCreate( vPeriodicTask, "Periodic", 1000, NULL, ulPeriodicTaskPriority, NULL );
/* Install the handler for the software interrupt. The syntax necessary to do
this is dependent on the FreeRTOS port being used. The syntax shown here can
only be used with the FreeRTOS windows port, where such interrupts are only
simulated. */
vPortSetInterruptHandler( mainINTERRUPT_NUMBER, ulExampleInterruptHandler );
/* Start the scheduler so the created task starts executing. */
vTaskStartScheduler();
/* As normal, the following line should never be reached. */
for( ;; );
}
Using Queues within an Interrupt Service Routine
在中断中使用队列
The xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() API Function
BaseType_t xQueueSendToFrontFromISR( QueueHandle_t xQueue,
void *pvItemToQueue
BaseType_t *pxHigherPriorityTaskWoken
);
BaseType_t xQueueSendToBackFromISR( QueueHandle_t xQueue,
void *pvItemToQueue
BaseType_t *pxHigherPriorityTaskWoken
);
- 返回值
- pdPASS
- errQUEUE_FULL
Considerations When Using a Queue From an ISR
思考在中断中使用队列
从中断中传输数据时,使用队列非常方便, 但是当数据以非常高的频繁传输时,队列就显的效率不够了。
使用更加有效的技术运用在量产代码中,包括
- Direct Memory Access (DMA)
- 当传输有中断是,使用task notification, 解锁阻塞的任务,来处理buffer数据
- a thread safe RAM buffer
- 将数据拷贝到 线程安全的 ram buffer中,当传输完成,或者检测到有传输中断产生时,使用task notification,解锁阻塞的任务,来处理ram buffer中的数据
- 在中断中先预处理接收到数据,将计算结果通过队列传输。
examples
- task 周期性产生软中断
static void vIntegerGenerator( void *pvParameters )
{
TickType_t xLastExecutionTime;
uint32_t ulValueToSend = 0;
int i;
/* Initialize the variable used by the call to vTaskDelayUntil(). */
xLastExecutionTime = xTaskGetTickCount();
for( ;; )
{
/* This is a periodic task. Block until it is time to run again.
will execute every 200ms. */
vTaskDelayUntil( &xLastExecutionTime, pdMS_TO_TICKS( 200 ) );
/* Send five numbers to the queue, each value one higher than the previous
value. The numbers are read from the queue by the interrupt service routine.
The interrupt service routine always empties the queue, so this task is
guaranteed to be able to write all five values without needing to specify a
block time. */
for( i = 0; i < 5; i++ )
{
xQueueSendToBack( xIntegerQueue, &ulValueToSend, 0 );
ulValueToSend++;
}
/* Generate the interrupt so the interrupt service routine can read the
values from the queue. The syntax used to generate a software interrupt is
dependent on the FreeRTOS port being used. The syntax used below can only be
used with the FreeRTOS Windows port, in which such interrupts are only
simulated.*/
vPrintString( "Generator task - About to generate an interrupt.\r\n" );
vPortGenerateSimulatedInterrupt( mainINTERRUPT_NUMBER );
vPrintString( "Generator task - Interrupt generated.\r\n\r\n\r\n" );
}
}
- ISR 中断处理
static uint32_t ulExampleInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken;
uint32_t ulReceivedNumber;
/* The strings are declared static const to ensure they are not allocated on the
interrupt service routine's stack, and so exist even when the interrupt service
routine is not executing. */
static const char *pcStrings[] =
{
"String 0\r\n",
"String 1\r\n",
"String 2\r\n",
"String 3\r\n"
};
/* As always, xHigherPriorityTaskWoken is initialized to pdFALSE to be able to
detect it getting set to pdTRUE inside an interrupt safe API function. Note that
as an interrupt safe API function can only set xHigherPriorityTaskWoken to
pdTRUE, it is safe to use the same xHigherPriorityTaskWoken variable in both
the call to xQueueReceiveFromISR() and the call to xQueueSendToBackFromISR(). */
xHigherPriorityTaskWoken = pdFALSE;
/* Read from the queue until the queue is empty. */
while( xQueueReceiveFromISR( xIntegerQueue,
&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( xStringQueue,
&pcStrings[ ulReceivedNumber ],
&xHigherPriorityTaskWoken );
}
/* If receiving from xIntegerQueue caused a task to leave the Blocked state, and
if the priority of the task that left the Blocked state is higher than the
priority of the task in the Running state, then xHigherPriorityTaskWoken will
have been set to pdTRUE inside xQueueReceiveFromISR().
If sending to xStringQueue caused a task to leave the Blocked state, and if the
priority of the task that left the Blocked state is higher than the priority of
the task in the Running state, then xHigherPriorityTaskWoken will have been set
to pdTRUE inside xQueueSendToBackFromISR().
xHigherPriorityTaskWoken is used as the parameter to portYIELD_FROM_ISR(). If
xHigherPriorityTaskWoken equals pdTRUE then calling portYIELD_FROM_ISR() will
request a context switch. If xHigherPriorityTaskWoken is still pdFALSE then
calling portYIELD_FROM_ISR() will have no effect.
The implementation of portYIELD_FROM_ISR() used by the Windows port includes a
return statement, which is why this function does not explicitly return a
value. */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
static void vStringPrinter( void *pvParameters )
{
char *pcString;
for( ;; )
{
/* Block on the queue to wait for data to arrive. */
xQueueReceive( xStringQueue, &pcString, portMAX_DELAY );
/* Print out the string received. */
vPrintString( pcString );
}
}
- main
int main( void )
{
/* Before a queue can be used it must first be created. Create both queues used
by this example. One queue can hold variables of type uint32_t, the other queue
can hold variables of type char*. Both queues can hold a maximum of 10 items. A
real application should check the return values to ensure the queues have been
successfully created. */
xIntegerQueue = xQueueCreate( 10, sizeof( uint32_t ) );
xStringQueue = xQueueCreate( 10, sizeof( char * ) );
/* Create the task that uses a queue to pass integers to the interrupt service
routine. The task is created at priority 1\. */
xTaskCreate( vIntegerGenerator, "IntGen", 1000, NULL, 1, NULL );
/* Create the task that prints out the strings sent to it from the interrupt
service routine. This task is created at the higher priority of 2\. */
xTaskCreate( vStringPrinter, "String", 1000, NULL, 2, NULL );
/* Install the handler for the software interrupt. The syntax necessary to do
this is dependent on the FreeRTOS port being used. The syntax shown here can
only be used with the FreeRTOS Windows port, where such interrupts are only
simulated. */
vPortSetInterruptHandler( mainINTERRUPT_NUMBER, ulExampleInterruptHandler );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
/* If all is well then main() will never reach here as the scheduler will now be
running the tasks. If main() does reach here then it is likely that there was
insufficient heap memory available for the idle task to be created. Chapter 2
provides more information on heap memory management. */
for( ;; );
}
Interrupt Nesting
硬件中断决定一个ISR什么时候执行,软件决定一个task什么时候执行,一个硬件中断可以打断task的执行,但是task不能强占ISR执行。
- 决定中断嵌套的常数
常数 | 描述 |
---|---|
configMAX_SYSCALL_INTERRUPT_PRIORITY(old) configMAX_API_CALL_INTERRUPT_PRIORITY(new) | 设置可以调用 FreeRTOS API(FromISR)的最高中断优先级 |
configKERNEL_INTERRUPT_PRIORITY | 设置系统tick中断的中断优先级,并且必需总是设置为最低中断优先级;若Ports没有用设置configMAX_SYSCALL_INTERRUPT_PRIORITY,则中断将使用configKERNEL_INTERRUPT_PRIORITY的优先级 |
- 数字优先级和逻辑优先级
逻辑优级代表,中断相比其它中断的优先权
数字优先级与逻辑优先级的关系,根据芯片架构的不同而不同 - 一个完整的中断嵌套模型需要设置
configMAX_SYSCALL_INTERRUPT_PRIORITY
比configKERNEL_INTERRUPT_PRIORITY
高一点的逻辑优先级
example
- 系统有7个独立的中断优先级
- 数字优先级7 比 数字优先级1 的逻辑优先级大!
configKERNEL_INTERRUPT_PRIORITY
设置为1configMAX_SYSCALL_INTERRUPT_PRIORITY
设置为3
- 当中断使用中断优先级[1,2,3]时,当系统或者应用运行在critical section时,将禁止中断运行, 这些中断优先级允许使用中断安全的FreeRTOS API(FromISR)
- 中断优先级大于4的,不受critical section影响,ISR在这些优先下,不能使用任何FreeRTOS API
- 比如电机控制对时间要求比较高,可以使用高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断优先级,确保响应时间不会因为scheduler而出现波动。
ARM Cortex-M1和ARM GIC注意点
A Note to ARM Cortex-M1 and ARM GIC Users
- 中断配置在Cortex-M处理器上是容易出错的,不过FreeRTOS会帮助检查中断配置,前提是
configASSERT()
在系统中被定义 - ARM Cortex 核心 和 ARM Generic Interrupt Controllers (GICs),使用低数字中断号代表高优先级, 高数字中断号代表低优先级。
- Cortex-M 中断控制器使用8-bit确定中断优先级,255代表最低优先级,0代表最高优先级。
- 然而 Cortex-M 处理器,实际上只用了8-bit的子集。实现多少bit根据不同的处理器系列而不同。一般实现高位bit,未实现的低位bit通常置为1
In Figure 62 the binary value 101 has been shifted into the most significant four bits because
the least significant four bits are not implemented. The unimplemented bits have been set to
1.
- 有些库需要的是位移之后的中断号,如上图的,1011111 (95)
- 有些库需要的是位移之前的中断号,如上图的,101(5)
configMAX_SYSCALL_INTERRUPT_PRIORITY
和configKERNEL_INTERRUPT_PRIORITY
需要设置为位移之后的中断号- 虽然在Cortex-M中,0被默认为最高优先级的中断号,但具体实现上并不能保证存在0中断号,所以移植时,configMAX_SYSCALL_INTERRUPT_PRIORITY,必修要修改