Closed lynx985 closed 7 months ago
Good question, Having a faster I2C bus can fetch the samples faster from the device. Measurements will still take their time. But you will have more cpu cycles to process measurements.
No experience with 3.4 MHz yet, however some common knowledge will apply. Pulses will be shorter so more affected by RC effects of the wires. So I expect 3.4 will only work with short wires (traces) less than (guess) 20 cm or 8 inch. Otherwise the capacitance of the wire will slow the rising and falling of the edges of the pulse. Partially this might be compensated by smaller pull up resistors. There will be a limit as current will rise a lot.
Another point of attention might be shielding the i2c wires to reduce noise. In a project I used an I2C LCD and due to noise within the machine the LCD failed roughly twice a minute or more. By shielding the wires this disturbance was gone.
Yes as tested, it does make a massive difference for processing data.
The high speed mode on the ADS1115 has to be activated with a command to the chip and is not active by default. I don't know how to do this.
Of course as you said one has to be extra careful with wiring and noise.
The high speed mode on the ADS1115 has to be activated with a command to the chip and is not active by default. I don't know how to do this
I do not recall a special setting or register bit for this. It might be related to limits of other parts used as explained here
Went through datasheet again, From 9.5.1.3
No special action is required to use the ADS111x in standard or fast mode, but high-speed mode must be activated. To activate high-speed mode, send a special address byte of 00001xxx following the START condition, where xxx are bits unique to the Hs-capable master. This byte is called the Hs master code, and is different from normal address bytes; the eighth bit does not indicate read/write status. The ADS111x do not acknowledge this byte; the I2C specification prohibits acknowledgment of the Hs master code. Upon receiving a master code, the ADS111x switch on Hs mode filters, and communicate at up to 3.4 MHz. The ADS111x switch out of Hs mode with the next STOP condition.
For more information on high-speed mode, consult the I2C specification.
So a special command is needed. Digging further...
Some warning from datasheet about too small pull ups
Pullup resistors are required on both the SDA and SCL lines because I2C bus drivers are open drain. The size of these resistors depends on the bus operating speed and capacitance of the bus lines. Higher-value resistors consume less power, but increase the transition times on the bus, thus limiting the bus speed. Lower-value resistors allow higher speed, but at the expense of higher power consumption. Long bus lines have higher capacitance and require smaller pullup resistors to compensate. Do not use resistors that are too small because the bus drivers may not be able to pull the bus lines low.
This link might explain the xxx in special address mentioned before.
So if I understand correctly, one has to know the capabilities of the I2C driver of the specific board if and which one of those eight modi is supported.
If the mode is known, then the next question is how to embed it in a command to read and write registers. Especially as the HS mode cancels after STOP the read might be.the more difficult as it includes a write to select the register.
my intuition says
bool ADS1X15::_writeRegister(uint8_t address, uint8_t reg, uint16_t value)
{
_wire->beginTransmission(address);
_wire->write((uint8_t)reg);
_wire->write((uint8_t)(value >> 8));
_wire->write((uint8_t)(value & 0xFF));
return (_wire->endTransmission() == 0);
}
uint16_t ADS1X15::_readRegister(uint8_t address, uint8_t reg)
{
_wire->beginTransmission(address);
_wire->write(reg);
_wire->endTransmission();
int rv = _wire->requestFrom((int) address, (int) 2);
if (rv == 2)
{
uint16_t value = _wire->read() << 8;
value += _wire->read();
return value;
}
return 0x0000;
}
should become something like
bool ADS1X15::_writeRegister(uint8_t address, uint8_t reg, uint16_t value, bool HighSpeed)
{
if (highSpeed)
{
_wire->beginTransmission(0b00001xxx); // to be determined.
_wire->write(address);
}
else
{
_wire->beginTransmission(address);
}
_wire->write((uint8_t)reg);
_wire->write((uint8_t)(value >> 8));
_wire->write((uint8_t)(value & 0xFF));
return (_wire->endTransmission() == 0);
}
uint16_t ADS1X15::_readRegister(uint8_t address, uint8_t reg, bool HighSpeed)
{
if (highSpeed)
{
_wire->beginTransmission(0b00001xxx); // to be determined.
_wire->write(address);
}
else
{
_wire->beginTransmission(address);
}
_wire->write(reg);
_wire->endTransmission(false); // send no STOP
int rv = _wire->requestFrom((int) address, (int) 2);
if (rv == 2)
{
uint16_t value = _wire->read() << 8;
value += _wire->read();
return value;
}
return 0x0000;
}
xxx to be determined, might be the code as discussed on the NXP forum.
Thanks for your work. If I understand it correctly, the master code can be chosen as one wishes https://www.i2c-bus.org/addressing/high-speed/ "The master codes are selectable by the designer and allow up to eight high speed masters to be connected in one system (master code ‘00001000’ should be reserved for test and diagnostic purposes)."
Thanks for that link, looks interesting. no time to read it yet however the first picture is in line with how I expected it would works.
The remark about code 0b00001000 is clearly not in line with that (green) table above I found.
OK, read it as it was rather short 😎
Given that 99.9% is single master, the above code ideas could be adapted so you can test the HS modus. (hardcode 0b0001001 as HS byte). This assumes of course the platform supports HS. Q: What happens when sending the HS byte and the platform doesn't understand? (to be tested).
https://www.i2c-bus.org/highspeed/ gives slightly more info about the how.
(back to my other project)
After a nights sleep and thinking about it again, this would be my first test code.
bool ADS1X15::_writeRegister(uint8_t address, uint8_t reg, uint16_t value, bool HighSpeed)
{
if (highSpeed)
{
_wire->beginTransmission(0b00001001); // master 001
_wire->endTransmission(false); // send no STOP
_wire.setClock(3400000);
}
_wire->beginTransmission(address);
_wire->write((uint8_t)reg);
_wire->write((uint8_t)(value >> 8));
_wire->write((uint8_t)(value & 0xFF));
bool b = (_wire->endTransmission() == 0);
_wire.setClock(100000); // back to default
return b;
}
uint16_t ADS1X15::_readRegister(uint8_t address, uint8_t reg, bool HighSpeed)
{
if (highSpeed)
{
_wire->beginTransmission(0b00001001); // master 001
_wire->endTransmission(false); // send no STOP
_wire.setClock(3400000);
}
_wire->beginTransmission(address);
_wire->write(reg);
_wire->endTransmission(false); // send no STOP
int rv = _wire->requestFrom((int) address, (int) 2);
_wire.setClock(100000); // back to default
if (rv == 2)
{
uint16_t value = _wire->read() << 8;
value += _wire->read();
return value;
}
return 0x0000;
}
(back to work)
thank for your effort, unfortunately i am unable to test it because i can't figure out where to put the "bool HighSpeed" elsewhere in the code to make it work and get errors from the compiler
Its rather late here now, will try to make a test branch tomorrow.
Or you can make the param a local variable that is hard coded true. That would be easiest way to see if it works.
Or you can make the param a local variable that is hard coded true. That would be easiest way to see if it works.
like this
bool ADS1X15::_writeRegister(uint8_t address, uint8_t reg, uint16_t value)
{
bool highSpeed = true;
if (highSpeed)
{
_wire->beginTransmission(0b00001001); // master 001
_wire->endTransmission(false); // send no STOP
_wire.setClock(3400000);
}
_wire->beginTransmission(address);
_wire->write((uint8_t)reg);
_wire->write((uint8_t)(value >> 8));
_wire->write((uint8_t)(value & 0xFF));
bool b = (_wire->endTransmission() == 0);
_wire.setClock(100000); // back to default
return b;
}
uint16_t ADS1X15::_readRegister(uint8_t address, uint8_t reg)
{
bool highSpeed = true;
if (highSpeed)
{
_wire->beginTransmission(0b00001001); // master 001
_wire->endTransmission(false); // send no STOP
_wire.setClock(3400000);
}
_wire->beginTransmission(address);
_wire->write(reg);
_wire->endTransmission(false); // send no STOP
int rv = _wire->requestFrom((int) address, (int) 2);
_wire.setClock(100000); // back to default
if (rv == 2)
{
uint16_t value = _wire->read() << 8;
value += _wire->read();
return value;
}
return 0x0000;
}
hi, thank for the code i tried it but it doesn't work the speed is always at 100 kHz i searched the web a bit and i don't think anyone managed to get 3.4 MHz working with arduino, at least i couldn't find a single example of it it also seems nessecary to set a register somewhere in the master-chip
i think it's too much effort to pursue given that it can run up to 1.2 MHz already
Maybe the boards I2C driver just does not implement the 3.4 Mhz. Do you have sources of the driver? Or a link?
Otherwise we close the issue, at least we gave it some serious try imho.
Just got an insight. The code as proposed cannot work as the beginTransmission() adds a write bit to the byte we send to select 3.4 Mhz. Furthermore it does not add that write bit to the device address as it is send with write().
Need to rewrite code with this insight tomorrow.
final try, the special byte has right-shifted one bit as the beginTransmission() will left-shift it and add a W (0) bit
bool ADS1X15::_writeRegister(uint8_t address, uint8_t reg, uint16_t value)
{
bool highSpeed = true;
if (highSpeed)
{
// master 010 as a 0 write bit will be added
_wire->beginTransmission(0b00000101);
_wire->endTransmission(false); // send no STOP
_wire.setClock(3400000);
}
_wire->beginTransmission(address);
_wire->write((uint8_t)reg);
_wire->write((uint8_t)(value >> 8));
_wire->write((uint8_t)(value & 0xFF));
bool b = (_wire->endTransmission() == 0);
_wire.setClock(100000); // back to default
return b;
}
uint16_t ADS1X15::_readRegister(uint8_t address, uint8_t reg)
{
bool highSpeed = true;
if (highSpeed)
{
// master 010 as a 0 write bit will be added
_wire->beginTransmission(0b00000101);
_wire->endTransmission(false); // send no STOP
_wire.setClock(3400000);
}
_wire->beginTransmission(address);
_wire->write(reg);
_wire->endTransmission(false); // send no STOP
int rv = _wire->requestFrom((int) address, (int) 2);
_wire.setClock(100000); // back to default
if (rv == 2)
{
uint16_t value = _wire->read() << 8;
value += _wire->read();
return value;
}
return 0x0000;
}
hi, thanks for your effort, but no luck the library is already great so i am not sad about it thanks again
OK, it was fun to investigate, learned a bit you may close the issue
hi
first: thank you for this library =)
I tested it today and found that the wire speed has a great influence on microcontroller performance. One factor is the time needed to transmit the data, but the other is the time it take to handle the pull-requests (aka ADS.isready) to the ADS1X15.
I was able to set the wire speed up to 1.2 MHz (adafruit feather M0 adalogger). The program was able to make pull-requests around 14'000 times per second and change the DAC output as often, while reading 600 samples per second from an ADS1115. At 400 kHz it managed to do that only around 6'000 times per second.
When i changed the code so that it would only do pull-requests every four cycles it did rund almost times faster.
Is it possible to increase the speed further? The ADS1X15 does have an high speed mode at up to 3.4 MHz, but I am not sure if the wire can run that fast. (I did test different pullups on SCL and SDA. There was no difference between 10k and 1.5 k Resistors)