Skip to content

Commit 90ff8bf

Browse files
authored
Create Thread Local Storage for micros()
micros() is using thread specific cycle counts. So, when micros() or delayMicroseconds() are called from different tasks, false rollover events are detected. The cycle count is task specific, and micros() was using `if(currentCycleCount < lastCycleCount) processRollover()`. The lastCycleCount variable was updated each time micros() exited with the current Tasks cycle count. So, if the next micros() or delayMicroseconds() was not called from the same Task lastCycleCount had no relation.
1 parent 798e73b commit 90ff8bf

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

cores/esp32/esp32-hal-misc.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,42 @@ void yield()
3838
}
3939

4040
portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;
41+
static pthread_key_t microsStore=NULL; // Thread Local Storage Handle
4142

42-
unsigned long IRAM_ATTR micros()
43-
{
44-
static unsigned long lccount = 0;
45-
static unsigned long overflow = 0;
46-
unsigned long ccount;
47-
portENTER_CRITICAL_ISR(&microsMux);
48-
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
49-
if(ccount < lccount){
50-
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
51-
}
52-
lccount = ccount;
53-
portEXIT_CRITICAL_ISR(&microsMux);
54-
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
43+
void* microsStoreDelete(void * storage){ // release thread local data when task is delete.
44+
if(storage) free(storage);
45+
}
46+
47+
unsigned long IRAM_ATTR micros(){
48+
49+
if (!microsStore){ // first Time Ever thread local not init'd
50+
portENTER_CRITICAL_ISR(&microsMux);
51+
pthread_key_create(&microsStore,microsStoreDelete); // create initial holder
52+
portEXIT_CRITICAL_ISR(&microsMux);
53+
}
54+
55+
uint32_t *ptr;// [0] is lastCount, [1] is overFlow
56+
57+
ptr = pthread_getspecific(microsStore); // get address of storage
58+
59+
if(ptr == NULL){ // first time in this thread, allocate mem, init it.
60+
portENTER_CRITICAL_ISR(&microsMux);
61+
ptr = (uint32_t*)malloc(sizeof(uint32_t)*2);
62+
pthread_setspecific(microsStore,ptr); // store the pointer to this thread's values
63+
ptr[0] = 0; // lastCount value
64+
ptr[1] = 0; // overFlow
65+
portEXIT_CRITICAL_ISR(&microsMux);
66+
}
67+
68+
unsigned long ccount;
69+
portENTER_CRITICAL_ISR(&microsMux);
70+
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
71+
if(ccount < ptr[0]){
72+
ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
73+
}
74+
ptr[0] = ccount;
75+
portEXIT_CRITICAL_ISR(&microsMux);
76+
return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
5577
}
5678

5779
unsigned long IRAM_ATTR millis()

0 commit comments

Comments
 (0)