Una cosa que he encontrado útil en varias máquinas es un simple conmutador de pila. Realmente no he escrito uno para el PIC, pero esperaría que el enfoque funcione bien en el PIC18 si ambos / todos los hilos usan un total de 31 o menos niveles de pila. En el 8051, la rutina principal es:
_tarea interruptor:
xch a, SP
xch a, _altSP
xch a, SP
jubilado
En el PIC, olvido el nombre del puntero de la pila, pero la rutina sería algo así como:
_tarea interruptor:
movlb _altSP >> 8
movf _altSP, w, b
movff _STKPTR, altSP
movwf _STKPTR, c
regreso
Al comienzo 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 de muerte dolorosa. En su lugar, debería llamar a _taskswitch cada vez que quiera ceder el control a la tarea principal; la tarea principal debería llamar a _taskswitch cuando quiera ceder el paso a la tarea secundaria. A menudo, uno tendrá pequeñas rutinas lindas como:
void delay_t1 (val corto sin signo)
{
hacer
taskwitch ();
while ((unsigned short) (milisegundo_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 cambio de tarea es tan rápido que intentar un cambio de tarea () mientras la otra tarea está esperando que expire un temporizador cambiará a la otra tarea, verificará el temporizador y volverá más rápido que un interruptor de tareas típico determinaría que no necesita cambiar de tarea.
Tenga en cuenta que la multitarea cooperativa tiene algunas limitaciones, pero evita la necesidad de muchos bloqueos y otros códigos relacionados con mutex en los casos en que los invariantes que se alteran temporalmente pueden restablecerse rápidamente.
(Editar): Un par de advertencias con respecto a las variables automáticas y tales:
- Si se llama a una rutina que utiliza el cambio de tareas desde ambos subprocesos, generalmente será necesario compilar dos copias de la rutina (posiblemente #incluyendo el mismo archivo fuente dos veces, con diferentes #definir declaraciones). Cualquier archivo de origen contendrá código para un solo subproceso o contendrá código que se compilará dos veces, una para cada subproceso, para que pueda usar macros como "#define delay (x) delay_t1 (x)" o #define delay (x) delay_tx (x) "dependiendo del hilo que esté usando.
- Creo que los compiladores PIC que no pueden "ver" una función que se llama asumirá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 tarea [un buen beneficio en comparación con Multi tareas preventivo]. Cualquiera que esté considerando un conmutador de tareas similar para cualquier otra CPU debe conocer las convenciones de registro en uso. Empujar registros antes de un cambio de tarea y abrirlos después es una manera fácil de ocuparse de las cosas, suponiendo que exista un espacio de pila adecuado.
La multitarea cooperativa no le permite a uno escapar completamente de los problemas de bloqueo y demás, pero realmente simplifica mucho las cosas. En un RTOS preventivo con un recolector de basura compactante, por ejemplo, es necesario permitir que se fijen los objetos. Cuando se usa un conmutador cooperativo, esto no es necesario siempre que el código asuma que los objetos GC se pueden mover cada vez que se llama a taskwitch (). Un colector de compactación que no tiene que preocuparse por los objetos anclados puede ser mucho más simple que uno que lo hace.