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




posted @ 2021-01-06 13:24  JerryZheng2020  阅读(3022)  评论(2编辑  收藏  举报