Estoy tratando de crear un RTOS semi-preventivo (cooperativo) para microcontroladores PIC x16. En mi pregunta anterior , aprendí que el acceso al puntero de la pila de hardware no es posible en estos núcleos. He mirado esta página en PIClist, y esto es lo que estoy tratando de implementar usando C.
Mi compilador es Microchip XC8 y actualmente estoy trabajando en un PIC16F616 con un oscilador RC interno de 4MHz seleccionado en los bits de configuración.
Aprendí que puedo acceder a los registros PCLATH y PCL con C, mirando el archivo de encabezado de mi compilador. Entonces, traté de implementar un conmutador de tareas simple.
Funciona como se desea en el depurador si detengo el depurador después de reiniciar, restablecer y configurar la PC en el cursor cuando el cursor no está en la primera línea ( TRISA=0;
) sino en otra línea (por ejemplo ANSEL=0;
). En el primer inicio del depurador recibo estos mensajes en Debugger Console
:
Launching
Programming target
User program running
No source code lines were found at current PC 0x204
Editar: No sé qué lo hizo funcionar, pero el depurador ahora funciona perfectamente. Por lo tanto, omita el resultado y el párrafo anteriores.
Editar: cambiar la definición principal de esta manera hace que el siguiente código funcione. Esto inicia la función principal en la dirección del programa 0x0099
. No sé qué causa esto. Esta no es una solución real. Ahora supongo que hay un error específico del compilador.
void main(void) @ 0x0099
{
Aquí está mi código C:
/*
* File: main.c
* Author: abdullah
*
* Created on 10 Haziran 2012 Pazar, 14:43
*/
#include <xc.h> // Include the header file needed by the compiler
__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & IOSCFS_4MHZ & BOREN_ON);
/*
* INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN
* WDT disabled and can be enabled by SWDTEN bit of the WDTCON register
* PWRT enabled
* MCLR pin function is digital input, MCLR internally tied to VDD
* Program memory code protection is disabled
* Internal Oscillator Frequency Select bit : 4MHz
* Brown-out Reset Selection bits : BOR enabled
*/
/*
* OS_initializeTask(); definition will copy the PCLATH register to the task's PCLATH holder, which is held in taskx.pch
* This will help us hold the PCLATH at the point we yield.
* After that, it will copy the (PCL register + 8) to current task's PCL holder which is held in taskx.pcl.
* 8 is added to PCL because this line plus the "return" takes 8 instructions.
* We will set the PCL after these instructions, because
* we want to be in the point after OS_initializeTask when we come back to this task.
* After all, the function returns without doing anything more. This will initialize the task's PCLATH and PCL.
*/
#define OS_initializeTask(); currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("return");
/*
* OS_yield(); definition will do the same stuff that OS_initializeTask(); definition do, however
* it will return to "taskswitcher" label, which is the start of OS_runTasks(); definition.
*/
#define OS_yield(); currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
/*
* OS_runTasks(); definition will set the "taskswitcher" label. After that it will change the
* current task to the next task, by pointing the next item in the linked list of "TCB"s.
* After that, it will change the PCLATH and PCL registers with the current task's. That will
* make the program continue the next task from the place it left last time.
*/
#define OS_runTasks(); asm("_taskswitcher");\
currentTask = currentTask -> next;\
PCLATH = currentTask->pch;\
PCL = currentTask->pcl;
typedef struct _TCB // Create task control block and type define it as "TCB"
{
unsigned char pch; // pch register will hold the PCLATH value of the task after the last yield.
unsigned char pcl; // pcl register will hold the PCL value of the task after the last yield.
struct _TCB* next; // This pointer points to the next task. We are creating a linked list.
} TCB;
TCB* currentTask; // This TCB pointer will point to the current task's TCB.
TCB task1; // Define the TCB for task1.
TCB task2; // Define the TCB for task2.
void fTask1(void); // Prototype the function for task1.
void fTask2(void); // Prototype the function for task2.
void main(void)
{
TRISA = 0; // Set all of the PORTA pins as outputs.
ANSEL = 0; // Set all of the analog input pins as digital i/o.
PORTA = 0; // Clear PORTA bits.
currentTask = &task1; // We will point the currentTask pointer to point the first task.
task1.next = &task2; // We will create a ringed linked list as follows:
task2.next = &task1; // task1 -> task2 -> task1 -> task2 ....
/*
* Before running the tasks, we should initialize the PCL and PCLATH registers for the tasks.
* In order to do this, we could have looked up the absolute address with a function pointer.
* However, it seems like this is not possible with this compiler (or all the x16 PICs?)
* What this compiler creates is a table of the addresses of the functions and a bunch of GOTOs.
* This will not let us get the absolute address of the function by doing something like:
* "currentTask->pcl=low(functionpointer);"
*/
fTask1(); // Run task1 so that we get the address of it and initialize pch and pcl registers.
currentTask = currentTask -> next; // Point the currentTask pointer to the next pointer which
fTask2(); // is task2. And run task2 so that we get the correct pch and pcl.
OS_runTasks(); // Task switcher. See the comments in the definitions above.
}
void fTask1(void)
{
OS_initializeTask(); // Initialize the task
while (1)
{
RA0 = ~RA0; // Toggle PORTA.0
OS_yield(); // Yield
RA0 = ~RA0; // Toggle PORTA.0
}
}
void fTask2(void)
{
OS_initializeTask(); // Initialize the task
while (1)
{
RA1 = ~RA1; // Toggle PORTA.1
OS_yield(); // Yield
RA1 = ~RA1; // Toggle PORTA.1
}
}
Y aquí está el archivo de listado de desmontaje que creó mi compilador. Empieza a las line 74
.
He programado el chip real, y ningún cambio en PORTA; No funciona.
¿Cuál es la razón por la que mi programa no funciona?
Eché un vistazo a la lista de ensamblados que proporcionó, y nada sobresale como obviamente roto.
Si yo fuera tú, mis próximos pasos serían:
(1) Elegiría algún otro método para parpadear los LED. El notorio "problema de lectura-modificación-escritura" puede (o no) ser provocado por el "XORWF PORTA, F" en la lista de ensamblados.
Quizás algo como:
(Si realmente desea ver explicaciones detalladas acerca de por qué "XORWF PORTA, F" a menudo causa problemas, consulte " ¿Qué causa que el encendido de un pin de salida en Microchip PIC16F690 apague espontáneamente otro pin en el mismo puerto? "; " ¿Qué sucede? cuando los datos se escriben en LATCH? ";" El problema de lectura-modificación-escritura ";" Leer antes de escribir ")
(2) Pasaría un solo paso por el código, asegurándome de que las variables se establezcan en los valores esperados y en la secuencia esperada. No estoy seguro de si existe un depurador de hardware de un solo paso para el PIC16F616, pero hay muchos excelentes simuladores de microcontroladores PIC como PICsim que pueden simular chips de la serie PIC16.
El código de un solo paso (en un simulador o con un depurador de hardware de un solo paso) es una buena manera de comprender los detalles de lo que realmente está sucediendo, confirmar que las cosas están sucediendo de la manera que pretendía y le permite ver las cosas que están sucediendo. Prácticamente imposible de ver cuando se ejecuta el programa a toda velocidad.
(3) Si todavía estoy perplejo, intentaría traducir el código para usar matrices en lugar de punteros. Algunas personas encuentran que usar punteros es un poco complicado y difícil de depurar. A menudo encuentro que, en el proceso de traducir código de puntero complicado en código orientado a matrices, descubro cuál es el error. Incluso si termino volviendo al código de puntero original y descartando la versión de matriz, el ejercicio es útil porque me ayudó a encontrar y corregir el error. (A veces, el compilador puede generar código más corto y más rápido a partir del código orientado a la matriz, por lo que hay veces que descarto el código de puntero original y mantengo la versión de la matriz)
Quizás algo como
fuente
Básicamente estaría de acuerdo con davidcary. Parece que podría funcionar.
Supongo que con esto quieres decir que funciona perfectamente en el simulador .
1) Verifique que sus tareas funcionen solas, en un entorno que no sea RTOS en el chip real.
2) Realizar depuración en circuito. Avance por el programa en el chip real y observe todas las variables relevantes para asegurarse de que todo vaya según lo planeado.
fuente
Solo miré tu código brevemente, pero no tiene sentido. En varios lugares está escribiendo a PCL y luego espera que ejecute otras instrucciones después de eso.
Como también dije antes, C es inapropiado para este tipo de acceso de bajo nivel de registros de hardware fundamentales. Realmente necesitas usar el ensamblaje para esto. Intentar descubrir por qué el código C no funciona es una pérdida de tiempo inútil.
fuente
A continuación se muestra la forma de hacerlo con el ensamblaje en línea utilizando el compilador XC8, ¡y funciona ahora! Sin embargo, necesito agregar desarrollar más código para guardar y restaurar el
STATUS
registro, lo que parece un poco más complicado de lo que es para un registro normal.Editar: el código ha cambiado. Consulte las versiones anteriores de esta publicación para el código anterior.
Y aquí está el archivo de encabezado
RTOS.h
:fuente
A continuación se muestra cómo implementar esto usando el ensamblaje. Acceda al mismo código con formato (enlaces a Pastebin) . ¿Cómo puede ser mejorado? Este es mi primer programa en la asamblea PIC, cualquier comentario es apreciado.
fuente