I am trying to create a semi-pre-emptive (co-operative) RTOS for PIC x16 microcontrollers. In my previous question, I've learnt that accessing hardware stack pointer is not possible in these cores. I have looked at this page in PIClist, and this is what I am trying to implement using C.
My compiler is Microchip XC8 and currently I am working on a PIC16F616 with 4MHz internal RC oscillator selected in the configuration bits.
I have learnt that I can access PCLATH and PCL registers with C, looking at the header file of my compiler. So, I tried to implement a simple task switcher.
It works as wanted in the debugger if I pause the debugger after restart, reset, and set PC at cursor when the cursor is not on the first line (TRISA=0;) but on an another line (for example ANSEL=0;). In the first start of the debugger I get these messages in the Debugger Console:
Launching Programming target User program running No source code lines were found at current PC 0x204 Edit: I don't know what made it work, but debugger now works perfectly. So, omit the above output and paragraph.
Edit: Changing the main definition like this makes the code below work. This starts the main function at program address 0x0099. I don't know what causes this. This is not a real solution. I am now guessing that there is a compiler specific error.
void main(void) @ 0x0099 { Here is my C code:
/* * 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 } } And here is the disassembly listing file that my compiler created. Starts at line 74.
I have programmed the actual chip, and no change on PORTA at all; it doesn't work.
What is the reason my program doesn't work?