FreeRTOS: Colas

prefacio

Al escribir aplicaciones de proyectos, a menudo se encuentra que una tarea se "comunica" con otra tarea. Cuando no hay un sistema operativo, las variables globales pueden resolver este problema, pero si todas las variables se usan en la aplicación que usa el sistema operativo para transferir información involucran el tema de la "administración de recursos", y las variables globales no son fáciles de mantener y, a menudo, en programas con lógica compleja, es imposible rastrear quién usa o cambia las variables globales. FreeRTOS proporciona un mecanismo llamado "cola" para esto.

1. Introducción a la cola

Las colas están preparadas para la comunicación entre tareas y entre tareas e interrupciones. Los mensajes se pueden transmitir entre tareas y entre tareas y entre tareas e interrupciones. Los elementos de datos limitados de tamaño fijo se pueden almacenar en la cola. Los datos que se van a intercambiar entre tareas y tareas, tareas e interrupciones se almacenan en colas, denominadas elementos de cola. El número máximo de elementos de datos que puede contener una cola se denomina longitud de la cola. Al crear una cola, se especifican el tamaño de los elementos de datos y la longitud de la cola. Dado que la cola se utiliza para transmitir mensajes, también se denomina cola de mensajes. ¡El semáforo en FreeRTOS también se implementa de acuerdo con la cola!

1.1 Almacenamiento de datos

Por lo general, la cola adopta un mecanismo de búfer de almacenamiento de tipo primero en entrar, primero en salir (FIFO), es decir, cuando los datos se envían a la cola (también llamados en cola), siempre se envían al final de la cola. , y cuando se extraen datos de la cola (también llamado dequeue) se extrae del encabezado de la cola. Pero también se puede usar el almacenamiento en búfer de último en entrar, primero en salir (LIFO), y la cola en FreeRTOS también proporciona un mecanismo de almacenamiento en búfer de LIFO.

Una vez que los datos se envían a la cola, se almacenarán en la cola, lo que significa que el valor original de los datos almacenados en la cola no es una referencia a los datos originales (es decir, un puntero a los datos). El proceso de transferencia también se llama transferencia de valor. A diferencia de UCOS, la cola de mensajes de UCOS adopta la transferencia de referencia y lo que se pasa es el puntero del mensaje. Pero la desventaja de esto es que el mensaje de flores pasado por referencia debe permanecer visible, es decir, el contenido del mensaje debe ser válido, en este caso, por ejemplo, las variables locales de la función se eliminarán en cualquier momento, pero la ventaja de pasar por referencia también es grande Obviamente, el tiempo de transferencia de datos a la cola se puede reducir considerablemente.
Otra ventaja del paso de valor es que después de que los datos se envían a la cola, el búfer que originalmente almacenó los datos se puede eliminar o sobrescribir, de modo que el búfer se puede reutilizar para siempre. Sin embargo, en el caso de la transmisión de información de la red, a menudo es necesario transmitir una gran cantidad de información, por lo que inevitablemente se perderá mucho tiempo en el proceso de transmisión de la cola de mensajes.En este caso, puede pasar la dirección del puntero del grupo de datos, siempre que obtenga el búfer después de la dirección del área, se puede usar el grupo de datos encabezado por la dirección.

1.2 Acceso multitarea

Cualquier tarea puede enviar mensajes a la cola o extraer mensajes de la cola.

1.3 Bloqueo de la cola

Cuando una tarea intenta leer un mensaje de una cola, se puede especificar un tiempo de bloqueo.Este tiempo de bloqueo es el momento en que la tarea se bloquea cuando el mensaje leído de la cola no es válido. Dequeue es para leer mensajes de la cola, y el bloqueo de dequeue es para la tarea de leer mensajes de la cola. Por ejemplo, la tarea A se utiliza para procesar los datos recibidos por el puerto serie. Después de que el puerto serie recibe los datos, los colocará en la cola Q, y la tarea A lee los datos de la cola Q. Pero si la cola Q está vacía en este momento, significa que todavía no hay datos, si la tarea A viene a leer en este momento, no debe obtener nada, entonces, ¿qué debo hacer? La Tarea A ahora tiene tres opciones,
una: terminar el proceso de lectura directamente sin leer los datos;
dos: esperar un período de tiempo, que es el llamado tiempo de bloqueo, y los datos leídos en la cola terminarán durante este período, de lo contrario, después de esperar a que llegue el tiempo de bloqueo, ingrese a la lista de listos desde la lista de demora;
3: configure el tiempo de espera al valor máximo portMAX_DELAY, es decir, si no se leen datos, ingresará al estado de bloqueo y esperará hasta que se reciben los datos.
Cuál elegir está determinado por el tiempo de bloqueo, y la unidad de tiempo de bloqueo es el número de tics del reloj.

1.4 Bloqueo de colas

Unirse a la cola significa enviar un mensaje a la cola y agregar el mensaje a la cola. Al igual que el bloqueo de la cola, cuando una tarea envía un mensaje a la cola, también se puede configurar el tiempo de bloqueo. Por ejemplo, la tarea B envía un mensaje a la cola de mensajes Q, pero la cola Q está llena en este momento, entonces el envío debe fallar. En este momento, la tarea B encontrará el mismo problema que la tarea anterior A. El proceso de procesamiento de estos dos casos es similar, excepto que uno es enviar un mensaje a la cola Q y el otro es leer un mensaje de la cola Q.

1.5 Diagrama del proceso de operación de la cola

Las siguientes imágenes muestran brevemente el proceso de entrar y salir de la cola.

1.5.1 Crear una cola

inserte la descripción de la imagen aquí
En la figura anterior, la tarea A quiere enviar un mensaje a la tarea B, y este mensaje es el valor de la variable x. Primero cree una cola y especifique la longitud de la cola y la longitud de cada mensaje. Aquí creamos una cola con una longitud de 4, porque se va a pasar el valor de x, y x es una variable de tipo int, por lo que la longitud de cada mensaje es la longitud de tipo int, que es de 4 bytes en STM32, es decir, cada Mensaje es de 4 bytes.

1.5.2 Enviar el primer mensaje a la cola

inserte la descripción de la imagen aquí
En la figura anterior, el valor de la variable x de la tarea A es 10 y este valor se envía a la cola de mensajes. En este momento, la longitud restante de la cola es 3. Como se mencionó anteriormente, el envío de mensajes a la cola se realiza mediante copia, por lo que una vez que se envía el mensaje, la variable x se puede usar nuevamente y asignarle otros valores.

1.5.3 Enviar un segundo mensaje a la cola

inserte la descripción de la imagen aquí
En la figura anterior, la tarea A envía otro mensaje a la cola, que es el nuevo valor de x, que aquí es 20. En este momento, la longitud restante de la cola es 2.

1.5.4 Leer mensajes de la cola

inserte la descripción de la imagen aquí
En la figura anterior, la tarea B lee mensajes de la cola y asigna el valor de los mensajes leídos a y, de modo que y es igual a 10. Después de que la tarea B lea el mensaje de la cola, puede optar por borrar el mensaje o no. Cuando elige borrar este mensaje, otras tareas o interrupciones no podrán recibir este mensaje, y el tamaño restante de la cola aumentará en uno para convertirse en 3. Si no se borra, otras tareas o interrupciones también pueden recibir este mensaje y el tamaño restante de la cola sigue siendo 2.

2. Estructura de la cola

Hay una estructura que se usa para describir la cola, llamada Queue_t, esta estructura está en el archivo, esta estructura está en el archivo queue.c

typedef struct QueueDefinition 
{
    
    
    //指向队列存储区开始地址
    int8_t * pcHead;           
    //指向存储区中下一个空闲区域
    int8_t * pcWriteTo;        
    union
    {
    
    
        QueuePointers_t xQueue;     
        SemaphoreData_t xSemaphore; 
    } u;
    //等待发送任务列表,那些因为队列满导致入队失败而进入阻塞态的任务就会挂到此列表上。
    List_t xTasksWaitingToSend;             
    //等待接收任务列表,那些因为队列空导致出队失败而进入阻塞态的任务就会挂到此列表上。
    List_t xTasksWaitingToReceive;          

    //队列中当前队列项数量,也就是消息数
    volatile UBaseType_t uxMessagesWaiting;
    //创建队列时指定的队列长度,也就是队列中最大允许的队列项(消息)数量
    UBaseType_t uxLength;                   
    //创建队列时指定的每个队列项(消息)最大长度,单位:字节
    UBaseType_t uxItemSize;                 

    //当队列上锁以后用来统计从队列中接收到的队列项数量,也就是出队的队列项数量,当队列没有上锁的话此字段为 queueUNLOCKED
    volatile int8_t cRxLock;               
    //当队列上锁以后用来统计发送到队列中的队列项数量,也就是入队的队列项数量,当队列没有上锁的话此字段为 queueUNLOCKED
    volatile int8_t cTxLock;                

    //如果使用静态存储的话此字段设置为 pdTURE。
    #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated; 
    #endif

    //队列集相关宏
    #if ( configUSE_QUEUE_SETS == 1 )
        struct QueueDefinition * pxQueueSetContainer;
    #endif

    //跟踪调试相关宏
    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxQueueNumber;
        uint8_t ucQueueType;
    #endif
} xQUEUE;

3. Creación de colas

3.1 Crear función

Esta función también se usa para crear una cola, pero el método estático para crear una cola requiere que el usuario asigne la memoria requerida. Esta función es esencialmente una asignación de línea de macro. Esta macro finalmente llama a la función xQueueGenericCreateStatic(). El prototipo de función es como sigue:

xQueueCreateStatic()
xQueueCreate()

Estas dos funciones son esencialmente macros y las funciones que realmente completan la creación de la cola son xQueueGenericCreate() y xQueueGenericCreateStatic().

3.2 Función xQueueCreateStatic()

Esta función utiliza un método estático para crear una cola, y el usuario asigna la memoria requerida por la cola.

QueueHandle_t xQueueCreateStatic(
								 UBaseType_t       uxQueueLength,
                                 UBaseType_t       uxItemSiz,
                                 uint8_t*          pucQueueStorageBuffer,
                                 StaticQueue_t*    pxQueueBuffer
							     )

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

3.3 Función xQueueCreate()

Esta función es esencialmente una macro, que se usa para crear dinámicamente una cola. Esta función es esencialmente una macro, que se usa para crear dinámicamente una cola. Esta macro finalmente llama a la función xQueueGenericCreate(). El prototipo de la función es el siguiente:

QueueHandle_t xQueueCreate(UBaseType_t       uxQueueLength,
                           UBaseType_t       uxItemSiz,)

inserte la descripción de la imagen aquí

3.4 Función xQueueGenericCreateStatic()

QueueHandle_t xQueueGenericCreateStatic( 
										const UBaseType_t uxQueueLength, 
										const UBaseType_t uxItemSize, 
										uint8_t *pucQueueStorage, 
										StaticQueue_t *pxStaticQueue, 
										const uint8_t ucQueueType 
										)
	

inserte la descripción de la imagen aquí

3.5 Función xQueueGenericCreate()

QueueHandle_t xQueueGenericCreate( 
									const UBaseType_t uxQueueLength, 
									const UBaseType_t uxItemSize, 
									const uint8_t ucQueueType 
								)

inserte la descripción de la imagen aquí

4. Envía un mensaje a la cola

4.1 Prototipo de Función

Después de crear la cola, puede enviar mensajes a la cola. FreeRTOS proporciona 8 funciones de API para enviar mensajes a la cola, como se muestra en la siguiente tabla:
inserte la descripción de la imagen aquí

4.2 Funciones xQueueSend(), xQueueSendToBack() y xQueueSendToFront()

Estas tres funciones se utilizan para enviar mensajes a la cola. Estas tres funciones son esencialmente macros. Las funciones xQueueSend() y xQueueSendToBack() son las mismas, y todas ingresan a la cola al revés, insertando nuevos mensajes en la parte posterior de la cola. La función xQueueSendToToFront() se pone en cola hacia adelante, es decir, inserta nuevos mensajes al principio de la cola. ¡Sin embargo! Estas tres funciones terminan llamando a la misma función: xQueueGenericSend(). Estas tres funciones solo se pueden usar en funciones de tarea y no se pueden usar en funciones de servicio de interrupción. Las funciones de servicio de interrupción tienen funciones dedicadas y terminan con "FromISR". Los prototipos de estas tres funciones son los siguientes:

BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait);
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,
 const void* pvItemToQueue,
 TickType_t xTicksToWait);
BaseType_t xQueueSendToToFront(QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait);

Parámetros:
xQueue: identificador de cola, que indica a qué cola enviar datos. Después de que la cola se haya creado correctamente,
se devolverá el identificador de cola de esta cola.
**pvItemToQueue:** apunta al mensaje que se va a enviar, y el mensaje se copiará en la cola cuando se envíe.
xTicksToWait: Tiempo de bloqueo, este parámetro indica el tiempo máximo para que la tarea entre en estado de bloqueo y espere a que la cola quede libre cuando la cola esté llena. Si es 0, regresará inmediatamente cuando la cola esté llena; si es portMAX_DELAY,
esperará hasta que haya un elemento de cola inactivo en la cola, es decir, espera muerta, pero la macro INCLUDE_vTaskSuspend debe ser 1.

Valor de retorno:
pdPASS: ¡Envíe el mensaje a la cola con éxito!
errQUEUE_FULL: La cola está llena y no se puede enviar el mensaje.

4.2 Función xQueueOverwrite()

Esta función también se usa para enviar datos a la cola. Cuando la cola está llena, los datos antiguos se sobrescribirán, independientemente de si los datos antiguos han sido eliminados por otras tareas o interrupciones. Esta función se usa a menudo para enviar mensajes a esas colas con una longitud de 1. Esta función también es una macro, y finalmente se llama a la función xQueueGenericSend(). El prototipo de la función es el siguiente:

BaseType_t xQueueOverwrite(QueueHandle_t xQueue,
						   const void * pvItemToQueue);

Parámetros:
xQueue: identificador de cola, que indica a qué cola enviar datos. Después de que la cola se haya creado correctamente, se devolverá el identificador de cola de esta cola.
**pvItemToQueue:** apunta al mensaje a enviar, que se copiará en la cola al enviar.

Valor de retorno:
pdPASS: si el mensaje se envía a la cola con éxito, esta función solo devolverá pdPASS. Debido a que a esta función no le importa si la cola está llena o no durante la ejecución de esta función, si está llena, sobrescribiré los datos antiguos y definitivamente tendrá éxito.

4.3 Función xQueueGenericSend()

Esta función es el verdadero trabajo. Todas las funciones de puesta en cola a nivel de tarea mencionadas anteriormente son llamadas en última instancia por esta función. Esta función también es en lo que nos centraremos más adelante. Echemos un vistazo al prototipo de la función primero:

BaseType_t xQueueGenericSend( QueueHandle_t xQueue, 
							  const void * const pvItemToQueue, 
							  TickType_t xTicksToWait, 
							  const BaseType_t xCopyPosition )

Parámetros:
xQueue: identificador de cola, que indica a qué cola enviar datos. Después de que la cola se haya creado correctamente, se devolverá el identificador de cola de esta cola.
**pvItemToQueue:** apunta al mensaje a enviar, que se copiará en la cola durante el proceso de envío.
xTicksToWait: Tiempo de bloqueo.
xCopyPosition: hay tres formas de unirse a la cola:
queueSEND_TO_BACK: cola hacia atrás
queueSEND_TO_FRONT: cola hacia adelante
queueOVERWRITE: sobrescribir la cola.

La función de puesta en cola de la API explicada anteriormente usa este parámetro para determinar qué método de puesta en cola usar.

Valor de retorno:
pdTRUE: ¡Envíe el mensaje a la cola con éxito!
errQUEUE_FULL: la cola está llena y el mensaje no se pudo enviar

4.4 Funciones xQueueSendFromISR(), xQueueSendToBackFromISR(), xQueueSendToFrontFromISR()

Estas tres funciones también envían mensajes a la cola, y estas tres funciones se utilizan en la función de servicio de interrupción. La esencia de estas tres funciones también es una macro, entre las cuales las funciones xQueueSendFromISR () y xQueueSendToBackFromISR () son iguales, todas ingresan a la cola al revés, es decir, insertan nuevos mensajes al final de la cola. La función xQueueSendToFrontFromISR () pone en cola hacia delante, es decir, inserta nuevos mensajes al principio de la cola. Estas tres funciones también llaman a la misma función xQueueGenericSendFromISR(). Los prototipos de estas tres funciones son los siguientes:

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
							 const void * pvItemToQueue,
							 BaseType_t * pxHigherPriorityTaskWoken);
							 
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,
								   const void * pvItemToQueue,
								   BaseType_t * pxHigherPriorityTaskWoken);
								   
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
								 	const void * pvItemToQueue,
								 	BaseType_t * pxHigherPriorityTaskWoken);

Parámetros:
xQueue: identificador de cola, que indica a qué cola enviar datos. Después de que la cola se haya creado correctamente, se devolverá el identificador de cola de esta cola.
**pvItemToQueue:** apunta al mensaje a enviar, que se copiará en la cola al enviar.
pxHigherPriorityTaskWoken: marque si cambiar de tarea después de salir de esta función. El valor de esta variable se establece mediante estas tres funciones. El usuario no necesita configurarlo. El usuario solo necesita proporcionar una variable para guardar este valor. Cuando este valor es pdTRUE, se debe realizar un cambio de tarea antes de salir de la función de servicio de interrupción.

Valor de retorno:
pdTRUE: ¡Envíe el mensaje a la cola con éxito!
errQUEUE_FULL: La cola está llena y no se puede enviar el mensaje.

4.5 Función xQueueOverwriteFromISR()

Esta función es la versión de nivel de interrupción de xQueueOverwrite(). Se usa en la función de servicio de interrupción para sobrescribir automáticamente los datos antiguos cuando la cola está llena. Esta función también es una macro, y en realidad se llama a la función xQueueGenericSendFromISR(). El prototipo de esta función es el siguiente:

BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,
								  const void * pvItemToQueue,
								  BaseType_t * pxHigherPriorityTaskWoken);

Los parámetros y el valor de retorno de esta función son los mismos que los de las tres funciones anteriores.

4.6 Función xQueueGenericSendFromISR()

Como se mencionó anteriormente, las cuatro funciones de puesta en cola de nivel de interrupción finalmente se llaman la función xQueueGenericSendFromISR(). Este es el verdadero maestro del trabajo, y también es una función que explicaremos en detalle a continuación. Primero, echemos un vistazo a la prototipo de esta función, como sigue:

BaseType_t xQueueGenericSendFromISR(QueueHandle_t xQueue,
									const void* pvItemToQueue,
									BaseType_t* pxHigherPriorityTaskWoken,
									BaseType_t xCopyPosition);

Parámetros:
xQueue: identificador de cola, que indica a qué cola enviar datos. Después de que la cola se haya creado correctamente, se devolverá el identificador de cola de esta cola.
**pvItemToQueue:** apunta al mensaje a enviar, que se copiará en la cola durante el proceso de envío.
pxHigherPriorityTaskWoken: marque si cambiar de tarea después de salir de esta función. El valor de esta variable se establece mediante estas tres funciones. El usuario no necesita configurarlo. El usuario solo necesita proporcionar una variable para guardar este valor. Cuando este valor es pdTRUE, se debe realizar un cambio de tarea antes de salir de la función de servicio de interrupción.

xCopyPosition: hay tres formas de unirse a la cola:
queueSEND_TO_BACK: cola hacia atrás
queueSEND_TO_FRONT: cola hacia adelante
queueOVERWRITE: sobrescribir la cola.

Valor de retorno:
pdTRUE: ¡Envíe el mensaje a la cola con éxito!
errQUEUE_FULL: La cola está llena y no se puede enviar el mensaje.

4.7 Función de puesta en cola general a nivel de tarea

Independientemente de si está poniendo en cola hacia atrás, poniendo en cola hacia adelante o anulando la puesta en cola, se llama a la función final de puesta en cola xQueueGenericSend(). Esta función se define en el archivo queue.c. El código de función reducido es el siguiente:

BaseType_t xQueueGenericSend( QueueHandle_t xQueue, 
							  const void * const pvItemToQueue, 
							  TickType_t xTicksToWait, 
							  const BaseType_t xCopyPosition )
{
    
    
	BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
	TimeOut_t xTimeOut;
	Queue_t * const pxQueue = ( Queue_t * ) xQueue;
	for( ;; )
	{
    
    
		taskENTER_CRITICAL(); //进入临界区
		{
    
    
			//查询队列现在是否还有剩余存储空间,如果采用覆写方式入队的话那就不用在
			//乎队列是不是满的啦。
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) ||\ (1)
				( xCopyPosition == queueOVERWRITE ) )
			{
    
    
				traceQUEUE_SEND( pxQueue );
				xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue,\ (2)
				 xCopyPosition );
				/**************************************************************************/
				/**************************省略掉与队列集相关代码**************************/
				/**************************************************************************/
				{
    
    
				//检查是否有任务由于等待消息而进入阻塞态
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ==\(3)
				pdFALSE )
				{
    
    
					if( xTaskRemoveFromEventList( &( pxQueue->\ (4)
						xTasksWaitingToReceive ) ) != pdFALSE )
					{
    
    
						//解除阻塞态的任务优先级最高,因此要进行一次任务切换
						queueYIELD_IF_USING_PREEMPTION(); (5)
					}
					else
					{
    
    
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else if( xYieldRequired != pdFALSE )
				{
    
    
					queueYIELD_IF_USING_PREEMPTION();
				}
				else
				{
    
    
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();
			return pdPASS; (6)
		}
		else
		{
    
    
			if( xTicksToWait == ( TickType_t ) 0 ) (7)
			{
    
    
				//队列是满的,并且没有设置阻塞时间的话就直接返回
				taskEXIT_CRITICAL();
				traceQUEUE_SEND_FAILED( pxQueue );
				return errQUEUE_FULL; (8)
			}
			else if( xEntryTimeSet == pdFALSE ) (9)
			{
    
    
				//队列是满的并且指定了任务阻塞时间的话就初始化时间结构体
				vTaskSetTimeOutState( &xTimeOut );
				xEntryTimeSet = pdTRUE;
			}
			else
			{
    
    
				//时间结构体已经初始化过了,
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
	taskEXIT_CRITICAL(); //退出临界区
	vTaskSuspendAll(); (10)
	prvLockQueue( pxQueue ); (11)
	//更新时间壮态,检查是否有超时产生
	if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) (12)
	{
    
    
		if( prvIsQueueFull( pxQueue ) != pdFALSE ) (13)
		{
    
    
			traceBLOCKING_ON_QUEUE_SEND( pxQueue );
			vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), \ (14)
			xTicksToWait );
			prvUnlockQueue( pxQueue ); (15)
			if( xTaskResumeAll() == pdFALSE ) (16)
			{
    
    
				portYIELD_WITHIN_API();
			}
		}
		else
		{
    
    
			//重试一次
			prvUnlockQueue( pxQueue ); (17)
			( void ) xTaskResumeAll();
		}
	}
	else
	{
    
    
		//超时产生
		prvUnlockQueue( pxQueue ); (18)
		( void ) xTaskResumeAll();
		traceQUEUE_SEND_FAILED( pxQueue );
		return errQUEUE_FULL; (19)
	}
	}
}

(1) Para enviar datos a la cola, primero debe verificar si la cola está llena. Si está llena, no debe enviarse. Cuando la cola no está llena o sobrescrita en la cola, el mensaje se puede poner en cola.

(2) Llame a la función prvCopyDataToQueue() para copiar el mensaje en la cola. Como se mencionó anteriormente, poner en cola se divide en poner en cola hacia atrás, poner en cola hacia adelante y poner en cola sobrescribir.Su implementación específica se completa en la función prvCopyDataToQueue(). Si se selecciona queueSEND_TO_BACK, el mensaje se copiará en el elemento de la cola señalado por el miembro de la estructura de la cola pcWriteTo. Después de que la copia sea exitosa, pcWriteTo aumentará los bytes de uxItemSize para apuntar al siguiente elemento de la cola. Al seleccionar queueSEND_TO_FRONT o queueOVERWRITE, el mensaje se copia en el elemento de la cola al que apunta u.pcReadFrom, y la posición de u.pcReadFrom también debe ajustarse. Cuando se escribe un mensaje en la cola, el miembro uxMessagesWaiting que cuenta el número actual de mensajes en la cola aumentará en uno, pero si elige sobrescribir la cola OVERWRITE, uxMessagesWaiting se reducirá en uno, de modo que uno menos y uno más es equivalente al número actual de mensajes en la cola no ha cambiado.

(3) Verifique si alguna tarea está bloqueada debido al mensaje de la cola de solicitudes, y la tarea bloqueada se colgará en la
lista xTasksWaitingToReceive de la cola.

(4) Una tarea está bloqueada debido a un mensaje de solicitud, porque se ha enviado un mensaje a la cola en (2), llame a la función xTaskRemoveFromEventList() para eliminar la tarea bloqueada de la lista xTasksWaitingToReceive y elimine esta tarea Agregado a la lista de listos, si el planificador está bloqueado, estas tareas se colgarán en la lista xPendingReadyList. Si la prioridad de la tarea desbloqueada es mayor que la prioridad de la tarea que se está ejecutando actualmente, se requiere un cambio de tarea. Cuando el valor de retorno de la función xTaskRemoveFromEventList() es pdTRUE, es necesario cambiar de tarea.

(5) Realizar el cambio de tareas.

(6) Regresar pdPASS, marcando el éxito de unirse al equipo.

(7), (2) a (6) son efectos muy ideales, es decir, la cola de mensajes no está llena y no hay ningún obstáculo para ingresar a la cola. Pero, ¿qué pasa cuando la cola está llena? Primero juzgue si el tiempo de bloqueo establecido es 0, si es 0, significa que no hay tiempo de bloqueo.

(8) De (7), se sabe que el tiempo de bloqueo es 0, luego devuelve directamente errQUEUE_FULL y marca que la cola está llena.

(9) Si el tiempo de bloqueo no es 0 y la estructura de tiempo no se ha inicializado, inicialice la variable de estructura de tiempo de espera una vez y llame a la función vTaskSetTimeOutState() para completar la inicialización de la variable de estructura de tiempo de espera xTimeOut. De hecho, es para registrar el valor xTickCount del contador de ticks del reloj del sistema actual y el número de desbordamientos xNumOfOverflows.

(10), el programador de tareas está bloqueado y la ejecución del código aquí muestra que la situación actual es que la cola está llena y se establece un tiempo de bloqueo distinto de cero. Luego, el siguiente paso es tomar las medidas correspondientes para la tarea, como agregar la tarea a la
lista xTasksWaitingToSend de la cola.

(11) Llamar a la función prvLockQueue() para bloquear la cola en realidad establece las variables miembro cRxLock y
cTxLock en la cola en queueLOCKED_UNMODIFIED.

(12) Llame a la función xTaskCheckForTimeOut() para actualizar la variable de estructura de tiempo de espera xTimeOut y verifique si el tiempo de bloqueo ha terminado.

(13), el tiempo de bloqueo aún no ha llegado, luego verifique si la cola todavía está llena.

(14), después de (12) y (13), se encuentra que el tiempo de bloqueo no ha llegado y la cola aún está llena, luego llame a la función vTaskPlaceOnEventList() para agregar la tarea a la lista xTasksWaitingToSend y la lista de retraso de la cola y establezca La tarea se elimina de la lista de preparados. ¡Aviso! Si el tiempo de bloqueo es portMAX_DELAY y la macro INCLUDE_vTaskSuspend es 1, la función vTaskPlaceOnEventList() agregará la tarea a la lista xSuspendedTaskList.

(15), la operación se completa, llame a la función prvUnlockQueue () para desbloquear la cola.

(16) Llame a la función xTaskResumeAll() para restaurar el programador de tareas

(17), el tiempo de bloqueo aún no ha llegado, pero la cola ahora tiene elementos de cola inactivos, así que inténtelo de nuevo.

(18), en comparación con el paso (12), ¡el tiempo de bloqueo ha terminado! Entonces no es necesario agregar tareas a esas listas, así que desbloquee la cola y restaure el programador de tareas.

(19), devuelve errQUEUE_FULL, indicando que la cola está llena.

4.8 Función de puesta en cola general a nivel de interrupción

Después de hablar sobre la función de poner en cola a nivel de tarea, echemos un vistazo a la función de poner en cola a nivel de interrupción xQueueGenericSendFromISR() Esta función implementa otras funciones de poner en cola a nivel de interrupción. La función de puesta en cola de nivel de interrupción es similar a la función de puesta en cola de nivel de tarea, y el código de función es el siguiente:

BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, 
									 const void * const pvItemToQueue, 
									 BaseType_t * const pxHigherPriorityTaskWoken, 
									 const BaseType_t xCopyPosition )
{
    
    
	BaseType_t xReturn;
	UBaseType_t uxSavedInterruptStatus;
	Queue_t * const pxQueue = ( Queue_t * ) xQueue;
	portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
    
    
		if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) ||\ (1)
	 		( xCopyPosition == queueOVERWRITE ) )
		{
    
    
			const int8_t cTxLock = pxQueue->cTxLock; (2)
			traceQUEUE_SEND_FROM_ISR( pxQueue );
			( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); (3)			
			{
    
    
				//队列上锁的时候就不能操作事件列表,队列解锁的时候会补上这些操作的。
				if( cTxLock == queueUNLOCKED ) (4)
				{
    
    
					/**************************************************************************/
					/**************************省略掉与队列集相关代码**************************/
					/**************************************************************************/
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == \ (5)
						pdFALSE )
					{
    
    
						if( xTaskRemoveFromEventList( &( pxQueue->\ (6)
							xTasksWaitingToReceive ) ) != pdFALSE )
						{
    
    
							//刚刚从事件列表中移除的任务对应的任务优先级更高,所以
							标记要进行任务切换
							if( pxHigherPriorityTaskWoken != NULL )
							{
    
    
								*pxHigherPriorityTaskWoken = pdTRUE; (7)
							}
							else
							{
    
    
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else
						{
    
    
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
    
    
						mtCOVERAGE_TEST_MARKER();
					}
				}
			}
			else
			{
    
    
				//cTxLock 加一,这样就知道在队列上锁期间向队列中发送了数据
				pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); (8)
			}
		xReturn = pdPASS; (9)
		}
		else
		{
    
    
			traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );
			xReturn = errQUEUE_FULL; (10)
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
	return xReturn;
}

(1) La cola no está llena o se adopta el método principal de puesta en cola, que es el estado más ideal.

(2) Lea la variable miembro xTxLock de la cola para determinar si la cola está bloqueada.

(3) Copie los datos en la cola.

(4) La cola está bloqueada. Por ejemplo, la función de poner en cola a nivel de tarea bloqueará la cola cuando opere la lista en la cola.

(5) Determinar si la lista de espera xTasksWaitingToReceive está vacía, si no está vacía, significa que una tarea está bloqueada al solicitar un mensaje.

(6) Eliminar la tarea correspondiente de la lista xTasksWaitingToReceive. El proceso es el mismo que el de la función de puesta en cola a nivel de tarea.

(7) Si la prioridad de la tarea recién eliminada de la lista xTasksWaitingToReceive es mayor que la prioridad de la tarea actual, marque pxHigherPriorityTaskWoken como pdTRUE, lo que indica que se requiere un cambio de tarea. Si desea cambiar de tarea, debe cambiar de tarea después de salir de esta función y antes de salir de la función de servicio de interrupción.

(8) Si la cola está bloqueada, agregue uno a la variable miembro de la cola cTxLock, lo que indica que se ha realizado una operación de puesta en cola, y se procesará en consecuencia cuando se desbloquee la cola (prvUnlockQueue()).

(9) Devuelve pdPASS, lo que indica que la entrada en la cola está completa.

(10) Si la cola está llena, devuelve errQUEUE_FULL directamente, lo que indica que la cola está llena.

5. Bloqueo y desbloqueo de colas

Al explicar la función de puesta en cola general a nivel de tarea y la función de puesta en cola general a nivel de interrupción, se mencionan el bloqueo y desbloqueo de la cola. El bloqueo y desbloqueo de la cola son dos funciones API: prvLockQueue() y prvUnlockQueue().
Veamos primero la función de bloqueo de cola prvLockQueue() Esta función es esencialmente una macro y se define de la siguiente manera:

#define prvLockQueue( pxQueue ) \
taskENTER_CRITICAL(); \
{
      
       \
	if( ( pxQueue )->cRxLock == queueUNLOCKED ) \
	{
      
       \
		( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED;\
	} \
	if( ( pxQueue )->cTxLock == queueUNLOCKED ) \
	{
      
       \
		( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED;\
	} \
} \
taskEXIT_CRITICAL()

La función prvLockQueue() es muy simple, simplemente establezca las variables miembro cRxLock y cTxLock en la cola en
queueLOCKED_UNMODIFIED.

Echemos un vistazo a la función de desbloqueo prvUnlockQueue() de la cola, la función es la siguiente:

static void prvUnlockQueue( Queue_t * const pxQueue )
{
    
    
	//上锁计数器(cTxLock 和 cRxLock)记录了在队列上锁期间,入队或出队的数量,当队列
	//上锁以后队列项是可以加入或者移除队列的,但是相应的列表不会更新。
	taskENTER_CRITICAL();
	{
    
    
		//处理 cTxLock。
		int8_t cTxLock = pxQueue->cTxLock;
		while( cTxLock > queueLOCKED_UNMODIFIED ) (1)
		{
    
    
			/**************************************************************************/
			/**************************省略掉与队列集相关代码**************************/
			/**************************************************************************/
			{
    
    
				//将任务从事件列表中移除
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == \ (2)
				pdFALSE )
				{
    
    
					if( xTaskRemoveFromEventList( &( pxQueue->\ (3)
					xTasksWaitingToReceive ) ) != pdFALSE )
					{
    
    
						//从列表中移除的任务优先级比当前任务的优先级高,因此要
						//进行任务切换。
						vTaskMissedYield(); (4)
					}
					else
					{
    
    	
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
    
    
					break;
				}
			}
			--cTxLock; (5)
		}
		pxQueue->cTxLock = queueUNLOCKED; (6)
	}
	taskEXIT_CRITICAL();
	//处理 cRxLock。
	taskENTER_CRITICAL();
	{
    
    
		int8_t cRxLock = pxQueue->cRxLock;
		while( cRxLock > queueLOCKED_UNMODIFIED ) (7)
		{
    
    
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
			{
    
    
				if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) !=\
				pdFALSE )
				{
    
    
					vTaskMissedYield();
				}
				else
				{
    
    
					mtCOVERAGE_TEST_MARKER();
				}
				--cRxLock;
			}
			else
			{
    
    
				break;
			}
		}
		pxQueue->cRxLock = queueUNLOCKED; 
	}
	taskEXIT_CRITICAL();
}

(1) Determinar si una interrupción ha enviado un mensaje a la cola. Como se mencionó en la sección anterior al explicar la función de puesta en cola general a nivel de interrupción, si la cola está bloqueada, entonces el contador de puesta en cola cTxLock se agregará al contador de puesta en cola cTxLock. después de que el mensaje se envíe correctamente a la cola. .

(2) Determine si la lista xTasksWaitingToReceive está vacía y, en caso contrario, elimine la tarea correspondiente de la lista.

(3) Eliminar la tarea de la lista xTasksWaitingToReceive.

(4) Si la prioridad de la tarea recién eliminada de la lista xTasksWaitingToReceive es mayor que la prioridad de la tarea actual, es necesario marcar que se requiere el cambio de tarea. Aquí se llama a la función vTaskMissedYield() para completar esta tarea.La función vTaskMissedYield() simplemente establece la variable global xYieldPending en pdTRUE. Entonces, ¿dónde se realiza el cambio de tareas real? En la función de procesamiento de ticks de reloj xTaskIncrementTick(), esta función juzgará el valor de xYieldPending para decidir si cambiar de tarea

(5) Disminuya cTxLock en uno cada vez que se procese uno, hasta que se procesen todos.

(6) Después de procesar, marque cTxLock como queueUNLOCKED, es decir, cTxLock no está bloqueado.

(7) Después de procesar cTxLock, xRxLock se procesará a continuación, y el proceso de procesamiento es muy similar a xTxLock

6. Leer mensajes de la cola

Si hay una cola, habrá una cola, y la cola es para obtener el elemento de la cola (mensaje) de la cola. La función de cola en FreeRTOS se muestra en la siguiente tabla:
inserte la descripción de la imagen aquí

6.1 Función xQueueReceive()

Esta función se usa para leer un mensaje (solicitud) de la cola en una tarea. Después de que la lectura sea exitosa, los datos en la cola se eliminarán. La esencia de esta función es una macro, y la función que realmente se ejecuta es xQueueGenericReceive( ). Esta función utiliza un método de copia al leer mensajes, por lo que el usuario debe proporcionar una matriz o un búfer para guardar los datos leídos, y la longitud de los datos leídos es cada elemento de la cola establecido al crear la cola. La longitud del prototipo de función es como sigue:

BaseType_t xQueueReceive(QueueHandle_t xQueue,
						 void * pvBuffer,
						 TickType_t xTicksToWait);

Parámetros:
xQueue: identificador de cola, que indica qué datos de cola leer y devolverá el identificador de cola de esta cola después de que la cola se haya creado correctamente.
pvBuffer: el búfer para guardar datos, los datos leídos se copiarán en este búfer durante el proceso de lectura de la cola.
xTicksToWait: Tiempo de bloqueo, este parámetro indica el tiempo máximo para que la tarea entre en estado de bloqueo y espere a que la cola tenga datos cuando la cola está vacía. Si es 0, regresará inmediatamente cuando la cola esté vacía; si es portMAX_DELAY,
esperará hasta que haya datos en la cola, es decir, espera muerta, pero la macro
INCLUDE_vTaskSuspend debe ser 1.

Valor de retorno:
pdTRUE: leer datos de la cola con éxito.
pdFALSE: no se pudieron leer los datos de la cola.

6.2 Función xQueuePeek()

¡Esta función se usa para leer un mensaje (solicitud) de la cola y solo se puede usar en tareas! Esta función no eliminará el mensaje después de que se haya leído con éxito
. Esta función es una macro y la función que se ejecuta realmente es xQueueGenericReceive(). Esta función utiliza un método de copia al leer mensajes, por lo que el usuario debe proporcionar una matriz o un búfer para guardar los datos leídos, y la longitud de los datos leídos es cada elemento de la cola establecido al crear la cola. La longitud del prototipo de función es como sigue:

BaseType_t xQueuePeek(QueueHandle_t xQueue,
					  void * pvBuffer,
					  TickType_t xTicksToWait);

Parámetros:
xQueue: identificador de cola, que indica qué datos de cola leer y devolverá el identificador de cola de esta cola después de que la cola se haya creado correctamente.
pvBuffer: el búfer para guardar datos, los datos leídos se copiarán en este búfer durante el proceso de lectura de la cola.
xTicksToWait: Tiempo de bloqueo, este parámetro indica el tiempo máximo para que la tarea entre en estado de bloqueo y espere a que la cola tenga datos cuando la cola está vacía. Si es 0, regresará inmediatamente cuando la cola esté vacía; si es portMAX_DELAY,
esperará hasta que haya datos en la cola, es decir, espera muerta, pero la macro INCLUDE_vTaskSuspend debe ser 1.

Valor de retorno:
pdTRUE: leer datos de la cola con éxito.
pdFALSE: no se pudieron leer los datos de la cola.

6.3 Función xQueueGenericReceive()

Ya sea la función xQueueReceive() o xQueuePeek(), es la función xQueueGenericReceive() la que se llama al final. Esta función es real. El prototipo de la función es el siguiente:

BaseType_t xQueueGenericReceive(QueueHandle_t xQueue,
								void* pvBuffer,
								TickType_t xTicksToWait
								BaseType_t xJustPeek)

Parámetros:
xQueue: identificador de cola, que indica qué datos de cola leer y devolverá el identificador de cola de esta cola después de que la cola se haya creado correctamente.
pvBuffer: el búfer para guardar datos, los datos leídos se copiarán en este búfer durante el proceso de lectura de la cola.
xTicksToWait: Tiempo de bloqueo, este parámetro indica el tiempo máximo para que la tarea entre en estado de bloqueo y espere a que la cola tenga datos cuando la cola está vacía. Si es 0, regresará inmediatamente cuando la cola esté vacía; si es portMAX_DELAY,
esperará hasta que haya datos en la cola, es decir, espera muerta, pero la macro
INCLUDE_vTaskSuspend debe ser 1.
xJustPeek: Marca si se elimina el elemento de la cola después de que la lectura sea exitosa, cuando es pdTRUE no es necesario eliminarlo, es decir, el elemento de la cola obtenido llamando a la función xQueueReceive() más tarde es el mismo. Cuando es
pdFALSE, se eliminará la entrada de la cola.

Valor de retorno:
pdTRUE: leer datos de la cola con éxito.
pdFALSE: no se pudieron leer los datos de la cola.

6.4 Función xQueueReceiveFromISR()

Esta función es la versión de interrupción de xQueueReceive(), que se usa para leer (solicitar) un mensaje de la cola en la función de servicio de interrupción, y los datos en la cola se eliminarán después de que la lectura sea exitosa. Esta función utiliza un método de copia al leer mensajes, por lo que el usuario debe proporcionar una matriz o un búfer para guardar los datos leídos. La longitud de los datos leídos es cada elemento de la cola establecido al crear la cola. La longitud del prototipo de función es la siguiente. :

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
								void* pvBuffer,
								BaseType_t * pxTaskWoken);

Parámetros:
xQueue: identificador de cola, que indica qué datos de cola leer y devolverá el identificador de cola de esta cola después de que la cola se haya creado correctamente.
pvBuffer: el búfer para guardar datos, los datos leídos se copiarán en este búfer durante el proceso de lectura de la cola.
pxTaskWoken: marca si se cambia de tarea después de salir de esta función. El valor de esta variable lo establece la función y el usuario no necesita configurarlo. El usuario solo necesita proporcionar una variable para guardar este valor. Cuando este valor
es pdTRUE, se debe realizar un cambio de tarea antes de salir de la función de servicio de interrupción.

Valor de retorno:
pdTRUE: leer datos de la cola con éxito.
pdFALSE: no se pudieron leer los datos de la cola.

6.5 Función xQueuePeekFromISR()

Esta función es una versión interrumpida de xQueuePeek(). Esta función no eliminará el mensaje después de leerlo con éxito. El prototipo de esta función es el siguiente:

BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,
							 void * pvBuffer)

Parámetros:
xQueue: identificador de cola, que indica qué datos de cola leer y devolverá el identificador de cola de esta cola después de que la cola se haya creado correctamente.
pvBuffer: el búfer para guardar datos, los datos leídos se copiarán en este búfer durante el proceso de lectura de la cola.

Valor de retorno:
pdTRUE: leer datos de la cola con éxito.
pdFALSE: no se pudieron leer los datos de la cola.

7. Ejemplo de programa de cola

7.1 Ejemplo de requisitos de diseño

Este ejemplo diseña tres tareas: start_task, task1_task, Keyprocess_task Las funciones de estas tres tareas son las siguientes: Las funciones de estas tres tareas son las siguientes:
start_task: se usa para crear otras: se usa para crear otras 2 tareas.
task1_task: lea el valor de la clave y luego envíelo a la cola: lea el valor de la clave y luego envíelo a la cola Key_Queue y verifique la capacidad restante de la cola y verifique la capacidad restante de la cola y otra información.
Keyprocess_task: tarea de procesamiento de claves, leer el mensaje en la cola Key_Queue y realizar el procesamiento correspondiente de acuerdo con los diferentes valores del mensaje.
La instancia necesita tres claves KEY_UP, KEY2 y KEY0, y diferentes claves corresponden a diferentes valores de clave.La tarea task1_task enviará estos valores a la cola Key_Queue.
Dos colas, Key_Queue y Message_Queue, se crean en el ejemplo.La cola Key_Queue se usa para pasar valores clave, y la cola Message_Queue se usa para pasar mensajes enviados desde el puerto serie.
El ejemplo también necesita dos interrupciones, una es la interrupción de recepción del puerto serie 1 y la otra es la interrupción del temporizador 2. Sus funciones son las siguientes: Interrupción de recepción del puerto serie 1: recibe los datos enviados por el puerto serie y envía los recibidos. datos a la cola Message_Queue. Interrupción del temporizador 2: el período de tiempo se establece en 500 ms, y el mensaje en la cola Message_Queue se lee en la interrupción de tiempo y se muestra en la pantalla LCD.

7.2 Código de ejemplo

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "key.h"
#include "beep.h"
#include "string.h"
#include "malloc.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
 
//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		256  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
 
//任务优先级
#define TASK1_TASK_PRIO		2
//任务堆栈大小	
#define TASK1_STK_SIZE 		256  
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);
 
//任务优先级
#define KEYPROCESS_TASK_PRIO 3
//任务堆栈大小	
#define KEYPROCESS_STK_SIZE  256 
//任务句柄
TaskHandle_t Keyprocess_Handler;
//任务函数
void Keyprocess_task(void *pvParameters);
 
 
//按键消息队列的数量
#define KEYMSG_Q_NUM    1  		//按键消息队列的数量  
#define MESSAGE_Q_NUM   4   	//发送数据的消息队列的数量 
QueueHandle_t Key_Queue;   		//按键值消息队列句柄
QueueHandle_t Message_Queue;	//信息队列句柄
 
//LCD刷屏时使用的颜色
int lcd_discolor[14]={
    
    	WHITE, BLACK, BLUE,  BRED,      
						GRED,  GBLUE, RED,   MAGENTA,       	 
						GREEN, CYAN,  YELLOW,BROWN, 			
						BRRED, GRAY };
 
//用于在LCD上显示接收到的队列的消息
//str: 要显示的字符串(接收到的消息)
void disp_str(u8* str)
{
    
    
	LCD_Fill(5,230,110,245,WHITE);					//先清除显示区域
	LCD_ShowString(5,230,100,16,16,str);
}
 
//加载主界面
void freertos_load_main_ui(void)
{
    
    
	POINT_COLOR = RED;
	LCD_ShowString(10,10,200,16,16,"ATK STM32F103/407");	
	LCD_ShowString(10,30,200,16,16,"FreeRTOS Examp 13-1");
	LCD_ShowString(10,50,200,16,16,"Message Queue");
	LCD_ShowString(10,70,220,16,16,"KEY_UP:LED1 KEY0:Refresh LCD");
	LCD_ShowString(10,90,200,16,16,"KEY1:SendMsg KEY2:BEEP");
	
	POINT_COLOR = BLACK;
	LCD_DrawLine(0,107,239,107);		//画线
	LCD_DrawLine(119,107,119,319);		//画线
	LCD_DrawRectangle(125,110,234,314);	//画矩形
	POINT_COLOR = RED;
	LCD_ShowString(0,130,120,16,16,"DATA_Msg Size:");
	LCD_ShowString(0,170,120,16,16,"DATA_Msg rema:");
	LCD_ShowString(0,210,100,16,16,"DATA_Msg:");
	POINT_COLOR = BLUE;
}
 
//查询Message_Queue队列中的总队列数量和剩余队列数量
void check_msg_queue(void)
{
    
    
    u8 *p;
	u8 msgq_remain_size;	//消息队列剩余大小
    u8 msgq_total_size;     //消息队列总大小
    
    taskENTER_CRITICAL();   //进入临界区
    msgq_remain_size=uxQueueSpacesAvailable(Message_Queue);//得到队列剩余大小
    msgq_total_size=uxQueueMessagesWaiting(Message_Queue)+uxQueueSpacesAvailable(Message_Queue);//得到队列总大小,总大小=使用+剩余的。
	p=mymalloc(SRAMIN,20);	//申请内存
	sprintf((char*)p,"Total Size:%d",msgq_total_size);	//显示DATA_Msg消息队列总的大小
	LCD_ShowString(10,150,100,16,16,p);
	sprintf((char*)p,"Remain Size:%d",msgq_remain_size);	//显示DATA_Msg剩余大小
	LCD_ShowString(10,190,100,16,16,p);
	myfree(SRAMIN,p);		//释放内存
    taskEXIT_CRITICAL();    //退出临界区
}
 
int main(void)
{
    
     
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);					//初始化延时函数
	uart_init(115200);     				//初始化串口
	LED_Init();		        			//初始化LED端口
	KEY_Init();							//初始化按键
	BEEP_Init();						//初始化蜂鸣器
	LCD_Init();							//初始化LCD
	TIM9_Int_Init(5000,16800-1);		//初始化定时器9,周期500ms
	my_mem_init(SRAMIN);            	//初始化内部内存池
    freertos_load_main_ui();        	//加载主UI
	
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}
 
//开始任务任务函数
void start_task(void *pvParameters)
{
    
    
    taskENTER_CRITICAL();           //进入临界区
	
	//创建消息队列
    Key_Queue=xQueueCreate(KEYMSG_Q_NUM,sizeof(u8));        //创建消息Key_Queue
    Message_Queue=xQueueCreate(MESSAGE_Q_NUM,USART_REC_LEN); //创建消息Message_Queue,队列项长度是串口接收缓冲区长度
	
    //创建TASK1任务
    xTaskCreate((TaskFunction_t )task1_task,             
                (const char*    )"task1_task",           
                (uint16_t       )TASK1_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )TASK1_TASK_PRIO,        
                (TaskHandle_t*  )&Task1Task_Handler);   
    //创建TASK2任务
    xTaskCreate((TaskFunction_t )Keyprocess_task,     
                (const char*    )"keyprocess_task",   
                (uint16_t       )KEYPROCESS_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )KEYPROCESS_TASK_PRIO,
                (TaskHandle_t*  )&Keyprocess_Handler); 
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}
 
//task1任务函数
void task1_task(void *pvParameters)
{
    
    
	u8 key,i=0;
    BaseType_t err;
	while(1)
	{
    
    
		key=KEY_Scan(0);            	//扫描按键
        if((Key_Queue!=NULL)&&(key))   	//消息队列Key_Queue创建成功,并且按键被按下
        {
    
    
            err=xQueueSend(Key_Queue,&key,10);
            if(err==errQUEUE_FULL)   	//发送按键值
            {
    
    
                printf("队列Key_Queue已满,数据发送失败!\r\n");
            }
        }
        i++;
        if(i%10==0) check_msg_queue();//检Message_Queue队列的容量
        if(i==50)
        {
    
    
            i=0;
            LED0=!LED0;
        }
        vTaskDelay(10);                           //延时10ms,也就是10个时钟节拍	
	}
}
 
 
//Keyprocess_task函数
void Keyprocess_task(void *pvParameters)
{
    
    
	u8 num,key;
	while(1)
	{
    
    
        if(Key_Queue!=NULL)
        {
    
    
            if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//请求消息Key_Queue
            {
    
    
                switch(key)
                {
    
    
                    case WKUP_PRES:		//KEY_UP控制LED1
                        LED1=!LED1;
                        break;
                    case KEY2_PRES:		//KEY2控制蜂鸣器
                        BEEP=!BEEP;
                        break;
                    case KEY0_PRES:		//KEY0刷新LCD背景
                        num++;
                        LCD_Fill(126,111,233,313,lcd_discolor[num%14]);
                        break;
                }
            }
        } 
		vTaskDelay(10);      //延时10ms,也就是10个时钟节拍	
	}
}

Video de Referencia: Átomos Puntuales
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_27928443/article/details/130871612
Recomendado
Clasificación