Просмотр исходного кода

Semaphore fromISR (#9)

* Semaphore fromISR

* Update FreeRTOS/queue.c

Co-authored-by: Man, Jianting (Meco) <920369182@qq.com>
tangzz98 3 лет назад
Родитель
Сommit
ec25e0d597
4 измененных файлов с 365 добавлено и 1 удалено
  1. 94 0
      FreeRTOS/include/queue.h
  2. 127 0
      FreeRTOS/include/semphr.h
  3. 62 1
      FreeRTOS/queue.c
  4. 82 0
      test/test_semaphore_isr.c

+ 94 - 0
FreeRTOS/include/queue.h

@@ -163,6 +163,100 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
  */
 void vQueueDelete( QueueHandle_t xQueue );
 
+BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
+                              BaseType_t * const pxHigherPriorityTaskWoken );
+
+/**
+ * queue. h
+ * @code{c}
+ * BaseType_t xQueueReceiveFromISR(
+ *                                     QueueHandle_t    xQueue,
+ *                                     void             *pvBuffer,
+ *                                     BaseType_t       *pxTaskWoken
+ *                                 );
+ * @endcode
+ *
+ * Receive an item from a queue.  It is safe to use this function from within an
+ * interrupt service routine.
+ *
+ * @param xQueue The handle to the queue from which the item is to be
+ * received.
+ *
+ * @param pvBuffer Pointer to the buffer into which the received item will
+ * be copied.
+ *
+ * @param pxTaskWoken A task may be blocked waiting for space to become
+ * available on the queue.  If xQueueReceiveFromISR causes such a task to
+ * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will
+ * remain unchanged.
+ *
+ * @return pdTRUE if an item was successfully received from the queue,
+ * otherwise pdFALSE.
+ *
+ * Example usage:
+ * @code{c}
+ *
+ * QueueHandle_t xQueue;
+ *
+ * // Function to create a queue and post some values.
+ * void vAFunction( void *pvParameters )
+ * {
+ * char cValueToPost;
+ * const TickType_t xTicksToWait = ( TickType_t )0xff;
+ *
+ *  // Create a queue capable of containing 10 characters.
+ *  xQueue = xQueueCreate( 10, sizeof( char ) );
+ *  if( xQueue == 0 )
+ *  {
+ *      // Failed to create the queue.
+ *  }
+ *
+ *  // ...
+ *
+ *  // Post some characters that will be used within an ISR.  If the queue
+ *  // is full then this task will block for xTicksToWait ticks.
+ *  cValueToPost = 'a';
+ *  xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ *  cValueToPost = 'b';
+ *  xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ *
+ *  // ... keep posting characters ... this task may block when the queue
+ *  // becomes full.
+ *
+ *  cValueToPost = 'c';
+ *  xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ * }
+ *
+ * // ISR that outputs all the characters received on the queue.
+ * void vISR_Routine( void )
+ * {
+ * BaseType_t xTaskWokenByReceive = pdFALSE;
+ * char cRxedChar;
+ *
+ *  while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+ *  {
+ *      // A character was received.  Output the character now.
+ *      vOutputCharacter( cRxedChar );
+ *
+ *      // If removing the character from the queue woke the task that was
+ *      // posting onto the queue cTaskWokenByReceive will have been set to
+ *      // pdTRUE.  No matter how many times this loop iterates only one
+ *      // task will be woken.
+ *  }
+ *
+ *  if( cTaskWokenByPost != ( char ) pdFALSE;
+ *  {
+ *      taskYIELD ();
+ *  }
+ * }
+ * @endcode
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR
+ * \ingroup QueueManagement
+ */
+BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
+                                 void * const pvBuffer,
+                                 BaseType_t * const pxHigherPriorityTaskWoken );
+
 /*
  * For internal use only.  Use xSemaphoreCreateMutex(),
  * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling

+ 127 - 0
FreeRTOS/include/semphr.h

@@ -547,6 +547,133 @@ typedef QueueHandle_t SemaphoreHandle_t;
     #define xSemaphoreGiveRecursive( xMutex )    xQueueGiveMutexRecursive( ( xMutex ) )
 #endif
 
+/**
+ * semphr. h
+ * @code{c}
+ * xSemaphoreGiveFromISR(
+ *                        SemaphoreHandle_t xSemaphore,
+ *                        BaseType_t *pxHigherPriorityTaskWoken
+ *                    );
+ * @endcode
+ *
+ * <i>Macro</i> to  release a semaphore.  The semaphore must have previously been
+ * created with a call to xSemaphoreCreateBinary() or xSemaphoreCreateCounting().
+ *
+ * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex())
+ * must not be used with this macro.
+ *
+ * This macro can be used from an ISR.
+ *
+ * @param xSemaphore A handle to the semaphore being released.  This is the
+ * handle returned when the semaphore was created.
+ *
+ * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xSemaphoreGiveFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL.
+ *
+ * Example usage:
+ * @code{c}
+ \#define LONG_TIME 0xffff
+ \#define TICKS_TO_WAIT 10
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ * // Repetitive task.
+ * void vATask( void * pvParameters )
+ * {
+ *  for( ;; )
+ *  {
+ *      // We want this task to run every 10 ticks of a timer.  The semaphore
+ *      // was created before this task was started.
+ *
+ *      // Block waiting for the semaphore to become available.
+ *      if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
+ *      {
+ *          // It is time to execute.
+ *
+ *          // ...
+ *
+ *          // We have finished our task.  Return to the top of the loop where
+ *          // we will block on the semaphore until it is time to execute
+ *          // again.  Note when using the semaphore for synchronisation with an
+ *          // ISR in this manner there is no need to 'give' the semaphore back.
+ *      }
+ *  }
+ * }
+ *
+ * // Timer ISR
+ * void vTimerISR( void * pvParameters )
+ * {
+ * static uint8_t ucLocalTickCount = 0;
+ * static BaseType_t xHigherPriorityTaskWoken;
+ *
+ *  // A timer tick has occurred.
+ *
+ *  // ... Do other time functions.
+ *
+ *  // Is it time for vATask () to run?
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *  ucLocalTickCount++;
+ *  if( ucLocalTickCount >= TICKS_TO_WAIT )
+ *  {
+ *      // Unblock the task by releasing the semaphore.
+ *      xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
+ *
+ *      // Reset the count so we release the semaphore again in 10 ticks time.
+ *      ucLocalTickCount = 0;
+ *  }
+ *
+ *  if( xHigherPriorityTaskWoken != pdFALSE )
+ *  {
+ *      // We can force a context switch here.  Context switching from an
+ *      // ISR uses port specific syntax.  Check the demo task for your port
+ *      // to find the syntax required.
+ *  }
+ * }
+ * @endcode
+ * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR
+ * \ingroup Semaphores
+ */
+#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken )    xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )
+
+/**
+ * semphr. h
+ * @code{c}
+ * xSemaphoreTakeFromISR(
+ *                        SemaphoreHandle_t xSemaphore,
+ *                        BaseType_t *pxHigherPriorityTaskWoken
+ *                    );
+ * @endcode
+ *
+ * <i>Macro</i> to  take a semaphore from an ISR.  The semaphore must have
+ * previously been created with a call to xSemaphoreCreateBinary() or
+ * xSemaphoreCreateCounting().
+ *
+ * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex())
+ * must not be used with this macro.
+ *
+ * This macro can be used from an ISR, however taking a semaphore from an ISR
+ * is not a common operation.  It is likely to only be useful when taking a
+ * counting semaphore when an interrupt is obtaining an object from a resource
+ * pool (when the semaphore count indicates the number of resources available).
+ *
+ * @param xSemaphore A handle to the semaphore being taken.  This is the
+ * handle returned when the semaphore was created.
+ *
+ * @param pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if taking the semaphore caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xSemaphoreTakeFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @return pdTRUE if the semaphore was successfully taken, otherwise
+ * pdFALSE
+ */
+#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )    xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )
+
 /**
  * semphr. h
  * @code{c}

+ 62 - 1
FreeRTOS/queue.c

@@ -130,7 +130,7 @@ static volatile rt_uint8_t sem_index = 0;
                 {
                     rt_sem_init( ( rt_sem_t ) pipc, name, 0, RT_IPC_FLAG_PRIO );
                     ( ( struct rt_semaphore_wrapper * ) pipc )->max_value = uxQueueLength;
-                    /* Mark as static so we can distinguish in vQueueDelete */
+                    /* Mark as dynamic so we can distinguish in vQueueDelete */
                     pipc->parent.type &= ~RT_Object_Class_Static;
                 }
             }
@@ -301,6 +301,39 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
 }
 /*-----------------------------------------------------------*/
 
+BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
+                              BaseType_t * const pxHigherPriorityTaskWoken )
+{
+    Queue_t * const pxQueue = xQueue;
+    struct rt_ipc_object *pipc;
+    rt_uint8_t type;
+    rt_base_t level;
+    rt_err_t err = -RT_ERROR
+
+    configASSERT( pxQueue );
+
+    pipc = pxQueue->rt_ipc;
+    RT_ASSERT( pipc != RT_NULL );
+    type = rt_object_get_type( &pipc->parent );
+    RT_ASSERT( type != RT_Object_Class_Mutex );
+    if ( type == RT_Object_Class_Semaphore )
+    {
+        level = rt_hw_interrupt_disable();
+        if ( ( ( rt_sem_t ) pipc )->value < ( ( struct rt_semaphore_wrapper * ) pipc )->max_value )
+        {
+            err = rt_sem_release( ( rt_sem_t ) pipc );
+        }
+        rt_hw_interrupt_enable( level );
+    }
+    if ( pxHigherPriorityTaskWoken != NULL )
+    {
+        *pxHigherPriorityTaskWoken = pdFALSE;
+    }
+
+    return rt_err_to_freertos( err );
+}
+/*-----------------------------------------------------------*/
+
 BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,
                                 TickType_t xTicksToWait )
 {
@@ -335,6 +368,34 @@ BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,
 }
 /*-----------------------------------------------------------*/
 
+BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
+                                 void * const pvBuffer,
+                                 BaseType_t * const pxHigherPriorityTaskWoken )
+{
+    Queue_t * const pxQueue = xQueue;
+    struct rt_ipc_object *pipc;
+    rt_uint8_t type;
+    rt_err_t err = -RT_ERROR
+
+    configASSERT( pxQueue );
+
+    pipc = pxQueue->rt_ipc;
+    RT_ASSERT( pipc != RT_NULL );
+    type = rt_object_get_type( &pipc->parent );
+    RT_ASSERT( type != RT_Object_Class_Mutex );
+    if ( type == RT_Object_Class_Semaphore )
+    {
+        err = rt_sem_take( ( rt_sem_t ) pipc, RT_WAITING_NO );
+    }
+    if ( pxHigherPriorityTaskWoken != NULL )
+    {
+        *pxHigherPriorityTaskWoken = pdFALSE;
+    }
+
+    return rt_err_to_freertos( err );
+}
+/*-----------------------------------------------------------*/
+
 void vQueueDelete( QueueHandle_t xQueue )
 {
     Queue_t * const pxQueue = xQueue;

+ 82 - 0
test/test_semaphore_isr.c

@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-24     yangjie      the first version
+ */
+
+/*
+ * Demo: semaphore
+ * This demo creates a periodic hard timer which releases a semaphore when timeout
+ * A thread blocks on the semaphore
+ *
+ */
+
+#include <rtthread.h>
+#include <FreeRTOS.h>
+#include <semphr.h>
+
+#define THREAD_PRIORITY         25
+#define THREAD_TIMESLICE        5
+
+/* Semaphore handle */
+static SemaphoreHandle_t dynamic_sem = RT_NULL;
+static rt_timer_t timer1;
+
+ALIGN(RT_ALIGN_SIZE)
+static char thread1_stack[1024];
+static struct rt_thread thread1;
+static void rt_thread1_entry(void *parameter)
+{
+    static rt_err_t result;
+    while (1)
+    {
+        result = xSemaphoreTake(dynamic_sem, portMAX_DELAY);
+        if (result != pdPASS)
+        {
+            rt_kprintf("thread1 take a dynamic semaphore, failed.\n");
+            return;
+        }
+        else
+        {
+            rt_kprintf("thread1 take a dynamic semaphore, succeeded.\n");
+        }
+    }
+}
+
+static void timeout(void *parameter)
+{
+    rt_kprintf("timer isr give semaphore\n");
+    xSemaphoreGiveFromISR(dynamic_sem, RT_NULL);
+}
+
+int semaphore_isr()
+{
+    /* Create a binary semaphore */
+    dynamic_sem = xSemaphoreCreateBinary();
+    if (dynamic_sem == RT_NULL)
+    {
+        rt_kprintf("create dynamic semaphore failed.\n");
+        return -1;
+    }
+    rt_thread_init(&thread1,
+                   "thread1",
+                   rt_thread1_entry,
+                   RT_NULL,
+                   &thread1_stack[0],
+                   sizeof(thread1_stack),
+                   THREAD_PRIORITY, THREAD_TIMESLICE);
+    rt_thread_startup(&thread1);
+    /* Create a hard timer with period of 5 seconds */
+    timer1 = rt_timer_create("timer1", timeout, RT_NULL, rt_tick_from_millisecond(5000), RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_HARD_TIMER);
+    if (timer1 != RT_NULL)
+    {
+        rt_timer_start(timer1);
+    }
+    return 0;
+}
+
+MSH_CMD_EXPORT(semaphore_isr, semaphore sample);