-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Strange behavior with Micros() and Queue #1160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@Leicas What is it doing that you don't like? Chuck. |
@stickbreaker yes, no problem,
without
Leicas |
@Leicas you are feeding a queue as fast as possible, you are draining the queue one element every 125us. Out of curiosity add uint32_t count = uxQueueMessagesWaiting( QueueHandle_t xQueue ); // Return the number of messages stored in a queue. and print the result in Chuck. |
@stickbreaker Yeah, that's some leftover code, I used to have a function that would feed small amount of data to the queue, less than the queue size and at a much lower rate. The problem is why does "micros()" stop counting microsecond when you use the with the counter:
But by curiosity I removed the |
@Leicas Well, it looks like your 3597419 is (2^32)/120 + actual us add this to your println: uint32_t netMic = deltaMic - ( UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ); For some reason the Chuck. |
@Leicas @me-no-dev I think I figured out what is happening. The __asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) ); This cycle count is a PER TASK count. So, depending on how long a task operates, its number changes. To solve this each task needs a task local 'lastMicroSecondTick' and 'rolloverOffset' Chuck. |
@Leicas Here is a potential fix: add the unsigned long IRAM_ATTR micros()
{
static __thread unsigned long lccount = 0;
static __thread unsigned long overflow = 0;
unsigned long ccount;
portENTER_CRITICAL_ISR(µsMux);
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
if(ccount < lccount){
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
}
lccount = ccount;
portEXIT_CRITICAL_ISR(µsMux);
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
} This might not be the best way, because, it will reserve 8 bytes in every Task's Stack but it is simple!
Chuck. |
@Leicas Well, __thread just caused Core Panics. Chuck. |
@Leicas Had to use pthread_keys: portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;
static pthread_key_t microsStore=NULL; //Thread local storage for rollover of micro() counter
void* microsStoreDelete(void * storage){ // called when a Task exits to release thread local storage
if(storage) free(storage);
}
unsigned long IRAM_ATTR micros(){
if (!microsStore){ // first Time Ever thread local not init'd
portENTER_CRITICAL_ISR(µsMux); // don't know if I really need it, but it only happens Once
pthread_key_create(µsStore,microsStoreDelete); // create initial holder
portEXIT_CRITICAL_ISR(µsMux);
}
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); // only happens once per thread (task)
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) );
if(ccount < ptr[0]){
ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
}
ptr[0] = ccount;
portEXIT_CRITICAL_ISR(µsMux);
return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
} Your original code now produces this:
@Leicas I had to change your void consumerTask( void * parameter){
int element;
uint32_t blockCount=0;
while(true) {
if(!xQueueReceive(queue, &element, 0)){ // no data avail
vTaskDelay(1); // let other task use this core
failCount++;
}
else {
delayMicroseconds(125);
blockCount++;
}
if(blockCount>5000){ // be polite, let lower priority task have 1ms
vTaskDelay(1); // task switch, wdt
blockCount=0;
}
}
} else I would get WDT timeouts on Core 0.
Chuck. |
Thanks a lot for all the work! I'll try this in my full project.
Le jeu. 1 mars 2018 à 20:51, chuck todd <[email protected]> a écrit :
… @Leicas <https://github.com/leicas> Had to use pthread_keys:
This is what I changed micros() inside of cores\esp32\esp32-hal-misc.c
<https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-misc.c>
portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;static pthread_key_t microsStore=NULL; //Thread local storage for rollover of micro() counter
void* microsStoreDelete(void * storage){ // called when a Task exits to release thread local storageif(storage) free(storage);
}
unsigned long IRAM_ATTR micros(){
if (!microsStore){ // first Time Ever thread local not init'd
portENTER_CRITICAL_ISR(µsMux); // don't know if I really need it, but it only happens Once
pthread_key_create(µsStore,microsStoreDelete); // create initial holder
portEXIT_CRITICAL_ISR(µsMux);
}
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); // only happens once per thread (task)
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) );
if(ccount < ptr[0]){
ptr[1] += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
}
ptr[0] = ccount;portEXIT_CRITICAL_ISR(µsMux);return ptr[1] + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
}
Your original code now produces this:
done in 2 ms 1909 mus
done in 2 ms 1909 mus
done in 2 ms 1910 mus
done in 2 ms 1907 mus
done in 2 ms 1911 mus
done in 2 ms 1912 mus
done in 2 ms 1900 mus
done in 2 ms 1910 mus
done in 2 ms 1910 mus
done in 1 ms 1909 mus
done in 2 ms 1919 mus
done in 2 ms 1909 mus
done in 2 ms 1910 mus
done in 2 ms 1909 mus
done in 2 ms 1910 mus
done in 2 ms 1912 mus
done in 2 ms 1906 mus
done in 2 ms 1904 mus
done in 2 ms 1908 mus
done in 2 ms 1909 mus
done in 1 ms 1912 mus
done in 2 ms 1918 mus
done in 2 ms 1907 mus
done in 2 ms 1911 mus
done in 2 ms 1910 mus
done in 2 ms 1911 mus
done in 2 ms 1909 mus
done in 2 ms 1908 mus
done in 2 ms 1910 mus
@Leicas <https://github.com/leicas> I had to change your consumerTask()
to be more polite with other tasks:
void consumerTask( void * parameter){int element;uint32_t blockCount=0;while(true) {
if(!xQueueReceive(queue, &element, 0)){ // no data avail
vTaskDelay(1); // let other task use this core
failCount++;
}
else {
delayMicroseconds(125);
blockCount++;
}
if(blockCount>5000){ // be polite, let lower priority task have 1ms
vTaskDelay(1); // task switch, wdt
blockCount=0;
}
}
}
else I would get WDT timeouts on Core 0.
Task watchdog got triggered. The following tasks did not reset the watchdog in time:
- IDLE (CPU 0)
Tasks currently running:
CPU 0: Consumer
CPU 1: loopTask
Chuck.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1160 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAuwQ6WZdA5UG6lHb5M4uCTANla-S1Auks5taKW4gaJpZM4SYed->
.
|
@Leicas with more playing, it looks like 1ms is not enough time for the idle process to complete it's housekeeping, I kept getting 'out of stackspace' errors until I increased the vTaskDelay() to 10ms. I don't know what the actual rules, recommendations are for sharing time with idle tasks. Just be aware if your loops are to tight you may need to design some vTaskDelay()'s into them. Chuck. |
@stickbreaker Everything is working with your last code changes. |
Hi together, first, thanks for your work. I still have problems with the delayMicroseconds() function. This function worked fine for me on the Arduino. But it looks to me that on the ESP using arduino-esp32 library the function to delay either block in total or it takes much longer than the expected microseconds to return. FYI: im using PlatformIO for both projects (Arduino & ESP32) and i updated the arduino-esp32 lib there ti this branch. Because i don't think it comes from this lib, because i cant be the first having this issue, i think i have setup my project wrong. Does someone has an idea, where this behavior comes from? |
The Esp32 is a multi tasking multi processor system. A call to Chuck. |
Thx for your reply, That makes totally sense to me. Thanks a lot. |
Hardware:
Board: Adafruit Huzzah ESP32
Core Installation/update date: 20/feb/2018
IDE name: Arduino IDE
Flash Frequency: 40Mhz
Upload Speed: 115200
Description:
Hi,
I noticed some strange things happening when using Micros for timing my code.
The sketch below show a minimal exemple, if you comment the xQueueSend(queue, &j, 0); line, everything works fine and Micros and Millis show consistent values.
But with the xQueueSend(queue, &j, 0);, I cannot figure what Micros is returning, and it doesn't make sense to me why using a fifo queue would impact timing.
Did I miss something obvious ?
Thanks
Sketch:
The text was updated successfully, but these errors were encountered: