Noisy Duty Cycle

The previous page discussed some issues with the infrared emissions of the test apparatus. The test apparatus includes a multimeter (for current and frequency measurement) as well as an oscilloscope (for delay measurement and detector output analysis). But, what I didn’t say was that one more test tool was necessary to generate the distance graph for the PNA4602M and TSOP4038.

There is a period between 100% detection and 0% detection where the output of the infrared detector fluctuates. If the digital value is high half the time and low half the time, we say it is 50% duty cycle. However, we usually express duty cycle as it relates to a nice stable-frequency wave (left below) as opposed to a seemingly random variable-frequency wave (right below).

Test apparatus emitting 38 kHz with 50 percent duty cycle      PNA4602 approximately 50 percent detection

Two examples of 50% duty-cycle square wave. Left: Regular, clean, repeating equal widths. Right: Noisy, inconsistent.

Nevertheless, both waveforms have a ratio where the total “up” time is about half of the total time. If you were to capture the time between the leading edge and trailing edge of a pulse, and divide that by the time between the leading edge and the next leading edge, you’d end up with the wrong answer on the image in the above right. Or, the value would be constantly changing. Such noisy signals can be difficult to measure using the duty-cycle feature of some multimeters.

To determine the correct result, I added an extra mode to my Baud Rate Detector to measure random duty cycle. Rather than timing wave edges, the tool takes a large number of samples (250,000+) in a period of one second, and simply divides the high samples by the total samples. Theoretically, this tool will measure any digital signal from 60 hertz to at least 125,000 hertz. Because other work in the microcontroller randomly shifts each moment of sampling, my tool potentially works up to half the crystal speed (currently 22 megahertz).

LED screens of the random duty cycle measurement tool

LED screens of the random duty cycle measurement tool.

The sampling code is really simple. The display code is a little more complex.

unsigned long gSamplesHigh; // 32-bits
unsigned long gSamplesTotal; // 32-bits

// Repeatedly sample somewhere in the main loop
if ( PinIsHigh(TIMER_CAPTURE_PORTIN, TIMER_CAPTURE_PIN) )
{
    gSamplesHigh++;
}// OOPS! This is wrong

gSamplesTotal++;


// Call approximately once a second (timing not critical)
// Tries to output sample result on the LED display
// as a percentage with two decimal places.
void ShowDutyCycle(void)
{
#define ALPHANUMERIC_SCREEN_LIMIT	7

    __flash char* messagePtr = null;

    unsigned short dutyCycle = 0;
    Byte decimalPlaces = 0;

    // String must hold up to 32-bit unsigned number
    // 2^32 expressed in decimal is 4294967295
    // That is "4294967295" + end-of-string
    char outputAsString [ 10 + 1 ];

    if ( gSamplesTotal == 0 )
    {
        // No samples collected yet.
        messagePtr = "Working";
    }
    else if ( gSamplesHigh > gSamplesTotal )
    {
        // Error. Should never happen.
        messagePtr = "HighErr";
    }
    else if ( gSamplesHigh > 42949672 ) // = 4294967295 / 100
    {
        // Error. Should never happen.
        // Can’t multiply by 100 to make into a percentage.
        messagePtr = "TooMany";
    }
    else // Show duty cycle
    {
        // Can we multiply by four places (10000) without overflow?
        if ( gSamplesHigh > 429496 ) // = 4294967295 / 10000
        {
            // No. So no decimal places. Just straight percentage.
            dutyCycle = (unsigned short)((gSamplesHigh*100)/gSamplesTotal);
        }
        else
        {
            // Yes. Show two decimal places.
            dutyCycle = (unsigned short)((gSamplesHigh*10000)/gSamplesTotal);
            decimalPlaces = 2;
        }

        CL_FloatingPointDecimalStringFromUnsignedShort ( outputAsString, dutyCycle,
                                                         decimalPlaces );

        /*
        CL_SerialTransmitConstString("Samples: ");
        CL_SerialTransmitSignedDecimalLong((long)gSamplesHigh,0);
        CL_SerialTransmitConstString(" / ");
        CL_SerialTransmitSignedDecimalLong((long)gSamplesTotal,0);
        CL_SerialTransmitConstString(" = ");
        CL_SerialTransmitString(outputAsString);
        CL_SerialTransmitConstString("%");
        CL_SerialTransmitCRLF();
        */

        CL_StringPad ( outputAsString, ALPHANUMERIC_SCREEN_LIMIT-1, ' ' );
        outputAsString[ALPHANUMERIC_SCREEN_LIMIT-1] = '%';
        outputAsString[ALPHANUMERIC_SCREEN_LIMIT] = EOS;
    }

    if ( messagePtr != null )
    {
        CL_CopyStringFromConstStringLimit(outputAsString,
                ALPHANUMERIC_SCREEN_LIMIT, messagePtr);
    }

    // Display on LED
    CL_AlphanumericCodesFromString(outputAsString);

    // Start sampling again
    gSamplesTotal = 0;
    gSamplesHigh = 0;
}
			

The sampling algorithm could be adapted to measure analog waveforms, such as sine waves or true noise, without altering the display routine. However, the frequency rate would drop considerably given the long analog-to-digital sampling time on an 8-bit microcontroller. Using an external ADC component would be an improvement.

Replacement for PNA4602M

In conclusion, the testing demonstrates that the Vishay TSOP4038 is an excellent replacement for the discontinued Panasonic PNA4602M infrared detector for purposes of object detection. The TSOP4038 has the same pinout, 38 kHz carrier frequency, 950 nm peak spectral sensitivity, and continuous detection capability. Furthermore, the TSOP4038 has the following advantages:

The last two benefits could possibly cause incompatibilities in existing circuits. The only disadvantage discovered in the comparison is the longer latency to detect the start and end of a signal.

Like many robot builders, I’m grateful to Vishay for creating this part. Now, go out and buy a bunch!