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

Queue fromISR functions. xQueueReset. (#12)

tangzz98 3 лет назад
Родитель
Сommit
3167906d79
3 измененных файлов с 463 добавлено и 2 удалено
  1. 309 0
      FreeRTOS/include/queue.h
  2. 66 2
      FreeRTOS/queue.c
  3. 88 0
      test/test_queue_isr.c

+ 309 - 0
FreeRTOS/include/queue.h

@@ -664,6 +664,305 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue,
  */
  */
 void vQueueDelete( QueueHandle_t xQueue );
 void vQueueDelete( QueueHandle_t xQueue );
 
 
+/**
+ * queue. h
+ * @code{c}
+ * BaseType_t xQueueSendToFrontFromISR(
+ *                                       QueueHandle_t xQueue,
+ *                                       const void *pvItemToQueue,
+ *                                       BaseType_t *pxHigherPriorityTaskWoken
+ *                                    );
+ * @endcode
+ *
+ * This is a macro that calls xQueueGenericSendFromISR().
+ *
+ * Post an item to the front of a queue.  It is safe to use this macro from
+ * within an interrupt service routine.
+ *
+ * Items are queued by copy not reference so it is preferable to only
+ * queue small items, especially when called from an ISR.  In most cases
+ * it would be preferable to store a pointer to the item being queued.
+ *
+ * @param xQueue The handle to the queue on which the item is to be posted.
+ *
+ * @param pvItemToQueue A pointer to the item that is to be placed on the
+ * queue.  The size of the items the queue will hold was defined when the
+ * queue was created, so this many bytes will be copied from pvItemToQueue
+ * into the queue storage area.
+ *
+ * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xQueueSendToFromFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @return pdTRUE if the data was successfully sent to the queue, otherwise
+ * errQUEUE_FULL.
+ *
+ * Example usage for buffered IO (where the ISR can obtain more than one value
+ * per call):
+ * @code{c}
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPrioritTaskWoken;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post the byte.
+ *      xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.
+ *  if( xHigherPriorityTaskWoken )
+ *  {
+ *      taskYIELD ();
+ *  }
+ * }
+ * @endcode
+ *
+ * \defgroup xQueueSendFromISR xQueueSendFromISR
+ * \ingroup QueueManagement
+ */
+#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
+    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
+
+
+/**
+ * queue. h
+ * @code{c}
+ * BaseType_t xQueueSendToBackFromISR(
+ *                                       QueueHandle_t xQueue,
+ *                                       const void *pvItemToQueue,
+ *                                       BaseType_t *pxHigherPriorityTaskWoken
+ *                                    );
+ * @endcode
+ *
+ * This is a macro that calls xQueueGenericSendFromISR().
+ *
+ * Post an item to the back of a queue.  It is safe to use this macro from
+ * within an interrupt service routine.
+ *
+ * Items are queued by copy not reference so it is preferable to only
+ * queue small items, especially when called from an ISR.  In most cases
+ * it would be preferable to store a pointer to the item being queued.
+ *
+ * @param xQueue The handle to the queue on which the item is to be posted.
+ *
+ * @param pvItemToQueue A pointer to the item that is to be placed on the
+ * queue.  The size of the items the queue will hold was defined when the
+ * queue was created, so this many bytes will be copied from pvItemToQueue
+ * into the queue storage area.
+ *
+ * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xQueueSendToBackFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @return pdTRUE if the data was successfully sent to the queue, otherwise
+ * errQUEUE_FULL.
+ *
+ * Example usage for buffered IO (where the ISR can obtain more than one value
+ * per call):
+ * @code{c}
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPriorityTaskWoken;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post the byte.
+ *      xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.
+ *  if( xHigherPriorityTaskWoken )
+ *  {
+ *      taskYIELD ();
+ *  }
+ * }
+ * @endcode
+ *
+ * \defgroup xQueueSendFromISR xQueueSendFromISR
+ * \ingroup QueueManagement
+ */
+#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
+    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
+
+/**
+ * queue. h
+ * @code{c}
+ * BaseType_t xQueueSendFromISR(
+ *                                   QueueHandle_t xQueue,
+ *                                   const void *pvItemToQueue,
+ *                                   BaseType_t *pxHigherPriorityTaskWoken
+ *                              );
+ * @endcode
+ *
+ * This is a macro that calls xQueueGenericSendFromISR().  It is included
+ * for backward compatibility with versions of FreeRTOS.org that did not
+ * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR()
+ * macros.
+ *
+ * Post an item to the back of a queue.  It is safe to use this function from
+ * within an interrupt service routine.
+ *
+ * Items are queued by copy not reference so it is preferable to only
+ * queue small items, especially when called from an ISR.  In most cases
+ * it would be preferable to store a pointer to the item being queued.
+ *
+ * @param xQueue The handle to the queue on which the item is to be posted.
+ *
+ * @param pvItemToQueue A pointer to the item that is to be placed on the
+ * queue.  The size of the items the queue will hold was defined when the
+ * queue was created, so this many bytes will be copied from pvItemToQueue
+ * into the queue storage area.
+ *
+ * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xQueueSendFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @return pdTRUE if the data was successfully sent to the queue, otherwise
+ * errQUEUE_FULL.
+ *
+ * Example usage for buffered IO (where the ISR can obtain more than one value
+ * per call):
+ * @code{c}
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPriorityTaskWoken;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post the byte.
+ *      xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.
+ *  if( xHigherPriorityTaskWoken )
+ *  {
+ *      // Actual macro used here is port specific.
+ *      portYIELD_FROM_ISR ();
+ *  }
+ * }
+ * @endcode
+ *
+ * \defgroup xQueueSendFromISR xQueueSendFromISR
+ * \ingroup QueueManagement
+ */
+#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
+    xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
+
+/**
+ * queue. h
+ * @code{c}
+ * BaseType_t xQueueGenericSendFromISR(
+ *                                         QueueHandle_t    xQueue,
+ *                                         const    void    *pvItemToQueue,
+ *                                         BaseType_t  *pxHigherPriorityTaskWoken,
+ *                                         BaseType_t  xCopyPosition
+ *                                     );
+ * @endcode
+ *
+ * It is preferred that the macros xQueueSendFromISR(),
+ * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place
+ * of calling this function directly.  xQueueGiveFromISR() is an
+ * equivalent for use by semaphores that don't actually copy any data.
+ *
+ * Post an item on a queue.  It is safe to use this function from within an
+ * interrupt service routine.
+ *
+ * Items are queued by copy not reference so it is preferable to only
+ * queue small items, especially when called from an ISR.  In most cases
+ * it would be preferable to store a pointer to the item being queued.
+ *
+ * @param xQueue The handle to the queue on which the item is to be posted.
+ *
+ * @param pvItemToQueue A pointer to the item that is to be placed on the
+ * queue.  The size of the items the queue will hold was defined when the
+ * queue was created, so this many bytes will be copied from pvItemToQueue
+ * into the queue storage area.
+ *
+ * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xQueueGenericSendFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the
+ * item at the back of the queue, or queueSEND_TO_FRONT to place the item
+ * at the front of the queue (for high priority messages).
+ *
+ * @return pdTRUE if the data was successfully sent to the queue, otherwise
+ * errQUEUE_FULL.
+ *
+ * Example usage for buffered IO (where the ISR can obtain more than one value
+ * per call):
+ * @code{c}
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPriorityTaskWokenByPost;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWokenByPost = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post each byte.
+ *      xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.  Note that the
+ *  // name of the yield function required is port specific.
+ *  if( xHigherPriorityTaskWokenByPost )
+ *  {
+ *      portYIELD_FROM_ISR();
+ *  }
+ * }
+ * @endcode
+ *
+ * \defgroup xQueueSendFromISR xQueueSendFromISR
+ * \ingroup QueueManagement
+ */
+BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue,
+                                     const void * const pvItemToQueue,
+                                     BaseType_t * const pxHigherPriorityTaskWoken,
+                                     const BaseType_t xCopyPosition );
 BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
 BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
                               BaseType_t * const pxHigherPriorityTaskWoken );
                               BaseType_t * const pxHigherPriorityTaskWoken );
 
 
@@ -782,6 +1081,12 @@ BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex,
                                      TickType_t xTicksToWait );
                                      TickType_t xTicksToWait );
 BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex );
 BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex );
 
 
+/*
+ * Reset a queue back to its original empty state.  The return value is now
+ * obsolete and is always set to pdPASS.
+ */
+#define xQueueReset( xQueue )    xQueueGenericReset( xQueue, pdFALSE )
+
 /*
 /*
  * Generic version of the function used to create a queue using dynamic memory
  * Generic version of the function used to create a queue using dynamic memory
  * allocation.  This is called by other functions and macros that create other
  * allocation.  This is called by other functions and macros that create other
@@ -806,6 +1111,10 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex );
                                              const uint8_t ucQueueType );
                                              const uint8_t ucQueueType );
 #endif
 #endif
 
 
+/* Not public API functions. */
+BaseType_t xQueueGenericReset( QueueHandle_t xQueue,
+                               BaseType_t xNewQueue );
+
 /* *INDENT-OFF* */
 /* *INDENT-OFF* */
 #ifdef __cplusplus
 #ifdef __cplusplus
     }
     }

+ 66 - 2
FreeRTOS/queue.c

@@ -49,6 +49,32 @@ static volatile rt_uint8_t queue_index = 0;
 
 
 /*-----------------------------------------------------------*/
 /*-----------------------------------------------------------*/
 
 
+BaseType_t xQueueGenericReset( QueueHandle_t xQueue,
+                               BaseType_t xNewQueue )
+{
+    Queue_t * const pxQueue = xQueue;
+    struct rt_ipc_object *pipc;
+    rt_uint8_t type;
+
+    configASSERT( pxQueue );
+
+    pipc = pxQueue->rt_ipc;
+    RT_ASSERT( pipc != RT_NULL );
+    type = rt_object_get_type( &pipc->parent );
+
+    if ( type == RT_Object_Class_Semaphore )
+    {
+        rt_sem_control( ( rt_sem_t ) pipc, RT_IPC_CMD_RESET, ( void * ) 0);
+    }
+    else if ( type == RT_Object_Class_MessageQueue )
+    {
+        rt_mq_control( ( rt_mq_t ) pipc, RT_IPC_CMD_RESET, RT_NULL );
+    }
+
+    return pdPASS;
+}
+/*-----------------------------------------------------------*/
+
 #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
 #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
 
 
     QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
     QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
@@ -284,8 +310,6 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
     rt_err_t err = -RT_ERROR;
     rt_err_t err = -RT_ERROR;
 
 
     configASSERT( pxQueue );
     configASSERT( pxQueue );
-    configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
-    configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
     #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
     #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
         {
         {
             configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
             configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
@@ -325,6 +349,42 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
 }
 }
 /*-----------------------------------------------------------*/
 /*-----------------------------------------------------------*/
 
 
+BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue,
+                                     const void * const pvItemToQueue,
+                                     BaseType_t * const pxHigherPriorityTaskWoken,
+                                     const BaseType_t xCopyPosition )
+{
+    Queue_t * const pxQueue = xQueue;
+    struct rt_ipc_object *pipc;
+    rt_uint8_t type;
+    rt_err_t err = -RT_ERROR;
+
+    configASSERT( pxQueue );
+    #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
+        {
+            configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
+        }
+    #endif
+
+    pipc = pxQueue->rt_ipc;
+    RT_ASSERT( pipc != RT_NULL );
+    type = rt_object_get_type( &pipc->parent );
+    if ( type == RT_Object_Class_MessageQueue )
+    {
+        if ( xCopyPosition == queueSEND_TO_BACK )
+        {
+            err = rt_mq_send( ( rt_mq_t ) pipc, pvItemToQueue, ( ( rt_mq_t ) pipc )->msg_size);
+        }
+        else if ( xCopyPosition == queueSEND_TO_FRONT )
+        {
+            err = rt_mq_urgent( ( rt_mq_t ) pipc, pvItemToQueue, ( ( rt_mq_t ) pipc )->msg_size );
+        }
+    }
+
+    return rt_err_to_freertos( err );
+}
+/*-----------------------------------------------------------*/
+
 BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
 BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
                               BaseType_t * const pxHigherPriorityTaskWoken )
                               BaseType_t * const pxHigherPriorityTaskWoken )
 {
 {
@@ -442,6 +502,10 @@ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
     {
     {
         err = rt_sem_take( ( rt_sem_t ) pipc, RT_WAITING_NO );
         err = rt_sem_take( ( rt_sem_t ) pipc, RT_WAITING_NO );
     }
     }
+    else if ( type == RT_Object_Class_MessageQueue )
+    {
+        err = rt_mq_recv( ( rt_mq_t ) pipc, pvBuffer, ( ( rt_mq_t ) pipc )->msg_size, RT_WAITING_NO );
+    }
     if ( pxHigherPriorityTaskWoken != NULL )
     if ( pxHigherPriorityTaskWoken != NULL )
     {
     {
         *pxHigherPriorityTaskWoken = pdFALSE;
         *pxHigherPriorityTaskWoken = pdFALSE;

+ 88 - 0
test/test_queue_isr.c

@@ -0,0 +1,88 @@
+/*
+ * 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: queue
+ * This demo creates a periodic hard timer which sends data to a queue when timeout
+ * A thread blocks to receive from the queue
+ *
+ */
+
+#include <rtthread.h>
+#include <FreeRTOS.h>
+#include <queue.h>
+
+#define THREAD_PRIORITY         25
+#define THREAD_TIMESLICE        5
+#define QUEUE_LENGTH            1
+#define ITEM_SIZE               sizeof( uint32_t )
+
+static QueueHandle_t xQueue = 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)
+{
+    BaseType_t xReturn;
+    uint32_t num = 0;
+    while (1)
+    {
+        xReturn = xQueueReceive(xQueue, &num, portMAX_DELAY);
+        if (xReturn != pdPASS)
+        {
+            rt_kprintf("Task 1 receive from queue failed\n");
+        }
+        else
+        {
+            rt_kprintf("Task 1 receive data %d from queue\n", num);
+        }
+        if (num >= 100)
+        {
+            return;
+        }
+    }
+}
+
+static void timeout(void *parameter)
+{
+    static uint32_t num = 0;
+    rt_kprintf("timer isr send data %d to queue\n", num);
+    xQueueSendToBackFromISR(xQueue, &num, NULL);
+    num += 1;
+}
+
+int queue_isr()
+{
+    xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);
+    if (xQueue == NULL)
+    {
+        rt_kprintf("create queue 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(queue_isr, queue sample);