/** * ####################################################################################### * GAYE Abdoulaye Walsimou, * Copyright(C) 2009 GAYE Abdoulaye Walsimou, . All rights reserved. * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License * (Version 2 or later) published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * ####################################################################################### * * \file port.c * \brief Implementation of functions in FreeRTOS/source/include/portable.h * \brief for the SDCC/PIC18 (pic16 arch in sddc world) port. * \brief It is based on existing PIC18 port. * \author GAYE Abdoulaye Walsimou, * \date February 2009 * ####################################################################################### */ /* Scheduler include files. */ #include "FreeRTOS.h" #include "task.h" #include "portmacro.h" #include #include /*---------------------------------------------------------------------- * Heap needed by memory management (malloc.h) in SDCC *----------------------------------------------------------------------*/ __data unsigned char heap[configTOTAL_HEAP_SIZE]; /*---------------------------------------------------------------------- * Implementation of functions defined in portable.h for the PIC18 port. *----------------------------------------------------------------------*/ /* Hardware setup for tick. */ #define portTIMER_FOSC_SCALE ((unsigned portLONG)4) /* Initial interrupt enable state for newly created tasks. This value is copied into INTCON when a task switches in for the first time. */ #define portINITAL_INTERRUPT_STATE 0xc0 /* Just bits within INTCON for the global interrupt flag.*/ #define portGLOBAL_INTERRUPT_FLAG 0x80 /* Constant used for context switch macro when we require the interrupt enable state to be unchanged when the interrupted task is switched back in. */ #define portINTERRUPTS_UNCHANGED 0x00 /* We require the address of the pxCurrentTCB variable, but don't want to know any details of its type. */ typedef void tskTCB; extern volatile tskTCB * volatile pxCurrentTCB; /* IO port constants. */ #define portBIT_SET ((unsigned portCHAR)1) #define portBIT_CLEAR ((unsigned portCHAR)0) /* * Perform hardware setup (timer0) to enable ticks. * Usage of timer0 only, for ticks generation, saves remainingperipherals for other * purpose. */ static void prvSetupTimerInterrupt(void); /* * Reload Value of timer0 when a tick occures. * The configuration of timer0 done in prvSetupTimerInterrupt,implies the bellow relation * between configTICK_RATE_HZ and TMR0ReloadValue. */ const unsigned portSHORT TMR0ReloadValue=(65536 - configCPU_CLOCK_HZ/(configTICK_RATE_HZ*portTIMER_FOSC_SCALE)); /* * ISR placed on the high priority vector. */ void prvLowInterrupt(void) __naked __interrupt 2; /*-----------------------------------------------------------*/ /* * See header file for description. */ portSTACK_TYPE *pxPortInitialiseStack(portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters) { unsigned portLONG ulAddress; unsigned portCHAR ucBlock; /*First store the function parameters. This is where the task will expect to find them when it starts running. */ ulAddress = (unsigned portLONG) pvParameters; *pxTopOfStack = (portSTACK_TYPE) (ulAddress & (unsigned portLONG)0x00ff); pxTopOfStack--; ulAddress >>= 8; *pxTopOfStack = (portSTACK_TYPE) (ulAddress & (unsigned portLONG)0x00ff); pxTopOfStack--; /*Now we save hardware and compiler registers*/ *pxTopOfStack = (portSTACK_TYPE)0x00; /* WREG. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x11; /* Status. */ pxTopOfStack--; /* INTCON is saved with interrupts enabled. */ *pxTopOfStack = (portSTACK_TYPE)portINITAL_INTERRUPT_STATE; /* INTCON */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x22; /* BSR. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x33; /* PRODL. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x44; /* PRODH. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x55; /* FSR0L. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x66; /* FSR0H. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x77; /* PCLATH. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x88; /* PCLATU. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0x99; /* FSR2H. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0xaa; /* FSR2L. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0xbb; /* TABLAT. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0xcc; /* TBLPTRL. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0xdd; /* TBLPTRH. */ pxTopOfStack--; *pxTopOfStack = (portSTACK_TYPE)0xee; /* TBLPTRU. */ pxTopOfStack--; /* Next the .registers sections. Assuming that it is portCOMPILER_MANAGED_MEMORY_SIZE*/ for( ucBlock = 0; ucBlock >= 8; /* TOS High. */ *pxTopOfStack = (portSTACK_TYPE)(ulAddress & (unsigned portLONG)0x00ff); pxTopOfStack--; ulAddress >>= 8; /* TOS Upper. */ *pxTopOfStack = (portSTACK_TYPE)(ulAddress & (unsigned portLONG)0x00ff); pxTopOfStack--; /* Store the number of return addresses on the hardware stack - so far only the address of the task entry point. */ *pxTopOfStack = (portSTACK_TYPE)1; pxTopOfStack--; return pxTopOfStack; } /*-----------------------------------------------------------*/ portBASE_TYPE xPortStartScheduler(void) { /* Setup a timer for the tick ISR is using the preemptive scheduler. */ prvSetupTimerInterrupt(); /* Restore the context of the first task to run. */ portRESTORE_CONTEXT(); /* Should not get here. Use the function name to stop compiler warnings. */ ( void ) prvLowInterrupt; return pdTRUE; } /*-----------------------------------------------------------*/ void vPortEndScheduler(void) { /* It is unlikely that the scheduler for the PIC port will get stopped once running. If required disable the tick interrupt here, then return to xPortStartScheduler(). */ } /*-----------------------------------------------------------*/ /* * Manual context switch. This is similar to the tick context switch, * but does not increment the tick count. It must be identical to the * tick context switch in how it stores the stack of a task. */ void vPortYield(void) __naked { /* This can get called with interrupts either enabled or disabled. We will save the INTCON register with the interrupt enable bits unmodified. */ portSAVE_CONTEXT(portINTERRUPTS_UNCHANGED); /* Switch to the highest priority task that is ready to run. */ vTaskSwitchContext(); /* Start executing the task we have just switched to. */ portRESTORE_CONTEXT(); } /*-----------------------------------------------------------*/ /* * Vector for ISR. Nothing here must alter any registers! */void prvLowInterrupt(void) __naked __interrupt 2 { if(INTCONbits.TMR0IF) { PORTDbits.RD0=PORTDbits.RD0^1; /* * Interrupt from regular tick. * This increments the tick count and, if using the preemptive scheduler, * performs a context switch. This must be identical to the manual * context switch in how it stores the context of a task. */ /* Interrupts must have been enabled for the ISR to fire, so we have to save the context with interrupts enabled. */ portSAVE_CONTEXT(portGLOBAL_INTERRUPT_FLAG); INTCONbits.TMR0IF=0; /*set TMR0L and TMR0H value*/ TMR0H=(unsigned portCHAR)((TMR0ReloadValue&(unsigned portSHORT)0xff00)>>8); TMR0L=(unsigned portCHAR)(TMR0ReloadValue&(unsigned portSHORT)0x00ff); /* Maintain the tick count. */ vTaskIncrementTick(); #if configUSE_PREEMPTION == 1 { /* Switch to the highest priority task that is ready to run. */ vTaskSwitchContext(); } #endif portRESTORE_CONTEXT(); } } /*-----------------------------------------------------------*/ /* * Setup Timer0 for a regular tick. */ static void prvSetupTimerInterrupt(void) { T0CONbits.TMR0ON=0; /*Timer0 off at the begining*/ T0CONbits.T08BIT=0; /*Timer0 as 16 bits timer*/ T0CONbits.T0CS=0; /*Timer0 works in timer mode*/ T0CONbits.T0SE=0; /*Timer0 will increment positive transition*/ T0CONbits.PSA=1; /*Timer0 prescaler is not assigned*/ INTCONbits.TMR0IE=1; /*Timer0 interrupts are allowed*/ INTCONbits.TMR0IF=0; /*Timer0 interrupt flag cleared*/ /*set TMR0L and TMR0H value*/ TMR0H=(unsigned portCHAR)((TMR0ReloadValue&(unsigned portSHORT)0xff00)>>8); TMR0L=(unsigned portCHAR)(TMR0ReloadValue&(unsigned portSHORT)0x00ff); RCONbits.IPEN=1; /* Interrupt priority feature enabled. FIXME: this is * something like hardware interrupt controller configu- * ration, so needs to be done somewhere else I think */ T0CONbits.TMR0ON=1; /*Now enable timer0*/ } void *pvPortMalloc(size_t xSize) { void *pvReturn; vTaskSuspendAll(); { pvReturn=malloc(xSize); } xTaskResumeAll(); return pvReturn; } void vPortFree(void *pv) { vTaskSuspendAll(); if(pv) { free(pv); } xTaskResumeAll(); }