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,必修要修改