logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

timer.c (4517B)


  1. #include <ch.h>
  2. #include "timer.h"
  3. static uint32_t ticks_offset = 0;
  4. static uint32_t last_ticks = 0;
  5. static uint32_t ms_offset = 0;
  6. static uint32_t saved_ms = 0;
  7. #if CH_CFG_ST_RESOLUTION < 32
  8. static uint32_t last_systime = 0;
  9. static uint32_t overflow = 0;
  10. #endif
  11. // Get the current system time in ticks as a 32-bit number.
  12. // This function must be called from within a system lock zone (so that it can safely use and update the static data).
  13. static inline uint32_t get_system_time_ticks(void) {
  14. uint32_t systime = (uint32_t)chVTGetSystemTimeX();
  15. #if CH_CFG_ST_RESOLUTION < 32
  16. // If the real system timer resolution is less than 32 bits, provide the missing bits by checking for the counter
  17. // overflow. For this to work, this function must be called at least once for every overflow of the system timer.
  18. // In the 16-bit case, the corresponding times are:
  19. // - CH_CFG_ST_FREQUENCY = 100000, overflow will occur every ~0.65 seconds
  20. // - CH_CFG_ST_FREQUENCY = 10000, overflow will occur every ~6.5 seconds
  21. // - CH_CFG_ST_FREQUENCY = 1000, overflow will occur every ~65 seconds
  22. if (systime < last_systime) {
  23. overflow += ((uint32_t)1) << CH_CFG_ST_RESOLUTION;
  24. }
  25. last_systime = systime;
  26. systime += overflow;
  27. #endif
  28. return systime;
  29. }
  30. #if CH_CFG_ST_RESOLUTION < 32
  31. static virtual_timer_t update_timer;
  32. // Update the system tick counter every half of the timer overflow period; this should keep the tick counter correct
  33. // even if something blocks timer interrupts for 1/2 of the timer overflow period.
  34. # define UPDATE_INTERVAL (((sysinterval_t)1) << (CH_CFG_ST_RESOLUTION - 1))
  35. // VT callback function to keep the overflow bits of the system tick counter updated.
  36. static void update_fn(struct ch_virtual_timer *timer, void *arg) {
  37. (void)arg;
  38. chSysLockFromISR();
  39. get_system_time_ticks();
  40. chVTSetI(&update_timer, UPDATE_INTERVAL, update_fn, NULL);
  41. chSysUnlockFromISR();
  42. }
  43. #endif
  44. // The highest multiple of CH_CFG_ST_FREQUENCY that fits into uint32_t. This number of ticks will necessarily
  45. // correspond to some integer number of seconds.
  46. #define OVERFLOW_ADJUST_TICKS ((uint32_t)((UINT32_MAX / CH_CFG_ST_FREQUENCY) * CH_CFG_ST_FREQUENCY))
  47. // The time in milliseconds which corresponds to OVERFLOW_ADJUST_TICKS ticks (this is a precise conversion, because
  48. // OVERFLOW_ADJUST_TICKS corresponds to an integer number of seconds).
  49. #define OVERFLOW_ADJUST_MS (TIME_I2MS(OVERFLOW_ADJUST_TICKS))
  50. void timer_init(void) {
  51. timer_clear();
  52. #if CH_CFG_ST_RESOLUTION < 32
  53. chVTObjectInit(&update_timer);
  54. chVTSet(&update_timer, UPDATE_INTERVAL, update_fn, NULL);
  55. #endif
  56. }
  57. void timer_clear(void) {
  58. chSysLock();
  59. ticks_offset = get_system_time_ticks();
  60. last_ticks = 0;
  61. ms_offset = 0;
  62. chSysUnlock();
  63. }
  64. __attribute__((weak)) void platform_timer_save_value(uint32_t value) {
  65. saved_ms = value;
  66. }
  67. __attribute__((weak)) uint32_t platform_timer_restore_value(void) {
  68. return saved_ms;
  69. }
  70. void timer_restore(void) {
  71. chSysLock();
  72. ticks_offset = get_system_time_ticks();
  73. last_ticks = 0;
  74. ms_offset = platform_timer_restore_value();
  75. chSysUnlock();
  76. }
  77. void timer_save(void) {
  78. platform_timer_save_value(timer_read32());
  79. }
  80. uint16_t timer_read(void) {
  81. return (uint16_t)timer_read32();
  82. }
  83. uint32_t timer_read32(void) {
  84. syssts_t sts = chSysGetStatusAndLockX();
  85. uint32_t ticks = get_system_time_ticks() - ticks_offset;
  86. if (ticks < last_ticks) {
  87. // The 32-bit tick counter overflowed and wrapped around. We cannot just extend the counter to 64 bits here,
  88. // because TIME_I2MS() may encounter overflows when handling a 64-bit argument; therefore the solution here is
  89. // to subtract a reasonably large number of ticks from the tick counter to bring its value below the 32-bit
  90. // limit again, and then add the equivalent number of milliseconds to the converted value. (Adjusting just the
  91. // converted value to account for 2**32 ticks is not possible in general, because 2**32 ticks may not correspond
  92. // to an integer number of milliseconds).
  93. ticks -= OVERFLOW_ADJUST_TICKS;
  94. ticks_offset += OVERFLOW_ADJUST_TICKS;
  95. ms_offset += OVERFLOW_ADJUST_MS;
  96. }
  97. last_ticks = ticks;
  98. uint32_t ms_offset_copy = ms_offset; // read while still holding the lock to ensure a consistent value
  99. chSysRestoreStatusX(sts);
  100. return (uint32_t)TIME_I2MS(ticks) + ms_offset_copy;
  101. }