Una cosa que he encontrado útil en varias máquinas es un simple conmutador de pila. En realidad no he escrito uno para el PIC, pero esperaría que el enfoque funcionara bien en el PIC18 si ambos / todos los subprocesos utilizan un total de 31 o menos niveles de pila. En el 8051, la rutina principal es:
_taskswitch:
xch a,SP
xch a,_altSP
xch a,SP
ret
En el PIC, olvido el nombre del puntero de pila, pero la rutina sería algo así como:
_taskswitch:
movlb _altSP >> 8
movf _altSP ,w,b
movff _STKPTR,altSP
movwf _STKPTR,c
return
Al inicio de su programa, llame a una rutina task2 () que carga altSP con la dirección de la pila alternativa (16 probablemente funcionaría bien para un PIC18Fxx) y ejecuta el bucle task2; esta rutina nunca debe volver o las cosas morirán una muerte dolorosa. En su lugar, debe llamar a _taskswitch siempre que quiera ceder el control a la tarea principal; la tarea principal debe llamar a _taskswitch siempre que quiera ceder a la tarea secundaria. A menudo, uno tendrá pequeñas rutinas lindas como:
void delay_t1(unsigned short val)
{
do
taskswitch();
while((unsigned short)(millisecond_clock - val) > 0xFF00);
}
Tenga en cuenta que el conmutador de tareas no tiene ningún medio para hacer ninguna "espera de condición"; Todo lo que soporta es un spinwait. Por otro lado, el interruptor de tareas es tan rápido que al intentar un interruptor de tareas () mientras que la otra tarea está esperando a que expire un temporizador, se cambiará a la otra tarea, verificará el temporizador y volverá más rápido que un conmutador de tareas típico determinaría que no es necesario cambiar las tareas.
Tenga en cuenta que la multitarea cooperativa tiene algunas limitaciones, pero evita la necesidad de un montón de bloqueo y otros códigos relacionados con la exclusión mutua en los casos en que las invariantes que están temporalmente alteradas pueden restablecerse rápidamente.
(Editar): Un par de advertencias sobre las variables automáticas y tales:
- si se llama a una rutina que usa el cambio de tareas desde ambos subprocesos, generalmente será necesario compilar dos copias de la rutina (posiblemente al #incluir el mismo archivo fuente dos veces, con diferentes #define sentencias). Cualquier archivo fuente dado contendrá código para un solo hilo, o bien contendrá código que se compilará dos veces, una vez para cada hilo, así que puedo usar macros como "#define delay (x) delay_t1 (x)" o #define delay (x) delay_tx (x) "dependiendo del hilo que estoy usando.
- Creo que los compiladores PIC que no pueden "ver" una función que se está llamando supondrán que dicha función puede destruir todos y cada uno de los registros de la CPU, evitando así la necesidad de guardar cualquier registro en la rutina de cambio de tareas beneficio en comparación con la multitarea preventiva]. Cualquier persona que esté considerando un conmutador de tareas similar para cualquier otra CPU debe conocer las convenciones de registro en uso. Presionar los registros antes de un cambio de tarea y abrirlos después es una forma fácil de cuidar las cosas, suponiendo que exista el espacio de pila adecuado.
La multitarea cooperativa no le permite a uno escapar por completo de los problemas del bloqueo, pero en realidad simplifica mucho las cosas. En un RTOS preventivo con un recolector de basura de compactación, por ejemplo, es necesario permitir que los objetos se fijen. Cuando se utiliza un conmutador cooperativo, esto no es necesario siempre que el código suponga que los objetos del GC pueden moverse en cualquier momento en que se llame a las funciones con el conmutador (). Un colector de compactación que no tiene que preocuparse por los objetos fijados puede ser mucho más simple que uno que sí.