Raspberry Pi ADC (measure analog voltages) using ADS1115
The raspberry pi only has digital inputs. With a capacitor and careful timing you can use that to measure analog voltage, but not with any temporal accuracy. Here I'm using an addon board (using an ADS1115 chip) which can take 860 samples a second with up to 15 bits of resolution.
Steps:
(1) set up i2c bus to connect to ADC
https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c
(2) characteristics of ADS1115 discussed here
http://www.bristolwatch.com/rpi/ads1115.html
(3) This link describes the wiring in pretty good detail and suggests that it's better to power the chip using 3.3v not 5v.
https://leanpub.com/rpcanalog/read#leanpub-auto-analog-to-digital-conversion-adc
(4) The actual wiring I used (breadboard):
https://alantechreview.blogspot.com/2020/04/photodiode-wiring-example-and-resources.html
(5) and now on the pi with the ADS1115 hooked up (5v power). black tube contains a photodiode
100k resistor is hidden inside the blue tape under the ADC board.
Steps:
(1) set up i2c bus to connect to ADC
https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c
(2) characteristics of ADS1115 discussed here
http://www.bristolwatch.com/rpi/ads1115.html
(3) This link describes the wiring in pretty good detail and suggests that it's better to power the chip using 3.3v not 5v.
https://leanpub.com/rpcanalog/read#leanpub-auto-analog-to-digital-conversion-adc
(4) The actual wiring I used (breadboard):
https://alantechreview.blogspot.com/2020/04/photodiode-wiring-example-and-resources.html
(5) and now on the pi with the ADS1115 hooked up (5v power). black tube contains a photodiode
100k resistor is hidden inside the blue tape under the ADC board.
Here's some C code, stolen and modified.
#include
#include
#include
#include
#include // read/write usleep
#include // exit function
#include // uint8_t, etc
#include // I2C bus definitions
#include
long long microsec() { // since epoc
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return ((long long) spec.tv_sec *1000000LL) + (spec.tv_nsec / 1.0e3); // Convert nanoseconds to microseconds (1.0e6 would give milliseconds)
}
// Setup to use ADC0 single ended
int fd;
int asd_address = 0x48; // Note PCF8591 defaults to 0x48!
int16_t val;
uint8_t writeBuf[3];
uint8_t readBuf[2];
float myfloat;
const float VPS = 4.096 / 32768.0; // volts per step
/* Signal Handler for ^C */
void sigint_handler(int sig_num)
{
printf("\n User provided signal handler for Ctrl+C \n");
// power down ASD1115
writeBuf[0] = 1; // config register is 1
writeBuf[1] = 0b11000011; // bit 15-8 0xC3 single shot on
writeBuf[2] = 0b10000101; // bits 7-0 0x85
if (write(fd, writeBuf, 3) != 3) {
perror("Write to register 1");
exit (1);
}
exit(0);
}
/*
The resolution of the ADC in single ended mode
we have 15 bit rather than 16 bit resolution,
the 16th bit being the sign of the differential
reading.
*/
int readval()
{
if (read(fd, readBuf, 2) != 2) // read conversion register
{
perror("Read conversion");
exit(-1);
}
return readBuf[0] << 8 | readBuf[1]; // could also multiply by 256 then add readBuf[1]
}
int main() {
signal(2, sigint_handler); // runs until ^c, this allows ADC cleanup
nice(-20); // as high priority as possible
// open device on /dev/i2c-1
if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) {
printf("Error: Couldn't open device! %d\n", fd);
exit (1);
}
// connect to ADS1115 as i2c slave
if (ioctl(fd, I2C_SLAVE, asd_address) < 0) {
printf("Error: Couldn't find device on address!\n");
exit (1);
}
// set config register and start conversion
// AIN0 and GND, 4.096v, 128s/s
// Refer to page 19 area of spec sheet
writeBuf[0] = 1; // config register is 1
// 76543210
writeBuf[1] = 0b11000010; // continuous, ANC0, amp gain
// bit 7 flag bit for single shot not used here
// Bits 6-4 input selection:
// 100 ANC0; 101 ANC1; 110 ANC2; 111 ANC3
// Bits (3-1) Amp gain. Defaults to 010, here we use 001, see P.19
// Bit 0 Operational mode of the ADS1115.
// 0 : Continuous conversion mode
// 1 : Power-down single-shot mode (default)
//76543210
writeBuf[2] = 0b11100101; // bits 7-0 0x85
// Bits 7-5 data rate default to 100 for 128SPS. 0=8, 111=860
// Bits 4-0 comparator functions see spec sheet.
// begin conversion
if (write(fd, writeBuf, 3) != 3) {
perror("Write to register 1");
exit (1);
}
sleep(1);
// set pointer to 0
readBuf[0] = 0;
if (write(fd, readBuf, 1) != 1) {
perror("Write register select");
exit(-1);
}
long long t;
int count = 0;
int buf[100];
float delta[100];
t = microsec();
while (count < 100)
{
buf[count] = readval();
delta[count]= (microsec()-t)/1000.0;
count++;
}
for (int i = 0; i < 100; i++)
{
printf("Raw %i \t@ %3.3fms \t volts: %3.3f\n",buf[i], delta[i], buf[i]*VPS );
} // end while loop
close(fd);
return 0;
}
Comments