qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

timer.c (4517B)


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