Welcome to mirror list, hosted at ThFree Co, Russian Federation.

freertos_port.c « Src « Core « BLE_HeartRateFreeRTOS_ANCS « BLE « Applications « P-NUCLEO-WB55.Nucleo « Projects - github.com/Flipper-Zero/STM32CubeWB.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: fec7382b3f2f60b07bc0a7f967ffd25fc724a1c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * File Name          : freertos_port.c
 * Description        : Custom porting of FreeRTOS functionalities
 *
 ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "app_common.h"

#include "FreeRTOS.h"
#include "task.h"
#include "stm32_lpm.h"

/* Private typedef -----------------------------------------------------------*/
typedef struct
{
  uint32_t LpTimeLeftOnEntry;
  uint8_t LpTimerFreeRTOS_Id;
} LpTimerContext_t;

/* Private defines -----------------------------------------------------------*/
#ifndef configSYSTICK_CLOCK_HZ
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
/* Ensure the SysTick is clocked at the same frequency as the core. */
#define portNVIC_SYSTICK_CLK_BIT        ( 1UL << 2UL )
#else
/* The way the SysTick is clocked is not modified in case it is not the same
        as the core. */
#define portNVIC_SYSTICK_CLK_BIT        ( 0 )
#endif

#define CPU_CLOCK_KHZ   ( configCPU_CLOCK_HZ / 1000 )

/* Constants required to manipulate the core.  Registers first... */
#define portNVIC_SYSTICK_CTRL_REG               ( * ( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG               ( * ( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG      ( * ( ( volatile uint32_t * ) 0xe000e018 ) )
#define portNVIC_SYSTICK_INT_BIT                ( 1UL << 1UL )
#define portNVIC_SYSTICK_ENABLE_BIT             ( 1UL << 0UL )
#define portNVIC_SYSTICK_COUNT_FLAG_BIT         ( 1UL << 16UL )

/* Private macros ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/*
 * The number of SysTick increments that make up one tick period.
 */
#if ( CFG_LPM_SUPPORTED != 0)
static uint32_t ulTimerCountsForOneTick;
static LpTimerContext_t LpTimerContext;
#endif

/* Global variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
#if ( CFG_LPM_SUPPORTED != 0)
static void LpTimerCb( void );
static void LpTimerInit( void );
static void LpTimerStart( uint32_t time_to_sleep );
static void LpEnter( void );
static uint32_t LpGetElapsedTime( void );
void vPortSetupTimerInterrupt( void );
#endif
/* Functions Definition ------------------------------------------------------*/

/**
 * @brief Implement the tickless feature
 *
 *
 * @param: xExpectedIdleTime is given in number of FreeRTOS Ticks
 * @retval: None
 */
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
  /* If low power is not used, do not stop the SysTick and continue execution */
#if ( CFG_LPM_SUPPORTED != 0)
  /**
   * Although this is not documented as such, when xExpectedIdleTime = 0xFFFFFFFF = (~0),
   * it likely means the system may enter low power for ever ( from a FreeRTOS point of view ).
   * Otherwise, for a FreeRTOS tick set to 1ms, that would mean it is requested to wakeup in 8 years from now.
   * When the system may enter low power mode for ever, FreeRTOS is not really interested to maintain a
   * systick count and when the system exits from low power mode, there is no need to update the count with
   * the time spent in low power mode
   */
    uint32_t ulCompleteTickPeriods;

  /* Stop the SysTick  to avoid the interrupt to occur while in the critical section.
   * Otherwise, this will prevent the device to enter low power mode
   * At this time, an update of the systick will not be considered
   *
   */
  portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;

  /* Enter a critical section but don't use the taskENTER_CRITICAL()
        method as that will mask interrupts that should exit sleep mode. */
  __disable_irq();
  __DSB();
  __ISB();

  /* If a context switch is pending or a task is waiting for the scheduler
        to be unsuspended then abandon the low power entry. */
  if( eTaskConfirmSleepModeStatus() == eAbortSleep )
  {
    /* Restart SysTick. */
    portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

    /* Re-enable interrupts - see comments above __disable_interrupt()
                call above. */
    __enable_irq();
  }
  else
  {
    if (xExpectedIdleTime != (~0))
    {
      /* Remove one tick to wake up before the event occurs */
      xExpectedIdleTime--;
      /* Start the low power timer */
      LpTimerStart( xExpectedIdleTime );
    }

    /* Enter low power mode */
    LpEnter( );

    if (xExpectedIdleTime != (~0))
    {
      /**
       * Get the number of FreeRTOS ticks that has been suppressed
       * In the current implementation, this shall be kept in critical section
       * so that the timer server return the correct elapsed time
       */
      ulCompleteTickPeriods = LpGetElapsedTime( );
      vTaskStepTick( ulCompleteTickPeriods );
    }

    /* Restart SysTick */
    portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
    portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
    portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;

    /* Exit with interrUpts enabled. */
    __enable_irq();
  }
#endif
}

/*
 * Setup the systick timer to generate the tick interrupts at the required
 * frequency and initialize a low power timer
 * The current implementation is kept as close as possible to the default tickless
 * mode provided.
 * The systick is still used when there is no need to go in low power mode.
 * When the system needs to enter low power mode, the tick is suppressed and a low power timer
 * is used over that time
 * Note that in sleep mode, the system clock is still running and the default tickless implementation
 * using systick could have been kept.
 * However, as at that time, it is not yet known whereas the low power mode that will be used is stop mode or
 * sleep mode, it is easier and simpler to go with a low power timer as soon as the tick need to be
 * suppressed.
 */
#if ( CFG_LPM_SUPPORTED != 0)
void vPortSetupTimerInterrupt( void )
{
  LpTimerInit( );

  /* Calculate the constants required to configure the tick interrupt. */
  ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );

  /* Stop and clear the SysTick. */
  portNVIC_SYSTICK_CTRL_REG = 0UL;
  portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;

  /* Configure SysTick to interrupt at the requested rate. */
  portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
  portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
#endif

/**
 * @brief The current implementation uses the hw_timerserver to provide a low power timer
 *        This may be replaced by another low power timer.
 *
 * @param  None
 * @retval None
 */
#if ( CFG_LPM_SUPPORTED != 0)
static void LpTimerInit( void )
{
  ( void ) HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(LpTimerContext.LpTimerFreeRTOS_Id), hw_ts_SingleShot, LpTimerCb);

  return;
}
#endif

/**
 * @brief Low power timer callback
 *
 * @param  None
 * @retval None
 */
#if ( CFG_LPM_SUPPORTED != 0)
static void LpTimerCb( void )
{
  /**
   * Nothing to be done
   */

  return;
}
#endif
/**
 * @brief  Request to start a low power timer ( running is stop mode )
 *
 * @param  time_to_sleep : Number of FreeRTOS ticks
 * @retval None
 */
#if ( CFG_LPM_SUPPORTED != 0)
static void LpTimerStart( uint32_t time_to_sleep )
{
  /* Converts the number of FreeRTOS ticks into hw timer tick */
  if(time_to_sleep <=  0x10C6)
  {
    /**
     * ( time_to_sleep * 1000 * 1000 ) fit a 32bits word
     */
    time_to_sleep = (time_to_sleep * 1000 * 1000 );
    time_to_sleep = time_to_sleep / ( CFG_TS_TICK_VAL * configTICK_RATE_HZ );
  }
  else if(time_to_sleep <= 0x418937)
  {
    /**
     * ( time_to_sleep * 1000 ) fit a 32bits word
     */
    time_to_sleep = (time_to_sleep * 1000);
    time_to_sleep = time_to_sleep / ( CFG_TS_TICK_VAL * configTICK_RATE_HZ );
    if(time_to_sleep <= 0x418937)
    {
      /**
       * ( time_to_sleep * 1000 ) fit a 32bits word
       */
      time_to_sleep = (time_to_sleep * 1000);
    }
    else
    {
      time_to_sleep = (~0); /* Max value */
    }
  }
  else
  {
    time_to_sleep = time_to_sleep / ( CFG_TS_TICK_VAL * configTICK_RATE_HZ );
    if(time_to_sleep <= 0x10C6)
    {
      /**
       * ( time_to_sleep * 1000 * 1000 ) fit a 32bits word
       */
      time_to_sleep = (time_to_sleep * 1000 * 1000 );
    }
    else
    {
      time_to_sleep = (~0); /* Max value */
    }
  }

  HW_TS_Start(LpTimerContext.LpTimerFreeRTOS_Id, time_to_sleep);

  /**
   * There might be other timers already running in the timer server that may elapse
   * before this one.
   * Store how long before the next event so that on wakeup, it will be possible to calculate
   * how long the tick has been suppressed
   */
  LpTimerContext.LpTimeLeftOnEntry = HW_TS_RTC_ReadLeftTicksToCount( );

  return;
}
#endif

/**
 * @brief  Enter low power mode
 *
 * @param  None
 * @retval None
 */
#if ( CFG_LPM_SUPPORTED != 0)
static void LpEnter( void )
{
#if ( CFG_LPM_SUPPORTED == 1)
  UTIL_LPM_EnterLowPower();
#endif
  return;
}
#endif

/**
 * @brief  Read how long the tick has been suppressed
 *
 * @param  None
 * @retval The number of tick rate (FreeRTOS tick)
 */
#if ( CFG_LPM_SUPPORTED != 0)
static uint32_t LpGetElapsedTime( void )
{
  uint64_t val_ticks, time_us;

  time_us = (CFG_TS_TICK_VAL) * (uint64_t)(LpTimerContext.LpTimeLeftOnEntry - HW_TS_RTC_ReadLeftTicksToCount( ));
  
  val_ticks = time_us * configTICK_RATE_HZ;
  val_ticks = val_ticks / (1000 * 1000);
  
  /* add a tick if the time elapsed is above 50 % of a tick */
  if( (time_us % (portTICK_PERIOD_MS * 1000) > (portTICK_PERIOD_MS * 1000 / 2)) )
  {
    val_ticks++;
  }

  /**
   * The system may have been out from another reason than the timer
   * Stop the timer after the elapsed time is calculated other wise, HW_TS_RTC_ReadLeftTicksToCount()
   * may return 0xFFFF ( TIMER LIST EMPTY )
   * It does not hurt stopping a timer that exists but is not running.
   */
  HW_TS_Stop(LpTimerContext.LpTimerFreeRTOS_Id);

  return (uint32_t)val_ticks;
}
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/