One module, two jobs: read a sensor while holding an analog output

You are building a small controller or test fixture that needs to read a sensor and send an adjustable voltage to another circuit. A module that does both looks convenient: fewer boards, fewer cables and one bus address. The important test comes after each function works on its own. Does the output stay where you set it while the software reads input channels?

We looked at this on our bench with a GraftSense PCF8591 ADC/DAC module, a slider module as the input and a multimeter on the output, driven from a GraftPort-RP2040 controller board running MicroPython. The two paths ran together, and the results also showed what the next experiment needs to be: measure the output throughout the input-reading sequence, rather than assuming two working examples will coexist unchanged.

Keep the input path and the output path separate

The PCF8591 contains an ADC, which turns an input voltage into a number, and a DAC, which turns a number into an output voltage. In our setup the slider fed AIN0, and the program separately requested a fixed output at AOUT. Moving the slider does not change that output; software would have to make that connection explicitly.

That distinction matters for a product. A test fixture could generate a known input for another board. A controller could send an adjustable setpoint to a compatible circuit. Neither application is demonstrated by reading a slider. First decide what the output represents, where it will connect, and what voltage range the receiving input accepts.

On this carrier, AOUT comes out on its own pad and header area, and that header is not fitted by default. Identify AOUT and its ground before attaching a meter; a nearby supply pin is not the programmable output. The carrier schematic shows a 3.3 V converter supply and reference rail next to connectors that also carry 5 V, so do not infer an analog input's allowable voltage from a connector's power pin.

Establish communication, then look at the actual signal

The test program used I2C0 with SDA on GP4, SCL on GP5 and a 100 kHz bus. It scanned addresses 0x48 through 0x4F and found the module at 0x48. It then read AIN0, read all four inputs, wrote the DAC setting and kept printing input voltages.

Only AIN0 was connected. The numbers printed for the other three inputs are not sensor measurements and should never appear as valid channels in a product interface. Moving the slider changed the AIN0 reading, as expected.

One caution about our captures: they are not one reconciled run. An early capture showed DAC code 84 and displayed 4.94 V on AIN0, a scale that does not match the 3.3 V configuration used later with code 128, and the example code itself also contained a 5 V setting. A 4.94 V software label does not prove that voltage was present at an ADC input powered from 3.3 V. Before reproducing the test, measure the rails and the input signal and use matching software settings. That is more useful than adjusting the scale factor until the display looks plausible.

Write a code, then measure AOUT

For the 3.3 V configuration, the program requested 1.65 V and wrote code 128. The console recorded the command:

=== Example 3: DAC output specified voltage ===
DAC output set to 1.65 V (Analog Value: 128)

The NXP PCF8591 datasheet gives the DAC transfer relationship over 256 steps across the reference span. With analog ground at zero and a 3.3 V reference, code 128 gives an ideal 1.65 V, and code 255 is one step below the reference. Real output adds component and circuit errors.

The driver calculates the code with round(target_voltage * 255 / reference_voltage). At this midpoint it happens to produce 128, but 255 is not the denominator in the datasheet's output formula. Log the integer sent to the device as well as the requested voltage, so the calculation can be checked independently.

With the meter on AOUT and ground, we read 1.660 V. That is close to the requested midpoint, but it is one point, without a recorded reference-voltage measurement, meter uncertainty or connected load. It does not establish accuracy across the range, and it does not prove a receiving circuit will see the same voltage.

Run the input-reading sequence while watching the output

The PCF8591 also uses its internal DAC during ADC conversion. Its output buffer has a hold function so the external output can be maintained during that work. The control byte still matters: input selection and output enable share the same configuration byte, so an input-reading routine has to preserve the intended output state.

A November 2012 Arduino forum thread describes trouble using the input and output functions together. Take the question from it, not the conclusion that either function is faulty on every board. For your prototype, hold one output setting, cycle through the inputs, and watch AOUT throughout. Use a scope if a brief disturbance matters; a steady meter display can miss one.

There is a concrete behavior to check in the driver we used: analog_read_all() sets its software output state to enabled. A function that sounds like "read the inputs" also chooses an output state. If your fixture should keep its output disabled until the user starts a test, verify that scanning the inputs does not override that. We found this by reading the code; we have not reproduced a hardware failure from it.

Then repeat the combined sequence with the receiving input attached. Loading is a separate variable: a 2015 Arduino forum report about a different PCF8591 carrier traced a reduced output to an LED and resistor load on the board. Do not copy its board modification blindly. Record the connected load and whether changes occur when the load is attached, when an ADC read starts, or when a new control byte is sent. Each of those points to a different cause.

Reading the output back is another experiment

Our test read a slider and wrote a fixed output. It did not wire AOUT back to an ADC input, and it did not run a loop that adjusts the output to correct a measured error. Calling it a validated closed-loop controller would skip those steps.

A loopback test could connect a suitable, in-range output to an input and compare the reading with an external meter. The code has to handle the converter's read sequence: the first byte in a read cycle is the result of the previous conversion. The driver has separate single-channel and multi-channel read paths, so verify which result belongs to which channel before using a returned value as feedback.

Keep that test predictable: hold a fixed DAC code, read a known input, change channels, and confirm which sample belongs to which channel before adding automatic correction. A readback that uses the same reference also cannot replace an independent check of the absolute voltage. We have not run these follow-up measurements yet.

Make the requested value and the observed value visible

In a prototype interface or test log, label the command and the measurement separately. "Requested: 1.65 V" and "meter: 1.660 V" tell the next person what happened. A single field called "output voltage" hides whether the value was calculated, returned by an ADC or measured externally.