shabaz designed a Data Acquisition Board for Pi Pico . In this post, I'm using it in continuous sample mode. Goal: get 1 second worth of samples, in 860 samples per second mode.
There are several options to trigger the reading of the samples. You could set up a timer that fires every 1162 µs. Then let it kick off a read. There is a 10% variation though - my device clocks at 850 samples, approx 2% slower.
I've used another option that isn't dependent on the exact data rate: the ADS1115 has a RDY pin. I've configured it to pulse each time a sample is ready.
I modified the PCB, and connected the RDY pin to GP13 with a bodge wire.
The RDY pin needs a pull-up. I tested if the RP2040 internal pull-up has enough oomph: yes.
In my code, I attached an interrupt to the rising edge of GP13. Each time, it 'll initiate a read over i2c.
Highlights of the code (it's attached, with *.uf2 firmware precompiled)
volatile bool rdy = false; void rdy_callback(uint gpio, uint32_t events) { rdy = true; } // board initialisation void board_init(void) { // ... // listener for ADC RDY gpio_init(13); gpio_pull_up(13); gpio_set_dir(13, false); gpio_set_irq_enabled_with_callback(13, GPIO_IRQ_EDGE_RISE, true, &rdy_callback); // ... } void adc_enable_rdy(uint8_t boardnum) { uint8_t buf[3]; buf[0] = ADS1115_REG_LO_THRESH; buf[1] = 0x00; buf[2] = 0x00; //Lo_thresh MS bit must be 0 i2c_write_blocking(i2c_port, adc_addr[boardnum], buf, 3, false); buf[0] = ADS1115_REG_HI_THRESH; buf[1] = 0x80; buf[2] = 0x00; //Hi_thresh MS bit must be 1 i2c_write_blocking(i2c_port, adc_addr[boardnum], buf, 3, false); } // ************ main function ******************* int main(void) { stdio_init_all(); board_init(); build_data_rate(BOARD0, DR_860); build_gain(BOARD0, AIN1, GAIN_2_048); // +- 2.048V #define SAMPLES 860 uint16_t buf[SAMPLES]; build_cont_conversion(0); adc_set_mux(BOARD0,0,false); adc_enable_rdy(0); uint8_t* buf8_ptr = (uint8_t*)buf; *buf8_ptr = ADS1115_REG_CONVERSION; i2c_write_blocking(i2c_port, adc_addr[BOARD0], buf8_ptr, 1, false); uint8_t* buf8_end_ptr = ((uint8_t*)buf) + (sizeof buf); rdy = false; while (buf8_ptr < buf8_end_ptr) { if (rdy) { rdy = false; i2c_read_blocking(i2c_port, adc_addr[BOARD0], buf8_ptr, 2, false); buf8_ptr += 2; } } uint16_t* buf16_ptr = buf; uint16_t* buf16_end_ptr = buf + (sizeof buf) / 2; while (buf16_ptr < buf16_end_ptr) { *buf16_ptr = *buf16_ptr >> 8 | *buf16_ptr << 8; // swap bytes printf("AIN1 = %.3f V\n", to_volts(BOARD0, 0, *buf16_ptr)); buf16_ptr++; } while (FOREVER) {} }
I didn't include the definitions and functions of shabaz ' source code example. They are available in the download.
The data collection loop is made as short as possible. I set the i2c baud rate to 1 MM. And removed the bit swapping and voltage conversion from the sample loop. I made a second loop that does these conversion activities once all data is collected.
This isn't needed for timekeeping. There's enough time for slower i2c , and to do bit swapping and voltage conversion.
What we can gain by taking them out, is to let the controller go low power while waiting for a next pulse. Not implemented, but worth investigating.
Enjoy!
Source and firmware:
adc_board_test_20231125.zip