daqifi/daqifi-nyquist-firmware

Convert Differential ADC Measurements to Signed Voltage Value

Closed this issue · 8 comments

Differential measurements are not properly converted to Volts through the SCPI command.

To reproduce the error, connect a battery (<=5V) or other floating source positive to pin 9 and negative to pin 8. This should create a negative differential voltage when reading channel 8. However the response needs to be converted (from 2's compliment) or the ADC output format changed:
SYSTem:POWer:STATe 1
CONFigure:ADC:CHANnel 8,1
CONFigure:ADC:CHANnel 9,1
CONFigure:ADC:SINGleend 8,0
CONFigure:ADC:SINGleend 9,0
MEASure:VOLTage:DC? 8

-2.8V across pin 8 and 9 results in a value of 5242878.67675781.

Although I have added code to covert to 2's complement still not giving a correct reading.
after adding 2's complement :
(connecting a AA alkaline cell, +ve to ch 8 and -ve to channel 9)
channel 9 reads : 0
channel 8 reads: randomly between 0.7 v to 1.3v
(connecting a AA alkaline cell, +ve to ch 9 and -ve to channel 8)
channel 9 reads : ~0.85
channel 8 reads: approx -1.2

i added this part to mc12badc.c

static double Convert2sComplementToSignedInt(uint32_t uVal) {
    if (uVal & 0x80000000) {
        uVal = (~uVal) + 1;
        return -(double)uVal;
    } else {
        return (double)uVal;
    }
}
double MC12b_ConvertToVoltage(                                              \
                        const MC12bChannelConfig* channelConfig,            \
                        const AInRuntimeConfig* runtimeConfig,              \
                        const AInSample* sample)
{
    
    double dataOut = 0.0;
    double range = pModuleRuntimeConfigMC12->Range;
    double scale = channelConfig->InternalScale;
    double CalM = runtimeConfig->CalM;

    dataOut = ( range * scale * CalM * Convert2sComplementToSignedInt(sample->Value))/              \
                        (pModuleConfigMC12->Resolution) + runtimeConfig->CalB;
    return (dataOut);
}

I think the MC12b_ConvertToVoltage function needs to be slightly revised:

double MC12b_ConvertToVoltage(                                              \
                        const MC12bChannelConfig* channelConfig,            \
                        const AInRuntimeConfig* runtimeConfig,              \
                        const AInSample* sample)
{
    
    double dataOut = 0.0;
    double range = pModuleRuntimeConfigMC12->Range;
    double scale = channelConfig->InternalScale;
    double CalM = runtimeConfig->CalM;
    bool isDiff = runtimeConfig->IsDifferential;
    
    if(isDiff)
    {
        dataOut = ( range * scale * CalM * 2 * Convert2sComplementToSignedInt(sample->Value))/              \
                        (pModuleConfigMC12->Resolution) + runtimeConfig->CalB;
    }
    else
    {
        dataOut = ( range * scale * CalM * (double)sample->Value)/              \
                            (pModuleConfigMC12->Resolution) + runtimeConfig->CalB;       
    }
    return (dataOut);
}

Note, the scalar 2 in the differential measurement. This is related to the range comment for differential measurements. However, I think there are bigger issues with the way the input is divided before the ADC. Essentially, the divider causes the input to be centered at 0V which will clip half of the signal.

ss1
ss2
ss3
I found another major bug, when _Streaming_Deferred_Interrupt_Task runs it calls Streaming_TriggerADC(&pBoardConfig->AInModules.Data[i]) after checking if the pBoardData->AInState.Data[i].AInTaskState == AINTASK_IDLE, whihc in truns calls ADC_TriggerConversion function, which in turn calls BoardData_Set to set the AInTaskState to AINTASK_CONVSTART.
Inside the function BoardData_Set there is a checking

if( index < g_BoardData.AInState.Size )
           {
               memcpy(                                                     \
                           &g_BoardData.AInState.Data[ index ].AInTaskState,\
                           pSetValue,                                      \
                           sizeof(AInSample) );
           }

but is g_BoardData.AInState.Size never initialized so its always zero, thus this if statement is never true and AInTaskState is never set to AINTASK_CONVSTART , ie its always AINTASK_IDLE, thus there is a chance that another conversion might start before the previous is fished

I think the MC12b_ConvertToVoltage function needs to be slightly revised:

double MC12b_ConvertToVoltage(                                              \
                        const MC12bChannelConfig* channelConfig,            \
                        const AInRuntimeConfig* runtimeConfig,              \
                        const AInSample* sample)
{
    
    double dataOut = 0.0;
    double range = pModuleRuntimeConfigMC12->Range;
    double scale = channelConfig->InternalScale;
    double CalM = runtimeConfig->CalM;
    bool isDiff = runtimeConfig->IsDifferential;
    
    if(isDiff)
    {
        dataOut = ( range * scale * CalM * 2 * Convert2sComplementToSignedInt(sample->Value))/              \
                        (pModuleConfigMC12->Resolution) + runtimeConfig->CalB;
    }
    else
    {
        dataOut = ( range * scale * CalM * (double)sample->Value)/              \
                            (pModuleConfigMC12->Resolution) + runtimeConfig->CalB;       
    }
    return (dataOut);
}

Note, the scalar 2 in the differential measurement. This is related to the range comment for differential measurements. However, I think there are bigger issues with the way the input is divided before the ADC. Essentially, the divider causes the input to be centered at 0V which will clip half of the signal.

Yes, I though the same because the there is a input divider referenced to ground

I was able to figure out the problem, I removed REP5_5 resistor array and sorted the input and out, then connected the battery to 8 and 9 and it +/-1.5 depending on polarity.
So i think without iterating the hardware we wont be able to give support for differential input.

I was able to figure out the problem, I removed REP5_5 resistor array and sorted the input and out, then connected the battery to 8 and 9 and it +/-1.5 depending on polarity. So i think without iterating the hardware we wont be able to give support for differential input.

Agreed. Let's clean up the modifications and keep them in the code for future use.