Rpi Pico/AD7994 weirdness - Page 1
Forum section: electronics / general

author: David Aurora
author_rank: Frequent Contributor
timestamp: August 21, 2023, 02:35:18 am
content: I'll preface this by saying I know practically nothing about coding. I've written stuff for Arduinos here and there over the years, but that's it.I'm currently trying to use an AD7994 (4 channel 12 bit I2C ADC) with a Raspberry Pi Pico for a project and I keep hitting the exact same issue. I won't bother posting code here at this point because I've tried all sorts of variations and nothing has worked, so I'm assuming I'm doing something fundamentally wrong and I need a fresh start.I can read in data OK, and manipulate it (with scaling etc) as I want. The problem is that no matter what I try, reading channel 3 always returns the value of channel 1. Even if I start with fresh code that only reads one channel and adjust the code to test channels one at a time, channel 3 will show the value at channel 1's input pin instead.I got suspicious of the ADC I was using so I just replaced it, but nope, the issue is not the hardware. There are no shorted pins or anything else either, I can measure directly at the ADC pins and voltages are as expected- just not in software. I guess the issue must be related to controlling the multiplexer in the ADC, but I don't understand why it works fine for channels 1/2/4 and just not 3.Any ideas on what might cause this? Or suggestions on a test code I could use as a starting point to see if I can get a proper read from all channels with code written by someone who isn't a moron like me?Thanks!

author: MikeK
author_rank: Super Contributor
timestamp: August 22, 2023, 12:51:40 am
content: There's an Arduino library...Did you try that?

author: David Aurora
author_rank: Frequent Contributor
timestamp: August 22, 2023, 07:07:21 am
content: Quote from: MikeK on August 22, 2023, 12:51:40 amNo, as I said I was using this as an opportunity to try out a Pico and start learning a bit of MicropythonIf it was critical I would have moved on in another direction, but as it's kind of a side project/learning thing I'm keen to try to understand what would cause this strange behaviour

author: Shonky
author_rank: Frequent Contributor
timestamp: August 22, 2023, 08:49:08 am
content: Post your absolute bare minimum code. I suspect you've got a bit in the wrong place or similar.

author: David Aurora
author_rank: Frequent Contributor
timestamp: August 22, 2023, 09:49:35 am
content: Quote from: Shonky on August 22, 2023, 08:49:08 amYeah next chance I get I'll put together a super bare bones test and post it here, hopefully the screw up is obvious to someone other than me

author: David Aurora
author_rank: Frequent Contributor
timestamp: March 11, 2024, 02:14:20 am
content: 10 years later I finally got back to thisOK, so I used ChatGPT as a starting point here. Again, I know absolutely nothing about writing Python/micropython stuff so the idea for this project was to start with a very basic code and build/modify from there to learn a bit. Like I know enough to flash some LEDs or perform some calculations or whatever, but absolutely nothing about interfacing with ADCs and so on.This thing is just supposed to read 4 voltages and I'll manipulate that info to do stuff from there. I've successfully written code to do what I want to do with that data multiple times now so apparently I'm not completely braindead, but given I can't get the right data in whatever else I'm writing is useless.After a few tweaks on the ChatGPT starting point the code below basically works (this has none of my data manipulation stuff, just bare bones reading), but with a couple issues:Code:import timefrom machine import I2C, Pin# Define the SDA and SCL pins for the Raspberry Pi Picoi2c = I2C(0, sda=Pin(0), scl=Pin(1)) # Use GPIO 0 for SDA, GPIO 1 for SCLad7994_address = 0x21 # Replace XX with the AD7994 I2C addressreference_voltage = 5 # Reference voltage in volts# Define the CONVST pinconvst_pin = Pin(2, Pin.OUT) # Use GPIO 2 as the CONVST pin# Function to initiate ADC conversiondef initiate_conversion():convst_pin.value(1) # Set CONVST pin hightime.sleep_us(10) # Minimum pulse width for CONVST (10 microseconds)convst_pin.value(0) # Set CONVST pin low# Function to read 12-bit data from the AD7994 and scale to voltagedef read_ad7994(i2c, address):# Read from all 4 channels sequentiallyreadings = []for channel in range(4):initiate_conversion() # Initiate ADC conversiontime.sleep_ms(10) # Wait for the ADC conversion to complete (adjust this delay if needed)i2c.writeto(address, bytes([0x80 | (channel << 4)])) # Write to the config register for the specified channeltime.sleep_ms(1) # Wait for the configuration write to complete (adjust this delay if needed)data = i2c.readfrom(address, 2)value = ((data << 8) | data) & 0xFFFvoltage = (value / 4095.0) * reference_voltagereadings.append(voltage)return tuple(readings)# Main loop to continuously read and print data from the AD7994while True:channel_voltages = read_ad7994(i2c, ad7994_address)print("Channel 1: {:.2f} V".format(channel_voltages))print("Channel 2: {:.2f} V".format(channel_voltages))print("Channel 3: {:.2f} V".format(channel_voltages))print("Channel 4: {:.2f} V".format(channel_voltages))time.sleep(2) # Add a delay between readings (adjust as needed)I've got this set up on a breadboard with a simple test- 1V going into channel 1, 2V going into channel 2, 3V going into channel 3, 4V going into channel 4.Problem 1 is that the order is reversed. So channel 1 prints as 4V/channel 4 prints as 1V, etc.That's not a big deal as I can change that in the print function, but I'd like to understand why this is happening in the first place.Problem 2 is the main problem- channel 2 in the code above (which is actually channel 3 on the ADC) should be printing 3V, but it always prints 1V. The other 3 channels all give the correct readings.There is DEFINITELY 3V on that pin, and I've even swapped IC's to rule out a bad ADC. Something about literally every working variation of the code that I've managed to punch in means it never reads this particular pin properly. And yeah, given that I know jack shit, I'm absolutely stumped as to why.If it helps, data sheet for the ADC is here-https://www.analog.com/media/en/technical-documentation/data-sheets/ad7993_7994.pdfI will absolutely buy a beer/coffee for the first person who can help me fix this/understand what is going on because I'm just bashing my head against the wall here haha

author: shabaz
author_rank: Frequent Contributor
timestamp: March 11, 2024, 03:10:25 am
content: I'm not familiar with the ADC (it seems overly complicated compared to typical ADS111x!), but am OK-ish with MicroPython. It looks like you're using the Mode 1 of operation (page 28 of theAD7994 datasheet.Not 100% sure how to do it, but it looks like you've got to first write to a config register to choose the desired channel, and then pulse the *CONVST pin to power-up the device and initiate the conversion, and then write to the device to select the result register, and then perform the read. Maybe the portions of code below (untested) may help move forward a bit (hopefully!), or it may need further tweaks. I may have misread bits of that datasheet portion, so it may need double-checking.Code:CONV_RESULT_REG = 0x00CONFIG_REG = 0x02def initiate_conversion():convst_pin.value(1) # Set CONVST pin hightime.sleep_us(20) # Minimum pulse width for CONVST (10 microseconds but we can make it 20 to be sure)convst_pin.value(0) # Set CONVST pin lowdef read_ad7994(i2c, address):# Read from all 4 channels sequentiallyreadings = []for channel in range(4):# select the channelb = bytes([CONFIG_REG, 0x08 | 1<<channel+4]) # 0x18/0x28/0x48/0x88 corresponds to channel 0-3i2c.writeto(address, b)# now toggle the CONV pininitiate_conversion() # Initiate ADC conversiontime.sleep_us(10) # Conversion time (could be made shorter later if it works)# set the device to the result (conversion result) registerb = bytes()i2c.writeto(address, b)# now read in the result for that channeldata = i2c.readfrom(address, 2)# get rid of the 4 most significant bitsvalue = ((data << 8) | data) & 0xFFF# scalingvoltage = (value / 4095.0) * reference_voltagereadings.append(voltage)return tuple(readings)

author: David Aurora
author_rank: Frequent Contributor
timestamp: March 11, 2024, 03:20:28 am
content: Quote from: shabaz on March 11, 2024, 03:10:25 amThat worked! Thanks! DM me your PayPal and I'll buy you that drink!I'll read through that code properly later on to try to wrap my thick skull around how it works for next time

author: shabaz
author_rank: Frequent Contributor
timestamp: March 11, 2024, 03:33:48 am
content: Ahh the offer alone is thanks enough, I liked the coding challenge : ) Very happy that it worked out. Thanks for testing it and confirming so quickly!

author: David Aurora
author_rank: Frequent Contributor
timestamp: March 11, 2024, 04:19:48 am
content: In that case let me know a charity/cause you'd like to support and I'll spread the good karma around a bitThanks again!

author: shabaz
author_rank: Frequent Contributor
timestamp: March 11, 2024, 05:31:09 am
content: Great idea! I contribute a [albeit tiny, I should do more] bit to Oxfam monthly, so would be nice to know they got a bit extra this month. Or any other helping-poor-related charity you're ok with. Thanks for this!

author: David Aurora
author_rank: Frequent Contributor
timestamp: March 11, 2024, 10:55:45 am
content: Consider it done