Una limitación que tienen TODOS los procesadores mononúcleo, es que solo pueden ejecutar una instrucción, y por tanto una tarea al mismo tiempo. Esto, hasta hace bien poco, los fabricantes lo intentaban solucionar aumentando la velocidad de estos procesadores, de ahí que los ultimos Pentium 4 llegaran hasta velocidades cercanas a los 4GHz, con rendimientos que distan mucho de los procesadores actuales de menor velocidad de doble núcleo. Si nos vamos al nivel de microcontrolador, el problema que se nos presenta es el mismo, solo podemos tener un hilo de ejecución, y aunque con las interrupciones podemos asegurar la ejecución a tiempo de una instrucción, no aseguramos que esta se ejecute por completo si salta otra interrupción. Esta limitación de solo poder ejecutar una instrucción al mismo tiempo, no ha evitado que procesadores como los Pentium 4, puedan mover sistemas multitarea, como por ejemplo Windows, en el cual continuamente estamos ejecutando varios procesos al mismo tiempo (Ctrl + Alt + Supr > Procesos). Para que esto sea posible, es necesario que haya "algo", que decida que debe ejecutar el procesador en cada momento, para que, aparentemente, y solo aparentemente, nuestro procesador sea multitarea, y es aquí donde los Sistemas Operativos entran en acción.
A pesar de que cuando leemos las palabras Sistema Operativo se nos viene a la cabeza Windows, Linux o MacOS, existen infinidad de sistemas operativos, cada uno con un fin diferente. Evidentemente, siendo este un blog de electrónica y microcontroladores, no podemos pretender que un microcontrolador de 8 bits mueva cualquiera de estos sistemas operativos, por lo menos las versiones más actuales, por ello en esta entrada voy a hablaros de FreeRTOS, un pequeño sistema operativo que nos permitirá hacer que nuestro pequeño PIC18F4550 pueda ejecutar varias tareas al mismo tiempo, como he dicho antes, aparentemente.
FreeRTOS es un sistema operativo de los denominados en tiempo real (Real Time Operating System), los cuales aseguran que las tareas se vayan a ejecutar en un tiempo determinado, y está diseñado para que quepa en un microcontrolador desde 8 hasta 32 bits. Para estas primeras pruebas, voy a utilizar ese micro que tanto gusta a mis lectores, el PIC18F4550. Lo primero que debemos hacer, es abrir un nuevo proyecto y agregar los archivos necesarios, tanto librerías como archivos de código, que en este caso no son demasiados. Los archivos los podéis descargar desde la página oficial de FreeRTOS. Cuando lo instalamos, no estamos instalando solo los códigos y las librerías para PIC18, también vienen incluidos los archivos para todos los microcontroladores compatibles con FreeRTOS (...\FreeRTOSV7.4.2\FreeRTOS\Demo). En nuestro caso, utilizamos el ejemplo para PIC18 en MPLAB, pero en esta ocasion no vamos a ejecuar los ejemplos directamente, sino que vamos a programar unos ejemplos nosotros más sencillos y que serviran para entender mejor su funcionamiento. Una vez hemos descargado e instalado el FreeRTOS, creamos un proyecto, y agregamos los archivos necesarios de forma que quede de la forma siguiente:
Una vez agregados los archivos es necesario hacer unas modificaciones de las propiedades del proyecto, en primer lugar debemos agregar una macro para el preprocesador, MPLAB_PIC18F_PORT, de foma que quede así.
Por ultimo lo que debemos hacer es en esa misma ventana, seleccionamos desde el desplegable "Memory Model", y seleccionamos para ambas memorias el modelo grande (large model). Una vez hecho todo esto, y definidos los paths para nuestros archivos, podemos empezar a escribir el código principal.
El primer programa que voy a poneros es uno sencillo, pero que se le puede sacar bastante juego, y se traba del manejo de 2 salidas del PIC parpadeando a 2 frecuencias, en principio diferentes. El código es el siguiente:
Por ultimo lo que debemos hacer es en esa misma ventana, seleccionamos desde el desplegable "Memory Model", y seleccionamos para ambas memorias el modelo grande (large model). Una vez hecho todo esto, y definidos los paths para nuestros archivos, podemos empezar a escribir el código principal.
El primer programa que voy a poneros es uno sencillo, pero que se le puede sacar bastante juego, y se traba del manejo de 2 salidas del PIC parpadeando a 2 frecuencias, en principio diferentes. El código es el siguiente:
// Programa de ejemplo FreeRTOS #include "p18f4550.h" #include "FreeRTOS.h" #include "task.h" #pragma config FOSC=HS #pragma config IESO=OFF #pragma config PWRT=ON #pragma config WDT=OFF #pragma config MCLRE=ON #pragma config XINST=OFF #pragma config DEBUG=OFF #pragma config FCMEN = OFF #pragma config LVP=OFF #pragma config PBADEN = OFF // Definimos la prioridad de las tareas #define PRIORIDAD_TAREA0 ( tskIDLE_PRIORITY + 1 ) #define PRIORIDAD_TAREA1 ( tskIDLE_PRIORITY + 1 ) // Prototipos de las tareas static void TAREA0( void *pvParameters ); static void TAREA1( void *pvParameters ); // Programa principal void main( void ){ TRISB=0x00; vPortInitialiseBlocks(); xTaskCreate( TAREA0, ( const char * const ) "T0", configMINIMAL_STACK_SIZE, NULL, PRIORIDAD_TAREA0, NULL ); xTaskCreate( TAREA1, ( const char * const ) "T1", configMINIMAL_STACK_SIZE, NULL, PRIORIDAD_TAREA1, NULL ); vTaskStartScheduler(); } // Tarea 0 static void TAREA0( void *pvParameters ){ while(1) { LATBbits.LATB0=!PORTBbits.RB0; vTaskDelay(250/portTICK_RATE_MS); } } // Tarea 1 static void TAREA1( void *pvParameters ){ while(1){ LATBbits.LATB1=!PORTBbits.RB1; vTaskDelay(350/portTICK_RATE_MS); } } // Interrupción de recepción del puerto serie void vSerialRxISR( void ){ } // Interrupción de transmisión del puerto serie void vSerialTxISR( void ){ }
Lo primero que se hace es declarar los prototipos de las funciones de cada tarea. Una vez hecho esto debemos crearlas, para ello se utiliza la expresión xTaskCreate( TAREA0, ( const char * const ) "T0", configMINIMAL_STACK_SIZE, NULL, PRIORIDAD_TAREA0, NULL ), de los campos de la función, los más importantes son el primero, que se corresponde con el nombre de la función que va a ejecutar, y el penúltimo que se corresponde con la prioridad, esto es por si se deben ejecutar las dos tareas al mismo tiempo, cual de estas será prioritaria. En este caso las dos tienen la misma prioridad. Una vez creadas las tareas, lo siguiente que hacemos es ejecutar el sistema operativo con vTaskStartScheduler(). Con esto tenemos finalizado el programa principal, lo siguiente es definir las tareas con el nombre que le hemos dado en sus prototipos. Como veis, cada tarea tiene un bucle infinito, pero esto no quiere decir ue se quede aquí indefinidamente, sino que una vez ha hecho su función, se queda "esperando" mediante la función vTaskDelay(tiempo_en_ms/portTICK_RATE_MS). La mayor diferencia de este código utilizando un RTOS, y otro que no lo utilice, es que en este, mientras estamos esperando a que la tarea deba volver a ejecutarse, el procesador no está ocupado, y puede atender a otras peticiones, como por ejemplo, la tarea 2. Habrá momentos, en los que las dos tareas deban ejecutarse al mismo tiempo, en nuestro caso esto ocurrirá en 1.750s (mínimo común múltiplo de 250 y 350 ms), y en este momento entrará en juego la prioridad que hayamos definido en cada tarea, ejecutándose en primer lugar la más prioritaria. Dependiendo de la frecuencia de nuestro sistema, el tiempo de retardo que tendrá la tarea menos prioritaria será mayor o menor (unos 80us para 48MHz). Para evitar problemas, las tareas mas importantes deberán ser las mas prioritarias, de forma que estas se ejecuten siempre a tiempo.
Uno de los problemas que tienen que solucionar los RTOS es el acceso a recursos, es decir, tenemos un LCD conectado a nuestro microcontrolador, y 2 tareas diferentes pueden escribir en el, pero ¿que pasa si uno está escribiendo y otro más prioritario quiere escribir? eso lo veremos las próximas entradas.
DESCARGA EJEMPLO
No hay comentarios:
Publicar un comentario