1.-Preparando el ambiente: como crear un proyecto con FreeRtos.
Bajo PIC18F (C18)
Cumpliendo con la tarea que prometimos que era utilizar un RTOS que este disponible sobre un gran numero de microcontroladores, iniciamos el curso con un tutorial paso a paso para crear un proyecto con FREERTOS para PIC18, ya que es el microcontrolador de mayor llegada para mucha gente. Luego una vez madurado pasaremos a una arquitectura ARM sobre el mismo RTOS de manera de aprovechar todo lo hecho o por hacer. Armar el ambiente de trabajo para tu RTOS puede que no sea una tarea simple la primera vez, ya que hay que tener en cuenta muchas cosas que son solo dependientes de la arquitectura utilizada, asi que aqui te daremos una mano para pasarlo lo mas pragmatico y rapidamente posible.Las imagenes son orientativas, no se incluiran todas las posibles.
- 1.Crear un proyecto con "proyect Wizard" eligiendo el PIC18F a utilizar
- 2.Elegir el C18 como compilador C ANSI 89.
- 3.Se deben agregar al proyecto bajo "sources" las fuentes del mismo
list.c
queue.c
task.c
Si se va a utilizar corutinas ->croutine.c - 4.Deben agregar al proyecto bajo "portable"
a. heap_x.c donde x es 1,2 o 3 dependiendo del modelo de memoria en \MemMang
1 es un modelo de memoria estatico, una vez tomada la memoria no se puede devolver.
2 es un modelo dinamico pero sin reodenamiento.
3 es un modelo completo de manejo de memoria.
b. port.c dependiente del dispositivo que en este caso es \MPLAB\PIC18F - 5.Se
debe agregar el archivo linker dpte del micro modificado para disponer
de un espacio de heap lineal mas grande, puedes comparar el original de
microchip con el provisto para entender como se modifica.
- 6.En opciones del compilador, se debe configurar:
a.Bajo categoria general, en Nivel de diagnostico poner "Errors, warnings and messages" que es similar a la directiva -w3
b.Bajo Memory model, usar stack model Multi bank (directiva -Ls)
c.Bajo Optimization seleccionar custom y deshabilitar procedural abstraction (directiva -Opa-)
d.bajo macro definition del C18 se debe agregar las siguientes directivas:
MPLAB_PIC18F_PORT
-nw 2074 -nw 2066
Tiene que quedar la directiva total en "-w3 -DMPLAB_PIC18F_PORT -D-nw 2074 -nw 2066 -Ls -Opa-" - 7.Debes agregar los path a las fuentes del FreeRTOS(\INCLUDE) en el "include search path" del C18.
Aunque tiene que ver con la instalacion del C18, debes tenes en el proyecto los path a los includes del C18 y a las librerias. - 8.Se debe agregar el archivo FreeRTOSConfig.h con los parametros de configuracion del sistema operativo, puedes usar como ejemplo el provisto aqui.
- 9.Debes agregar tu archivo con el main con la configuracion de arranque ejecucion de tus tareas y del Scheduler.
El ejemplo tiene paths a los archivos en duro osea que deben ser modificados por los path de donde se encuentra tu compilador, tu MPLAB y el FreeRTOS.
Se pueden hacer path relativos al path actual de tu proyecto, eso es si tu ubicas tu proyecto dentro del arbol de archivos de FreeRtos.
Como nota adicional la unica tarea cargada no hace mas que demorar 10 ticks y volverse a dormir...
No era el objetivo de este post mostrar las capacidades del RTOS, eso queda para otros post.
Agregado:
Para poder manterner path a los archivos de tu proyecto y del source, es preferible ubicar tu proyecto dentro del arbol de directorios de FreeRTOS.
Yo cree una carpeta al mismo nivel que los demos de manera de utilizar path relativos y hacerlo mas flexible en su adaptacion.
\Demo
\License
\Projects<--Carpeta agregada
\Source
\TraceCon
Nota Importante: manejo del port
Es importante remarcar ciertos aspectos del uso del RTOS FreeRTOS con el compilador C18.
Este compilador como todos los que manejan arquitecturas de 8 bits limitadas, aboga por la eficiencia en velocidad, tamaño de codigo de programa y uso de memoria RAM.
Por ende algunas limitaciones tiene:
Aunque el modelo permite reentrancia, no la realiza a traves de manejo de memoria en el stack sino que genera una estructura de datos adicional estatica, es mas rapido pero escapa del estandar.
Ademas para mantener la integridad en calculos matematicos en variables atomizables (por ejemplo enteros en 32 bits que llegan 4 bytes) maneja en una posicion fija buen numero de posiciones de memoria.
Con respecto al FREERTOS, este requiere una gran area de memoria lineal para manejar su queue (cola), el problema es que el C18 maneja bancos de 256 bytes por lo que se debe modificar el archivo linker script para unir varias de estas secciones, pero lo importante es NO UNIRLAS TODAS ya que el compilador todavia necesita utilizar algunas de estas secciones para sus propositos.
El tamaño de esta queue se declara en el archivo FreeRTOSConfig.h y se llama configTOTAL_HEAP_SIZE.
Por ende las siguientes modificaciones al modelo son requeridas:
- Modificar el archivo lkr (EJ: 18f2620.lkr) uniendo varias secciones de ram en una sola (pero no todas)
- Modificar en el port para C18 (port.c) el numero de datos usados por matematica que se deben guardar en el stack.
Para esto se debe mirar el archivo .map que genera en la misma carpeta de proyecto y contar cuantos bytes
consume MATH_DATA y .tmpdata juntas este numero menos uno es el valor a poner en portCOMPILER_MANAGED_MEMORY_SIZE. - Agregar tantas lineas de :
MOVFF POSTINC0, PREINC1
y
MOVFF POSTDEC1, POSTDEC0
en la parte de salvar y restaurar contexto respectivamente, como el numero que tenemos en portCOMPILER_MANAGED_MEMORY_SIZE.
AT91SAM7S
De la misma manera que se realizo para el Tutorial paso a paso para crear un proyecto con FreeRtos para PIC18F en este caso pasamos a un microcontrolador mas importante como el AT91SAM7S.
En este caso simplemente recomiendo utilizar el cascaron que brindo aqui ya que es funcional y de aqui solo hay que agregar las fuentes para cada caso especifico. Una cosa a dejar en claro es: porque si todos los micros basados en ARM tienen la misma arquitectura, los ports (adaptaciones para el RTOS), son diferentes? Esto se debe a que aunque la arquitectura del procesador sea la misma, difieren las implementaciones de los perifericos (timers que son usados por el RTOS para el context switch, sistema de interrupciones, etc) y tambien es totalmente diferente el tipo de compilador usado por cada IDE (KEIL, IAR, GCC, etc) con lo que cada uno requerira un tunning especifico.
En el caso de KEIL no posee un port avalado por FREERTOS para la linea SAM7S con lo que utilizamos uno NO AVALADO pero totalmente funcional.
Es en realidad para un AT91SAM7X pero es exactamente lo mismo ya que las diferencias la maneja la libreria de KEIL.
Lo que si hay que agregar en el header file portable.h ubicado en FreeRTOS\Source\include\ la siguiente linea:
En este caso simplemente recomiendo utilizar el cascaron que brindo aqui ya que es funcional y de aqui solo hay que agregar las fuentes para cada caso especifico. Una cosa a dejar en claro es: porque si todos los micros basados en ARM tienen la misma arquitectura, los ports (adaptaciones para el RTOS), son diferentes? Esto se debe a que aunque la arquitectura del procesador sea la misma, difieren las implementaciones de los perifericos (timers que son usados por el RTOS para el context switch, sistema de interrupciones, etc) y tambien es totalmente diferente el tipo de compilador usado por cada IDE (KEIL, IAR, GCC, etc) con lo que cada uno requerira un tunning especifico.
En el caso de KEIL no posee un port avalado por FREERTOS para la linea SAM7S con lo que utilizamos uno NO AVALADO pero totalmente funcional.
Es en realidad para un AT91SAM7X pero es exactamente lo mismo ya que las diferencias la maneja la libreria de KEIL.
Lo que si hay que agregar en el header file portable.h ubicado en FreeRTOS\Source\include\ la siguiente linea:
Código:
#ifdef KEIL_SAM7
#include "..\..\Source\portable\Keil\SAM7\portmacro.h"
#endif
Con esto hariamos uso al port de SAM7 para KEIL que debemos ubicar en el path del include (./Source/portable/Keil/SAM7/).
Estas librerias son:
- portISR.c
- portasm.s
- port.c
De esta manera le de decimos al FREERTOS que si tiene soporte para el sistema en cuestion.
A continuacion muestro una imagen del espacio de trabajo:
Tenemos las fuentes particulares del proyecto (en este caso solo main.c y el assembler de arranque), el codigo del port especifico para este micro y las funciones propias del RTOS. Si desearamos modificar opciones del build debemos utilizar el modulo de opciones de build: El que tiene la varita es el de "options for target", aqui modificamos el tipo de micro a usar, las posiciones de memoria a usar, el tipo de debug, los path al codigo C/C++ y ASM, etc.El que tiene 3 cajitas de colores permite sacar o agregar modulos c/c++/h/asm al proyecto, ademas de documentacion. La tarea de prueba levanta 4 instancias de la tarea blinkear led y cada una captura 1 de los 4 leds disponibles de la placa de desarrollo de ATMEL.
El archivo zip tiene la carpeta raiz del proyecto con lo que no se van a perder para instalarlo en el directorio de FreeRTOS.
ARM7_LPC2106_Keil_GCC
Hola gente del Foro, siguiendo mi post anterior ARM Toolchains
y luego de mucho peludear por la Web para hacer compilar primero una
demo para ARM-GCC pero desde la IDE de Keil (uVision), logré, luego
después de más peludeo, hacer compilar uno de los ports de FreeRTOS
"ARM7_LPC2106_GCC" (o sea un port de FreeRTOS para NXP LPC2106 usando el
GNU C Compiler [es decir GCC]) usando, al igual que antes, el Keil
uVision como IDE. A este nuevo toolchain lo llamé "ARM7_LPC2106_Keil_GCC"
el cual adjunto. Para configurar el Keil uVision me ayudé mucho con
otro port de FreeRTOS pero para el Keil "ARM7_LPC2129_Keil_RVDS", con el
compilador que viene junto con él, es decir el RVDS (RealView
Development Suite).
Por qué Keil? bueno porque, como expliqué en el post anterior, es el UNICO que soporta el programador que ya tengo (ULink), por otro lado si bien no lo puedo asegurar, toda la parte de debug y simulación debe de estar muy bien soportada teniendo en cuenta que Keil forma parte de ARM desde hace un tiempo.
Para qué armé este Toolchain? Bueno, siempre fui partidario de usar plataformas abiertas y además de un modo u otro GNU y FreeRTOS de un modo u otro llegan a todos lados.
Qué hay que tener en cuenta al armar un Toolchain como este? Como puse mas arriba el Toolchain que armé es la suma del source/makefile del port "ARM7_LPC2106_GCC" mirando al mismo tiempo la configuración del IDE del uVision en el port "ARM7_LPC2129_Keil_RVDS". En principio lo que se hace es armar las carpetas del port para GCC copiando solo Sources(.c), Headers(.h), Startup Code(.s) y Linker Script(.ld), y luego crear el proyecto con el Keil ya configurado para compilar con GCC para luego traspasar las directivas del precompilador y los path a las librerías que se pueden sacar del makefile. Si no entienden cómo funciona el makefile lo que pueden hacer es compilarlo (aparte en una carpeta específica para GCC) primero con el GCC, es decir escribir 'make'. Esto es muy importante porque antes de hacer funcionar el GCC con el IDE del Keil sería bueno asegurarse que funciona solito, es decir, como fue concebido. En mi caso, para 'robar' los parámetros que el makefile le pasa al compilador y al linker usé un viejo truco de DOS (ahora 'Simbolo de sistema' en Windows) que es enviar la salida por defecto (CON) de cualquier aplicación de consola a un archivo de esta forma: "make > make_out.txt". Ésto nos ejecuta el 'make' pero en lugar de mostarnos el resultado de lo que va haciendo en la consola(pantalla), lo graba en el archivo 'make_out.txt'. En este port en particular había varias formas de compilarlo (Debug(RAM)/Target(ROM) y ARM/Thumb, es decir 4 combinaciones) preparadas en archivos batch de DOS(.bat), yo usé 'ROM_ARM.bat' y acá les pego el resultado:
(NOTA: le quité la opción -fno-dwarf2-cfi-asm al makefile porque no compila con el nuevo GNU-ARM)
De donde se rescata:
En "Options for Target":
En "Setup File Extensions, Books and Environment":
Esto compila en Keil uVision sin errores y sin warnings como muestro acá:
Claro
que 'del dicho al hecho hay un gran trecho' y todo ésto si bien compila
no está probado sobre en un target real, es decir, una placa de
desarrollo. En teoría debería ser igual de efectivo que el port original
para GCC (por razones obvias), pero se los comento para que lo tengan
en cuenta.
Lo que rescato de todo ésto es que tengo un buen simulador/debugger (Keil uVision) en un entorno abierto muy portable (ARM-GCC + FreeRTOS). No quedo atado con ésto ni a compiladores propietarios (Keil RealView, IAR C/C++ Compiler, etc) ni a RTOS propietarios (Keil RTX, IAR PowerPack, Rowley CTL, etc).
Los parametros son:
configUSE_PREEMPTION:
Se configura con 1 que funcione como un RTOS preventivo y con 0 como un RTOS cooperativo
configUSE_TICK_HOOK
Se configura con 1 si se desea utilizar un tick externo al provisto por el proyecto 0 si se usa el propio del port.
configCPU_CLOCK_HZ
Se configura la velocidad del reloj del sistema (es un parametro que se le pasa para referencia del RTOS)
configTICK_RATE_HZ
Se configura cada cuanto se desea que se genere un tick de sistema, si es muy alto se tiene mucha definicion pero mucho overhead.
El valor de referencia es 1000
configMAX_PRIORITIES
Es el numero maximo de prioridades a utilizar, cuanto mas grande mas memoria se consume.
Se debe tener en cuenta que varias tareas pueden tener la misma prioridad (metodo de round robin para tareas de = prioridad).
configMINIMAL_STACK_SIZE
Tamaño de la pila minimo, se debe tratar de no tocar del valor de referencia de la demo.
configTOTAL_HEAP_SIZE
Tamaño de la cola de memoria disponible para el RTOS. De aqui se alimenta el sistema operativo para su manejo (colas, semaforos, mutex, etc).
configMAX_TASK_NAME_LEN
Esta es la longitud maxima de nombre de la tarea
configUSE_TRACE_FACILITY
Utilizado para trazabilidad.
configUSE_16_BIT_TICKS
Tamaño de los registros de conteo de Ticks, impacta directamente en el tiempo total posible a contar, en la memoria utilizada para tal fin y el overhead generado por conteos con mas longitud de palabra.
Depende mucho de la arquitectura del micro (8, 16 o 32 bits)
configIDLE_SHOULD_YIELD
Util solo si el RTOS esta configurado en modo preventivo y se usa la tarea IDLE.
Afecta al funcionamiento de las tareas en IDLE, su eficiencia y overhead.
configUSE_USE_MUTEXES
Si se setea en 1 se utilizan MUTEXES
configUSE_RECURSIVE_MUTEXES
SI se setea en 1 se pueden usar MUTEXES recursivos
configUSE_COUNTING_SEMAPHORES
Si se setea en 1 los semaforos no seran binarios sino que contaran numero de ocurrencias a costo de mas memoria.
configUSE_ALTERNATIVE_API
Si se setea en 1 se utilizan APIs alternativas que tienen menor costo en peso de codigo y velocidad a costa de usarlas con consideraciones.
configCHECK_FOR_STACK_OVERFLOW
Si se setea en 1 se checkea por overflow del stack.
configUSE_CO_ROUTINES
Si se setea en 1 se utilizaran corutinas (debe incluirse la source corutine.c) para manejo cooperativo (puede ser mixto con preventivo)
configMAX_CO_ROUTINE_PRIORITIES
Numero de prioridades de las corutinas
configKERNEL_INTERRUPT_PRIORITY
Solo disponible para PIC24/DSPIC y Cortex M3 processors, se puede configurar las prioridades de las interrupciones y como afectan al RTOS.
PARAMETROS INCLUDE
Esto es una funcionalidad muy buena del RTOS, permite anular del proceso de BUILD (compilacion), a toda funcion que no se desea con solo poner la definicion con su nombre de la siguiente forma.
#define INCLUDE_vTaskDelete 1 Aqui se incluye en el proceso de build a la tarea "borrar Tarea"
#define INCLUDE_vTaskDelete 0 Aqui se excluye del proceso de build a la tarea "borrar Tarea"
Por qué Keil? bueno porque, como expliqué en el post anterior, es el UNICO que soporta el programador que ya tengo (ULink), por otro lado si bien no lo puedo asegurar, toda la parte de debug y simulación debe de estar muy bien soportada teniendo en cuenta que Keil forma parte de ARM desde hace un tiempo.
Para qué armé este Toolchain? Bueno, siempre fui partidario de usar plataformas abiertas y además de un modo u otro GNU y FreeRTOS de un modo u otro llegan a todos lados.
Qué hay que tener en cuenta al armar un Toolchain como este? Como puse mas arriba el Toolchain que armé es la suma del source/makefile del port "ARM7_LPC2106_GCC" mirando al mismo tiempo la configuración del IDE del uVision en el port "ARM7_LPC2129_Keil_RVDS". En principio lo que se hace es armar las carpetas del port para GCC copiando solo Sources(.c), Headers(.h), Startup Code(.s) y Linker Script(.ld), y luego crear el proyecto con el Keil ya configurado para compilar con GCC para luego traspasar las directivas del precompilador y los path a las librerías que se pueden sacar del makefile. Si no entienden cómo funciona el makefile lo que pueden hacer es compilarlo (aparte en una carpeta específica para GCC) primero con el GCC, es decir escribir 'make'. Esto es muy importante porque antes de hacer funcionar el GCC con el IDE del Keil sería bueno asegurarse que funciona solito, es decir, como fue concebido. En mi caso, para 'robar' los parámetros que el makefile le pasa al compilador y al linker usé un viejo truco de DOS (ahora 'Simbolo de sistema' en Windows) que es enviar la salida por defecto (CON) de cualquier aplicación de consola a un archivo de esta forma: "make > make_out.txt". Ésto nos ejecuta el 'make' pero en lugar de mostarnos el resultado de lo que va haciendo en la consola(pantalla), lo graba en el archivo 'make_out.txt'. En este port en particular había varias formas de compilarlo (Debug(RAM)/Target(ROM) y ARM/Thumb, es decir 4 combinaciones) preparadas en archivos batch de DOS(.bat), yo usé 'ROM_ARM.bat' y acá les pego el resultado:
(NOTA: le quité la opción -fno-dwarf2-cfi-asm al makefile porque no compila con el nuevo GNU-ARM)
Código:
C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set USE_THUMB_MODE=NO C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set DEBUG= C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set OPTIM=-O3 C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set RUN_MODE=RUN_FROM_ROM C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>set LDSCRIPT=lpc2106-rom.ld C:\!Raul\SourcesPruebas\ARM7_LPC2106_GCC\Demo\ARM7_LPC2106_GCC>make arm-elf-gcc -c -Wall -Wextra -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wsign-compare -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused -D RUN_FROM_ROM -D GCC_ARM7 -I. -I../../Source/include -I../Common/include -mcpu=arm7tdmi -Tlpc2106-rom.ld -O3 -fomit-frame-pointer -fno-strict-aliasing ../../Source/portable/GCC/ARM7_LPC2000/portISR.c -o ../../Source/portable/GCC/ARM7_LPC2000/portISR.o (...para el resto de los .c es lo mismo...) arm-elf-gcc -Wall -Wextra -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wsign-compare -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused -D RUN_FROM_ROM -D GCC_ARM7 -I. -I../../Source/include -I../Common/include -mcpu=arm7tdmi -Tlpc2106-rom.ld -O3 -fomit-frame-pointer -fno-strict-aliasing ../../Source/portable/GCC/ARM7_LPC2000/portISR.o serial/serialISR.o main.o serial/serial.o ParTest/ParTest.o ../Common/Minimal/integer.o ../Common/Minimal/flash.o ../Common/Minimal/PollQ.o ../Common/Minimal/comtest.o ../Common/Minimal/flop.o ../Common/Minimal/semtest.o ../Common/Minimal/dynamic.o ../Common/Minimal/BlockQ.o ../../Source/tasks.o ../../Source/queue.o ../../Source/list.o ../../Source/portable/MemMang/heap_2.o ../../Source/portable/GCC/ARM7_LPC2000/port.o -nostartfiles boot.s -Xlinker -ortosdemo.elf -Xlinker -M -Xlinker -Map=rtosdemo.map arm-elf-objcopy rtosdemo.elf -O ihex rtosdemo.hex
- Preprocesor Symbols:
- RUN_FROM_ROM
- GCC_ARM7
- Include Paths:
- .
- ../Common/include
- ../../Source/include
- ../../Source/portable/GCC/ARM7_LPC2000
- Misc Controls:
- -fomit-frame-pointer
- -fno-strict-aliasing
- Optimization:
- -O3
- Linker Script File:
- lpc2106-rom.ld
En "Options for Target":
En "Setup File Extensions, Books and Environment":
Código:
Build target 'RTOSDemo_ARM' assembling boot.s... compiling ParTest.c... compiling main.c... compiling serial.c... compiling serialISR.c... compiling tasks.c... compiling list.c... compiling queue.c... compiling port.c... compiling heap_2.c... compiling portISR.c... compiling flash.c... compiling comtest.c... compiling BlockQ.c... compiling dynamic.c... compiling PollQ.c... compiling semtest.c... compiling flop.c... compiling integer.c... linking... creating hex file... "RTOSDemo.elf" - 0 Error(s), 0 Warning(s).
Lo que rescato de todo ésto es que tengo un buen simulador/debugger (Keil uVision) en un entorno abierto muy portable (ARM-GCC + FreeRTOS). No quedo atado con ésto ni a compiladores propietarios (Keil RealView, IAR C/C++ Compiler, etc) ni a RTOS propietarios (Keil RTX, IAR PowerPack, Rowley CTL, etc).
2.-Configurar el modo de funcionamiento del sistema Operativo
Una gran variedad de parametros estan disponibles para configurar este versatil sistema operativo en tiempo real. Muchos
de ellos estan pensados para impactar de manera positiva en el tamaño
del codigo o en la velocidad de respuesta del mismo o inclusive como va a
funcionar. Todos estos parametros estan ubicados en el header
file llamado "FreeRTOSConfig.h" (se incluye como ejemplo un config para
PIC18F)
Código:
/*
FreeRTOS.org V5.0.0 - Copyright (C) 2003-2008 Richard Barry.
This file is part of the FreeRTOS.org distribution.
FreeRTOS.org is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
FreeRTOS.org is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FreeRTOS.org; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes FreeRTOS.org, without being obliged to provide
the source code for any proprietary components. See the licensing section
of http://www.FreeRTOS.org for full details of how and when the exception
can be applied.
***************************************************************************
***************************************************************************
* *
* SAVE TIME AND MONEY! We can port FreeRTOS.org to your own hardware, *
* and even write all or part of your application on your behalf. *
* See http://www.OpenRTOS.com for details of the services we provide to *
* expedite your project. *
* *
***************************************************************************
***************************************************************************
Please ensure to read the configuration and relevant port sections of the
online documentation.
http://www.FreeRTOS.org - Documentation, latest information, license and
contact details.
http://www.SafeRTOS.com - A version that is certified for use in safety
critical systems.
http://www.OpenRTOS.com - Commercial support, development, porting,
licensing and training services.
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include <p18cxxx.h>
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configTICK_RATE_HZ ( ( portTickType ) 1000 )
#define configCPU_CLOCK_HZ ( ( unsigned portLONG ) 20000000 )
#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 4 )
#define configMINIMAL_STACK_SIZE ( 105 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) 3600 )
#define configMAX_TASK_NAME_LEN ( 4 )
#define configUSE_TRACE_FACILITY 0
#define configUSE_16_BIT_TICKS 1
#define configIDLE_SHOULD_YIELD 1
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 0
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 0
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#endif /* FREERTOS_CONFIG_H */
Los parametros son:
configUSE_PREEMPTION:
Se configura con 1 que funcione como un RTOS preventivo y con 0 como un RTOS cooperativo
configUSE_TICK_HOOK
Se configura con 1 si se desea utilizar un tick externo al provisto por el proyecto 0 si se usa el propio del port.
configCPU_CLOCK_HZ
Se configura la velocidad del reloj del sistema (es un parametro que se le pasa para referencia del RTOS)
configTICK_RATE_HZ
Se configura cada cuanto se desea que se genere un tick de sistema, si es muy alto se tiene mucha definicion pero mucho overhead.
El valor de referencia es 1000
configMAX_PRIORITIES
Es el numero maximo de prioridades a utilizar, cuanto mas grande mas memoria se consume.
Se debe tener en cuenta que varias tareas pueden tener la misma prioridad (metodo de round robin para tareas de = prioridad).
configMINIMAL_STACK_SIZE
Tamaño de la pila minimo, se debe tratar de no tocar del valor de referencia de la demo.
configTOTAL_HEAP_SIZE
Tamaño de la cola de memoria disponible para el RTOS. De aqui se alimenta el sistema operativo para su manejo (colas, semaforos, mutex, etc).
configMAX_TASK_NAME_LEN
Esta es la longitud maxima de nombre de la tarea
configUSE_TRACE_FACILITY
Utilizado para trazabilidad.
configUSE_16_BIT_TICKS
Tamaño de los registros de conteo de Ticks, impacta directamente en el tiempo total posible a contar, en la memoria utilizada para tal fin y el overhead generado por conteos con mas longitud de palabra.
Depende mucho de la arquitectura del micro (8, 16 o 32 bits)
configIDLE_SHOULD_YIELD
Util solo si el RTOS esta configurado en modo preventivo y se usa la tarea IDLE.
Afecta al funcionamiento de las tareas en IDLE, su eficiencia y overhead.
configUSE_USE_MUTEXES
Si se setea en 1 se utilizan MUTEXES
configUSE_RECURSIVE_MUTEXES
SI se setea en 1 se pueden usar MUTEXES recursivos
configUSE_COUNTING_SEMAPHORES
Si se setea en 1 los semaforos no seran binarios sino que contaran numero de ocurrencias a costo de mas memoria.
configUSE_ALTERNATIVE_API
Si se setea en 1 se utilizan APIs alternativas que tienen menor costo en peso de codigo y velocidad a costa de usarlas con consideraciones.
configCHECK_FOR_STACK_OVERFLOW
Si se setea en 1 se checkea por overflow del stack.
configUSE_CO_ROUTINES
Si se setea en 1 se utilizaran corutinas (debe incluirse la source corutine.c) para manejo cooperativo (puede ser mixto con preventivo)
configMAX_CO_ROUTINE_PRIORITIES
Numero de prioridades de las corutinas
configKERNEL_INTERRUPT_PRIORITY
Solo disponible para PIC24/DSPIC y Cortex M3 processors, se puede configurar las prioridades de las interrupciones y como afectan al RTOS.
PARAMETROS INCLUDE
Esto es una funcionalidad muy buena del RTOS, permite anular del proceso de BUILD (compilacion), a toda funcion que no se desea con solo poner la definicion con su nombre de la siguiente forma.
#define INCLUDE_vTaskDelete 1 Aqui se incluye en el proceso de build a la tarea "borrar Tarea"
#define INCLUDE_vTaskDelete 0 Aqui se excluye del proceso de build a la tarea "borrar Tarea"
3.-Tipos de variables y convencion de nombres
Aunque se pueden utilizar los tipos de variables que provee tu compilador, y a manera de unificar entre diferentes plataformas, el FREERTOS utiliza tipos de variables propios que no son mas que definiciones de tipo que son actualizados en tiempo de compilacion. De esta manera, el codigo que se realiza para un compilador "X" funciona en otro compilador "Y".Los tipos estandar son:
portCHAR Es un char, una palabra entera de 8 bits o un caracter ASCII estandar
portFLOAT Es un numero con punto flotante de precision simple
portDOUBLE Es un numero con punto flotante de precision doble
portLONG Es un entero de 32 bits
portSHORT Es un entero de 16 bits
portSTACK_TYPE es el tipo de datos y tamaño consumidos por el stack de memoria
Hay dos tipos de variables muy fuertemente unidos a la arquitectura del micro:
portBASE_TYPE Es el tipo de variable entera basica atada al tipo de procesador, el que genera menos overhead.
portTickType Es configurable en longitud (16 o 32 bits) dependiendo de lo elegido en la Configuracion del RTOS
Convencion de nombres
Las aplicaciones que desarrolles bajo el RTOS deberian cumplir con las reglas de nombres que se establecen para una mejor lectura y comprension del codigo.
Los nombres de las funciones utilizan el metodo lower camelcase donde el primer caracter hace referencia al tipo de variable que se retorna.
Si se retorna void lleva al principio v
Si se retorna char lleva al principio c
Si se retorna short lleva al principio s
Si se retorna long lleva al principio l
Si se retorna double lleva al principio d
Si se retorna variables enumeradas lleva al principio e
Si se retorna otros tipos (por ej estructuras) lleva al principio x
Si se retorna un puntero a un tipo de datos se pone adicionalmente p (ej: ps es puntero a un short)
Adicionalmente, si la variable es sin signo (unsigned) se pone delante una u.
Si son funciones privadas, adicionalmente se pone prv
Siempre se hace referencia en el nombre al modulo en donde se han definido (por ejemplo vTaskDelete esta en el modulo task.c).
4.-Inicializacion del RTOS, creacion y destruccion de tareas
Una vez que tenemos el ambiente de desarrollo listo, solo nos queda agregar en el programa principal (main) las llamadas a las APIs que se encargan de:
1.Para ejecutar una tarea especifica se llama a la funcion xTaskCreate que toma los siguientes parametros en el mismo orden:
pvTaskCode Puntero al nombre de la tarea.
pcName Un nombre auto descriptivo de la tarea para ayudar en el proceso de debug. la longitud max se define con configMAX_TASK_NAME_LEN.
usStackDepth Es el tamaño del stack de tareas definido como el nro de variables que el stack puede almacenar, no el nro de bytes.
pvParameters puntero a los parametros que va a recibir la tarea.
uxPriority La prioridad con la que va a correr la tarea (depende del nro de prioridades que se configuro)
pvCreatedTask Se usa para pasar el manejador por la cual la tarea puede ser referenciada.
Devuelve un indicador de exito (pdPASS) o fracaso (un error code) en la creacion de la tarea.
Ejemplo:
2. Para destruir una tarea y recuperar la memoria que utilizaba en la cola, se debe llamar a la funcion vTaskDelete donde el unico parametro que se le pasa es el el manejador a la tarea.
Tareas Suicidas:
Agregue un pequeño ejemplo en donde se arranca una tarea que crea tiene como funcion crear otra tarea y se autodestruye.
La segunda realiza la misma tarea que la primera aunque no comparte codigo en comun.
- Inicializar la memoria del sistema
- Ejecutar el Scheduler o administrador de tareas
- Ejecutar las tareas padre.
Para la inicializacion de la memoria se llama a la funcion vPortInitialiseBlocks() que depende directamente del port y del modelo de memoria utilizado. Para ejecutar el Sheduler y comenzar el proceso de administracion se llama a la funcion vTaskStartScheduler,
esta funcion tambien se encarga de la configuracion previa del
periferico encargado para el tick del sistema y de las interrupciones.
Si tuvo exito en su arranque no retorna nunca mas a menos que se llame a la funcion vTaskEndScheduler que permite continuar la ejecucion justo en el punto en donde se ejecuto vTaskStartScheduler.
Si tuvo exito en su arranque no retorna nunca mas a menos que se llame a la funcion vTaskEndScheduler que permite continuar la ejecucion justo en el punto en donde se ejecuto vTaskStartScheduler.
1.Para ejecutar una tarea especifica se llama a la funcion xTaskCreate que toma los siguientes parametros en el mismo orden:
pvTaskCode Puntero al nombre de la tarea.
pcName Un nombre auto descriptivo de la tarea para ayudar en el proceso de debug. la longitud max se define con configMAX_TASK_NAME_LEN.
usStackDepth Es el tamaño del stack de tareas definido como el nro de variables que el stack puede almacenar, no el nro de bytes.
pvParameters puntero a los parametros que va a recibir la tarea.
uxPriority La prioridad con la que va a correr la tarea (depende del nro de prioridades que se configuro)
pvCreatedTask Se usa para pasar el manejador por la cual la tarea puede ser referenciada.
Devuelve un indicador de exito (pdPASS) o fracaso (un error code) en la creacion de la tarea.
Ejemplo:
Código:
void main( void )
{
xTaskHandle xHandle;
/* Inicializo Hardware. */
vPortInitialiseBlocks();
/* Arranco la tarea */
xTaskCreate( vTareaDePrueba, ( const portCHAR * const ) "Prueba", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, Handle );
/* Arranco el scheduler. */
vTaskStartScheduler();
}
2. Para destruir una tarea y recuperar la memoria que utilizaba en la cola, se debe llamar a la funcion vTaskDelete donde el unico parametro que se le pasa es el el manejador a la tarea.
Código:
void vDestruirMiTarea( void )
{
vTaskDelete( xHandle );
}
Tareas Suicidas:
Agregue un pequeño ejemplo en donde se arranca una tarea que crea tiene como funcion crear otra tarea y se autodestruye.
La segunda realiza la misma tarea que la primera aunque no comparte codigo en comun.
5.-Consideraciones para Multiples instancias de la misma tarea
De la misma manera que en un Sistema Operativo clasico, ya podemos generar multiples instancias del codigo de una sola funcion. Cada una de ellas tendra, gracias al RTOS, una porcion de los recursos del micro, para alojar sus datos y ejecutarse correctamente. Los recursos que pidamos a traves del RTOS seran instanciados con su tarea especifica, pero que pasa con las variables declaradas en la funcion?
Son instanciadas en conjunto con la tarea!!, cada Tarea poseera sus propias posiciones de memoria para sus variables. Esto lo podemos constatar mirando la ubicacion de dichas variables en el mapa de memoria. En el siguiente ejemplo se crean 2 instancias de la misma funcion, donde cada una recibe un parametro diferente que las identifica (mas alla del nombre que se le pasa).
Código:
void main( void )
{
/* Inicializo Hardware. */
vPortInitialiseBlocks();
/* Arranco la tarea */
xTaskCreate( vTarea, ( const portCHAR * const ) "Prueba1", configMINIMAL_STACK_SIZE, &cProcId1, tskIDLE_PRIORITY + 2, NULL );
xTaskCreate( vTarea, ( const portCHAR * const ) "Prueba2", configMINIMAL_STACK_SIZE, &cProcId2, tskIDLE_PRIORITY + 2, NULL );
/* Arranco el scheduler. */
vTaskStartScheduler();
}
Luego cada instancia chequea ese parametro para saber cual de las dos instancias se esta ejecutando en dicho momento.
Código:
static void vTarea( void *pvParameters )
{
portCHAR *cProcId,cVariableUnica;
for( ;; )
{
/* Demoro 100 Ticks */
vTaskDelay( 100 );
cProcId = pvParameters;`
if (*cProcId == 1)
{
cVariableUnica = 1;
portNOP();
}
else if (*cProcId == 2)
{
cVariableUnica = 2;
portNOP();
}
else
{
portNOP(); //Caso de error no deberia entrar aca
cVariableUnica = 3;
}
}
}
Si visualizamos el mapa de memoria veremos que cuando se ejecuta cada una de las tareas se muestra para la misma variable una direccion diferente de memoria.
6.-Uso de las prioridades
Ahora que ya podemos crear tareas debemos pensar en como utilizar eficiente y responsablemente el tiempo del micro. Eficientemente porque obviamente hay tareas que no requieren mucho tiempo de procesador ni tanta prioridad para completarla y responsablemente ya que configurando prioridades mal en conjunto con una mala programacion pueden producir estragos en el funcionamiento del sistema.A continuacion pondre una serie de ejemplos:
Tenemos 3 tareas:
- 1.Atender pedidos por usart.
- 2.Atender teclado
- 3.Realizar una tarea de procesamiento ininterrumpida muy larga.
EJ 1:
Evidentemente en este sistema tendremos prioridades, y para el caso usaremos 3.
1.Los pedidos de usart deberian tener la MAXIMA prioridad, ya que deben atenderse inmediatamente despues de recibirse.
En el instante posterior de haber concluido dicha tarea, se vuelve a dormir y otras tareas de menor prioridad pueden ejecutarse.
2.Atender el teclado tambien es un evento asincronico, que interrumpe en un instante y luego se vuelve a dormir. Es menos prioritario que el anterior.
3.Una tarea de mucho tiempo de procesamiento tendra menor prioridad ya que pedira cuanto tiempo de micro se tenga, por ende se hace cuando no hay nada mas que hacer.
EJ2: Que pasa si la tarea 3 tuviese la maxima prioridad?
Como es una tarea que nunca se duerme (por ej para relegar procesador o para esperar un evento), estara siempre activa deseosa por tiempo de procesador. Si es la tarea de mayor prioridad, las tareas de menor prioridad NUNCA se ejecutaran, porque compiten contra una tarea mas prioritaria, por ende dichas tareas MUEREN DE INANICION.
EJ3: Que pasa si las tareas 1 y 2 tuviesen la misma prioridad? (y la 3 fuese de menor prioridad)
En este caso, y si es que ambas tuviesen que ejecutarse, el RTOS se encargaria de compartir entre las dos el tiempo de micro por el mecanismo de Round Robin.
Este mecanismo simplemente cicla entre las tareas de igual prioridad.
EJ4: Y si las tareas 1 y 2 son las mas prioritarias y nunca relegan el micro para la tarea 3?
Entonces tendria que existir un mecanismo de incremento de la prioridad de la tarea 3 para evitar su muerte por inanicion.
La encargada de dicha funcion seria una tarea de la mas alta prioridad que chequea cada un periodo de tiempo que todas las tareas tengan oportunidad de ejecutar y para las que no lo logran se les aumenta la prioridad.
Una vez que se logra inmediatamente se le retorna su prioridad nominal.
7.-Control sobre la tarea
El sistema operativo FREERTOS da una serie de APIs orientadas al control optimo de la tarea en curso, osea que generen un cambio en las condiciones del mismo sin necesidad de otras fuentes externas (semaforos, colas, mutex, etc).Ellas son:
- vTaskDelay:
Genera una demora en la ejecucion de la proxima tarea medida en ticks de sistema(dicha demora NO es a base de consumo de ciclos de reloj) . - vTaskDelayUntil:
Genera una demora hasta un instante de tiempo especifico, ideal para poder realizar periodos ciclicos con una frecuencia fija.
Se basa en el uso del reloj del sistema. - uxTaskPriorityGet:
Recupera la prioridad de una tarea entregandosele la informacion de su handler. - vTaskPrioritySet:
Altera la prioridad de una tarea entregandosele la informacion de su handler. - vTaskSuspend:
Suspende la ejecucion de una tarea, no importa la prioridad que ella tenga. - vTaskResume:
Continua la ejecucion de una tarea suspendida. - vTaskResumeFromISR:
Idem anterior pero adaptada para poder ser llamada desde interrupciones.
8.-Control sobre el Kernel
Control sobre el Kernel:De la misma manera que con las tareas, el RTOS provee herramientas para el control del funcionamiento del sistema operativo.
En su mayoria son funciones que proveen soporte y proteccion a dilemas de programacion como son las secciones criticas con recursos compartidos entre las tareas y el kernel (secciones criticas a nivel kernel) y las secciones criticas a nivel de aplicacion, para ello estan las siguientes APIs y los metodos de sincronizacion entre tareas.
- vTaskStartScheduler:Comienza el funcionamiento del sistema operativo.
- vTaskEndScheduler:Termina
el funcionamiento del sistema operativo, el codigo siguiente a ejecutar
es el que se encuentra despues de la llamada a vTaskStartScheduler.
Es muy util cuando se desea hacer uso del procesador para una tarea dedicada como actualizacion del firmware, etc. Luego puede llamarse nuevamente a vTaskStartScheduler para continuar con el funcionamiento del RTOS. - taskDISABLE_INTERRUPTS: Deshabilita TODA fuente de interrupcion y con ello el analisis de cualquier fuente de la misma.
Se utiliza para los casos mas estrictos de SECCION CRITICA donde NADA puede interrumpir a las intrucciones posteriores.
Se usa en conjunto con taskENABLE_INTERRUPTS para delimitar la zona critica.
Su uso mas habitual es cuando el procesador debe estar totalmente devoto a una tarea critica en tiempos mas que por uso de recursos compartidos (aunque puede tambien serlo). - taskENABLE_INTERRUPTS:Rehabilita TODAS las fuentes de interrupcion.
- taskENTER_CRITICAL: Deshabilita la fuente de interrupcion ligada al Tick del sistema y acceso al algoritmo preventivo.
Este es otro tipo de seccion critica de kernel donde se protege los recursos (puede ser solamente datos) compartidos entre las tareas o Kernel y las interrupciones al tick de sistema y al algoritmo preventivo.
Otras fuentes de interrupcion siguen siendo funcionales (USART, SPI, INTs, etc) - taskEXIT_CRITICAL: Rehabilita las fuentes de interrupcion provenientes del tick del RTOS.
- vTaskSuspendAll: Se da comienzo a una seccion critica de sistema operativo a nivel de aplicacion.
Se atienden todos los tipos de interrupciones (se cuentan los ticks por ejemplo) pero no se generan cambios de contexto de tareas.
Esto es para evitar secciones criticas entre dos tareas que acceden a un mismo recurso y por ser de corto plazo no es necesario utilizar mecanismos de sincronizacion y exclusion mutua. - xTaskResumeAll: Se rehabilita el modo preventivo de las tareas.
- taskYIELD: Fuerza al sistema operativo a realizar un cambio de contexto.
Esta pensado especificamente para el modo de trabajo cooperativo pero funciona bajo modo preventivo igualmente.
Cuanto mas se la llame en una tarea mas granularidad se tendra pero se generara mas overhead por uso del sistema operativo.
9.-Semaforos
Los semaforos son la herramienta que posee el Sistema Operativo en tiempo real para realizar coordinacion entre tareas.Dos de los usos habituales de los semaforos son:
- 1.Sincronizacion para la toma de un recurso comun: Este no es mas que otro modo de resolver secciones criticas como se explica en el post de control sobre el kernel
Lo que se busca es utilizar un semaforo como "llave" para el uso de un recurso compartido. Si dicha llave esta en uso no se puede acceder a dicho recurso, o se espera por ella o se indica el error pero no se utiliza el recurso de manera que se protege el mismo de un uso incorrecto. - 2.Sincronizacion para la atencion de eventos asincronicos: En este caso se utiliza al semaforo como indicador de evento y disparo de actividades.
Es un caso simple del uso de colas, donde en este caso no se posee mensaje a enviar.
Una tarea es la generadora de eventos (en FREERTOS la que hace Give) y la otra tarea que espera dicho evento (en FREERTOS la que hace Take).
Se puede computar cual sera el intervalo de tiempo que se espera por el semaforo, si no se tiene exito en tomarlo se retorna luego de dicho tiempo y se indica con error. Si se logro capturar se despierta a la tarea que llamo a xSemaphoreGive() y se indica exito.
Semaforos binarios
Mutexes
Las funciones para semaforos son:
- 1.De creacion
vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )
Crea un semaforo BINARIO si es que xSemaphore no retorna NULL.
xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount )
Igual al anterior, crea un semaforo pero con capacidad de conteo, delimitado por uxMaxCount que es la cantidad maxima posible a contar y uxInitialCount que es el valor inicial de conteo.
Tambien se puede usar como 1.contador de eventos o 2.Administracion de recursos.
xSemaphoreHandle xSemaphoreCreateMutex( void )
Crea un semaforo de caracteristicas binarias pero con capacidad de exclusion mutua, que significa que posee manejo de prioridades.
En un caso de competencia, gana el mutex la tarea que tenga mayor prioridad.
xSemaphoreHandle xSemaphoreCreateRecursiveMutex( void )
Crea un Mutex recursivo. - 2.De toma de recurso o espera de evento
xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xBlockTime )
Cuando se llama a esta funcion, la tarea esperara "xBlockTime" de tiempo por el semaforo "xSemaphore", luego de ese tiempo si no se libero (o genero evento) se reanuda la ejecucion de la tarea y se informa error.
Si se libero se captura el semaforo para asegurar que nadie utilizara dicho semaforo.
xSemaphoreTakeRecursive( xSemaphoreHandle xMutex, portTickType xBlockTime )
Toma de manera recursiva un semaforo para asegurarselo tantas veces como se llamo a la funcion. - 3.De entrega de recurso o generacion de evento
xSemaphoreGive( xSemaphoreHandle xSemaphore )
En este caso se libera al semaforo para que otras tareas puedan usar el recurso asociado con la misma o se genera un evento para despertar a la tarea que espera por dicho semaforo.
xSemaphoreGiveRecursive( xSemaphoreHandle xMutex )
Se devuelve dicho semaforo de manera recursiva.
Para que otra tarea lo pueda usar, la tarea que tiene capturado dicho semaforo debe llamar a esta funcion tantas veces como antes llamo a xSemaphoreTakeRecursive.
xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore, portBASE_TYPE *pxHigherPriorityTaskWoken )
Igual que xSemaphoreGive, pero con contemplaciones especiales para su uso desde interrupciones.
En "ejemplo_semaforos_evento.ZIP" se utiliza un semaforo binario como generador de evento, recuerden posicionarlo dentro de la carpeta "Projects" para que compile y corrijan los path a la carpeta de su compilador C18.
En "ejemplo_semaforos_recurso.ZIP" se utiliza semaforo binario como "llave" al recurso "puerto C" del microcontrolador.
Una tarea quiere escribir hacia el puerto y la otra quiere leer de su entrada, para ello uno requiere configurarlo en salida y escribir y el otro configurarlo en entrada y leer.
Con el uso de un semaforo arbitramos su uso de manera correcta sin que se mezclen pasos de ambas tareas por culpa del context switch del RTOS.
10.-Colas
Trabajo en procesoEl uso de colas (queue) es el metodo estandar de comunicacion entre procesos o tareas.
Una cola es una estructura de datos, caracterizada por ser una secuencia de elementos en la que la operación de inserción "push" se realiza por un extremo y la operación de extracción "pop" por el otro. También se le llama estructura FIFO (del inglés First In First Out), debido a que el primer elemento en entrar será también el primero en salir (segun Wikipedia).
En sistemas operativos, cuando una tarea desea enviar un mensaje a otra, "empuja" el mensaje a dicha cola (si es que hay lugar en la misma, sino debera esperar o informar error) por un extremo.
Del otro lado habra una tarea a la espera de mensajes y cuando uno llega se despertara para analizarlos realizando una "extraccion" por el otro extremo de la cola.
En FREERTOS el metodo de creacion de una cola y asignacion de un espacio de memoria esta dado por la funcion:
xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize );
Donde xQueueHandle almacena la referencia a la instancia, si retorna NULO o cero es que no pudo crearlo.
uxQueueLength la cantidad de posiciones que tendra la cola y uxItemSize representa cantidad de datos que consumira en bytes cada elemento.
Para enviar mensajes se dispone de portBASE_TYPE xQueueSend( xQueueHandle xQueue, const void * pvItemToQueue, portTickType xTicksToWait );
Para recibir mensajes se dispone de portBASE_TYPE xQueueReceive( xQueueHandle xQueue, void *pvBuffer, portTickType xTicksToWait );
Donde xTicksToWait representa el tiempo que se esperara para posicionar el mensaje en la cola o esperar por un mensaje de la misma.
Si se setea INCLUDE_vTaskSuspend en 1 con el valor portMAX_DELAY se suspende por tiempo indefinido la tarea hasta que se libere espacio en la cola o se reciba un mensaje dependiendo el caso.
xQueue representa la cola por donde se mueven los mensajes y pvItemToQueue el mensaje en si.
Ambas funciones retornan como resultado el exito (pdPASS) o fracaso (un codigo de error) en la tarea.
El ejemplo a continuacion representa a dos tareas que se envian un mensaje del tipo char que se va incrementado de a uno por exito en cada tarea.
Para demostrar el correcto uso de colas, si alguna de las dos no envia el mensaje en contestacion o no lo recibe por error las tareas se duermen.
Como siempre ubiquenlo en la carpeta de proyectos que creamos.