36
36
#error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
37
37
#endif
38
38
39
- #ifndef configSYSTICK_CLOCK_HZ
40
- #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
41
- /* Ensure the SysTick is clocked at the same frequency as the core. */
42
- #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
43
- #else
44
-
45
- /* The way the SysTick is clocked is not modified in case it is not the same
46
- * as the core. */
47
- #define portNVIC_SYSTICK_CLK_BIT ( 0 )
48
- #endif
49
-
50
39
/* Constants required to manipulate the core. Registers first... */
51
40
#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
52
41
#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
53
42
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) )
54
43
#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
55
44
/* ...then bits in the registers. */
45
+ #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
56
46
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
57
47
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
58
48
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
59
49
#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL )
50
+ #define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL )
60
51
#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL )
61
52
62
53
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
84
75
/* A fiddle factor to estimate the number of SysTick counts that would have
85
76
* occurred while the SysTick counter is stopped during tickless idle
86
77
* calculations. */
87
- #define portMISSED_COUNTS_FACTOR ( 45UL )
78
+ #define portMISSED_COUNTS_FACTOR ( 94UL )
88
79
89
80
/* For strict compliance with the Cortex-M spec the task start address should
90
81
* have bit-0 clear, as it is loaded into the PC on exit from an ISR. */
91
82
#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
92
83
84
+ /* Let the user override the default SysTick clock rate. If defined by the
85
+ * user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the
86
+ * configuration register. */
87
+ #ifndef configSYSTICK_CLOCK_HZ
88
+ #define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ )
89
+ /* Ensure the SysTick is clocked at the same frequency as the core. */
90
+ #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( portNVIC_SYSTICK_CLK_BIT )
91
+ #else
92
+
93
+ /* Select the option to clock SysTick not at the same frequency as the core.
94
+ * The clock used is often a divided version of the core clock. */
95
+ #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 )
96
+ #endif
97
+
93
98
/*
94
99
* Setup the timer to generate the tick interrupts. The implementation in this
95
100
* file is weak to allow application writers to change the timer used to
@@ -354,7 +359,7 @@ void xPortSysTickHandler( void )
354
359
#pragma WEAK( vPortSuppressTicksAndSleep )
355
360
void vPortSuppressTicksAndSleep ( TickType_t xExpectedIdleTime )
356
361
{
357
- uint32_t ulReloadValue , ulCompleteTickPeriods , ulCompletedSysTickDecrements ;
362
+ uint32_t ulReloadValue , ulCompleteTickPeriods , ulCompletedSysTickDecrements , ulSysTickDecrementsLeft ;
358
363
TickType_t xModifiableIdleTime ;
359
364
360
365
/* Make sure the SysTick reload value does not overflow the counter. */
@@ -363,22 +368,6 @@ void xPortSysTickHandler( void )
363
368
xExpectedIdleTime = xMaximumPossibleSuppressedTicks ;
364
369
}
365
370
366
- /* Stop the SysTick momentarily. The time the SysTick is stopped for
367
- * is accounted for as best it can be, but using the tickless mode will
368
- * inevitably result in some tiny drift of the time maintained by the
369
- * kernel with respect to calendar time. */
370
- portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT ;
371
-
372
- /* Calculate the reload value required to wait xExpectedIdleTime
373
- * tick periods. -1 is used because this code will execute part way
374
- * through one of the tick periods. */
375
- ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
376
-
377
- if ( ulReloadValue > ulStoppedTimerCompensation )
378
- {
379
- ulReloadValue -= ulStoppedTimerCompensation ;
380
- }
381
-
382
371
/* Enter a critical section but don't use the taskENTER_CRITICAL()
383
372
* method as that will mask interrupts that should exit sleep mode. */
384
373
__asm( " cpsid i" );
@@ -389,23 +378,49 @@ void xPortSysTickHandler( void )
389
378
* to be unsuspended then abandon the low power entry. */
390
379
if ( eTaskConfirmSleepModeStatus () == eAbortSleep )
391
380
{
392
- /* Restart from whatever is left in the count register to complete
393
- * this tick period. */
394
- portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG ;
395
-
396
- /* Restart SysTick. */
397
- portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT ;
398
-
399
- /* Reset the reload register to the value required for normal tick
400
- * periods. */
401
- portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL ;
402
-
403
- /* Re-enable interrupts - see comments above __disable_interrupt()
404
- * call above. */
381
+ /* Re-enable interrupts - see comments above the cpsid instruction
382
+ * above. */
405
383
__asm( " cpsie i" );
406
384
}
407
385
else
408
386
{
387
+ /* Stop the SysTick momentarily. The time the SysTick is stopped for
388
+ * is accounted for as best it can be, but using the tickless mode will
389
+ * inevitably result in some tiny drift of the time maintained by the
390
+ * kernel with respect to calendar time. */
391
+ portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
392
+
393
+ /* Use the SysTick current-value register to determine the number of
394
+ * SysTick decrements remaining until the next tick interrupt. If the
395
+ * current-value register is zero, then there are actually
396
+ * ulTimerCountsForOneTick decrements remaining, not zero, because the
397
+ * SysTick requests the interrupt when decrementing from 1 to 0. */
398
+ ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG ;
399
+
400
+ if ( ulSysTickDecrementsLeft == 0 )
401
+ {
402
+ ulSysTickDecrementsLeft = ulTimerCountsForOneTick ;
403
+ }
404
+
405
+ /* Calculate the reload value required to wait xExpectedIdleTime
406
+ * tick periods. -1 is used because this code normally executes part
407
+ * way through the first tick period. But if the SysTick IRQ is now
408
+ * pending, then clear the IRQ, suppressing the first tick, and correct
409
+ * the reload value to reflect that the second tick period is already
410
+ * underway. The expected idle time is always at least two ticks. */
411
+ ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
412
+
413
+ if ( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 )
414
+ {
415
+ portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT ;
416
+ ulReloadValue -= ulTimerCountsForOneTick ;
417
+ }
418
+
419
+ if ( ulReloadValue > ulStoppedTimerCompensation )
420
+ {
421
+ ulReloadValue -= ulStoppedTimerCompensation ;
422
+ }
423
+
409
424
/* Set the new reload value. */
410
425
portNVIC_SYSTICK_LOAD_REG = ulReloadValue ;
411
426
@@ -434,8 +449,8 @@ void xPortSysTickHandler( void )
434
449
configPOST_SLEEP_PROCESSING ( xExpectedIdleTime );
435
450
436
451
/* Re-enable interrupts to allow the interrupt that brought the MCU
437
- * out of sleep mode to execute immediately. see comments above
438
- * __disable_interrupt() call above. */
452
+ * out of sleep mode to execute immediately. See comments above
453
+ * the cpsid instruction above. */
439
454
__asm( " cpsie i" );
440
455
__asm( " dsb" );
441
456
__asm( " isb" );
@@ -455,26 +470,22 @@ void xPortSysTickHandler( void )
455
470
* be, but using the tickless mode will inevitably result in some tiny
456
471
* drift of the time maintained by the kernel with respect to calendar
457
472
* time*/
458
- portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );
473
+ portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT );
459
474
460
- /* Determine if the SysTick clock has already counted to zero and
461
- * been set back to the current reload value (the reload back being
462
- * correct for the entire expected idle time) or if the SysTick is yet
463
- * to count to zero (in which case an interrupt other than the SysTick
464
- * must have brought the system out of sleep mode). */
475
+ /* Determine whether the SysTick has already counted to zero. */
465
476
if ( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
466
477
{
467
478
uint32_t ulCalculatedLoadValue ;
468
479
469
- /* The tick interrupt is already pending, and the SysTick count
470
- * reloaded with ulReloadValue. Reset the
471
- * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick
472
- * period. */
480
+ /* The tick interrupt ended the sleep (or is now pending), and
481
+ * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG
482
+ * with whatever remains of the new tick period. */
473
483
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
474
484
475
485
/* Don't allow a tiny value, or values that have somehow
476
486
* underflowed because the post sleep hook did something
477
- * that took too long. */
487
+ * that took too long or because the SysTick current-value register
488
+ * is zero. */
478
489
if ( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
479
490
{
480
491
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
@@ -489,11 +500,29 @@ void xPortSysTickHandler( void )
489
500
}
490
501
else
491
502
{
492
- /* Something other than the tick interrupt ended the sleep.
493
- * Work out how long the sleep lasted rounded to complete tick
503
+ /* Something other than the tick interrupt ended the sleep. */
504
+
505
+ /* Use the SysTick current-value register to determine the
506
+ * number of SysTick decrements remaining until the expected idle
507
+ * time would have ended. */
508
+ ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG ;
509
+ #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
510
+ {
511
+ /* If the SysTick is not using the core clock, the current-
512
+ * value register might still be zero here. In that case, the
513
+ * SysTick didn't load from the reload register, and there are
514
+ * ulReloadValue + 1 decrements remaining, not zero. */
515
+ if ( ulSysTickDecrementsLeft == 0 )
516
+ {
517
+ ulSysTickDecrementsLeft = ulReloadValue + 1UL ;
518
+ }
519
+ }
520
+ #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
521
+
522
+ /* Work out how long the sleep lasted rounded to complete tick
494
523
* periods (not the ulReload value which accounted for part
495
524
* ticks). */
496
- ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG ;
525
+ ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft ;
497
526
498
527
/* How many complete tick periods passed while the processor
499
528
* was waiting? */
@@ -504,13 +533,26 @@ void xPortSysTickHandler( void )
504
533
portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements ;
505
534
}
506
535
507
- /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
508
- * again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
509
- * value. */
536
+ /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again,
537
+ * then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If
538
+ * the SysTick is not using the core clock, temporarily configure it to
539
+ * use the core clock. This configuration forces the SysTick to load
540
+ * from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next
541
+ * cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready
542
+ * to receive the standard value immediately. */
510
543
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL ;
511
- portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT ;
512
- vTaskStepTick ( ulCompleteTickPeriods );
544
+ portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ;
513
545
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL ;
546
+ #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT )
547
+ {
548
+ /* The temporary usage of the core clock has served its purpose,
549
+ * as described above. Resume usage of the other clock. */
550
+ portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ;
551
+ }
552
+ #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */
553
+
554
+ /* Step the tick to account for any tick periods that elapsed. */
555
+ vTaskStepTick ( ulCompleteTickPeriods );
514
556
515
557
/* Exit with interrupts enabled. */
516
558
__asm( " cpsie i" );
@@ -542,7 +584,7 @@ void vPortSetupTimerInterrupt( void )
542
584
543
585
/* Configure SysTick to interrupt at the requested rate. */
544
586
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL ;
545
- portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
587
+ portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
546
588
}
547
589
/*-----------------------------------------------------------*/
548
590
0 commit comments