2 FreeRTOS V6.1.1 - Copyright (C) 2011 Real Time Engineers Ltd.
\r
4 ***************************************************************************
\r
8 * + New to FreeRTOS, *
\r
9 * + Wanting to learn FreeRTOS or multitasking in general quickly *
\r
10 * + Looking for basic training, *
\r
11 * + Wanting to improve your FreeRTOS skills and productivity *
\r
13 * then take a look at the FreeRTOS books - available as PDF or paperback *
\r
15 * "Using the FreeRTOS Real Time Kernel - a Practical Guide" *
\r
16 * http://www.FreeRTOS.org/Documentation *
\r
18 * A pdf reference manual is also available. Both are usually delivered *
\r
19 * to your inbox within 20 minutes to two hours when purchased between 8am *
\r
20 * and 8pm GMT (although please allow up to 24 hours in case of *
\r
21 * exceptional circumstances). Thank you for your support! *
\r
23 ***************************************************************************
\r
25 This file is part of the FreeRTOS distribution.
\r
27 FreeRTOS is free software; you can redistribute it and/or modify it under
\r
28 the terms of the GNU General Public License (version 2) as published by the
\r
29 Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
\r
30 ***NOTE*** The exception to the GPL is included to allow you to distribute
\r
31 a combined work that includes FreeRTOS without being obliged to provide the
\r
32 source code for proprietary components outside of the FreeRTOS kernel.
\r
33 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
\r
34 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
35 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
\r
36 more details. You should have received a copy of the GNU General Public
\r
37 License and the FreeRTOS license exception along with FreeRTOS; if not it
\r
38 can be viewed here: http://www.freertos.org/a00114.html and also obtained
\r
39 by writing to Richard Barry, contact details for whom are available on the
\r
44 http://www.FreeRTOS.org - Documentation, latest information, license and
\r
47 http://www.SafeRTOS.com - A version that is certified for use in safety
\r
50 http://www.OpenRTOS.com - Commercial support, development, porting,
\r
51 licensing and training services.
\r
54 /*-----------------------------------------------------------
\r
55 * Implementation of functions defined in portable.h for the Cygnal port.
\r
56 *----------------------------------------------------------*/
\r
58 /* Standard includes. */
\r
61 /* Scheduler includes. */
\r
62 #include "FreeRTOS.h"
\r
65 /* Constants required to setup timer 2 to produce the RTOS tick. */
\r
66 #define portCLOCK_DIVISOR ( ( unsigned long ) 12 )
\r
67 #define portMAX_TIMER_VALUE ( ( unsigned long ) 0xffff )
\r
68 #define portENABLE_TIMER ( ( unsigned char ) 0x04 )
\r
69 #define portTIMER_2_INTERRUPT_ENABLE ( ( unsigned char ) 0x20 )
\r
71 /* The value used in the IE register when a task first starts. */
\r
72 #define portGLOBAL_INTERRUPT_BIT ( ( portSTACK_TYPE ) 0x80 )
\r
74 /* The value used in the PSW register when a task first starts. */
\r
75 #define portINITIAL_PSW ( ( portSTACK_TYPE ) 0x00 )
\r
77 /* Macro to clear the timer 2 interrupt flag. */
\r
78 #define portCLEAR_INTERRUPT_FLAG() TMR2CN &= ~0x80;
\r
80 /* Used during a context switch to store the size of the stack being copied
\r
82 data static unsigned char ucStackBytes;
\r
84 /* Used during a context switch to point to the next byte in XRAM from/to which
\r
85 a RAM byte is to be copied. */
\r
86 xdata static portSTACK_TYPE * data pxXRAMStack;
\r
88 /* Used during a context switch to point to the next byte in RAM from/to which
\r
89 an XRAM byte is to be copied. */
\r
90 data static portSTACK_TYPE * data pxRAMStack;
\r
92 /* We require the address of the pxCurrentTCB variable, but don't want to know
\r
93 any details of its type. */
\r
94 typedef void tskTCB;
\r
95 extern volatile tskTCB * volatile pxCurrentTCB;
\r
98 * Setup the hardware to generate an interrupt off timer 2 at the required
\r
101 static void prvSetupTimerInterrupt( void );
\r
103 /*-----------------------------------------------------------*/
\r
105 * Macro that copies the current stack from internal RAM to XRAM. This is
\r
106 * required as the 8051 only contains enough internal RAM for a single stack,
\r
107 * but we have a stack for every task.
\r
109 #define portCOPY_STACK_TO_XRAM() \
\r
111 /* pxCurrentTCB points to a TCB which itself points to the location into \
\r
112 which the first stack byte should be copied. Set pxXRAMStack to point \
\r
113 to the location into which the first stack byte is to be copied. */ \
\r
114 pxXRAMStack = ( xdata portSTACK_TYPE * ) *( ( xdata portSTACK_TYPE ** ) pxCurrentTCB ); \
\r
116 /* Set pxRAMStack to point to the first byte to be coped from the stack. */ \
\r
117 pxRAMStack = ( data portSTACK_TYPE * data ) configSTACK_START; \
\r
119 /* Calculate the size of the stack we are about to copy from the current \
\r
120 stack pointer value. */ \
\r
121 ucStackBytes = SP - ( configSTACK_START - 1 ); \
\r
123 /* Before starting to copy the stack, store the calculated stack size so \
\r
124 the stack can be restored when the task is resumed. */ \
\r
125 *pxXRAMStack = ucStackBytes; \
\r
127 /* Copy each stack byte in turn. pxXRAMStack is incremented first as we \
\r
128 have already stored the stack size into XRAM. */ \
\r
129 while( ucStackBytes ) \
\r
132 *pxXRAMStack = *pxRAMStack; \
\r
137 /*-----------------------------------------------------------*/
\r
140 * Macro that copies the stack of the task being resumed from XRAM into
\r
143 #define portCOPY_XRAM_TO_STACK() \
\r
145 /* Setup the pointers as per portCOPY_STACK_TO_XRAM(), but this time to \
\r
146 copy the data back out of XRAM and into the stack. */ \
\r
147 pxXRAMStack = ( xdata portSTACK_TYPE * ) *( ( xdata portSTACK_TYPE ** ) pxCurrentTCB ); \
\r
148 pxRAMStack = ( data portSTACK_TYPE * data ) ( configSTACK_START - 1 ); \
\r
150 /* The first value stored in XRAM was the size of the stack - i.e. the \
\r
151 number of bytes we need to copy back. */ \
\r
152 ucStackBytes = pxXRAMStack[ 0 ]; \
\r
154 /* Copy the required number of bytes back into the stack. */ \
\r
159 *pxRAMStack = *pxXRAMStack; \
\r
161 } while( ucStackBytes ); \
\r
163 /* Restore the stack pointer ready to use the restored stack. */ \
\r
164 SP = ( unsigned char ) pxRAMStack; \
\r
166 /*-----------------------------------------------------------*/
\r
169 * Macro to push the current execution context onto the stack, before the stack
\r
170 * is moved to XRAM.
\r
172 #define portSAVE_CONTEXT() \
\r
175 /* Push ACC first, as when restoring the context it must be restored \
\r
176 last (it is used to set the IE register). */ \
\r
178 /* Store the IE register then disable interrupts. */ \
\r
199 /*-----------------------------------------------------------*/
\r
202 * Macro that restores the execution context from the stack. The execution
\r
203 * context was saved into the stack before the stack was copied into XRAM.
\r
205 #define portRESTORE_CONTEXT() \
\r
221 /* The next byte of the stack is the IE register. Only the global \
\r
222 enable bit forms part of the task context. Pop off the IE then set \
\r
223 the global enable bit to match that of the stored IE register. */ \
\r
231 /* Finally pop off the ACC, which was the first register saved. */ \
\r
236 /*-----------------------------------------------------------*/
\r
239 * See header file for description.
\r
241 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )
\r
243 unsigned long ulAddress;
\r
244 portSTACK_TYPE *pxStartOfStack;
\r
246 /* Leave space to write the size of the stack as the first byte. */
\r
247 pxStartOfStack = pxTopOfStack;
\r
250 /* Place a few bytes of known values on the bottom of the stack.
\r
251 This is just useful for debugging and can be uncommented if required.
\r
252 *pxTopOfStack = 0x11;
\r
254 *pxTopOfStack = 0x22;
\r
256 *pxTopOfStack = 0x33;
\r
260 /* Simulate how the stack would look after a call to the scheduler tick
\r
263 The return address that would have been pushed by the MCU. */
\r
264 ulAddress = ( unsigned long ) pxCode;
\r
265 *pxTopOfStack = ( portSTACK_TYPE ) ulAddress;
\r
268 *pxTopOfStack = ( portSTACK_TYPE ) ( ulAddress );
\r
271 /* Next all the registers will have been pushed by portSAVE_CONTEXT(). */
\r
272 *pxTopOfStack = 0xaa; /* acc */
\r
275 /* We want tasks to start with interrupts enabled. */
\r
276 *pxTopOfStack = portGLOBAL_INTERRUPT_BIT;
\r
279 /* The function parameters will be passed in the DPTR and B register as
\r
280 a three byte generic pointer is used. */
\r
281 ulAddress = ( unsigned long ) pvParameters;
\r
282 *pxTopOfStack = ( portSTACK_TYPE ) ulAddress; /* DPL */
\r
285 *pxTopOfStack = ( portSTACK_TYPE ) ulAddress; /* DPH */
\r
288 *pxTopOfStack = ( portSTACK_TYPE ) ulAddress; /* b */
\r
291 /* The remaining registers are straight forward. */
\r
292 *pxTopOfStack = 0x02; /* R2 */
\r
294 *pxTopOfStack = 0x03; /* R3 */
\r
296 *pxTopOfStack = 0x04; /* R4 */
\r
298 *pxTopOfStack = 0x05; /* R5 */
\r
300 *pxTopOfStack = 0x06; /* R6 */
\r
302 *pxTopOfStack = 0x07; /* R7 */
\r
304 *pxTopOfStack = 0x00; /* R0 */
\r
306 *pxTopOfStack = 0x01; /* R1 */
\r
308 *pxTopOfStack = 0x00; /* PSW */
\r
310 *pxTopOfStack = 0xbb; /* BP */
\r
312 /* Dont increment the stack size here as we don't want to include
\r
313 the stack size byte as part of the stack size count.
\r
315 Finally we place the stack size at the beginning. */
\r
316 *pxStartOfStack = ( portSTACK_TYPE ) ( pxTopOfStack - pxStartOfStack );
\r
318 /* Unlike most ports, we return the start of the stack as this is where the
\r
319 size of the stack is stored. */
\r
320 return pxStartOfStack;
\r
322 /*-----------------------------------------------------------*/
\r
325 * See header file for description.
\r
327 portBASE_TYPE xPortStartScheduler( void )
\r
329 /* Setup timer 2 to generate the RTOS tick. */
\r
330 prvSetupTimerInterrupt();
\r
332 /* Make sure we start with the expected SFR page. This line should not
\r
333 really be required. */
\r
336 /* Copy the stack for the first task to execute from XRAM into the stack,
\r
337 restore the task context from the new stack, then start running the task. */
\r
338 portCOPY_XRAM_TO_STACK();
\r
339 portRESTORE_CONTEXT();
\r
341 /* Should never get here! */
\r
344 /*-----------------------------------------------------------*/
\r
346 void vPortEndScheduler( void )
\r
348 /* Not implemented for this port. */
\r
350 /*-----------------------------------------------------------*/
\r
353 * Manual context switch. The first thing we do is save the registers so we
\r
354 * can use a naked attribute.
\r
356 void vPortYield( void ) _naked
\r
358 /* Save the execution context onto the stack, then copy the entire stack
\r
359 to XRAM. This is necessary as the internal RAM is only large enough to
\r
360 hold one stack, and we want one per task.
\r
362 PERFORMANCE COULD BE IMPROVED BY ONLY COPYING TO XRAM IF A TASK SWITCH
\r
364 portSAVE_CONTEXT();
\r
365 portCOPY_STACK_TO_XRAM();
\r
367 /* Call the standard scheduler context switch function. */
\r
368 vTaskSwitchContext();
\r
370 /* Copy the stack of the task about to execute from XRAM into RAM and
\r
371 restore it's context ready to run on exiting. */
\r
372 portCOPY_XRAM_TO_STACK();
\r
373 portRESTORE_CONTEXT();
\r
375 /*-----------------------------------------------------------*/
\r
377 #if configUSE_PREEMPTION == 1
\r
378 void vTimer2ISR( void ) interrupt 5 _naked
\r
380 /* Preemptive context switch function triggered by the timer 2 ISR.
\r
381 This does the same as vPortYield() (see above) with the addition
\r
382 of incrementing the RTOS tick count. */
\r
384 portSAVE_CONTEXT();
\r
385 portCOPY_STACK_TO_XRAM();
\r
387 vTaskIncrementTick();
\r
388 vTaskSwitchContext();
\r
390 portCLEAR_INTERRUPT_FLAG();
\r
391 portCOPY_XRAM_TO_STACK();
\r
392 portRESTORE_CONTEXT();
\r
395 void vTimer2ISR( void ) interrupt 5
\r
397 /* When using the cooperative scheduler the timer 2 ISR is only
\r
398 required to increment the RTOS tick count. */
\r
400 vTaskIncrementTick();
\r
401 portCLEAR_INTERRUPT_FLAG();
\r
404 /*-----------------------------------------------------------*/
\r
406 static void prvSetupTimerInterrupt( void )
\r
408 unsigned char ucOriginalSFRPage;
\r
410 /* Constants calculated to give the required timer capture values. */
\r
411 const unsigned long ulTicksPerSecond = configCPU_CLOCK_HZ / portCLOCK_DIVISOR;
\r
412 const unsigned long ulCaptureTime = ulTicksPerSecond / configTICK_RATE_HZ;
\r
413 const unsigned long ulCaptureValue = portMAX_TIMER_VALUE - ulCaptureTime;
\r
414 const unsigned char ucLowCaptureByte = ( unsigned char ) ( ulCaptureValue & ( unsigned long ) 0xff );
\r
415 const unsigned char ucHighCaptureByte = ( unsigned char ) ( ulCaptureValue >> ( unsigned long ) 8 );
\r
417 /* NOTE: This uses a timer only present on 8052 architecture. */
\r
419 /* Remember the current SFR page so we can restore it at the end of the
\r
421 ucOriginalSFRPage = SFRPAGE;
\r
424 /* TMR2CF can be left in its default state. */
\r
425 TMR2CF = ( unsigned char ) 0;
\r
427 /* Setup the overflow reload value. */
\r
428 RCAP2L = ucLowCaptureByte;
\r
429 RCAP2H = ucHighCaptureByte;
\r
431 /* The initial load is performed manually. */
\r
432 TMR2L = ucLowCaptureByte;
\r
433 TMR2H = ucHighCaptureByte;
\r
435 /* Enable the timer 2 interrupts. */
\r
436 IE |= portTIMER_2_INTERRUPT_ENABLE;
\r
438 /* Interrupts are disabled when this is called so the timer can be started
\r
440 TMR2CN = portENABLE_TIMER;
\r
442 /* Restore the original SFR page. */
\r
443 SFRPAGE = ucOriginalSFRPage;
\r