Arduino Due analogRead() giving erratic values

Hi. I am using an Arduino Due.

I am trying to monitor the output of an x-ray tube. The x-ray generator is pulsatile, and produces an integer number of x-ray pulses 1/120 s in duration, one after another. My approach is to place a photodiode (I have an OSRAM BPW-34) in the beam and collect and measure the total charge liberated in the photodiode by the x rays. The charge amplifier circuit is shown below:

The op amps are two sides of an LMC6482 IC (datasheet: http://www.ti.com/lit/ds/symlink/lmc6482.pdf). The photodiode is zero-biased. The output of the voltage follower is connected to analog pin A0.

First, to check if the charge amplifier circuit is working as expected, I disconnected the voltage follower output from A0 and connected an oscilloscope probe at that node. I set the generator to give 4 pulses and took a photo of the oscilloscope trace (below). The centre horizontal line is 0V. The trigger level is a little high so the trace starts above 0 V.

The curve looks as expected: the cumulative integral of the pulses. After reaching a maximum voltage (here seen to be about 0.5 V), the voltage decays with a time constant of 1 s. Repeating exposures several seconds apart, the oscilloscope trace had the same shape each time, and consistently reached a max voltage of 0.5 or 0.49x V. This indicates to me that the circuit minus the Arduino works as expected.

Next, I disconnected the oscilloscope probe and reconnected the analog pin. Here is the relevant part of the code for exposure and analog read. The voltage follower output should be sampled by the Arduino just after the peak of the voltage trace.

const int MONITOR_DIODE = A0;
const int EXPOSURE_SWITCH = 24;

// monitor diode value
int diodeVoltage = 0;

// these delays ensure that the exposure switch remains closed for the entire duration of the exposure pulses
const int relayDelayTime = 10; // milliseconds
const int zeroCrossingDelayTime = 9; // milliseconds

// delay timing variables
unsigned long integrationPreviousMillis = 0;
unsigned long integrationCurrentMillis = 0;

void setup() {

  pinMode(MONITOR_DIODE, INPUT);
  pinMode(EXPOSURE_SWITCH, OUTPUT);
  digitalWrite(EXPOSURE_SWITCH, HIGH); // exposure switch is open when high
}

void expose(int integrationTime) { // integrationTime in milliseconds
  
  // close exposure switch for the integration time
  digitalWrite(EXPOSURE_SWITCH, LOW);

  integrationPreviousMillis = millis();
  integrationCurrentMillis = integrationPreviousMillis;
  while (integrationCurrentMillis - integrationPreviousMillis < (integrationTime + relayDelayTime + zeroCrossingDelayTime)) {
    integrationCurrentMillis = millis();
  }
  digitalWrite(EXPOSURE_SWITCH, HIGH); // open exposure switch
  
  // read voltage from monitor diode
  diodeVoltage = analogRead(MONITOR_DIODE);
}

Assuming the time to execute the digitalWrite() and analogRead() functions is negligible, the time between the actual end of exposure and when the analogRead() function is executed can vary by up to 10 ms (due to the random nature of the zero crossing delay), and so the voltage at the output can vary by up to 1%.

Here are the actual values that the ADC reported, for 20 exposures spaced 5 s apart:

614, 638, 619, 450, 645, 544, 473, 580, 486, 531, 531, 563, 631, 603, 593, 510, 617, 500, 473, 570

or the corresponding voltages (in V):

0.4948, 0.5141, 0.4988, 0.3626, 0.5198, 0.4384, 0.3812, 0.4674, 0.3916, 0.4279, 0.4279, 0.4537, 0.5085, 0.4859, 0.4779, 0.4110, 0.4972, 0.4029, 0.3812, 0.4593

So for some readings it dropped by 75-80% and then jumped back up to the expected range and so on. While I do expect some variation in the x-ray output (which is why I am trying to measure it in the first place), these readings are far too erratic, and the earlier oscilloscope measurements seem to confirm that.

I wondered if perhaps the analog read was perturbing the voltage on the output node so I connected the analog pin and the oscilloscope at the same time. The oscilloscope showed the same consistent shape and max voltage while the ADC reading varied erratically. The oscilloscope voltage decayed slowly but smoothly to 0 after the max voltage was reached, which suggests that the voltage on the output node is not being perturbed by the analog read.

To see if the analog pin is otherwise working as expected, I connected it to +3.3V and it read 4095 every time. I connected it to GND and it read 0 every time. I used a voltage divider (1.2 kOhm and 1 kOhm) and it read 1795 +/- 1 every time. When reading the voltage follower output when there had been no x-ray exposure, the ADC reports a value of 0 every time. So the analog pin does seem to be working properly.

I also tried changing the analog pin and swapping out the op amp. As I saw suggested in some other threads, I tried grounding all of the other analog pins and doing multiple sequential analog reads and keeping the last value.

I am stumped as to what the cause of the erratic reading may be. If anybody has any ideas and possible fixes, I would greatly appreciate it.

Your post reminds me of my early days in electronics installing and repairing X-Ray machines and later the first-gen of CAT scanners.

To me, an X-ray generator was a vacuum tube with a rotating anode of beryllium and a chest-sized high-voltage (100-150 kV) transformer. Have things changed much?

Do you have access to a lab power supply? I would try using a good stable voltage to replace the photodiode- a voltage source not from the Arduino.

Next, can you expand the horizontal to make sure there is no AC component on the output of the amplifier. If there is, that could explain the variable A0 readings. Try adding a small capacitor on the output of the amp.

Here are a few setup)_ entries I made with a DUE that seemed to work out quite well for stableand quick readings

 ADC->ADC_MR |= 0x80;  //set free running mode on ADC
  REG_ADC_MR = (REG_ADC_MR & 0xFFF0FFFF) | 0x00020000; // adc start up value
  // ADC->ADC_CHER = 0x80; //enable ADC on pin A0

I not really sure what they mean at this moment but I am sure you can figure it out.

You can get a bit quicker reads of the GPIO digital pins by using direct read/writes

// example: digitalWriteDirect(13, HIGH)
inline void digitalWriteDirect(int pin, boolean val)
{
  if (val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}
// example: digitalReadDirect(12)
inline int digitalReadDirect(int pin)
{
  return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
}

You might want to trigger the DUE to do the analog reads from a signal from the x-ray machine. I am sure that there is a anode up to speed signal, which then triggers the x-ray to pulse, which could be tapped to interrupt the DUE to take reading(s).

Some tips for more accurate 12-bit analog readings with the DUE:

You want to power your DUE board thru the jack with a battery pack because powering it via the USB cable gives an unstable voltage reference. Of course you can use the programming port to output values to the Monitor, but this way the PC won't power the board.

In between 2 measures from (e.g. A0), read a second analog input (e.g. A1) connected to ground through a small resistor (say 100 ohms). This ensures that the S&H capacitor is purged of residual charges between readings. The resistor is important to reduce current flow, but you want it small enough that the capacitor discharges in a reasonable time.

For more precise measures and timings, don't use arduino functions but direct register programming (as stated in the previous post). Search for ADC conversions sketches with direct register programming in the DUE sub forum, idem for PIO set/clear.

Thanks for the replies.

SteveMann:
To me, an X-ray generator was a vacuum tube with a rotating anode of beryllium and a chest-sized high-voltage (100-150 kV) transformer. Have things changed much?

The x-ray generator in our lab is from 1975 and the x-ray tube is from the early 80s so the equipment may be similar to what you worked with in the past. X-ray tubes haven't changed all that much in the last 60 years but the generators have. Ours is a single-phase full-wave rectified generator (which, since the line voltage is 60 Hz in Canada, is why it produces 1/120 s pulses) and the transformer is a large 600 lbs chest. These days the generators are high frequency and the transformers are smaller. While some tubes have beryllium windows, I suspect the tubes you worked with actually had tungsten or molybdenum anodes.

Idahowalker:
You might want to trigger the DUE to do the analog reads from a signal from the x-ray machine. I am sure that there is a anode up to speed signal, which then triggers the x-ray to pulse, which could be tapped to interrupt the DUE to take reading(s).

There is a rotor inhibit signal but the way I have things set up, I don't think it can be of much use for interrupting the Due to take an analog reading. I can provide more information if desired but the gist is that the rotor is controlled separately from the exposure (via a digital output named ROTOR_SWITCH) and the rotor gets up to speed 100s of milliseconds (a not-so-predictable amount of time) before the exposure switch is closed.

SteveMann:
Next, can you expand the horizontal to make sure there is no AC component on the output of the amplifier. If there is, that could explain the variable A0 readings. Try adding a small capacitor on the output of the amp.

This is still with the photodiode, but decreasing the time per division doesn't seem to reveal an AC component.

1 ms/div, between 3rd and 4th pulse, 0 V is now the horizontal line above the division size labels:

0.1 ms/div, 3rd pulse:

1 us/div, after last pulse:

I first tried the easier fixes.

ard_newbie:
You want to power your DUE board thru the jack with a battery pack because powering it via the USB cable gives an unstable voltage reference. Of course you can use the programming port to output values to the Monitor, but this way the PC won't power the board.

So I have been powering the Due over USB since I was concerned that the estimated power dissipation in the regulator was a little over 1 W with an unregulated adapter that I had. I did find a regulated 12 V AC/DC adapter (estimated power dissipation 0.75 W), however. Unfortunately the fluctuations in the analog read did not go away when I plugged it in.

ard_newbie:
In between 2 measures from (e.g. A0), read a second analog input (e.g. A1) connected to ground through a small resistor (say 100 ohms). This ensures that the S&H capacitor is purged of residual charges between readings. The resistor is important to reduce current flow, but you want it small enough that the capacitor discharges in a reasonable time.

Unfortunately this did not fix the problem.

Idahowalker:
Here are a few setup)_ entries I made with a DUE that seemed to work out quite well for stableand quick readings

I added these lines to the end of my setup function but it didn't help. As a side effect, when I connected the analog pin to GND, rather than reading 0 each time, it reported values between 17 and 40.

Next I will put a function generator or bench supply in place of the photodiode and also read more about direct register programming and look at the ADC section in the ATSAM3X8E datasheet.

Yes, molybdenum. It's been that long. (I even have an old tube in my basement).

Like you, I like to know what is happening rather than compensate for the problem. But, have you considered averaging the readings?

Have you tried powering the Arduino with a battery?