Autor Tema: Tareas cooperativas con PIC  (Leído 683 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5415
Tareas cooperativas con PIC
« en: 06 de Abril de 2020, 07:09:10 »
Hola amigos,

He vuelto a encontrarme con la necesidad de implemetar la orden YIELD y hacer tareas cooperativas, así que volví a programar un task switcher con 2 tareas (la pila o stack del pic con solo 16 direcciones no da para más)

Con este programa se pueden hacer en un pic 2 cosas simultaneamente, sin preocuparte de nada, solo hay que insertar cada cierto tiempo la orden yield() para que una tarea le pase la ejecución a la otra tarea.

Dejo aquí el programa para quien le interese.

main.c
Código: C
  1. /*
  2.  * Multitask cooperative routines
  3.  *
  4.  * License:
  5.  *  
  6.  * This file is subject to the terms and conditions of the GNU General
  7.  * Public License Version 3. See the file "COPYING" in the main directory
  8.  * of this archive for more details.
  9.  *
  10.  */
  11.  
  12. #include <xc.h>
  13. #include "rtos.h"
  14.  
  15. /*
  16.  *  Inits CPU
  17.  */
  18. void cpu_init(void) {
  19.    // Disable interrupts
  20.    INTCON = 0;
  21.  
  22.    // Oscillator
  23.    OSCCON1bits.NOSC = 0b010; // 010 = External oscillator with 4x PLL
  24.  
  25.    WDTCON0bits.SWDTEN = 0; // Watchdog disabled
  26.  
  27.    // Init ports
  28.    ANSELA = 0;
  29.    ANSELB = 0;
  30.    ANSELC = 0;
  31.    LATA = 0x00;
  32.    LATB = 0x00;
  33.    LATC = 0x00;
  34.    TRISA = 0xFF;
  35.    TRISB = 0xFF;
  36.    TRISC = 0xFF;
  37.  
  38.    // Enable interrupts
  39.    INTCONbits.PEIE = 1;
  40.    INTCONbits.GIE = 1;
  41. }
  42.  
  43.  
  44. int main(void) {
  45.    cpu_init();
  46.    rtos_init();
  47. }
  48.  

la rutina main() no se debe modificar, porque ahora todas las instrucciones deben ir dentro de las dos tareas:

rtos.c
Código: C
  1. /*
  2.  * Multitask cooperative routines
  3.  *
  4.  * License:
  5.  *  
  6.  * This file is subject to the terms and conditions of the GNU General
  7.  * Public License Version 3. See the file "COPYING" in the main directory
  8.  * of this archive for more details.
  9.  *
  10.  */
  11.  
  12. #include <xc.h>
  13. #include "config.h"
  14. #include "rtos.h"
  15.  
  16. char stack_num, stack0, stack1;
  17. char rtos_intcon;
  18. addr_t rtos_addr;
  19.  
  20.  
  21. /*
  22.  * Yield: choose context between tasks
  23.  */
  24. void yield(void) {
  25.    rtos_intcon = INTCON;
  26.    INTCONbits.GIE = 0;
  27.  
  28.    if (stack_num == 0) {
  29.       stack0 = STKPTR;
  30.       STKPTR = stack1;
  31.       stack_num = 1;
  32.    } else {
  33.       stack1 = STKPTR;
  34.       STKPTR = stack0;
  35.       stack_num = 0;
  36.    }
  37.  
  38.    INTCON = rtos_intcon;
  39. }
  40.  
  41. /*
  42.  * Init RTOS adding tasks context to STACK
  43.  */
  44. void rtos_init(void) {
  45.    rtos_intcon = INTCON;
  46.    INTCONbits.GIE = 0;
  47.    stack0 = 0;
  48.    stack1 = STACK_SIZE/2;
  49.  
  50.    task0();
  51.    STKPTR = stack0;
  52.    TOSL = rtos_addr.byte0;
  53.    TOSH = rtos_addr.byte1;
  54.  
  55.    task1();
  56.    STKPTR = stack1;
  57.    TOSL = rtos_addr.byte0;
  58.    TOSH = rtos_addr.byte1;
  59.  
  60.    INTCON = rtos_intcon;
  61.  
  62.    stack_num = 1;
  63.    yield();
  64. }
  65.  
  66.  
  67. /*
  68.  * Read function address
  69.  */
  70. void rtos_add_function(void) {
  71.    // Guarda el estado actual de las interrupciones
  72.    rtos_intcon = INTCON;
  73.    INTCONbits.GIE = 0;
  74.  
  75.    // Extraer la dirección de la última llamada (POP)
  76.    rtos_addr.byte0 = TOSL;
  77.    rtos_addr.byte1 = TOSH;
  78.    STKPTR--;
  79.    INTCON = rtos_intcon;
  80. }
  81.  
  82.  
  83. /*
  84.  * TASK 0
  85.  */
  86. void task0(void) {
  87.    rtos_add_function();
  88.  
  89.    INTCONbits.GIE = 1;
  90.    while (1) {
  91.       TRISCbits.TRISC4 = 0;
  92.       LATCbits.LATC4 = 1;
  93.       LATCbits.LATC4 = 0;
  94.       yield();
  95.    }
  96. }
  97.  
  98.  
  99. /*
  100.  * TASK 1
  101.  */
  102. void task1(void) {
  103.    rtos_add_function();
  104.  
  105.    while (1) {
  106.       TRISCbits.TRISC5 = 0;
  107.       LATCbits.LATC5 = 1;
  108.       LATCbits.LATC5 = 0;
  109.       yield();
  110.    }
  111. }
  112.  

Las tareas en este ejemplo simplemente encienden y apagan un pin cada una.


Por último los includes:

config.h
Código: C
  1. /*
  2.  * Configuration parameters
  3.  */
  4.  
  5. #define _FOSC         32000000
  6. #define _XTAL_FREQ    _FOSC
  7. #define STACK_SIZE    16
  8.  
  9. // PIC16F18446 Configuration Bit Settings
  10.  
  11. // CONFIG1
  12. #pragma config FEXTOSC = HS     // External Oscillator mode selection bits (Oscillator not enabled)
  13. #pragma config RSTOSC = EXT4X   // Power-up default value for COSC bits (HFINTOSC = 32MHz)
  14. #pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; i/o or oscillator function on OSC2)
  15. #pragma config CSWEN = ON       // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
  16. #pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit (FSCM timer enabled)
  17.  
  18. // CONFIG2
  19. #pragma config MCLRE = OFF      // Master Clear Enable bit (MCLR pin is Master Clear function)
  20. #pragma config LPBOREN = OFF    // Low-Power BOR enable bit (ULPBOR disabled)
  21. #pragma config BOREN = ON       // Brown-out reset enable bits (Brown-out Reset Enabled, SBOREN bit is ignored)
  22. #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices)
  23. #pragma config ZCD = OFF        // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR.)
  24. #pragma config PPS1WAY = OFF    // Peripheral Pin Select one-way control (The PPSLOCK bit can be cleared and set only once in software)
  25. #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)
  26.  
  27. // CONFIG3
  28. #pragma config WDTCPS = WDTCPS_31// WDT Period Select bits (Divider ratio 1:65536; software control of WDTPS)
  29. #pragma config WDTE = OFF        // WDT operating mode (WDT enabled regardless of sleep; SWDTEN ignored)
  30. #pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
  31. #pragma config WDTCCS = SC      // WDT input clock selector (Software Control)
  32.  
  33. // CONFIG4
  34. #pragma config BBSIZE = BB512   // Boot Block Size Selection bits (512 words boot block size)
  35. #pragma config BBEN = OFF       // Boot Block Enable bit (Boot Block disabled)
  36. #pragma config SAFEN = ON       // Storage Area Flash (SAF) enable bit
  37. #pragma config WRTAPP = OFF     // Application Block Write Protection bit (Application Block not write protected)
  38. #pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block not write protected)
  39. #pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration Register not write protected)
  40. #pragma config WRTSAF = OFF     // Storage Area Flash Write Protection bit (SAF not write protected)
  41. #pragma config LVP = OFF        // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/Vpp pin function is MCLR.)
  42.  
  43. // CONFIG5
  44. #pragma config CP = OFF         // UserNVM Program memory code protection bit (UserNVM code protection disabled)
  45.  
  46.  

rtos.h
Código: C
  1. #ifndef RTOS_H
  2. #define RTOS_H
  3.  
  4. // Function declaration
  5. void rtos_add_function(void);
  6. void rtos_init(void);
  7. void rtos_yield(void);
  8. void task0(void);
  9. void task1(void);
  10.  
  11. // Type definition
  12. typedef union {
  13.    struct {
  14.       unsigned char byte0;
  15.       unsigned char byte1;
  16.    };
  17.    unsigned int word;
  18. } addr_t;
  19.  
  20.  
  21. #endif

« Última modificación: 07 de Abril de 2020, 06:05:14 por Picuino »

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5415
Re:Tareas cooperativas con PIC
« Respuesta #1 en: 06 de Abril de 2020, 07:15:40 »
La imagen del osciloscopio se ve así:

 

* NewFile1.png
(15.14 kB, 640x468 - visto 92 veces)


El cambio entre tareas (cambio de contexto) tarda 4 microsegundos, es decir 32 ciclos de instrucción para la frecuencia de 32MHz a la que trabaja el micro.
« Última modificación: 06 de Abril de 2020, 07:18:17 por Picuino »

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5415
Re:Tareas cooperativas con PIC
« Respuesta #2 en: 06 de Abril de 2020, 07:24:26 »
El programa es muy sencillo y ocupa muy poco. No tiene ningún recurso más, típico en los sitemas RTOS, pero como ventaja es muy sencillo, apenas ocupa 140 words de memoria sin optimizar el compilador y permite anidar funciones dentro de las tareas task0 y task1.

El micro utilizado es un PIC16F18446, pero se puede adaptar fácilmente a otros micros que tengan acceso al stack con los registros STKPTR, TOSH y TOSL.


Niveles de Stack utilizados
Para asegurarse de que no se sobrepasa la pila hay que contar cuántos niveles de pila utiliza cada tarea.
La propia tarea ya ocupa un nivel de pila.
La llamada a yield() ocupa otro nivel
Las interrupciones, en caso de haberlas, ocuparían un tercer nivel.
Cada llamada a funciones fuera del task ocuparían otro nivel más.

En total hay 8 niveles de pila por cada tarea. Eso significa que se pueden anidar otras 5 llamadas a subfunciones antes de llenar la pila.

Un saludo.
« Última modificación: 07 de Abril de 2020, 06:07:04 por Picuino »

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5415
Re:Tareas cooperativas con PIC
« Respuesta #3 en: 06 de Abril de 2020, 07:33:56 »
Para el que quiera modificar el programa, solo tiene que adaptarlo al micro PIC con el que esté trabajando y modificar las tareas o task añadiendo su propio código.

Este es el ejemplo de una tarea:

Código: C
  1. /*
  2.  * TASK 0
  3.  */
  4. void task0(void) {
  5.    rtos_add_function();
  6.  
  7.    INTCONbits.GIE = 1;
  8.    while (1) {
  9.       TRISCbits.TRISC4 = 0;
  10.       LATCbits.LATC4 = 1;
  11.       LATCbits.LATC4 = 0;
  12.       yield();
  13.    }
  14. }

La función rtos_add_function(); es necesaria para que se añada esta tarea a la pila y lo sepa el task switcher que será el encargado de cambiar entre una tarea y la otra.

La orden while(1) es necesaria para que la tarea se ejecute siempre en bucle. No puede haber un return de esta tarea porque no hay ninguna dirección de retorno en la pila.

La orden yield(); cambiará la ejecución a la otra tarea. Hay que colocarla en las esperas o cada pocas instrucciones para que las dos tareas tengan tiempo de ejecución.

El código de usuario se coloca dentro de la orden while(1) {  }
En este ejemplo es un simple encendido y apagado de un pin del micro.


En caso de necesitar activar las interrupciones es importante hacerlo una vez que todo está preparado, en el sitio en el que ahora está la instrucción INTCONbits.GIE = 1;, después de rtos_add_function();
Esto es para prevenir problemas a la hora de preparar la pila o stack para las tareas cooperativas.



« Última modificación: 06 de Abril de 2020, 07:38:15 por Picuino »

Conectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17744
    • MicroPIC
Re:Tareas cooperativas con PIC
« Respuesta #4 en: 07 de Abril de 2020, 02:44:14 »
Este hilo me parece un soplo de aire. Hace tanto que no me meto en la arena con estas cosas...
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5415
Re:Tareas cooperativas con PIC
« Respuesta #5 en: 07 de Abril de 2020, 05:17:28 »
Hola Nocturno, y hace tiempo que no coincidimos. Me alegro de leerte.
¿No programas últimamente o te refieres a que no haces cosas con tareas cooperativas?

Conectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17744
    • MicroPIC
Re:Tareas cooperativas con PIC
« Respuesta #6 en: 07 de Abril de 2020, 05:30:47 »
Hace meses que no programo; ya lo echo de menos. Pero volveré, lo aseguro.
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5415
Re:Tareas cooperativas con PIC
« Respuesta #7 en: 07 de Abril de 2020, 06:02:11 »
Yo también llevaba tiempo sin meterme en un proyecto y lo echaba bastante de menos.
Un saludo.
« Última modificación: 07 de Abril de 2020, 09:40:48 por Picuino, Razón: Spelling »