Measuring a Pulse Width

Introduction

The demonstration shows how a macro can be used to optimise code by compiling it inline.

When measurement of a pulse width to sub-microsecond resolution is required - for instance, measuring the high or low pulse width of an incoming analog signal - a comparator can be combined with a timer to provide the pulse width.

Microchip PIC has published a "Compiled Tips 'N Tricks Guide" that explains how to do certain tasks with Microchip PIC 8-bit microcontrollers.

This guide provides the steps needed to perform the task of measuring a pulse width. It provides guidance on measuring a pulse width using Timer 1 and the CCP module. This guidance was used as the basis for the GCBASIC port shown below. The guidance was generic, and in this example, polling the CCP flag bit was more convenient than using an interrupt.

In the demonstration shown below, a 16F1829 microcontroller operating at 32 MHz uses the internal oscillator. The demonstration code is based on a macro that uses Timer1 and CCP4. However, any of the four CCP modules could be used, as the 16F1829 microcontroller has four CCP modules.

The timer resolution of this method uses a timer prescaler of 1:8 and a microcontroller frequency of 32 MHz, giving a pulse width resolution of 1us. With a timer prescaler of 1:2 and the microcontroller frequency of 32 MHz, the resolution is 250 ns.

The accuracy is dependent upon the accuracy of the system clock, but oscilloscope measurements have shown an accuracy of +/- 1us from 3us to 1000us.

In this demonstration the following was implemented:

  • GCBASIC is used with a macro to ensure the generated assembler is inline, so the timing is consistent and no subroutines are called.
  • Another microcontroller was used to generate the pulses to be measured.
  • A TEK THS730A oscilloscope was used to measure/verify pulse widths.
  • A 4x20 LCD module with an I2C backpack was used to display the results. However, as an alternative, a serial output
    to a terminal program to view the data could be used.

This demonstration could be improved by adding code to poll the TIMER1 overflow flag. If the timer overflows, then either no pulse was detected, or the pulse was longer than allowed by the prescaler/OSC settings. In this case, return a value of zero for pulse width.

Usage:

To get the positive pulse width, use:

    PULSE_IN

PULSE_IN returns a global word variable, Pulse_Width.

Demonstration Program:

    #Chip 16F1829, 32
    #CONFIG MCLRE = OFF

    'Setup Software I2C
    #define I2C_MODE Master
    #define I2C_DATA PORTA.2
    #define I2C_CLOCK PORTC.0
    #define I2C_DISABLE_INTERRUPTS ON

    'Set up LCD
    #define LCD_IO 10
    #define LCD_WIDTH 20                ;specified lcd width for clarity only.  20 is the default width
    #define LCD_SPEED FAST
    #define LCD_Backlight_On_State  1
    #define LCD_Backlight_Off_State 0

    'Note: This example can be improved by adding code to poll the 'TIMER1 overflow flag. IF the timer overflows, then either no 'pulse was detected or the pulse was longer than allowed by the 'prescaler/OSC settings. In this case, return a value of zero 'for pulse width.

    CLS
    PRINT "Pulse Width Test"
    DIM PULSE_WIDTH AS WORD
    DIR PORTC.6 IN

    'Setup timer
    'Set timer1 using PS1_2 gives 250ns resolution
    InitTimer1 OSC, PS1_8
    wait 1 s
    CLS

    'MAIN PROGRAM LOOP
    DO
      PULSE_IN    'Call the Macro to get positive pulse width.          ' <<< invoking a macro so the timing-critical code runs inline
      Locate 0,0
      PRINT Pulse_Width
      PRINT "    "
      wait 1 s
    Loop

    MACRO PULSE_IN  'Measure Pulse Width
      'Configure CCP4  to Capture rising edge
       CCP4CON = 5   'Set to 00000101
       StartTimer 1
       CCP4IF = 0

       do while CCP4IF = 0    'Wait for rising edge
       loop

       TMR1H = 0: TMR1L = 0   'Clear timer to zero
       CCP4IF = 0             'Clear flag


       'Configure CCP4 to Capture Falling Edge
       CCP4CON = 4  '00000100'

       do while CCP4IF = 0   'Wait for falling edge
       loop

       StopTimer 1            'Stop the time
       Pulse_Width = TIMER1   'Save the timer value
       CCP4IF = 0             'Clear the CCP4 flag
    End MACRO

Key line: PULSE_IN 'Call the Macro to get positive pulse width. — because PULSE_IN is a macro, this call is expanded inline at compile time rather than becoming a subroutine call; this removes the call/return overhead and its variable timing jitter, which matters here since the code between the rising and falling edge polls directly determines the measured pulse width.

See Also: