www.freertos.org
Dependents: mbed_freeRTOS_send
Fork of FreeRTOS by
Source/portable/RVDS/ARM_CM3/port.c@1:6db425da9b9e, 2016-05-18 (annotated)
- Committer:
- ser1516
- Date:
- Wed May 18 21:25:21 2016 +0000
- Revision:
- 1:6db425da9b9e
- Parent:
- 0:8e57f3e9cc89
queue implementada com accelarometro e pot a enviar para o can - ainda nao testado
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rgrover1 | 0:8e57f3e9cc89 | 1 | /* |
rgrover1 | 0:8e57f3e9cc89 | 2 | FreeRTOS V7.6.0 - Copyright (C) 2013 Real Time Engineers Ltd. |
rgrover1 | 0:8e57f3e9cc89 | 3 | All rights reserved |
rgrover1 | 0:8e57f3e9cc89 | 4 | |
rgrover1 | 0:8e57f3e9cc89 | 5 | VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. |
rgrover1 | 0:8e57f3e9cc89 | 6 | |
rgrover1 | 0:8e57f3e9cc89 | 7 | *************************************************************************** |
rgrover1 | 0:8e57f3e9cc89 | 8 | * * |
rgrover1 | 0:8e57f3e9cc89 | 9 | * FreeRTOS provides completely free yet professionally developed, * |
rgrover1 | 0:8e57f3e9cc89 | 10 | * robust, strictly quality controlled, supported, and cross * |
rgrover1 | 0:8e57f3e9cc89 | 11 | * platform software that has become a de facto standard. * |
rgrover1 | 0:8e57f3e9cc89 | 12 | * * |
rgrover1 | 0:8e57f3e9cc89 | 13 | * Help yourself get started quickly and support the FreeRTOS * |
rgrover1 | 0:8e57f3e9cc89 | 14 | * project by purchasing a FreeRTOS tutorial book, reference * |
rgrover1 | 0:8e57f3e9cc89 | 15 | * manual, or both from: http://www.FreeRTOS.org/Documentation * |
rgrover1 | 0:8e57f3e9cc89 | 16 | * * |
rgrover1 | 0:8e57f3e9cc89 | 17 | * Thank you! * |
rgrover1 | 0:8e57f3e9cc89 | 18 | * * |
rgrover1 | 0:8e57f3e9cc89 | 19 | *************************************************************************** |
rgrover1 | 0:8e57f3e9cc89 | 20 | |
rgrover1 | 0:8e57f3e9cc89 | 21 | This file is part of the FreeRTOS distribution. |
rgrover1 | 0:8e57f3e9cc89 | 22 | |
rgrover1 | 0:8e57f3e9cc89 | 23 | FreeRTOS is free software; you can redistribute it and/or modify it under |
rgrover1 | 0:8e57f3e9cc89 | 24 | the terms of the GNU General Public License (version 2) as published by the |
rgrover1 | 0:8e57f3e9cc89 | 25 | Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. |
rgrover1 | 0:8e57f3e9cc89 | 26 | |
rgrover1 | 0:8e57f3e9cc89 | 27 | >>! NOTE: The modification to the GPL is included to allow you to distribute |
rgrover1 | 0:8e57f3e9cc89 | 28 | >>! a combined work that includes FreeRTOS without being obliged to provide |
rgrover1 | 0:8e57f3e9cc89 | 29 | >>! the source code for proprietary components outside of the FreeRTOS |
rgrover1 | 0:8e57f3e9cc89 | 30 | >>! kernel. |
rgrover1 | 0:8e57f3e9cc89 | 31 | |
rgrover1 | 0:8e57f3e9cc89 | 32 | FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY |
rgrover1 | 0:8e57f3e9cc89 | 33 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
rgrover1 | 0:8e57f3e9cc89 | 34 | FOR A PARTICULAR PURPOSE. Full license text is available from the following |
rgrover1 | 0:8e57f3e9cc89 | 35 | link: http://www.freertos.org/a00114.html |
rgrover1 | 0:8e57f3e9cc89 | 36 | |
rgrover1 | 0:8e57f3e9cc89 | 37 | 1 tab == 4 spaces! |
rgrover1 | 0:8e57f3e9cc89 | 38 | |
rgrover1 | 0:8e57f3e9cc89 | 39 | *************************************************************************** |
rgrover1 | 0:8e57f3e9cc89 | 40 | * * |
rgrover1 | 0:8e57f3e9cc89 | 41 | * Having a problem? Start by reading the FAQ "My application does * |
rgrover1 | 0:8e57f3e9cc89 | 42 | * not run, what could be wrong?" * |
rgrover1 | 0:8e57f3e9cc89 | 43 | * * |
rgrover1 | 0:8e57f3e9cc89 | 44 | * http://www.FreeRTOS.org/FAQHelp.html * |
rgrover1 | 0:8e57f3e9cc89 | 45 | * * |
rgrover1 | 0:8e57f3e9cc89 | 46 | *************************************************************************** |
rgrover1 | 0:8e57f3e9cc89 | 47 | |
rgrover1 | 0:8e57f3e9cc89 | 48 | http://www.FreeRTOS.org - Documentation, books, training, latest versions, |
rgrover1 | 0:8e57f3e9cc89 | 49 | license and Real Time Engineers Ltd. contact details. |
rgrover1 | 0:8e57f3e9cc89 | 50 | |
rgrover1 | 0:8e57f3e9cc89 | 51 | http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, |
rgrover1 | 0:8e57f3e9cc89 | 52 | including FreeRTOS+Trace - an indispensable productivity tool, a DOS |
rgrover1 | 0:8e57f3e9cc89 | 53 | compatible FAT file system, and our tiny thread aware UDP/IP stack. |
rgrover1 | 0:8e57f3e9cc89 | 54 | |
rgrover1 | 0:8e57f3e9cc89 | 55 | http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High |
rgrover1 | 0:8e57f3e9cc89 | 56 | Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS |
rgrover1 | 0:8e57f3e9cc89 | 57 | licenses offer ticketed support, indemnification and middleware. |
rgrover1 | 0:8e57f3e9cc89 | 58 | |
rgrover1 | 0:8e57f3e9cc89 | 59 | http://www.SafeRTOS.com - High Integrity Systems also provide a safety |
rgrover1 | 0:8e57f3e9cc89 | 60 | engineered and independently SIL3 certified version for use in safety and |
rgrover1 | 0:8e57f3e9cc89 | 61 | mission critical applications that require provable dependability. |
rgrover1 | 0:8e57f3e9cc89 | 62 | |
rgrover1 | 0:8e57f3e9cc89 | 63 | 1 tab == 4 spaces! |
rgrover1 | 0:8e57f3e9cc89 | 64 | */ |
rgrover1 | 0:8e57f3e9cc89 | 65 | |
rgrover1 | 0:8e57f3e9cc89 | 66 | /*----------------------------------------------------------- |
rgrover1 | 0:8e57f3e9cc89 | 67 | * Implementation of functions defined in portable.h for the ARM CM3 port. |
rgrover1 | 0:8e57f3e9cc89 | 68 | *----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 69 | |
rgrover1 | 0:8e57f3e9cc89 | 70 | /* Scheduler includes. */ |
rgrover1 | 0:8e57f3e9cc89 | 71 | #include "FreeRTOS.h" |
rgrover1 | 0:8e57f3e9cc89 | 72 | #include "task.h" |
rgrover1 | 0:8e57f3e9cc89 | 73 | |
rgrover1 | 0:8e57f3e9cc89 | 74 | #ifndef configKERNEL_INTERRUPT_PRIORITY |
rgrover1 | 0:8e57f3e9cc89 | 75 | #define configKERNEL_INTERRUPT_PRIORITY 255 |
rgrover1 | 0:8e57f3e9cc89 | 76 | #endif |
rgrover1 | 0:8e57f3e9cc89 | 77 | |
rgrover1 | 0:8e57f3e9cc89 | 78 | #if configMAX_SYSCALL_INTERRUPT_PRIORITY == 0 |
rgrover1 | 0:8e57f3e9cc89 | 79 | #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html |
rgrover1 | 0:8e57f3e9cc89 | 80 | #endif |
rgrover1 | 0:8e57f3e9cc89 | 81 | |
rgrover1 | 0:8e57f3e9cc89 | 82 | #ifndef configSYSTICK_CLOCK_HZ |
rgrover1 | 0:8e57f3e9cc89 | 83 | #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ |
rgrover1 | 0:8e57f3e9cc89 | 84 | #endif |
rgrover1 | 0:8e57f3e9cc89 | 85 | |
rgrover1 | 0:8e57f3e9cc89 | 86 | /* The __weak attribute does not work as you might expect with the Keil tools |
rgrover1 | 0:8e57f3e9cc89 | 87 | so the configOVERRIDE_DEFAULT_TICK_CONFIGURATION constant must be set to 1 if |
rgrover1 | 0:8e57f3e9cc89 | 88 | the application writer wants to provide their own implementation of |
rgrover1 | 0:8e57f3e9cc89 | 89 | vPortSetupTimerInterrupt(). Ensure configOVERRIDE_DEFAULT_TICK_CONFIGURATION |
rgrover1 | 0:8e57f3e9cc89 | 90 | is defined. */ |
rgrover1 | 0:8e57f3e9cc89 | 91 | #ifndef configOVERRIDE_DEFAULT_TICK_CONFIGURATION |
rgrover1 | 0:8e57f3e9cc89 | 92 | #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 0 |
rgrover1 | 0:8e57f3e9cc89 | 93 | #endif |
rgrover1 | 0:8e57f3e9cc89 | 94 | |
rgrover1 | 0:8e57f3e9cc89 | 95 | /* Constants required to manipulate the core. Registers first... */ |
rgrover1 | 0:8e57f3e9cc89 | 96 | #define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile unsigned long * ) 0xe000e010 ) ) |
rgrover1 | 0:8e57f3e9cc89 | 97 | #define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile unsigned long * ) 0xe000e014 ) ) |
rgrover1 | 0:8e57f3e9cc89 | 98 | #define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile unsigned long * ) 0xe000e018 ) ) |
rgrover1 | 0:8e57f3e9cc89 | 99 | #define portNVIC_SYSPRI2_REG ( * ( ( volatile unsigned long * ) 0xe000ed20 ) ) |
rgrover1 | 0:8e57f3e9cc89 | 100 | /* ...then bits in the registers. */ |
rgrover1 | 0:8e57f3e9cc89 | 101 | #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) |
rgrover1 | 0:8e57f3e9cc89 | 102 | #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) |
rgrover1 | 0:8e57f3e9cc89 | 103 | #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) |
rgrover1 | 0:8e57f3e9cc89 | 104 | #define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) |
rgrover1 | 0:8e57f3e9cc89 | 105 | #define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) |
rgrover1 | 0:8e57f3e9cc89 | 106 | #define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) |
rgrover1 | 0:8e57f3e9cc89 | 107 | |
rgrover1 | 0:8e57f3e9cc89 | 108 | #define portNVIC_PENDSV_PRI ( ( ( unsigned long ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) |
rgrover1 | 0:8e57f3e9cc89 | 109 | #define portNVIC_SYSTICK_PRI ( ( ( unsigned long ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) |
rgrover1 | 0:8e57f3e9cc89 | 110 | |
rgrover1 | 0:8e57f3e9cc89 | 111 | /* Constants required to check the validity of an interrupt priority. */ |
rgrover1 | 0:8e57f3e9cc89 | 112 | #define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) |
rgrover1 | 0:8e57f3e9cc89 | 113 | #define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) |
rgrover1 | 0:8e57f3e9cc89 | 114 | #define portAIRCR_REG ( * ( ( volatile unsigned long * ) 0xE000ED0C ) ) |
rgrover1 | 0:8e57f3e9cc89 | 115 | #define portMAX_8_BIT_VALUE ( ( unsigned char ) 0xff ) |
rgrover1 | 0:8e57f3e9cc89 | 116 | #define portTOP_BIT_OF_BYTE ( ( unsigned char ) 0x80 ) |
rgrover1 | 0:8e57f3e9cc89 | 117 | #define portMAX_PRIGROUP_BITS ( ( unsigned char ) 7 ) |
rgrover1 | 0:8e57f3e9cc89 | 118 | #define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) |
rgrover1 | 0:8e57f3e9cc89 | 119 | #define portPRIGROUP_SHIFT ( 8UL ) |
rgrover1 | 0:8e57f3e9cc89 | 120 | |
rgrover1 | 0:8e57f3e9cc89 | 121 | /* Constants required to set up the initial stack. */ |
rgrover1 | 0:8e57f3e9cc89 | 122 | #define portINITIAL_XPSR ( 0x01000000 ) |
rgrover1 | 0:8e57f3e9cc89 | 123 | |
rgrover1 | 0:8e57f3e9cc89 | 124 | /* Constants used with memory barrier intrinsics. */ |
rgrover1 | 0:8e57f3e9cc89 | 125 | #define portSY_FULL_READ_WRITE ( 15 ) |
rgrover1 | 0:8e57f3e9cc89 | 126 | |
rgrover1 | 0:8e57f3e9cc89 | 127 | /* The systick is a 24-bit counter. */ |
rgrover1 | 0:8e57f3e9cc89 | 128 | #define portMAX_24_BIT_NUMBER ( 0xffffffUL ) |
rgrover1 | 0:8e57f3e9cc89 | 129 | |
rgrover1 | 0:8e57f3e9cc89 | 130 | /* A fiddle factor to estimate the number of SysTick counts that would have |
rgrover1 | 0:8e57f3e9cc89 | 131 | occurred while the SysTick counter is stopped during tickless idle |
rgrover1 | 0:8e57f3e9cc89 | 132 | calculations. */ |
rgrover1 | 0:8e57f3e9cc89 | 133 | #define portMISSED_COUNTS_FACTOR ( 45UL ) |
rgrover1 | 0:8e57f3e9cc89 | 134 | |
rgrover1 | 0:8e57f3e9cc89 | 135 | /* Each task maintains its own interrupt status in the critical nesting |
rgrover1 | 0:8e57f3e9cc89 | 136 | variable. */ |
rgrover1 | 0:8e57f3e9cc89 | 137 | static unsigned portBASE_TYPE uxCriticalNesting = 0xaaaaaaaa; |
rgrover1 | 0:8e57f3e9cc89 | 138 | |
rgrover1 | 0:8e57f3e9cc89 | 139 | /* |
rgrover1 | 0:8e57f3e9cc89 | 140 | * Setup the timer to generate the tick interrupts. The implementation in this |
rgrover1 | 0:8e57f3e9cc89 | 141 | * file is weak to allow application writers to change the timer used to |
rgrover1 | 0:8e57f3e9cc89 | 142 | * generate the tick interrupt. |
rgrover1 | 0:8e57f3e9cc89 | 143 | */ |
rgrover1 | 0:8e57f3e9cc89 | 144 | void vPortSetupTimerInterrupt( void ); |
rgrover1 | 0:8e57f3e9cc89 | 145 | |
rgrover1 | 0:8e57f3e9cc89 | 146 | /* |
rgrover1 | 0:8e57f3e9cc89 | 147 | * Exception handlers. |
rgrover1 | 0:8e57f3e9cc89 | 148 | */ |
rgrover1 | 0:8e57f3e9cc89 | 149 | void xPortPendSVHandler( void ); |
rgrover1 | 0:8e57f3e9cc89 | 150 | void xPortSysTickHandler( void ); |
rgrover1 | 0:8e57f3e9cc89 | 151 | void vPortSVCHandler( void ); |
rgrover1 | 0:8e57f3e9cc89 | 152 | |
rgrover1 | 0:8e57f3e9cc89 | 153 | /* |
rgrover1 | 0:8e57f3e9cc89 | 154 | * Start first task is a separate function so it can be tested in isolation. |
rgrover1 | 0:8e57f3e9cc89 | 155 | */ |
rgrover1 | 0:8e57f3e9cc89 | 156 | static void prvStartFirstTask( void ); |
rgrover1 | 0:8e57f3e9cc89 | 157 | |
rgrover1 | 0:8e57f3e9cc89 | 158 | /* |
rgrover1 | 0:8e57f3e9cc89 | 159 | * Used to catch tasks that attempt to return from their implementing function. |
rgrover1 | 0:8e57f3e9cc89 | 160 | */ |
rgrover1 | 0:8e57f3e9cc89 | 161 | static void prvTaskExitError( void ); |
rgrover1 | 0:8e57f3e9cc89 | 162 | |
rgrover1 | 0:8e57f3e9cc89 | 163 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 164 | |
rgrover1 | 0:8e57f3e9cc89 | 165 | /* |
rgrover1 | 0:8e57f3e9cc89 | 166 | * The number of SysTick increments that make up one tick period. |
rgrover1 | 0:8e57f3e9cc89 | 167 | */ |
rgrover1 | 0:8e57f3e9cc89 | 168 | #if configUSE_TICKLESS_IDLE == 1 |
rgrover1 | 0:8e57f3e9cc89 | 169 | static unsigned long ulTimerCountsForOneTick = 0; |
rgrover1 | 0:8e57f3e9cc89 | 170 | #endif /* configUSE_TICKLESS_IDLE */ |
rgrover1 | 0:8e57f3e9cc89 | 171 | |
rgrover1 | 0:8e57f3e9cc89 | 172 | /* |
rgrover1 | 0:8e57f3e9cc89 | 173 | * The maximum number of tick periods that can be suppressed is limited by the |
rgrover1 | 0:8e57f3e9cc89 | 174 | * 24 bit resolution of the SysTick timer. |
rgrover1 | 0:8e57f3e9cc89 | 175 | */ |
rgrover1 | 0:8e57f3e9cc89 | 176 | #if configUSE_TICKLESS_IDLE == 1 |
rgrover1 | 0:8e57f3e9cc89 | 177 | static unsigned long xMaximumPossibleSuppressedTicks = 0; |
rgrover1 | 0:8e57f3e9cc89 | 178 | #endif /* configUSE_TICKLESS_IDLE */ |
rgrover1 | 0:8e57f3e9cc89 | 179 | |
rgrover1 | 0:8e57f3e9cc89 | 180 | /* |
rgrover1 | 0:8e57f3e9cc89 | 181 | * Compensate for the CPU cycles that pass while the SysTick is stopped (low |
rgrover1 | 0:8e57f3e9cc89 | 182 | * power functionality only. |
rgrover1 | 0:8e57f3e9cc89 | 183 | */ |
rgrover1 | 0:8e57f3e9cc89 | 184 | #if configUSE_TICKLESS_IDLE == 1 |
rgrover1 | 0:8e57f3e9cc89 | 185 | static unsigned long ulStoppedTimerCompensation = 0; |
rgrover1 | 0:8e57f3e9cc89 | 186 | #endif /* configUSE_TICKLESS_IDLE */ |
rgrover1 | 0:8e57f3e9cc89 | 187 | |
rgrover1 | 0:8e57f3e9cc89 | 188 | /* |
rgrover1 | 0:8e57f3e9cc89 | 189 | * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure |
rgrover1 | 0:8e57f3e9cc89 | 190 | * FreeRTOS API functions are not called from interrupts that have been assigned |
rgrover1 | 0:8e57f3e9cc89 | 191 | * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. |
rgrover1 | 0:8e57f3e9cc89 | 192 | */ |
rgrover1 | 0:8e57f3e9cc89 | 193 | #if ( configASSERT_DEFINED == 1 ) |
rgrover1 | 0:8e57f3e9cc89 | 194 | static unsigned char ucMaxSysCallPriority = 0; |
rgrover1 | 0:8e57f3e9cc89 | 195 | static unsigned long ulMaxPRIGROUPValue = 0; |
rgrover1 | 0:8e57f3e9cc89 | 196 | static const volatile unsigned char * const pcInterruptPriorityRegisters = ( unsigned char * ) portNVIC_IP_REGISTERS_OFFSET_16; |
rgrover1 | 0:8e57f3e9cc89 | 197 | #endif /* configASSERT_DEFINED */ |
rgrover1 | 0:8e57f3e9cc89 | 198 | |
rgrover1 | 0:8e57f3e9cc89 | 199 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 200 | |
rgrover1 | 0:8e57f3e9cc89 | 201 | /* |
rgrover1 | 0:8e57f3e9cc89 | 202 | * See header file for description. |
rgrover1 | 0:8e57f3e9cc89 | 203 | */ |
rgrover1 | 0:8e57f3e9cc89 | 204 | portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) |
rgrover1 | 0:8e57f3e9cc89 | 205 | { |
rgrover1 | 0:8e57f3e9cc89 | 206 | /* Simulate the stack frame as it would be created by a context switch |
rgrover1 | 0:8e57f3e9cc89 | 207 | interrupt. */ |
rgrover1 | 0:8e57f3e9cc89 | 208 | pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ |
rgrover1 | 0:8e57f3e9cc89 | 209 | *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ |
rgrover1 | 0:8e57f3e9cc89 | 210 | pxTopOfStack--; |
rgrover1 | 0:8e57f3e9cc89 | 211 | *pxTopOfStack = ( portSTACK_TYPE ) pxCode; /* PC */ |
rgrover1 | 0:8e57f3e9cc89 | 212 | pxTopOfStack--; |
rgrover1 | 0:8e57f3e9cc89 | 213 | *pxTopOfStack = ( portSTACK_TYPE ) prvTaskExitError; /* LR */ |
rgrover1 | 0:8e57f3e9cc89 | 214 | |
rgrover1 | 0:8e57f3e9cc89 | 215 | pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ |
rgrover1 | 0:8e57f3e9cc89 | 216 | *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */ |
rgrover1 | 0:8e57f3e9cc89 | 217 | pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ |
rgrover1 | 0:8e57f3e9cc89 | 218 | |
rgrover1 | 0:8e57f3e9cc89 | 219 | return pxTopOfStack; |
rgrover1 | 0:8e57f3e9cc89 | 220 | } |
rgrover1 | 0:8e57f3e9cc89 | 221 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 222 | |
rgrover1 | 0:8e57f3e9cc89 | 223 | static void prvTaskExitError( void ) |
rgrover1 | 0:8e57f3e9cc89 | 224 | { |
rgrover1 | 0:8e57f3e9cc89 | 225 | /* A function that implements a task must not exit or attempt to return to |
rgrover1 | 0:8e57f3e9cc89 | 226 | its caller as there is nothing to return to. If a task wants to exit it |
rgrover1 | 0:8e57f3e9cc89 | 227 | should instead call vTaskDelete( NULL ). |
rgrover1 | 0:8e57f3e9cc89 | 228 | |
rgrover1 | 0:8e57f3e9cc89 | 229 | Artificially force an assert() to be triggered if configASSERT() is |
rgrover1 | 0:8e57f3e9cc89 | 230 | defined, then stop here so application writers can catch the error. */ |
rgrover1 | 0:8e57f3e9cc89 | 231 | configASSERT( uxCriticalNesting == ~0UL ); |
rgrover1 | 0:8e57f3e9cc89 | 232 | portDISABLE_INTERRUPTS(); |
rgrover1 | 0:8e57f3e9cc89 | 233 | for( ;; ); |
rgrover1 | 0:8e57f3e9cc89 | 234 | } |
rgrover1 | 0:8e57f3e9cc89 | 235 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 236 | |
rgrover1 | 0:8e57f3e9cc89 | 237 | __asm void vPortSVCHandler( void ) |
rgrover1 | 0:8e57f3e9cc89 | 238 | { |
rgrover1 | 0:8e57f3e9cc89 | 239 | PRESERVE8 |
rgrover1 | 0:8e57f3e9cc89 | 240 | |
rgrover1 | 0:8e57f3e9cc89 | 241 | ldr r3, =pxCurrentTCB /* Restore the context. */ |
rgrover1 | 0:8e57f3e9cc89 | 242 | ldr r1, [r3] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ |
rgrover1 | 0:8e57f3e9cc89 | 243 | ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ |
rgrover1 | 0:8e57f3e9cc89 | 244 | ldmia r0!, {r4-r11} /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ |
rgrover1 | 0:8e57f3e9cc89 | 245 | msr psp, r0 /* Restore the task stack pointer. */ |
rgrover1 | 0:8e57f3e9cc89 | 246 | mov r0, #0 |
rgrover1 | 0:8e57f3e9cc89 | 247 | msr basepri, r0 |
rgrover1 | 0:8e57f3e9cc89 | 248 | orr r14, #0xd |
rgrover1 | 0:8e57f3e9cc89 | 249 | bx r14 |
rgrover1 | 0:8e57f3e9cc89 | 250 | } |
rgrover1 | 0:8e57f3e9cc89 | 251 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 252 | |
rgrover1 | 0:8e57f3e9cc89 | 253 | __asm void prvStartFirstTask( void ) |
rgrover1 | 0:8e57f3e9cc89 | 254 | { |
rgrover1 | 0:8e57f3e9cc89 | 255 | PRESERVE8 |
rgrover1 | 0:8e57f3e9cc89 | 256 | |
rgrover1 | 0:8e57f3e9cc89 | 257 | /* Use the NVIC offset register to locate the stack. */ |
rgrover1 | 0:8e57f3e9cc89 | 258 | ldr r0, =0xE000ED08 |
rgrover1 | 0:8e57f3e9cc89 | 259 | ldr r0, [r0] |
rgrover1 | 0:8e57f3e9cc89 | 260 | ldr r0, [r0] |
rgrover1 | 0:8e57f3e9cc89 | 261 | /* Set the msp back to the start of the stack. */ |
rgrover1 | 0:8e57f3e9cc89 | 262 | msr msp, r0 |
rgrover1 | 0:8e57f3e9cc89 | 263 | /* Globally enable interrupts. */ |
rgrover1 | 0:8e57f3e9cc89 | 264 | cpsie i |
rgrover1 | 0:8e57f3e9cc89 | 265 | /* Call SVC to start the first task. */ |
rgrover1 | 0:8e57f3e9cc89 | 266 | svc 0 |
rgrover1 | 0:8e57f3e9cc89 | 267 | nop |
rgrover1 | 0:8e57f3e9cc89 | 268 | } |
rgrover1 | 0:8e57f3e9cc89 | 269 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 270 | |
rgrover1 | 0:8e57f3e9cc89 | 271 | /* |
rgrover1 | 0:8e57f3e9cc89 | 272 | * See header file for description. |
rgrover1 | 0:8e57f3e9cc89 | 273 | */ |
rgrover1 | 0:8e57f3e9cc89 | 274 | portBASE_TYPE xPortStartScheduler( void ) |
rgrover1 | 0:8e57f3e9cc89 | 275 | { |
rgrover1 | 0:8e57f3e9cc89 | 276 | #if( configASSERT_DEFINED == 1 ) |
rgrover1 | 0:8e57f3e9cc89 | 277 | { |
rgrover1 | 0:8e57f3e9cc89 | 278 | volatile unsigned long ulOriginalPriority; |
rgrover1 | 0:8e57f3e9cc89 | 279 | volatile char * const pcFirstUserPriorityRegister = ( char * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); |
rgrover1 | 0:8e57f3e9cc89 | 280 | volatile unsigned char ucMaxPriorityValue; |
rgrover1 | 0:8e57f3e9cc89 | 281 | |
rgrover1 | 0:8e57f3e9cc89 | 282 | /* Determine the maximum priority from which ISR safe FreeRTOS API |
rgrover1 | 0:8e57f3e9cc89 | 283 | functions can be called. ISR safe functions are those that end in |
rgrover1 | 0:8e57f3e9cc89 | 284 | "FromISR". FreeRTOS maintains separate thread and ISR API functions to |
rgrover1 | 0:8e57f3e9cc89 | 285 | ensure interrupt entry is as fast and simple as possible. |
rgrover1 | 0:8e57f3e9cc89 | 286 | |
rgrover1 | 0:8e57f3e9cc89 | 287 | Save the interrupt priority value that is about to be clobbered. */ |
rgrover1 | 0:8e57f3e9cc89 | 288 | ulOriginalPriority = *pcFirstUserPriorityRegister; |
rgrover1 | 0:8e57f3e9cc89 | 289 | |
rgrover1 | 0:8e57f3e9cc89 | 290 | /* Determine the number of priority bits available. First write to all |
rgrover1 | 0:8e57f3e9cc89 | 291 | possible bits. */ |
rgrover1 | 0:8e57f3e9cc89 | 292 | *pcFirstUserPriorityRegister = portMAX_8_BIT_VALUE; |
rgrover1 | 0:8e57f3e9cc89 | 293 | |
rgrover1 | 0:8e57f3e9cc89 | 294 | /* Read the value back to see how many bits stuck. */ |
rgrover1 | 0:8e57f3e9cc89 | 295 | ucMaxPriorityValue = *pcFirstUserPriorityRegister; |
rgrover1 | 0:8e57f3e9cc89 | 296 | |
rgrover1 | 0:8e57f3e9cc89 | 297 | /* Use the same mask on the maximum system call priority. */ |
rgrover1 | 0:8e57f3e9cc89 | 298 | ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; |
rgrover1 | 0:8e57f3e9cc89 | 299 | |
rgrover1 | 0:8e57f3e9cc89 | 300 | /* Calculate the maximum acceptable priority group value for the number |
rgrover1 | 0:8e57f3e9cc89 | 301 | of bits read back. */ |
rgrover1 | 0:8e57f3e9cc89 | 302 | ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; |
rgrover1 | 0:8e57f3e9cc89 | 303 | while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) |
rgrover1 | 0:8e57f3e9cc89 | 304 | { |
rgrover1 | 0:8e57f3e9cc89 | 305 | ulMaxPRIGROUPValue--; |
rgrover1 | 0:8e57f3e9cc89 | 306 | ucMaxPriorityValue <<= ( unsigned char ) 0x01; |
rgrover1 | 0:8e57f3e9cc89 | 307 | } |
rgrover1 | 0:8e57f3e9cc89 | 308 | |
rgrover1 | 0:8e57f3e9cc89 | 309 | /* Shift the priority group value back to its position within the AIRCR |
rgrover1 | 0:8e57f3e9cc89 | 310 | register. */ |
rgrover1 | 0:8e57f3e9cc89 | 311 | ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; |
rgrover1 | 0:8e57f3e9cc89 | 312 | ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; |
rgrover1 | 0:8e57f3e9cc89 | 313 | |
rgrover1 | 0:8e57f3e9cc89 | 314 | /* Restore the clobbered interrupt priority register to its original |
rgrover1 | 0:8e57f3e9cc89 | 315 | value. */ |
rgrover1 | 0:8e57f3e9cc89 | 316 | *pcFirstUserPriorityRegister = ulOriginalPriority; |
rgrover1 | 0:8e57f3e9cc89 | 317 | } |
rgrover1 | 0:8e57f3e9cc89 | 318 | #endif /* conifgASSERT_DEFINED */ |
rgrover1 | 0:8e57f3e9cc89 | 319 | |
rgrover1 | 0:8e57f3e9cc89 | 320 | /* Make PendSV and SysTick the lowest priority interrupts. */ |
rgrover1 | 0:8e57f3e9cc89 | 321 | portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; |
rgrover1 | 0:8e57f3e9cc89 | 322 | portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; |
rgrover1 | 0:8e57f3e9cc89 | 323 | |
rgrover1 | 0:8e57f3e9cc89 | 324 | /* Start the timer that generates the tick ISR. Interrupts are disabled |
rgrover1 | 0:8e57f3e9cc89 | 325 | here already. */ |
rgrover1 | 0:8e57f3e9cc89 | 326 | vPortSetupTimerInterrupt(); |
rgrover1 | 0:8e57f3e9cc89 | 327 | |
rgrover1 | 0:8e57f3e9cc89 | 328 | /* Initialise the critical nesting count ready for the first task. */ |
rgrover1 | 0:8e57f3e9cc89 | 329 | uxCriticalNesting = 0; |
rgrover1 | 0:8e57f3e9cc89 | 330 | |
rgrover1 | 0:8e57f3e9cc89 | 331 | /* Start the first task. */ |
rgrover1 | 0:8e57f3e9cc89 | 332 | prvStartFirstTask(); |
rgrover1 | 0:8e57f3e9cc89 | 333 | |
rgrover1 | 0:8e57f3e9cc89 | 334 | /* Should not get here! */ |
rgrover1 | 0:8e57f3e9cc89 | 335 | return 0; |
rgrover1 | 0:8e57f3e9cc89 | 336 | } |
rgrover1 | 0:8e57f3e9cc89 | 337 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 338 | |
rgrover1 | 0:8e57f3e9cc89 | 339 | void vPortEndScheduler( void ) |
rgrover1 | 0:8e57f3e9cc89 | 340 | { |
rgrover1 | 0:8e57f3e9cc89 | 341 | /* It is unlikely that the CM3 port will require this function as there |
rgrover1 | 0:8e57f3e9cc89 | 342 | is nothing to return to. */ |
rgrover1 | 0:8e57f3e9cc89 | 343 | } |
rgrover1 | 0:8e57f3e9cc89 | 344 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 345 | |
rgrover1 | 0:8e57f3e9cc89 | 346 | void vPortYield( void ) |
rgrover1 | 0:8e57f3e9cc89 | 347 | { |
rgrover1 | 0:8e57f3e9cc89 | 348 | /* Set a PendSV to request a context switch. */ |
rgrover1 | 0:8e57f3e9cc89 | 349 | portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 350 | |
rgrover1 | 0:8e57f3e9cc89 | 351 | /* Barriers are normally not required but do ensure the code is completely |
rgrover1 | 0:8e57f3e9cc89 | 352 | within the specified behaviour for the architecture. */ |
rgrover1 | 0:8e57f3e9cc89 | 353 | __dsb( portSY_FULL_READ_WRITE ); |
rgrover1 | 0:8e57f3e9cc89 | 354 | __isb( portSY_FULL_READ_WRITE ); |
rgrover1 | 0:8e57f3e9cc89 | 355 | } |
rgrover1 | 0:8e57f3e9cc89 | 356 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 357 | |
rgrover1 | 0:8e57f3e9cc89 | 358 | void vPortEnterCritical( void ) |
rgrover1 | 0:8e57f3e9cc89 | 359 | { |
rgrover1 | 0:8e57f3e9cc89 | 360 | portDISABLE_INTERRUPTS(); |
rgrover1 | 0:8e57f3e9cc89 | 361 | uxCriticalNesting++; |
rgrover1 | 0:8e57f3e9cc89 | 362 | __dsb( portSY_FULL_READ_WRITE ); |
rgrover1 | 0:8e57f3e9cc89 | 363 | __isb( portSY_FULL_READ_WRITE ); |
rgrover1 | 0:8e57f3e9cc89 | 364 | } |
rgrover1 | 0:8e57f3e9cc89 | 365 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 366 | |
rgrover1 | 0:8e57f3e9cc89 | 367 | void vPortExitCritical( void ) |
rgrover1 | 0:8e57f3e9cc89 | 368 | { |
rgrover1 | 0:8e57f3e9cc89 | 369 | uxCriticalNesting--; |
rgrover1 | 0:8e57f3e9cc89 | 370 | if( uxCriticalNesting == 0 ) |
rgrover1 | 0:8e57f3e9cc89 | 371 | { |
rgrover1 | 0:8e57f3e9cc89 | 372 | portENABLE_INTERRUPTS(); |
rgrover1 | 0:8e57f3e9cc89 | 373 | } |
rgrover1 | 0:8e57f3e9cc89 | 374 | } |
rgrover1 | 0:8e57f3e9cc89 | 375 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 376 | |
rgrover1 | 0:8e57f3e9cc89 | 377 | __asm void xPortPendSVHandler( void ) |
rgrover1 | 0:8e57f3e9cc89 | 378 | { |
rgrover1 | 0:8e57f3e9cc89 | 379 | extern uxCriticalNesting; |
rgrover1 | 0:8e57f3e9cc89 | 380 | extern pxCurrentTCB; |
rgrover1 | 0:8e57f3e9cc89 | 381 | extern vTaskSwitchContext; |
rgrover1 | 0:8e57f3e9cc89 | 382 | |
rgrover1 | 0:8e57f3e9cc89 | 383 | PRESERVE8 |
rgrover1 | 0:8e57f3e9cc89 | 384 | |
rgrover1 | 0:8e57f3e9cc89 | 385 | mrs r0, psp |
rgrover1 | 0:8e57f3e9cc89 | 386 | |
rgrover1 | 0:8e57f3e9cc89 | 387 | ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */ |
rgrover1 | 0:8e57f3e9cc89 | 388 | ldr r2, [r3] |
rgrover1 | 0:8e57f3e9cc89 | 389 | |
rgrover1 | 0:8e57f3e9cc89 | 390 | stmdb r0!, {r4-r11} /* Save the remaining registers. */ |
rgrover1 | 0:8e57f3e9cc89 | 391 | str r0, [r2] /* Save the new top of stack into the first member of the TCB. */ |
rgrover1 | 0:8e57f3e9cc89 | 392 | |
rgrover1 | 0:8e57f3e9cc89 | 393 | stmdb sp!, {r3, r14} |
rgrover1 | 0:8e57f3e9cc89 | 394 | mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY |
rgrover1 | 0:8e57f3e9cc89 | 395 | msr basepri, r0 |
rgrover1 | 0:8e57f3e9cc89 | 396 | bl vTaskSwitchContext |
rgrover1 | 0:8e57f3e9cc89 | 397 | mov r0, #0 |
rgrover1 | 0:8e57f3e9cc89 | 398 | msr basepri, r0 |
rgrover1 | 0:8e57f3e9cc89 | 399 | ldmia sp!, {r3, r14} |
rgrover1 | 0:8e57f3e9cc89 | 400 | |
rgrover1 | 0:8e57f3e9cc89 | 401 | ldr r1, [r3] |
rgrover1 | 0:8e57f3e9cc89 | 402 | ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ |
rgrover1 | 0:8e57f3e9cc89 | 403 | ldmia r0!, {r4-r11} /* Pop the registers and the critical nesting count. */ |
rgrover1 | 0:8e57f3e9cc89 | 404 | msr psp, r0 |
rgrover1 | 0:8e57f3e9cc89 | 405 | bx r14 |
rgrover1 | 0:8e57f3e9cc89 | 406 | nop |
rgrover1 | 0:8e57f3e9cc89 | 407 | } |
rgrover1 | 0:8e57f3e9cc89 | 408 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 409 | |
rgrover1 | 0:8e57f3e9cc89 | 410 | void xPortSysTickHandler( void ) |
rgrover1 | 0:8e57f3e9cc89 | 411 | { |
rgrover1 | 0:8e57f3e9cc89 | 412 | /* The SysTick runs at the lowest interrupt priority, so when this interrupt |
rgrover1 | 0:8e57f3e9cc89 | 413 | executes all interrupts must be unmasked. There is therefore no need to |
rgrover1 | 0:8e57f3e9cc89 | 414 | save and then restore the interrupt mask value as its value is already |
rgrover1 | 0:8e57f3e9cc89 | 415 | known. */ |
rgrover1 | 0:8e57f3e9cc89 | 416 | ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); |
rgrover1 | 0:8e57f3e9cc89 | 417 | { |
rgrover1 | 0:8e57f3e9cc89 | 418 | /* Increment the RTOS tick. */ |
rgrover1 | 0:8e57f3e9cc89 | 419 | if( xTaskIncrementTick() != pdFALSE ) |
rgrover1 | 0:8e57f3e9cc89 | 420 | { |
rgrover1 | 0:8e57f3e9cc89 | 421 | /* A context switch is required. Context switching is performed in |
rgrover1 | 0:8e57f3e9cc89 | 422 | the PendSV interrupt. Pend the PendSV interrupt. */ |
rgrover1 | 0:8e57f3e9cc89 | 423 | portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 424 | } |
rgrover1 | 0:8e57f3e9cc89 | 425 | } |
rgrover1 | 0:8e57f3e9cc89 | 426 | portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); |
rgrover1 | 0:8e57f3e9cc89 | 427 | } |
rgrover1 | 0:8e57f3e9cc89 | 428 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 429 | |
rgrover1 | 0:8e57f3e9cc89 | 430 | #if configUSE_TICKLESS_IDLE == 1 |
rgrover1 | 0:8e57f3e9cc89 | 431 | |
rgrover1 | 0:8e57f3e9cc89 | 432 | __weak void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime ) |
rgrover1 | 0:8e57f3e9cc89 | 433 | { |
rgrover1 | 0:8e57f3e9cc89 | 434 | unsigned long ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; |
rgrover1 | 0:8e57f3e9cc89 | 435 | portTickType xModifiableIdleTime; |
rgrover1 | 0:8e57f3e9cc89 | 436 | |
rgrover1 | 0:8e57f3e9cc89 | 437 | /* Make sure the SysTick reload value does not overflow the counter. */ |
rgrover1 | 0:8e57f3e9cc89 | 438 | if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) |
rgrover1 | 0:8e57f3e9cc89 | 439 | { |
rgrover1 | 0:8e57f3e9cc89 | 440 | xExpectedIdleTime = xMaximumPossibleSuppressedTicks; |
rgrover1 | 0:8e57f3e9cc89 | 441 | } |
rgrover1 | 0:8e57f3e9cc89 | 442 | |
rgrover1 | 0:8e57f3e9cc89 | 443 | /* Stop the SysTick momentarily. The time the SysTick is stopped for |
rgrover1 | 0:8e57f3e9cc89 | 444 | is accounted for as best it can be, but using the tickless mode will |
rgrover1 | 0:8e57f3e9cc89 | 445 | inevitably result in some tiny drift of the time maintained by the |
rgrover1 | 0:8e57f3e9cc89 | 446 | kernel with respect to calendar time. */ |
rgrover1 | 0:8e57f3e9cc89 | 447 | portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 448 | |
rgrover1 | 0:8e57f3e9cc89 | 449 | /* Calculate the reload value required to wait xExpectedIdleTime |
rgrover1 | 0:8e57f3e9cc89 | 450 | tick periods. -1 is used because this code will execute part way |
rgrover1 | 0:8e57f3e9cc89 | 451 | through one of the tick periods. */ |
rgrover1 | 0:8e57f3e9cc89 | 452 | ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); |
rgrover1 | 0:8e57f3e9cc89 | 453 | if( ulReloadValue > ulStoppedTimerCompensation ) |
rgrover1 | 0:8e57f3e9cc89 | 454 | { |
rgrover1 | 0:8e57f3e9cc89 | 455 | ulReloadValue -= ulStoppedTimerCompensation; |
rgrover1 | 0:8e57f3e9cc89 | 456 | } |
rgrover1 | 0:8e57f3e9cc89 | 457 | |
rgrover1 | 0:8e57f3e9cc89 | 458 | /* Enter a critical section but don't use the taskENTER_CRITICAL() |
rgrover1 | 0:8e57f3e9cc89 | 459 | method as that will mask interrupts that should exit sleep mode. */ |
rgrover1 | 0:8e57f3e9cc89 | 460 | __disable_irq(); |
rgrover1 | 0:8e57f3e9cc89 | 461 | |
rgrover1 | 0:8e57f3e9cc89 | 462 | /* If a context switch is pending or a task is waiting for the scheduler |
rgrover1 | 0:8e57f3e9cc89 | 463 | to be unsuspended then abandon the low power entry. */ |
rgrover1 | 0:8e57f3e9cc89 | 464 | if( eTaskConfirmSleepModeStatus() == eAbortSleep ) |
rgrover1 | 0:8e57f3e9cc89 | 465 | { |
rgrover1 | 0:8e57f3e9cc89 | 466 | /* Restart from whatever is left in the count register to complete |
rgrover1 | 0:8e57f3e9cc89 | 467 | this tick period. */ |
rgrover1 | 0:8e57f3e9cc89 | 468 | portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; |
rgrover1 | 0:8e57f3e9cc89 | 469 | |
rgrover1 | 0:8e57f3e9cc89 | 470 | /* Restart SysTick. */ |
rgrover1 | 0:8e57f3e9cc89 | 471 | portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 472 | |
rgrover1 | 0:8e57f3e9cc89 | 473 | /* Reset the reload register to the value required for normal tick |
rgrover1 | 0:8e57f3e9cc89 | 474 | periods. */ |
rgrover1 | 0:8e57f3e9cc89 | 475 | portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; |
rgrover1 | 0:8e57f3e9cc89 | 476 | |
rgrover1 | 0:8e57f3e9cc89 | 477 | /* Re-enable interrupts - see comments above __disable_irq() call |
rgrover1 | 0:8e57f3e9cc89 | 478 | above. */ |
rgrover1 | 0:8e57f3e9cc89 | 479 | __enable_irq(); |
rgrover1 | 0:8e57f3e9cc89 | 480 | } |
rgrover1 | 0:8e57f3e9cc89 | 481 | else |
rgrover1 | 0:8e57f3e9cc89 | 482 | { |
rgrover1 | 0:8e57f3e9cc89 | 483 | /* Set the new reload value. */ |
rgrover1 | 0:8e57f3e9cc89 | 484 | portNVIC_SYSTICK_LOAD_REG = ulReloadValue; |
rgrover1 | 0:8e57f3e9cc89 | 485 | |
rgrover1 | 0:8e57f3e9cc89 | 486 | /* Clear the SysTick count flag and set the count value back to |
rgrover1 | 0:8e57f3e9cc89 | 487 | zero. */ |
rgrover1 | 0:8e57f3e9cc89 | 488 | portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; |
rgrover1 | 0:8e57f3e9cc89 | 489 | |
rgrover1 | 0:8e57f3e9cc89 | 490 | /* Restart SysTick. */ |
rgrover1 | 0:8e57f3e9cc89 | 491 | portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 492 | |
rgrover1 | 0:8e57f3e9cc89 | 493 | /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can |
rgrover1 | 0:8e57f3e9cc89 | 494 | set its parameter to 0 to indicate that its implementation contains |
rgrover1 | 0:8e57f3e9cc89 | 495 | its own wait for interrupt or wait for event instruction, and so wfi |
rgrover1 | 0:8e57f3e9cc89 | 496 | should not be executed again. However, the original expected idle |
rgrover1 | 0:8e57f3e9cc89 | 497 | time variable must remain unmodified, so a copy is taken. */ |
rgrover1 | 0:8e57f3e9cc89 | 498 | xModifiableIdleTime = xExpectedIdleTime; |
rgrover1 | 0:8e57f3e9cc89 | 499 | configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); |
rgrover1 | 0:8e57f3e9cc89 | 500 | if( xModifiableIdleTime > 0 ) |
rgrover1 | 0:8e57f3e9cc89 | 501 | { |
rgrover1 | 0:8e57f3e9cc89 | 502 | __dsb( portSY_FULL_READ_WRITE ); |
rgrover1 | 0:8e57f3e9cc89 | 503 | __wfi(); |
rgrover1 | 0:8e57f3e9cc89 | 504 | __isb( portSY_FULL_READ_WRITE ); |
rgrover1 | 0:8e57f3e9cc89 | 505 | } |
rgrover1 | 0:8e57f3e9cc89 | 506 | configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); |
rgrover1 | 0:8e57f3e9cc89 | 507 | |
rgrover1 | 0:8e57f3e9cc89 | 508 | /* Stop SysTick. Again, the time the SysTick is stopped for is |
rgrover1 | 0:8e57f3e9cc89 | 509 | accounted for as best it can be, but using the tickless mode will |
rgrover1 | 0:8e57f3e9cc89 | 510 | inevitably result in some tiny drift of the time maintained by the |
rgrover1 | 0:8e57f3e9cc89 | 511 | kernel with respect to calendar time. */ |
rgrover1 | 0:8e57f3e9cc89 | 512 | portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 513 | |
rgrover1 | 0:8e57f3e9cc89 | 514 | /* Re-enable interrupts - see comments above __disable_irq() call |
rgrover1 | 0:8e57f3e9cc89 | 515 | above. */ |
rgrover1 | 0:8e57f3e9cc89 | 516 | __enable_irq(); |
rgrover1 | 0:8e57f3e9cc89 | 517 | |
rgrover1 | 0:8e57f3e9cc89 | 518 | if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) |
rgrover1 | 0:8e57f3e9cc89 | 519 | { |
rgrover1 | 0:8e57f3e9cc89 | 520 | unsigned long ulCalculatedLoadValue; |
rgrover1 | 0:8e57f3e9cc89 | 521 | |
rgrover1 | 0:8e57f3e9cc89 | 522 | /* The tick interrupt has already executed, and the SysTick |
rgrover1 | 0:8e57f3e9cc89 | 523 | count reloaded with ulReloadValue. Reset the |
rgrover1 | 0:8e57f3e9cc89 | 524 | portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick |
rgrover1 | 0:8e57f3e9cc89 | 525 | period. */ |
rgrover1 | 0:8e57f3e9cc89 | 526 | ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); |
rgrover1 | 0:8e57f3e9cc89 | 527 | |
rgrover1 | 0:8e57f3e9cc89 | 528 | /* Don't allow a tiny value, or values that have somehow |
rgrover1 | 0:8e57f3e9cc89 | 529 | underflowed because the post sleep hook did something |
rgrover1 | 0:8e57f3e9cc89 | 530 | that took too long. */ |
rgrover1 | 0:8e57f3e9cc89 | 531 | if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) |
rgrover1 | 0:8e57f3e9cc89 | 532 | { |
rgrover1 | 0:8e57f3e9cc89 | 533 | ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); |
rgrover1 | 0:8e57f3e9cc89 | 534 | } |
rgrover1 | 0:8e57f3e9cc89 | 535 | |
rgrover1 | 0:8e57f3e9cc89 | 536 | portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; |
rgrover1 | 0:8e57f3e9cc89 | 537 | |
rgrover1 | 0:8e57f3e9cc89 | 538 | /* The tick interrupt handler will already have pended the tick |
rgrover1 | 0:8e57f3e9cc89 | 539 | processing in the kernel. As the pending tick will be |
rgrover1 | 0:8e57f3e9cc89 | 540 | processed as soon as this function exits, the tick value |
rgrover1 | 0:8e57f3e9cc89 | 541 | maintained by the tick is stepped forward by one less than the |
rgrover1 | 0:8e57f3e9cc89 | 542 | time spent waiting. */ |
rgrover1 | 0:8e57f3e9cc89 | 543 | ulCompleteTickPeriods = xExpectedIdleTime - 1UL; |
rgrover1 | 0:8e57f3e9cc89 | 544 | } |
rgrover1 | 0:8e57f3e9cc89 | 545 | else |
rgrover1 | 0:8e57f3e9cc89 | 546 | { |
rgrover1 | 0:8e57f3e9cc89 | 547 | /* Something other than the tick interrupt ended the sleep. |
rgrover1 | 0:8e57f3e9cc89 | 548 | Work out how long the sleep lasted rounded to complete tick |
rgrover1 | 0:8e57f3e9cc89 | 549 | periods (not the ulReload value which accounted for part |
rgrover1 | 0:8e57f3e9cc89 | 550 | ticks). */ |
rgrover1 | 0:8e57f3e9cc89 | 551 | ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; |
rgrover1 | 0:8e57f3e9cc89 | 552 | |
rgrover1 | 0:8e57f3e9cc89 | 553 | /* How many complete tick periods passed while the processor |
rgrover1 | 0:8e57f3e9cc89 | 554 | was waiting? */ |
rgrover1 | 0:8e57f3e9cc89 | 555 | ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; |
rgrover1 | 0:8e57f3e9cc89 | 556 | |
rgrover1 | 0:8e57f3e9cc89 | 557 | /* The reload value is set to whatever fraction of a single tick |
rgrover1 | 0:8e57f3e9cc89 | 558 | period remains. */ |
rgrover1 | 0:8e57f3e9cc89 | 559 | portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; |
rgrover1 | 0:8e57f3e9cc89 | 560 | } |
rgrover1 | 0:8e57f3e9cc89 | 561 | |
rgrover1 | 0:8e57f3e9cc89 | 562 | /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG |
rgrover1 | 0:8e57f3e9cc89 | 563 | again, then set portNVIC_SYSTICK_LOAD_REG back to its standard |
rgrover1 | 0:8e57f3e9cc89 | 564 | value. The critical section is used to ensure the tick interrupt |
rgrover1 | 0:8e57f3e9cc89 | 565 | can only execute once in the case that the reload register is near |
rgrover1 | 0:8e57f3e9cc89 | 566 | zero. */ |
rgrover1 | 0:8e57f3e9cc89 | 567 | portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; |
rgrover1 | 0:8e57f3e9cc89 | 568 | portENTER_CRITICAL(); |
rgrover1 | 0:8e57f3e9cc89 | 569 | { |
rgrover1 | 0:8e57f3e9cc89 | 570 | portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 571 | vTaskStepTick( ulCompleteTickPeriods ); |
rgrover1 | 0:8e57f3e9cc89 | 572 | portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; |
rgrover1 | 0:8e57f3e9cc89 | 573 | } |
rgrover1 | 0:8e57f3e9cc89 | 574 | portEXIT_CRITICAL(); |
rgrover1 | 0:8e57f3e9cc89 | 575 | } |
rgrover1 | 0:8e57f3e9cc89 | 576 | } |
rgrover1 | 0:8e57f3e9cc89 | 577 | |
rgrover1 | 0:8e57f3e9cc89 | 578 | #endif /* #if configUSE_TICKLESS_IDLE */ |
rgrover1 | 0:8e57f3e9cc89 | 579 | |
rgrover1 | 0:8e57f3e9cc89 | 580 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 581 | |
rgrover1 | 0:8e57f3e9cc89 | 582 | /* |
rgrover1 | 0:8e57f3e9cc89 | 583 | * Setup the SysTick timer to generate the tick interrupts at the required |
rgrover1 | 0:8e57f3e9cc89 | 584 | * frequency. |
rgrover1 | 0:8e57f3e9cc89 | 585 | */ |
rgrover1 | 0:8e57f3e9cc89 | 586 | #if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 |
rgrover1 | 0:8e57f3e9cc89 | 587 | |
rgrover1 | 0:8e57f3e9cc89 | 588 | void vPortSetupTimerInterrupt( void ) |
rgrover1 | 0:8e57f3e9cc89 | 589 | { |
rgrover1 | 0:8e57f3e9cc89 | 590 | /* Calculate the constants required to configure the tick interrupt. */ |
rgrover1 | 0:8e57f3e9cc89 | 591 | #if configUSE_TICKLESS_IDLE == 1 |
rgrover1 | 0:8e57f3e9cc89 | 592 | { |
rgrover1 | 0:8e57f3e9cc89 | 593 | ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); |
rgrover1 | 0:8e57f3e9cc89 | 594 | xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; |
rgrover1 | 0:8e57f3e9cc89 | 595 | ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); |
rgrover1 | 0:8e57f3e9cc89 | 596 | } |
rgrover1 | 0:8e57f3e9cc89 | 597 | #endif /* configUSE_TICKLESS_IDLE */ |
rgrover1 | 0:8e57f3e9cc89 | 598 | |
rgrover1 | 0:8e57f3e9cc89 | 599 | /* Configure SysTick to interrupt at the requested rate. */ |
rgrover1 | 0:8e57f3e9cc89 | 600 | portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;; |
rgrover1 | 0:8e57f3e9cc89 | 601 | portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; |
rgrover1 | 0:8e57f3e9cc89 | 602 | } |
rgrover1 | 0:8e57f3e9cc89 | 603 | |
rgrover1 | 0:8e57f3e9cc89 | 604 | #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ |
rgrover1 | 0:8e57f3e9cc89 | 605 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 606 | |
rgrover1 | 0:8e57f3e9cc89 | 607 | __asm unsigned long ulPortSetInterruptMask( void ) |
rgrover1 | 0:8e57f3e9cc89 | 608 | { |
rgrover1 | 0:8e57f3e9cc89 | 609 | PRESERVE8 |
rgrover1 | 0:8e57f3e9cc89 | 610 | |
rgrover1 | 0:8e57f3e9cc89 | 611 | mrs r0, basepri |
rgrover1 | 0:8e57f3e9cc89 | 612 | mov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITY |
rgrover1 | 0:8e57f3e9cc89 | 613 | msr basepri, r1 |
rgrover1 | 0:8e57f3e9cc89 | 614 | bx r14 |
rgrover1 | 0:8e57f3e9cc89 | 615 | } |
rgrover1 | 0:8e57f3e9cc89 | 616 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 617 | |
rgrover1 | 0:8e57f3e9cc89 | 618 | __asm void vPortClearInterruptMask( unsigned long ulNewMask ) |
rgrover1 | 0:8e57f3e9cc89 | 619 | { |
rgrover1 | 0:8e57f3e9cc89 | 620 | PRESERVE8 |
rgrover1 | 0:8e57f3e9cc89 | 621 | |
rgrover1 | 0:8e57f3e9cc89 | 622 | msr basepri, r0 |
rgrover1 | 0:8e57f3e9cc89 | 623 | bx r14 |
rgrover1 | 0:8e57f3e9cc89 | 624 | } |
rgrover1 | 0:8e57f3e9cc89 | 625 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 626 | |
rgrover1 | 0:8e57f3e9cc89 | 627 | __asm unsigned long vPortGetIPSR( void ) |
rgrover1 | 0:8e57f3e9cc89 | 628 | { |
rgrover1 | 0:8e57f3e9cc89 | 629 | PRESERVE8 |
rgrover1 | 0:8e57f3e9cc89 | 630 | |
rgrover1 | 0:8e57f3e9cc89 | 631 | mrs r0, ipsr |
rgrover1 | 0:8e57f3e9cc89 | 632 | bx r14 |
rgrover1 | 0:8e57f3e9cc89 | 633 | } |
rgrover1 | 0:8e57f3e9cc89 | 634 | /*-----------------------------------------------------------*/ |
rgrover1 | 0:8e57f3e9cc89 | 635 | |
rgrover1 | 0:8e57f3e9cc89 | 636 | #if( configASSERT_DEFINED == 1 ) |
rgrover1 | 0:8e57f3e9cc89 | 637 | |
rgrover1 | 0:8e57f3e9cc89 | 638 | void vPortValidateInterruptPriority( void ) |
rgrover1 | 0:8e57f3e9cc89 | 639 | { |
rgrover1 | 0:8e57f3e9cc89 | 640 | unsigned long ulCurrentInterrupt; |
rgrover1 | 0:8e57f3e9cc89 | 641 | unsigned char ucCurrentPriority; |
rgrover1 | 0:8e57f3e9cc89 | 642 | |
rgrover1 | 0:8e57f3e9cc89 | 643 | /* Obtain the number of the currently executing interrupt. */ |
rgrover1 | 0:8e57f3e9cc89 | 644 | ulCurrentInterrupt = vPortGetIPSR(); |
rgrover1 | 0:8e57f3e9cc89 | 645 | |
rgrover1 | 0:8e57f3e9cc89 | 646 | /* Is the interrupt number a user defined interrupt? */ |
rgrover1 | 0:8e57f3e9cc89 | 647 | if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) |
rgrover1 | 0:8e57f3e9cc89 | 648 | { |
rgrover1 | 0:8e57f3e9cc89 | 649 | /* Look up the interrupt's priority. */ |
rgrover1 | 0:8e57f3e9cc89 | 650 | ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; |
rgrover1 | 0:8e57f3e9cc89 | 651 | |
rgrover1 | 0:8e57f3e9cc89 | 652 | /* The following assertion will fail if a service routine (ISR) for |
rgrover1 | 0:8e57f3e9cc89 | 653 | an interrupt that has been assigned a priority above |
rgrover1 | 0:8e57f3e9cc89 | 654 | configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API |
rgrover1 | 0:8e57f3e9cc89 | 655 | function. ISR safe FreeRTOS API functions must *only* be called |
rgrover1 | 0:8e57f3e9cc89 | 656 | from interrupts that have been assigned a priority at or below |
rgrover1 | 0:8e57f3e9cc89 | 657 | configMAX_SYSCALL_INTERRUPT_PRIORITY. |
rgrover1 | 0:8e57f3e9cc89 | 658 | |
rgrover1 | 0:8e57f3e9cc89 | 659 | Numerically low interrupt priority numbers represent logically high |
rgrover1 | 0:8e57f3e9cc89 | 660 | interrupt priorities, therefore the priority of the interrupt must |
rgrover1 | 0:8e57f3e9cc89 | 661 | be set to a value equal to or numerically *higher* than |
rgrover1 | 0:8e57f3e9cc89 | 662 | configMAX_SYSCALL_INTERRUPT_PRIORITY. |
rgrover1 | 0:8e57f3e9cc89 | 663 | |
rgrover1 | 0:8e57f3e9cc89 | 664 | Interrupts that use the FreeRTOS API must not be left at their |
rgrover1 | 0:8e57f3e9cc89 | 665 | default priority of zero as that is the highest possible priority, |
rgrover1 | 0:8e57f3e9cc89 | 666 | which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, |
rgrover1 | 0:8e57f3e9cc89 | 667 | and therefore also guaranteed to be invalid. |
rgrover1 | 0:8e57f3e9cc89 | 668 | |
rgrover1 | 0:8e57f3e9cc89 | 669 | FreeRTOS maintains separate thread and ISR API functions to ensure |
rgrover1 | 0:8e57f3e9cc89 | 670 | interrupt entry is as fast and simple as possible. |
rgrover1 | 0:8e57f3e9cc89 | 671 | |
rgrover1 | 0:8e57f3e9cc89 | 672 | The following links provide detailed information: |
rgrover1 | 0:8e57f3e9cc89 | 673 | http://www.freertos.org/RTOS-Cortex-M3-M4.html |
rgrover1 | 0:8e57f3e9cc89 | 674 | http://www.freertos.org/FAQHelp.html */ |
rgrover1 | 0:8e57f3e9cc89 | 675 | configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); |
rgrover1 | 0:8e57f3e9cc89 | 676 | } |
rgrover1 | 0:8e57f3e9cc89 | 677 | |
rgrover1 | 0:8e57f3e9cc89 | 678 | /* Priority grouping: The interrupt controller (NVIC) allows the bits |
rgrover1 | 0:8e57f3e9cc89 | 679 | that define each interrupt's priority to be split between bits that |
rgrover1 | 0:8e57f3e9cc89 | 680 | define the interrupt's pre-emption priority bits and bits that define |
rgrover1 | 0:8e57f3e9cc89 | 681 | the interrupt's sub-priority. For simplicity all bits must be defined |
rgrover1 | 0:8e57f3e9cc89 | 682 | to be pre-emption priority bits. The following assertion will fail if |
rgrover1 | 0:8e57f3e9cc89 | 683 | this is not the case (if some bits represent a sub-priority). |
rgrover1 | 0:8e57f3e9cc89 | 684 | |
rgrover1 | 0:8e57f3e9cc89 | 685 | If the application only uses CMSIS libraries for interrupt |
rgrover1 | 0:8e57f3e9cc89 | 686 | configuration then the correct setting can be achieved on all Cortex-M |
rgrover1 | 0:8e57f3e9cc89 | 687 | devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the |
rgrover1 | 0:8e57f3e9cc89 | 688 | scheduler. Note however that some vendor specific peripheral libraries |
rgrover1 | 0:8e57f3e9cc89 | 689 | assume a non-zero priority group setting, in which cases using a value |
rgrover1 | 0:8e57f3e9cc89 | 690 | of zero will result in unpredicable behaviour. */ |
rgrover1 | 0:8e57f3e9cc89 | 691 | configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); |
rgrover1 | 0:8e57f3e9cc89 | 692 | } |
rgrover1 | 0:8e57f3e9cc89 | 693 | |
rgrover1 | 0:8e57f3e9cc89 | 694 | #endif /* configASSERT_DEFINED */ |
rgrover1 | 0:8e57f3e9cc89 | 695 | |
rgrover1 | 0:8e57f3e9cc89 | 696 |