api-hal-timebase.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #include <api-hal-timebase.h>
  2. #include <api-hal-timebase-timer.h>
  3. #include <api-hal-power.h>
  4. #include <FreeRTOS.h>
  5. #include <cmsis_os.h>
  6. #define API_HAL_TIMEBASE_CLK_FREQUENCY 32768
  7. #define API_HAL_TIMEBASE_TICK_PER_SECOND 1024
  8. #define API_HAL_TIMEBASE_CLK_PER_TICK (API_HAL_TIMEBASE_CLK_FREQUENCY / API_HAL_TIMEBASE_TICK_PER_SECOND)
  9. #define API_HAL_TIMEBASE_TICK_PER_EPOCH (API_HAL_TIMEBASE_TIMER_MAX / API_HAL_TIMEBASE_CLK_PER_TICK)
  10. #define API_HAL_TIMEBASE_MAX_SLEEP (API_HAL_TIMEBASE_TICK_PER_EPOCH - 1)
  11. typedef struct {
  12. // Sleep control
  13. volatile uint16_t insomnia;
  14. // Tick counters
  15. volatile uint32_t in_sleep;
  16. volatile uint32_t in_awake;
  17. // Error counters
  18. volatile uint32_t sleep_error;
  19. volatile uint32_t awake_error;
  20. } ApiHalTimbase;
  21. ApiHalTimbase api_hal_timebase = {
  22. .insomnia = 0,
  23. .in_sleep = 0,
  24. .in_awake = 0,
  25. .sleep_error = 0,
  26. .awake_error = 0,
  27. };
  28. void api_hal_timebase_init() {
  29. api_hal_timebase_timer_init();
  30. LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
  31. LL_LPTIM_EnableIT_CMPM(API_HAL_TIMEBASE_TIMER);
  32. LL_LPTIM_EnableIT_ARRM(API_HAL_TIMEBASE_TIMER);
  33. LL_LPTIM_SetAutoReload(API_HAL_TIMEBASE_TIMER, API_HAL_TIMEBASE_TIMER_MAX);
  34. LL_LPTIM_SetCompare(API_HAL_TIMEBASE_TIMER, API_HAL_TIMEBASE_CLK_PER_TICK);
  35. LL_LPTIM_StartCounter(API_HAL_TIMEBASE_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
  36. }
  37. uint16_t api_hal_timebase_insomnia_level() {
  38. return api_hal_timebase.insomnia;
  39. }
  40. void api_hal_timebase_insomnia_enter() {
  41. api_hal_timebase.insomnia++;
  42. }
  43. void api_hal_timebase_insomnia_exit() {
  44. api_hal_timebase.insomnia--;
  45. }
  46. void LPTIM2_IRQHandler(void) {
  47. // Autoreload
  48. const bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_TIMEBASE_TIMER);
  49. if(arrm_flag) {
  50. LL_LPTIM_ClearFLAG_ARRM(API_HAL_TIMEBASE_TIMER);
  51. }
  52. if(LL_LPTIM_IsActiveFlag_CMPM(API_HAL_TIMEBASE_TIMER)) {
  53. LL_LPTIM_ClearFLAG_CMPM(API_HAL_TIMEBASE_TIMER);
  54. // Store important value
  55. uint16_t cnt = api_hal_timebase_timer_get_cnt();
  56. uint16_t cmp = api_hal_timebase_timer_get_cmp();
  57. uint16_t current_tick = cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
  58. uint16_t compare_tick = cmp / API_HAL_TIMEBASE_CLK_PER_TICK;
  59. // Calculate error
  60. // happens when HAL or other high priority IRQ takes our time
  61. int32_t error = (int32_t)compare_tick - current_tick;
  62. api_hal_timebase.awake_error += ((error>0) ? error : -error);
  63. // Calculate and set next tick
  64. uint16_t next_tick = current_tick + 1;
  65. api_hal_timebase_timer_set_cmp(next_tick * API_HAL_TIMEBASE_CLK_PER_TICK);
  66. // Notify OS
  67. api_hal_timebase.in_awake ++;
  68. if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
  69. xPortSysTickHandler();
  70. }
  71. }
  72. }
  73. static inline uint32_t api_hal_timebase_sleep(TickType_t expected_idle_ticks) {
  74. // Store important value before going to sleep
  75. const uint16_t before_cnt = api_hal_timebase_timer_get_cnt();
  76. const uint16_t before_tick = before_cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
  77. // Calculate and set next wakeup compare value
  78. const uint16_t expected_cnt = (before_tick + expected_idle_ticks - 2) * API_HAL_TIMEBASE_CLK_PER_TICK;
  79. api_hal_timebase_timer_set_cmp(expected_cnt);
  80. HAL_SuspendTick();
  81. // Go to stop2 mode
  82. api_hal_power_deep_sleep();
  83. HAL_ResumeTick();
  84. // Spin till we are in timer safe zone
  85. while(!api_hal_timebase_timer_is_safe()) {}
  86. // Store current counter value, calculate current tick
  87. const uint16_t after_cnt = api_hal_timebase_timer_get_cnt();
  88. const uint16_t after_tick = after_cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
  89. // Store and clear interrupt flags
  90. // we don't want handler to be called after renabling IRQ
  91. bool cmpm_flag = LL_LPTIM_IsActiveFlag_CMPM(API_HAL_TIMEBASE_TIMER);
  92. if (cmpm_flag) LL_LPTIM_ClearFLAG_CMPM(API_HAL_TIMEBASE_TIMER);
  93. bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_TIMEBASE_TIMER);
  94. if (arrm_flag) LL_LPTIM_ClearFLAG_ARRM(API_HAL_TIMEBASE_TIMER);
  95. // Calculate and set next wakeup compare value
  96. const uint16_t next_cmp = (after_tick + 1) * API_HAL_TIMEBASE_CLK_PER_TICK;
  97. api_hal_timebase_timer_set_cmp(next_cmp);
  98. // Calculate ticks count spent in sleep and perform sanity checks
  99. int32_t completed_ticks = arrm_flag ? (int32_t)before_tick - after_tick : (int32_t)after_tick - before_tick;
  100. return completed_ticks;
  101. }
  102. void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
  103. if (!api_hal_power_deep_available() || api_hal_timebase.insomnia) {
  104. return;
  105. }
  106. // Limit mount of ticks to maximum that timer can count
  107. if (expected_idle_ticks > API_HAL_TIMEBASE_MAX_SLEEP) {
  108. expected_idle_ticks = API_HAL_TIMEBASE_MAX_SLEEP;
  109. }
  110. // Stop IRQ handling, no one should disturb us till we finish
  111. __disable_irq();
  112. // Confirm OS that sleep is still possible
  113. // And check if timer is in safe zone
  114. // (8 clocks till any IRQ event or ongoing synchronization)
  115. if (eTaskConfirmSleepModeStatus() == eAbortSleep
  116. || !api_hal_timebase_timer_is_safe()) {
  117. __enable_irq();
  118. return;
  119. }
  120. uint32_t completed_ticks = api_hal_timebase_sleep(expected_idle_ticks);
  121. assert(completed_ticks >= 0);
  122. // Reenable IRQ
  123. __enable_irq();
  124. // Notify system about time spent in sleep
  125. if (completed_ticks > 0) {
  126. api_hal_timebase.in_sleep += completed_ticks;
  127. if (completed_ticks > expected_idle_ticks) {
  128. // We are late, count error
  129. api_hal_timebase.sleep_error += (completed_ticks - expected_idle_ticks);
  130. // Freertos is not happy when we overleep
  131. // But we are not going to tell her
  132. vTaskStepTick(expected_idle_ticks);
  133. } else {
  134. vTaskStepTick(completed_ticks);
  135. }
  136. }
  137. }