Hi All,
I'm testing a driver for an accelorometer/gyroscope.
There is a sequence in the driver which reads timestamp value during initialisation and correllates it to the ESP's RTC clock (system time), to calculate the drift between the two, so it can map timestamp data from the sensor to system time.
This is needed because there is a FIFO in the sensor which is read in bursts, and the data packets in the FIFO contain the timestamp information pair with the data. Normally when data accumulates in the FIFO and FIFO becomes full, it fires a GPIO interrupt, which triggers the ESP to read the FIFO empty.
In this case becuase the relation between the sensor timestamp and system time is established, we can process the read data properly.
This way I don't have to call I2C reads at a fixed frequency, which is good.
It is important that this relatively short calibration process is not dusturbed by anything, however the I2C drivers reliance on FreeRTOS make it difficult as I can not perform a read in critical section, because it causes a crash due to I2C interrupt and interrupt WDT triggering an abort()... or maybe I'm doing it wrong?
Is there a good way to do this somehow, without crashing, but still somewhat keeping the code execution protected?
This is done normally once on startup, when nothing else is running. Maybe vTaskSuspendAll() is enough to not let the scheduler mess with it?
Looking forward to read your opinions regarding this.
Thanks
Vader(Ben)
Reading critical I2C data
- Vader_Mester
- Posts: 300
- Joined: Tue Dec 05, 2017 8:28 pm
- Location: Hungary
- Contact:
Reading critical I2C data
Code: Select all
task_t coffeeTask()
{
while(atWork){
if(!xStreamBufferIsEmpty(mug)){
coffeeDrink(mug);
} else {
xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
}
}
vTaskDelete(NULL);
}
Re: Reading critical I2C data
Which I2C device are you using?
As I understand you;
(1) At initialisation you want to read the I2C device's timestamp in a low latency/no interrupts way such that you know I2C time and ESP 'world' time.
(2) The I2C device stores readings in an internal FIFO. When the FIFO is full you fetch all the readings and their associated time stamp.
(3) You I2C sample 'world' time using I2C timestamp and extrapolating from baseline established (1)
(4) You use 'FIFO full' trigger so as to reduce I2C sampling rate.
If so please remember that the two clocks will drift and at some point you will loose more accuracy than FreeRTOS!
It seems strange that you use FIFO full. If you wait until full then you might miss a value.
I would be inclined to record ESP time from your interrupt. I would bet that you can configure the I2C device to interrupt on single value (not empty). If you want to transfer a block of readings then you can count readings (INTs) and then collect at a full threshold of choice. By using single reading ready (not empty) you have the means to record ESP timestamps with high precision.
As I understand you;
(1) At initialisation you want to read the I2C device's timestamp in a low latency/no interrupts way such that you know I2C time and ESP 'world' time.
(2) The I2C device stores readings in an internal FIFO. When the FIFO is full you fetch all the readings and their associated time stamp.
(3) You I2C sample 'world' time using I2C timestamp and extrapolating from baseline established (1)
(4) You use 'FIFO full' trigger so as to reduce I2C sampling rate.
If so please remember that the two clocks will drift and at some point you will loose more accuracy than FreeRTOS!
It seems strange that you use FIFO full. If you wait until full then you might miss a value.
I would be inclined to record ESP time from your interrupt. I would bet that you can configure the I2C device to interrupt on single value (not empty). If you want to transfer a block of readings then you can count readings (INTs) and then collect at a full threshold of choice. By using single reading ready (not empty) you have the means to record ESP timestamps with high precision.
& I also believe that IDF CAN should be fixed.
- Vader_Mester
- Posts: 300
- Joined: Tue Dec 05, 2017 8:28 pm
- Location: Hungary
- Contact:
Re: Reading critical I2C data
Yes, that's the intention. I'm using an ICM42605, InvenSense made a driver which I ported to ESP32, still in the works though.
Basically they did this in their driver/examples, and they did it with disabled interrupts (Note that this driver was made for non freeRTOS ARM stuff)
Yapp(2) The I2C device stores readings in an internal FIFO. When the FIFO is full you fetch all the readings and their associated time stamp.
In this case, the only thing I do is I store and offset variable in ram, so I can quickly calculate what the timestamp means in ESP RTC clock.(3) You I2C sample 'world' time using I2C timestamp and extrapolating from baseline established (1)
The rate at which the ICM device records values is maximum 100Hz. Counting the Interrupt latency of the ESP32, there is still ample time to read it out before the next value is measured and stored.(4) You use 'FIFO full' trigger so as to reduce I2C sampling rate.
If so please remember that the two clocks will drift and at some point you will loose more accuracy than FreeRTOS!
It seems strange that you use FIFO full. If you wait until full then you might miss a value.
Yep. InvenSense implemented a simple ringbuffer, to which they store timestamp data, in my case, I use gettimofday() and store the time value on the ringbuffer, by also counting up a semaphore. A task constantly takes (decreases) this semaphore, retrieving timestamp data from ringbuffer, and it also reads the FIFO, and process the data.I would be inclined to record ESP time from your interrupt.
I have the feeling this may be enough for my usecase, without calibrating the clock in the beggining, but I need to look at what InvenSense intended with their driver
Yes you can of course.I would bet that you can configure the I2C device to interrupt on single value (not empty).
This is a very good idea, I might use this instead. Although since FreeRTOS uses a lot of critical sections I might miss the interrupt.If you want to transfer a block of readings then you can count readings (INTs) and then collect at a full threshold of choice.
By using single reading ready (not empty) you have the means to record ESP timestamps with high precision.
Although, since I only measure at a relatively low freq, ms accuracy is enough
Code: Select all
task_t coffeeTask()
{
while(atWork){
if(!xStreamBufferIsEmpty(mug)){
coffeeDrink(mug);
} else {
xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
}
}
vTaskDelete(NULL);
}
Re: Reading critical I2C data
Only if you configure the interrupt as being level. Try edge.Although since FreeRTOS uses a lot of critical sections I might miss the interrupt
There are a few critical sections but not such that you would notice. Actually that is possibly a lie. I have seen the I2C output clock stretch for >1.2mS.
Just goes to show, if you post often enough - infinite monkeysThis is a very good idea
& I also believe that IDF CAN should be fixed.
- Vader_Mester
- Posts: 300
- Joined: Tue Dec 05, 2017 8:28 pm
- Location: Hungary
- Contact:
Re: Reading critical I2C data
Yeah, if you look at FreeRTOS source code, critical sections and vTaskSuspendAll() everywhere. I kinda understand, cause this is a way to make things thread safe, and also this a good way for FreeRTOS to protect itself from it's own tick interruptPeterR wrote: ↑Wed May 19, 2021 8:59 amOnly if you configure the interrupt as being level. Try edge.Although since FreeRTOS uses a lot of critical sections I might miss the interrupt
There are a few critical sections but not such that you would notice. Actually that is possibly a lie. I have seen the I2C output clock stretch for >1.2mS.
It's been a while since I posted, so I'm very happy what came outJust goes to show, if you post often enough - infinite monkeysThis is a very good idea
Code: Select all
task_t coffeeTask()
{
while(atWork){
if(!xStreamBufferIsEmpty(mug)){
coffeeDrink(mug);
} else {
xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
}
}
vTaskDelete(NULL);
}
Who is online
Users browsing this forum: MicroController and 166 guests