Reading critical I2C data

User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Reading critical I2C data

Postby Vader_Mester » Tue May 18, 2021 2:30 pm

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)

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);
}

PeterR
Posts: 621
Joined: Mon Jun 04, 2018 2:47 pm

Re: Reading critical I2C data

Postby PeterR » Tue May 18, 2021 5:48 pm

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.
& I also believe that IDF CAN should be fixed.

User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: Reading critical I2C data

Postby Vader_Mester » Tue May 18, 2021 7:09 pm

PeterR wrote:
Tue May 18, 2021 5:48 pm
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.
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)
(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.
Yapp
(3) You I2C sample 'world' time using I2C timestamp and extrapolating from baseline established (1)
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.
(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.
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.
I would be inclined to record ESP time from your interrupt.
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 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 :mrgreen:
I would bet that you can configure the I2C device to interrupt on single value (not empty).
Yes you can of course.
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.
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.
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);
}

PeterR
Posts: 621
Joined: Mon Jun 04, 2018 2:47 pm

Re: Reading critical I2C data

Postby PeterR » Wed May 19, 2021 8:59 am

Although since FreeRTOS uses a lot of critical sections I might miss the interrupt
Only if you configure the interrupt as being level. Try edge.
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.
This is a very good idea
Just goes to show, if you post often enough - infinite monkeys ;)
& I also believe that IDF CAN should be fixed.

User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: Reading critical I2C data

Postby Vader_Mester » Wed May 19, 2021 9:53 am

PeterR wrote:
Wed May 19, 2021 8:59 am
Although since FreeRTOS uses a lot of critical sections I might miss the interrupt
Only if you configure the interrupt as being level. Try edge.
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.
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 interrupt :mrgreen:
This is a very good idea
Just goes to show, if you post often enough - infinite monkeys ;)
It's been a while since I posted, so I'm very happy what came out :)

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