Explanation:
The PulseOut command is a reliable method for generating pulses if accuracy is not critical;
the PulseOut command uses a calculation of the clock speed for the timing.
If you need better accuracy and resolution, then an alternative approach is required.
To generate pulses in the 100 us to 2500 us range, with an accuracy of +/- 1us over this range, is practical using the approach shown in this example.
This example code works on a midrange PIC16F690 operating at 8MHz. However, it should work on any Microchip PIC microcontroller, but may need some minor modifications.
Usage:
Pulse_Out_us ( word_value )
How It Works:
Timer1 is loaded with a preset value based upon the variable passed to the subroutine.
The timer (Timer1) is started and the pulse pin (the output pin) is set high.
When Timer1 overflows, the Timer1 interrupt flag bit (TMR1IF) is set. This causes the program to
exit a polling loop and set the pulse pin off. Then, Timer1 is stopped, the TMR1IF flag is cleared, and the subroutine exits.
This method supports delays between 5 us and 65535 us, and uses Timer1.
Test Results:
These tests were completed using a Saleae Logic Analyzer.
| Pulse setting | Time Results |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Demonstration program:
;**************************************
; Code: Output an accurate pulse
; Author: William Roth 03/13/2021
;**************************************
#chip 16F690,8
; ---- Define Hardware settings
; ---- Define I2C settings - CHANGE PORTS AS REQUIRED
#define I2C_MODE Master
#define I2C_DATA PORTB.4
#define I2C_CLOCK PORTB.6
#define I2C_DISABLE_INTERRUPTS ON
; ---- Set up LCD - Using I2C LCD Backpack
#define LCD_IO 10
#define LCD_WIDTH 20 ;specified lcd width for clarity only. 20 is the default width
#define LCD_I2C_Address_1 0x4e ; default to 0x4E
; ---- May need to use SLOW or MEDIUM if your LCD is a slower device.
#define LCD_SPEED Medium
#define LCD_Backlight_On_State 1
#define LCD_Backlight_Off_State 0
CLS
; ---- USART settings
#define USART_BAUD_RATE 38400
#define USART_TX_BLOCKING
DIR PORTB.7 OUT
; ---- Setup Pulse parameters
#define PulsePin PORTC.4
Dim Time_us As WORD
Dir PulsePin Out 'Pulsout pin
Set PulsePin off
; ---- Setup Timer
InitTimer1 Osc, PS1_2 'For 8Mhz Chip
'InitTimer1 Osc, PS1_4, 'For 16 Mhz Chip
TMR1H = 0: TMR1L = 0 'Clear timer1
TMR1IF = 0 'Clear timer1 int flag
TMR1IE = on 'Enable timer1 Interrupt (Flag only)
' **** This is the MAIN loop *****
Do
PULSE_OUT_US (2500) 'Measured as 2501.375 us ' <<< invoking the timer-based precision pulse subroutine
wait 19 ms
Pulse_Out_US (1000) 'Measured as 1000.750 us
wait 19 ms
Pulse_Out_US (100) 'Measured as 100.125 us
wait 19 ms
Pulse_Out_US (10) 'Measured as 10.125 us
Wait 19 ms
loop
SUB PULSE_OUT_US (IN Variable as WORD)
TMR1H = 65535 - Variable_H 'Timer 1 Preset High
TMR1L = (65535 - Variable) + 4 'Timer 1 Preset Low
Set TMR1ON ON 'Start timer1
Set PulsePin ON 'Set Pin high
Do While TMR1IF = 0 'Wait for Timer1 overflow
Loop
Set PulsePin off ' Pin Low
Set TMR1ON OFF ' Stop timer 1
TMR1IF = 0 'Clear the Int flag
END SUBKey line: PULSE_OUT_US (2500) 'Measured as 2501.375 us — presets Timer1 so that it overflows after almost exactly 2500us, then polls TMR1IF in a tight loop to catch the overflow with minimal jitter; this timer-preset approach is what achieves the +/- 1us accuracy
that plain PulseOut cannot guarantee.
See Also:

