Closed MaffooClock closed 2 years ago
Actually, I might be stupid.
Maybe this is part of the I2C spec, and so the Wire library (or platform or whatever) handles this? I briefly skimmed through those libraries to confirm, and consulted the I2C spec, but didn't find what I was looking for.
I tested higher clock speeds with this library and was able to reach 3MHz -- isConnected()
returned true at 3.4MHz, but all data was 0.
So I guess I was wrong about this library needing to perform the activation.
Thanks for this issue.
Never used this high speed as 400KHz works stable for almost all devices. Still indeed interesting for those boards that can handle 3.4MHz. (an UNO can do ~800KHz under ideal conditions) Never seen the needed commands in any ADS library or sketch.
What board are you using?
(for future reference)
void ADS1X15::setWireClock(uint32_t clockSpeed)
{
_clockSpeed = clockSpeed;
_wire->setClock(_clockSpeed);
// Section 9.5.1.3 (page 23) of the datasheet - https://www.ti.com/lit/ds/symlink/ads1115.pdf
if (clockSpeed > 400)
{
// insert magic here
}
}
Never used this high speed as 400KHz works stable for almost all devices.
I would agree.
However, I'm using an ADS1114 to read a 60Hz sine wave -- at 860S/s, the best I can do is about 14 samples per full cycle of the wave. (A faster ADC would be smarter, but I've got a $8k reel of these, so I'm kinda stuck with it. But I digress.) Thus, I'm trying to put the squeeze on every possible optimization, and in this case, jumping from 400kHz to 3MHz has really improved accuracy.
What board are you using?
It's a MicroMod RP2040 (SparkFun's version of the Raspberry Pi Pico) mounted to a custom PCB which has multiple ADS1114s.
If you have a whole real of those guys why not put 2 side by side? if you read them alternating you might combine the samples ? using 2 different interrupt pins to get timing per sample on micros level?
never tried but there is no law of nature that forbids this trick ...
might even work for more than 2 ...
That is a clever idea. 🤔
I'm interested in a minimal code version as it would make a great example.
@MaffooClock A quick sketch for 4x ADS1114 (stripped / converted example) It is blocking but it should show the idea is working.
//
// FILE: ADS_1114_four.ino
// AUTHOR: Rob.Tillaart
// PURPOSE: demo reading four ADS1114 modules in parallel
// URL: https://github.com/RobTillaart/ADS1X15
// Note all IO with the sensors are guarded by an isConnected()
// this is max robust, in non critical application one may either
// cache the value or only verify it in setup (least robust).
// Less robust may cause the application to hang - watchdog reset ?
#include "ADS1X15.h"
ADS1114 ADS[4];
uint16_t val[4];
uint32_t last = 0, now = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
for (uint8_t i = 0; i < 4; i++)
{
uint8_t address = 0x48 + i;
ADS[i] = ADS1114(address);
Serial.print(address, HEX);
Serial.print(" ");
Serial.println(ADS[i].begin() ? "connected" : "not connected");
ADS[i].setDataRate(4); // 7 is fastest, but more noise
}
ADS_request_all();
}
void loop()
{
// Serial.println(__FUNCTION__);
// wait until all is read...
while(ADS_read_all());
// we have all values
ADS_print_all();
delay(1000); // wait a second. <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< comment this linw
ADS_request_all();
}
void ADS_request_all()
{
// Serial.println(__FUNCTION__);
for (int i = 0; i < 4; i++)
{
if (ADS[i].isConnected()) ADS[i].requestADC(0);
delayMicroseconds(200); // get them evenly spaced in time ...
}
}
bool ADS_read_all()
{
for (int i = 0; i < 4; i++)
{
if (ADS[i].isConnected() && ADS[i].isBusy()) return true;
}
// Serial.print("IDX:\t");
// Serial.println(idx);
for (int i = 0; i < 4; i++)
{
if (ADS[i].isConnected())
{
val[i] = ADS[i].getValue();
}
}
ADS_request_all();
return false;
}
void ADS_print_all()
{
// Serial.println(__FUNCTION__);
// TIMESTAMP
now = millis();
Serial.print(now - last);
last = now;
Serial.println();
// PRINT ALL VALUES
for (int i = 0; i < 4; i++)
{
Serial.print(val[i]);
Serial.print("\t");
}
Serial.println();
}
// -- END OF FILE --
@MaffooClock
Another quick one - (have no hardware to test real thing ) for two ADS1114 in continuous mode.
//
// FILE: ADS_1114_two_continuous.ino
// AUTHOR: Rob.Tillaart
// PURPOSE: demo reading four ADS1114 modules in parallel
// URL: https://github.com/RobTillaart/ADS1X15
#include "ADS1X15.h"
ADS1114 ADS_1(0X48);
ADS1114 ADS_2(0X49);
// four interrupt flags
volatile bool RDY_1 = false;
volatile bool RDY_2 = false;
int16_t val_1 = 0;
int16_t val_2 = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
// SETUP FIRST ADS1115
ADS_1.begin();
ADS_1.setGain(0); // 6.144 volt
ADS_1.setDataRate(7);
// SET ALERT RDY PIN
ADS_1.setComparatorThresholdHigh(0x8000);
ADS_1.setComparatorThresholdLow(0x0000);
ADS_1.setComparatorQueConvert(0);
// SET INTERRUPT HANDLER TO CATCH CONVERSION READY
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), adsReady_1, RISING);
ADS_1.setMode(0); // continuous mode
ADS_1.readADC(0); // trigger first read
// SETUP SECOND ADS1115
ADS_2.begin();
ADS_2.setGain(0); // 6.144 volt
ADS_2.setDataRate(7);
// SET ALERT RDY PIN
ADS_2.setComparatorThresholdHigh(0x8000);
ADS_2.setComparatorThresholdLow(0x0000);
ADS_2.setComparatorQueConvert(0);
// SET INTERRUPT HANDLER TO CATCH CONVERSION READY
pinMode(3, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(3), adsReady_2, RISING);
ADS_2.setMode(0); // continuous mode
ADS_2.readADC(0); // trigger first read
}
void loop()
{
if (handleConversion() == true)
{
Serial.print('\t');
Serial.print(val_1);
Serial.print('\t');
Serial.print(val_2);
Serial.println();
}
}
// catch interrupt and set flag device 1
void adsReady_1()
{
RDY_1 = true;
}
// catch interrupt and set flag device 1
void adsReady_2()
{
RDY_2 = true;
}
// handle conversions that are ready
bool handleConversion()
{
bool rv = false;
if (RDY_1)
{
// save the last value
val_1 = ADS_1.getValue();
ADS_1.readADC(0);
RDY_1 = false;
rv = true;
}
if (RDY_2)
{
// save the last value
val_2 = ADS_2.getValue();
ADS_2.readADC(0);
RDY_2 = false;
rv = true;
}
return rv;
}
// -- END OF FILE --
@MaffooClock Made any progress? Can you confirm the examples work?
My apologies for going dark... got busy, I'm sure you know how that is :)
Actually, I might be stupid.
As I mentioned in my second reply, the "magic" required by the ADS111x to switch into high-speed mode might actually be an I2C thing and not a ADX111x thing? And thus the Wire library (or hardware library) handles that? I only took a few moments to try to confirm, but didn't find anything.
With that thought, I Just Tried Itâ„¢ -- I set the clock to 3.4MHz, and I was able to communicate with the ADS1114 using your library without any additional steps.
I guess now you're addressing my side quest of getting the maximum possible speed, and had a clever suggestion to use tandem ADX111x's. For this project, I've already got a custom PCB for my Teensy and RP2040 -based device (dual MCUs), with integrated ADS1114's. I don't really have a way to test the multi-ADC idea with what I'm working on. Plus, I don't plan on re-spinning these PCBs to accommodate this design.
So, no sir, I haven't tested your ideas.
However, I can say that with your library I am able to sample a 60Hz sine wave accurately enough through multiple ADS1114s to calculate phase angle between voltage and current (resistive vs capacitive vs inductive loads), which allows calculation of VA (apparent power) and watts (true power), and thus power factor. The ideal way to do this would be to use an ADC that can take more than ~14 samples during on 60Hz cycle, but from my tests, this is plenty accurate for my project.
I appreciate your interest and efforts -- in almost every issue in you projects, you're always engaged and helpful, and that's awesome.
I set the clock to 3.4MHz, and I was able to communicate...
Good to know, tested with which processor?
No problem that you are busy (quite familiar with that :) and cannot test the examples As the examples are pretty straight forward derivates of tested code I expect them to work and hey, it are examples. So I will merge the two ADS1114 examples + fix of other open issue and release a new version. That will close this issue, however if questions arise you can always reopen it . Feel free to report progress if you want.
Nice board BTW
Issue #22 added this handy method for setting the I2C clock, however, it might be incomplete.
Section 9.5.1.3 (page 23) of the datasheet says that while the ADS111x supports all three speed modes, high-speed mode (>400kHz) must be activated:
It'd be pretty slick to see the
ADS1X15::setWireClock()
method perform this activation.