FreeRTOS學習筆記5——Interrupt


TOC

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
  • 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 to portYIELD_FROM_ISR(), ensuring the ISR returns directly to the task to which interrupt processing is being deferred. 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 semaphoregiving a semaphore 根據使用環境的不同具有不同的意義,在中斷同步的場景下,binary semaphore可以設想為長度為1的隊列

    Taking a semaphore and giving 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 of xSemaphoreGive(),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 functionDaemon 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.

不同中斷有不同的時間約束(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_PRIORITYconfigKERNEL_INTERRUPT_PRIORITY高一點的邏輯優先級

example

  • 系統有7個獨立的中斷優先級
  • 數字優先級7 比 數字優先級1 的邏輯優先級大!
  • configKERNEL_INTERRUPT_PRIORITY設置為1
  • configMAX_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_PRIORITYconfigKERNEL_INTERRUPT_PRIORITY 需要設置為位移之后的中斷號
  • 雖然在Cortex-M中,0被默認為最高優先級的中斷號,但具體實現上並不能保證存在0中斷號,所以移植時,configMAX_SYSCALL_INTERRUPT_PRIORITY,必修要修改





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM