Are concurrent I2C calls possible?
Are concurrent I2C calls possible?
I want to query two of my digital sensors as often as possible. When the two are on the same port one has to finish a request/reply cycle on one sensor before one can query the other. So querying the the two sensors is just as fast as querying one sensor twice.
But how about the two sensors being on different ports? Can one query the two at the same time. And, will this be as fast as querying a single sensor? I know how to do it, but I don't know how the firmware handles this.
I did an initial test, in robotC, that gave some disappointing results. I query a single sensor in 4msec, two sensors on the same port in 8 msec and two sensors on different ports in 24 msec. My I2C drivers are not thread save, this might have influenced the results of the last test.
But how about the two sensors being on different ports? Can one query the two at the same time. And, will this be as fast as querying a single sensor? I know how to do it, but I don't know how the firmware handles this.
I did an initial test, in robotC, that gave some disappointing results. I query a single sensor in 4msec, two sensors on the same port in 8 msec and two sensors on different ports in 24 msec. My I2C drivers are not thread save, this might have influenced the results of the last test.
My blog: nxttime.wordpress.com
-
- Posts: 1818
- Joined: 02 Oct 2010, 02:19
- Location: Michigan USA
- Contact:
Re: Are concurrent I2C calls possible?
The NXT uses bit banged I2C, and so trying to do two two things at once, it is like multitasking. The NXT can only do one thing at a time. It would be like running parallel tasks. It would appear that they are both running at the same time, but it really has to take turns. If it has to keep changing code stacks, it makes sense it would be slower than running one, and then the other (or two on the same port).
Matt
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
Re: Are concurrent I2C calls possible?
All of the NXC I2C API functions are written so that they can be called simultaneously on separate threads so I think you could query two sensors on different ports faster than two back-to-back queries to a sensor on a single port. I'll run some timing tests with a couple HiTechnic devices later this evening.
I just ran some tests now and I am not seeing any kind of serialization in I2C calls using NXC and the standard firmware. Not 100% simultaneous, but each request is taking about 7 ms to execute and CurrentTick on one thread is showing a value that is 1 or 2 ms less than the value of CurrentTick on the second thread.
The code below is tweaked to remove a 15 millisecond delay in the thread-safe subroutines that underly the I2CBytes API function. I will be changing that code in the next release so that it does not always wait a minimum of 15 milliseconds.
John Hansen
I just ran some tests now and I am not seeing any kind of serialization in I2C calls using NXC and the standard firmware. Not 100% simultaneous, but each request is taking about 7 ms to execute and CurrentTick on one thread is showing a value that is 1 or 2 ms less than the value of CurrentTick on the second thread.
The code below is tweaked to remove a 15 millisecond delay in the thread-safe subroutines that underly the I2CBytes API function. I will be changing that code in the next release so that it does not always wait a minimum of 15 milliseconds.
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
-
- Posts: 323
- Joined: 29 Sep 2010, 05:03
Re: Are concurrent I2C calls possible?
It may depend which speed i2c you are using. In leJOS the standard speed (9.6Kbps) version is driven from a timer interrupt and so two (or more) on different ports will sort of run in parallel (in effect each bit gets sent in the "gap" between the bits on the other ports). However when using high speed mode (125Kbps), there is no "gap" as such so you will not gain any speed advantage... It may well be the same for RobotC (but I have no idea if it is)...
Andy
Andy
Re: Are concurrent I2C calls possible?
Thanks all for the input.
I just did some clean tests without any function calls that are not thread safe.
In standard I2C mode it takes 4msec to query 2 bytes of data. It does not make a difference if I query one or two sensors in parallel.
In fast mode it takes 2msec to query 2 bytes of data when running just one task/sensor. When running 2 tasks/sensors in parallel it takes 2.6 msec to query both the sensors.
When querying 2 sensors in fast mode I get errors frequently, whenever an error occurs it is on both the ports and robotC does not recover from it. The only remedy is to reboot the NXT.
I just did some clean tests without any function calls that are not thread safe.
In standard I2C mode it takes 4msec to query 2 bytes of data. It does not make a difference if I query one or two sensors in parallel.
In fast mode it takes 2msec to query 2 bytes of data when running just one task/sensor. When running 2 tasks/sensors in parallel it takes 2.6 msec to query both the sensors.
When querying 2 sensors in fast mode I get errors frequently, whenever an error occurs it is on both the ports and robotC does not recover from it. The only remedy is to reboot the NXT.
Re: Are concurrent I2C calls possible?
Can you post your test program, please? It would benefit the LEGO MINDSTORMS community as a whole to see examples of these kind of timing tests.
John Hansen
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
Re: Are concurrent I2C calls possible?
Sure.
The includes and the lines between // configure accl; are needed to initialize the accelerometer. These are quite specific to the custom sensor that I use on port 3 and can be removed if you want to use it with your own sensor.
The includes and the lines between // configure accl; are needed to initialize the accelerometer. These are quite specific to the custom sensor that I use on port 3 and can be removed if you want to use it with your own sensor.
Code: Select all
#include "math.c"
#include "matrixDirectives.c"
#include "I2C.c"
#define IMU_speed 25
#include "IMU_acclMatrix.c"
task tS1();
task tS3();
task both();
task main();
task main()
{
//startTask(tS3);
//startTask(tS1);
startTask(both);
while(true)
{
wait1Msec(1);
}
}
task tS1()
{
ubyte msg[16];
ubyte reply[6];
tSensors port=S1;
float time;
SensorType[port]=sensorI2CCustomFastSkipStates;
while(true)
{
ClearTimer(T1);
msg[0]=2;
msg[1]=0x04;
msg[2]=0x45;
while (nI2CStatus[port] != NO_ERR)
wait1Msec(1);
sendI2CMsg(port, msg[0], 2);
while (nI2CBytesReady[port]==0 )
wait1Msec(1);
readI2CReply(port, reply[0], 2);
time=499.0/500.0*time+Time1[T1]/500.0;
nxtDisplayTextLine(1,"%3.2f", time);
}
}
task tS3()
{
ubyte msg[16];
ubyte reply[2];
tSensors port=S3;
float time;
SensorType[port]=sensorI2CCustomFastSkipStates;
// configure accl;
I2C_accl.port=port;
I2C_accl.address=0xa6;
Accl_init();
accl.initialized=true;
// configure accl;
erasedisplay();
while(true)
{
ClearTimer(T3);
msg[0]=2;
msg[1]=0xa6;
msg[2]=0x1b;
while (nI2CStatus[port] != NO_ERR)
wait1Msec(1);
sendI2CMsg(port, msg[0], 2);
while (nI2CBytesReady[port]==0 )
wait1Msec(1);
readI2CReply(port, reply[0], 2);
time=499.0/500.0*time+Time1[T3]/500.0;
nxtDisplayTextLine(3,"%3.2f", time);
}
}
task both()
{
ubyte msg[16];
ubyte reply[6];
tSensors port1=S1;
float time;
SensorType[port1]=sensorI2CCustomFastSkipStates;
tSensors port=S3;
SensorType[port]=sensorI2CCustomFastSkipStates;
// configure accl;
I2C_accl.port=port;
I2C_accl.address=0xa6;
Accl_init();
accl.initialized=true;
// configure accl;
erasedisplay();
while(true)
{
ClearTimer(T1);
msg[0]=2;
msg[1]=0x04;
msg[2]=0x45;
while (nI2CStatus[port1] != NO_ERR)
wait1Msec(1);
sendI2CMsg(port, msg[0], 2);
while (nI2CBytesReady[port1]==0 )
wait1Msec(1);
readI2CReply(port1, reply[0], 2);
msg[0]=2;
msg[1]=0xa6;
msg[2]=0x1b;
while (nI2CStatus[port] != NO_ERR)
wait1Msec(1);
sendI2CMsg(port, msg[0], 2);
while (nI2CBytesReady[port]==0 )
wait1Msec(1);
readI2CReply(port, reply[0], 2);
time=499.0/500.0*time+Time1[T1]/500.0;
nxtDisplayTextLine(1,"%3.2f", time);
}
}
My blog: nxttime.wordpress.com
-
- Posts: 323
- Joined: 29 Sep 2010, 05:03
Re: Are concurrent I2C calls possible?
Hi,
I'm a little confused as to what the actual results are that you see. So when you run the test what are the results displayed on the screen in the two modes?
Thanks
Andy
I'm a little confused as to what the actual results are that you see. So when you run the test what are the results displayed on the screen in the two modes?
Thanks
Andy
Re: Are concurrent I2C calls possible?
These are the results
Code: Select all
Fast I2C Standard I2C
S1. 2.00 msec. 3.96 msec
S3/S1 2.60 mesc 4.01 msec
Both 4.00 msec. Not tested
My blog: nxttime.wordpress.com
Re: Are concurrent I2C calls possible?
I don't really understand how the time= equation works but it seems to do the right thing in my NXC version. Here it is:
There's got to be a straightforward explanation why RobotC can make an I2C call (in slow mode) in half the time it takes the standard firmware to do the same thing. I get about 8ms per transaction using the above code. I guess I will pour over the firmware source code carefully to see if there is anything I can do to speed it up without breaking anything.
John Hansen
Code: Select all
task tS1()
{
SetSensorLowspeed(S1);
byte msg[] = {0x02, 0x42};
byte reply[6];
byte count;
float time;
unsigned long T1;
while(true)
{
count = 2;
T1 = CurrentTick();
while (I2CCheckStatus(S1) != NO_ERR)
Wait(0);
I2CWrite(S1, count, msg);
while (I2CBytesReady(S1) == 0)
Wait(0);
I2CRead(S1, count, reply);
time = 499.0/500.0*time + (CurrentTick()-T1) / 500.0;
TextOut(0, LCD_LINE1, FormatNum("%3.2f" , time));
}
}
task tS3()
{
SetSensorLowspeed(S3);
byte msg[] = {0x02, 0x42};
byte reply[6];
byte count;
float time;
unsigned long T3;
while(true)
{
count = 2;
T3 = CurrentTick();
while (I2CCheckStatus(S3) != NO_ERR)
Wait(0);
I2CWrite(S3, count, msg);
while (I2CBytesReady(S3) == 0)
Wait(0);
I2CRead(S3, count, reply);
time = 499.0/500.0*time + (CurrentTick()-T3) / 500.0;
TextOut(0, LCD_LINE3, FormatNum("%3.2f" , time));
}
}
task both()
{
SetSensorLowspeed(S1);
SetSensorLowspeed(S3);
byte msg1[] = {0x02, 0x42};
byte msg2[] = {0x02, 0x42};
byte reply[6];
byte count;
float time;
unsigned long T1;
while(true)
{
count = 2;
T1 = CurrentTick();
while (I2CCheckStatus(S1) != NO_ERR)
Wait(0);
I2CWrite(S1, count, msg1);
while (I2CBytesReady(S1) == 0)
Wait(0);
I2CRead(S1, count, reply);
while (I2CCheckStatus(S3) != NO_ERR)
Wait(0);
I2CWrite(S3, count, msg2);
while (I2CBytesReady(S3) == 0)
Wait(0);
I2CRead(S3, count, reply);
time = 499.0/500.0*time + (CurrentTick()-T1) / 500.0;
TextOut(0, LCD_LINE1, FormatNum("%3.2f" , time));
}
}
task main()
{
Precedes(tS1, tS3);
// Precedes(both);
/*
SetSensorLowspeed(S1);
SetSensorLowspeed(S3);
while (true)
{
TextOut(0, LCD_LINE1, I2CVendorId(S1, I2C_ADDR_DEFAULT));
TextOut(0, LCD_LINE2, I2CVendorId(S3, I2C_ADDR_DEFAULT));
Wait(SEC_1);
}
*/
}
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
Who is online
Users browsing this forum: No registered users and 2 guests