¿Cómo implementar el cambio de pila simple en los núcleos PIC12 / 16?

7

Estoy tratando de entender cómo funcionan los sistemas operativos en tiempo real. He mirado los códigos fuente de algunos RTOS. Quiero aprender creando mi RTOS simple, algo como FLIRT .

Estoy usando la serie PIC16 y el compilador XC8 C con MPLABX. También quiero implementar esta serie muy simple de RTOS a PIC12.

Entonces, decidí que debería comenzar por aprender a manipular la pila (como supercat hizo en esta respuesta ) y empecé a buscar y encontré AN818 que se titula "Manipular la pila del microcontrolador PIC18 ". Citado de la nota de aplicación:

  

Tradicionalmente, la pila del microcontrolador solo ha sido   Se utiliza como espacio de almacenamiento para direcciones de retorno de subrutinas.   o rutinas de interrupción, donde todos los ‘push’ y ‘pop’   Las operaciones estaban ocultas.

     

En su mayor parte, los usuarios tenían   No hay acceso directo a la información en la pila. los   El microcontrolador PIC18 se aparta de esta tradición.   ligeramente. Con el nuevo núcleo PIC18, los usuarios ahora tienen   Accede a la pila y puede modificar el puntero de pila.   y apilar datos directamente.

Estoy confundido. ¿Por qué existen RTOS para microcontroladores PIC que funcionan con núcleos PIC16? Por ejemplo, OSA RTOS está disponible para PIC12 / 16 con el compilador mikroC .

¿Puede dirigirme a algunos recursos o, si es posible, dar ejemplos para que pueda aprender sobre el cambio de pila?

    

3 respuestas

4

Cada RTOS para un PIC que no tiene una pila direccionable por software generalmente requiere que todas las tareas, excepto una, tengan su trabajo dividido en partes ininterrumpibles que comienzan y terminan en el nivel de pila superior; La operación "rendimiento de la tarea" no utiliza una llamada de función, sino una secuencia como

// This code is part of task C (assume for this example, there are tasks
// called A, B, C
  movlw JumpC4 & 255
  goto TASK_SWITCH_FROM_C
TargetC4:

En otra parte del código estaría el código:

TASK_SWITCH_FROM_A:
  movwf nextJumpA  // Save state of task C
  // Now dispatch next instruction for task A
  movlw TaskB_Table >> 8
  movwf PCLATH
  movf  nextJumpB,w
  movwf PCL
TASK_SWITCH_FROM_B:
  movwf nextJumpB  // Save state of task C
  // Now dispatch next instruction for task A
  movlw TaskC_Table >> 8
  movwf PCLATH
  movf  nextJumpC,w
  movwf PCL
TASK_SWITCH_FROM_C:
  movwf nextJumpC  // Save state of task C
  // Now dispatch next instruction for task A
  movlw TaskA_Table >> 8
  movwf PCLATH
  movf  nextJumpA,w
  movwf PCL

Al final del código, para cada tarea, habrá una tabla de salto; cada tabla debería encajar dentro de una página de 256 palabras (y, por lo tanto, podría tener un máximo de 256 saltos)

TaskC_Table:
JumpC0 : goto TargetC0
JumpC1 : goto TargetC1
JumpC2 : goto TargetC2
JumpC3 : goto TargetC3
JumpC4 : goto TargetC4
...etc.

Efectivamente, el movlw al inicio de la secuencia de cambio de tarea carga el registro W con el LSB de la dirección de la instrucción en JumpC4 . El código en TASK_SWITCH_FROM_C ocultaría ese valor en algún lugar y luego enviaría el código para la tarea A. Luego, después de que se ejecute TASK_SWITCH_FROM_B , la dirección JumpC4 almacenada se volverá a cargar en W y el sistema saltará a la instrucción señalada por lo tanto Esa instrucción sería un goto TargetC4 , que a su vez reanudaría la ejecución en la instrucción que sigue a la secuencia de cambio de tarea. Tenga en cuenta que el cambio de tareas no utiliza la pila en absoluto.

Si uno quisiera hacer un cambio de tarea dentro de una función llamada, podría ser posible si la llamada y el retorno de esa función se manejaran de una manera similar a la anterior (es probable que uno tenga que ajustar la llamada a la función en una macro especial para forzar la generacion del codigo apropiado). Tenga en cuenta que el compilador en sí no sería capaz de generar código como el anterior. En su lugar, las macros en el código fuente generarían directivas en el archivo en lenguaje ensamblador. Un programa suministrado por el proveedor de RTOS leería el archivo en lenguaje ensamblador, buscaría esas directivas y generaría el código de vectorización adecuado.

    
respondido por el supercat
4

Los PIC tradicionales de 14 bits (en su mayoría PIC 16 y algunos PIC 12) no tienen forma de que el programa acceda a la pila de llamadas. Esto, por lo tanto, hace imposible un verdadero sistema multitarea.

Sin embargo, todavía puedes tener lo que yo llamo pseudo tareas. Esta es una rutina que se llama periódicamente desde el bucle del evento principal. Mantiene una dirección de reinicio internamente. Cuando el bucle par principal lo llama, salta a la dirección de reinicio de la pseudo tarea privada de ese módulo. Después de algún procesamiento, el código de la tarea invoca una macro YIELD que establece la dirección de reinicio inmediatamente después de la macro y luego regresa de la llamada original. Como la pila de llamadas no se puede deshacer, YIELD solo puede invocarse desde el nivel de tarea superior. Aún así, esta es una abstracción útil.

Puede ver un ejemplo de dicha pseudo tarea que se utiliza para procesar una secuencia de comandos desde una computadora host en mi módulo de plantilla QQQ_CMD.ASPIC. En este caso, el retorno a la persona que llama está oculto en la macro GETBYTE, que parece ir y obtener el siguiente byte de entrada desde el punto de vista de la tarea. Instale la versión de mi PIC Development Tools desde la página de descargas de software . QQQ_CMD.ASPIC estará en el SOURCE > Directorio PIC dentro del directorio de instalación del software.

Para ver cómo se puede realizar la multitarea real cuando tiene acceso a la pila de llamadas, consulte TASK.INS.ASPIC en el mismo directorio. Ese es mi sistema multitarea genérico para el PIC 18. Puede ver lo mismo para la arquitectura dsPIC mirando TASK.INS.DSPIC en SOURCE > Directorio DSPIC.

    
respondido por el Olin Lathrop
2

Los chips de "núcleo de 14 bits" de la serie PIC16 tienen una "RAM de pila de hardware" que es completamente independiente e independiente de la "RAM de datos". (Incluso tiene un tamaño y ancho diferente al de la memoria RAM de datos). El "diagrama de bloque central" para cualquiera de esos chips muestra claramente que la "pila de hardware" está conectada solo al contador del programa; la única instrucción que escribe en esa pila de hardware es (los diversos tipos de) CALL, y la única instrucción las lecturas de esa pila de hardware son (los distintos tipos de) RETURN.

El "cambio de pila" es imposible para la "pila de hardware" en estos chips de "núcleo de 14 bits".

Esto hace que la mayoría de los métodos tradicionales de cambio de tareas del sistema operativo sean imposibles en esta serie de chips.

Hay algunos RTOS que se ejecutan en esta serie de chips, por lo tanto, debe haber algunos métodos de cambio de tareas que sean posibles en este chip ( "Métodos de multitarea específicos del microcontrolador PIC" ).

En la práctica, todos los RTOS que he visto tienen algún tipo de conmutador de tareas cooperativo eso "cede" de una tarea a la siguiente solo en el código de nivel superior de cada tarea. Debido a que el rendimiento nunca proviene de una subrutina, no hay direcciones de retorno en la pila de hardware.

En principio, es posible programar estos chips para construir una pila de software en la RAM y usar eso para simular una pila de retorno. (Utilizando una "pila definida por el usuario" en la RAM de datos, en lugar de la pila de hardware). Entonces podrías usar todas las cosas tradicionales de cambio de tareas del SO, como dar a cada tarea un bloque separado de RAM para su pila local, etc. No sé si la complejidad adicional y las secuencias de llamadas no estándar valdrían la pena.

    
respondido por el davidcary

Lea otras preguntas en las etiquetas