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:
parent
1256f2e539
commit
7141e3e29d
@ -38,20 +38,45 @@ void yield()
|
|||||||
}
|
}
|
||||||
|
|
||||||
portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;
|
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()
|
unsigned long IRAM_ATTR micros()
|
||||||
{
|
{
|
||||||
static unsigned long lccount = 0;
|
if (!microsStore) { // first Time Ever thread local not init'd
|
||||||
static unsigned long overflow = 0;
|
portENTER_CRITICAL_ISR(µsMux);
|
||||||
unsigned long ccount;
|
pthread_key_create(µsStore,microsStoreDelete); // create initial holder
|
||||||
portENTER_CRITICAL_ISR(µsMux);
|
portEXIT_CRITICAL_ISR(µsMux);
|
||||||
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
|
|
||||||
if(ccount < lccount){
|
|
||||||
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
|
|
||||||
}
|
}
|
||||||
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(µsMux);
|
||||||
|
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(µsMux);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long ccount;
|
||||||
|
|
||||||
|
portENTER_CRITICAL_ISR(µsMux);
|
||||||
|
__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(µsMux);
|
portEXIT_CRITICAL_ISR(µsMux);
|
||||||
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
|
|
||||||
|
return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long IRAM_ATTR millis()
|
unsigned long IRAM_ATTR millis()
|
||||||
|
Loading…
Reference in New Issue
Block a user