micros() returning inconsistend values when call from different tasks (#1165)

The cycle count that micros() is using to report timing is a PER Task value.  When micros() or delayMicroseconds() is called from different Tasks, the lastCycleCount value may have no relationship to the current Task specific cycleCount. If the current cycleCount is less than the saved lastCycleCount a rollover condition is assumed. This erroneous conditions results in incorrect delays and reported microseconds.  This fix creates thread local storage so that each Task will have accurate microsecond reporting and delays.  The reported microseconds are not real time, they are microseconds of the current Task execution.
This commit is contained in:
chuck todd 2018-03-04 13:17:34 -07:00 committed by Me No Dev
parent 1256f2e539
commit 7141e3e29d

View File

@ -38,20 +38,45 @@ void yield()
}
portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;
static pthread_key_t microsStore=NULL; // Thread Local Storage Handle
void* microsStoreDelete(void * storage) { // release thread local data when task is delete.
if(storage) free(storage);
}
unsigned long IRAM_ATTR micros()
{
static unsigned long lccount = 0;
static unsigned long overflow = 0;
unsigned long ccount;
portENTER_CRITICAL_ISR(&microsMux);
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
if(ccount < lccount){
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
if (!microsStore) { // first Time Ever thread local not init'd
portENTER_CRITICAL_ISR(&microsMux);
pthread_key_create(&microsStore,microsStoreDelete); // create initial holder
portEXIT_CRITICAL_ISR(&microsMux);
}
lccount = ccount;
uint32_t *ptr;// [0] is lastCount, [1] is overFlow
ptr = pthread_getspecific(microsStore); // get address of storage
if(ptr == NULL) { // first time in this thread, allocate mem, init it.
portENTER_CRITICAL_ISR(&microsMux);
ptr = (uint32_t*)malloc(sizeof(uint32_t)*2);
pthread_setspecific(microsStore,ptr); // store the pointer to this thread's values
ptr[0] = 0; // lastCount value
ptr[1] = 0; // overFlow
portEXIT_CRITICAL_ISR(&microsMux);
}
unsigned long ccount;
portENTER_CRITICAL_ISR(&microsMux);
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) ); //get cycle count
if(ccount < ptr[0]) { // overflow occurred
ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
}
ptr[0] = ccount;
portEXIT_CRITICAL_ISR(&microsMux);
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
}
unsigned long IRAM_ATTR millis()