View Full Version : Programming a timer/counter for square wave output to a port pin

7th November 2011, 19:55
As I'm a more hardware hack than super coder- does anyone know of a way to program a 16-bit timer/counter to present preset frequency square waves to output to a port pin? I would like to mimic what the Intel 82C54 does in Mode 3. Any suggestions greatly appreciated..
Thanks in advance!
Dan (AKA Glenlivet)

7th November 2011, 21:11
If you want a single shot pulse, you could use a timer interrupt, and toggle the pin after x amount of counts. If you want continuous pulse, then you could use a hardware pwm pin. If you want bursts of pwm, then use a mixture of both. ie. a continuous square wave at a set frequency, then change the tris state on the pin to give bursts


25th July 2012, 08:34
if you want you can set the clock to internal and then in hardware set clock out it will be osc / 4 so on 4 megs you will get 1 meg out but please note i strongly recomend using mplab c for this as your delay in machine cycles if you wanna produce lower frequencys like 125khz you set the osccon reg to 500khz and dont set a xtal speed on proton but use the asm directive NOP for delays and simplify your code to non xtal declares dependant delays

25th July 2012, 12:29
This should do what you want, I used it to send pulses out to a CNC motor, I have cut all the crap out but all you need to do to alter the time is change the ScaledPotValue (it usewd a pot to determine the pulse width).


;**** Added by Fuse Configurator ****
; Use the Fuse Configurator plug-in to change these settings

Device = 18F4620
Xtal = 4 'Define clock

OSC = XT ;XT oscillator
FCMEN = OFF ;Fail-Safe Clock Monitor disabled
IESO = OFF ;Oscillator Switchover mode disabled
PWRT = OFF ;PWRT disabled
BOREN = SBORDIS ;Brown-out Reset enabled in hardware only (SBOREN is disabled)
BORV = 3 ;Minimum setting
WDT = OFF ;WDT disabled (control is placed on the SWDTEN bit)
WDTPS = 32768 ;1:32768
CCP2MX = PORTC ;CCP2 input/output is multiplexed with RC1
PBADEN = OFF ;PORTB<4:0> pins are configured as digital I/O on Reset
LPT1OSC = OFF ;Timer1 configured for higher power operation
MCLRE = OFF ;RE3 input pin enabled; MCLR disabled
STVREN = On ;Stack full/underflow will cause Reset
LVP = OFF ;Single-Supply ICSP disabled
XINST = OFF ;Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
Debug = OFF ;Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
CP0 = OFF ;Block 0 (000800-003FFFh) not code-protected
CP1 = OFF ;Block 1 (004000-007FFFh) not code-protected
CP2 = OFF ;Block 2 (008000-00BFFFh) not code-protected
CP3 = OFF ;Block 3 (00C000-00FFFFh) not code-protected
CPB = OFF ;Boot block (000000-0007FFh) not code-protected
CPD = OFF ;Data EEPROM not code-protected
WRT0 = OFF ;Block 0 (000800-003FFFh) not write-protected
WRT1 = OFF ;Block 1 (004000-007FFFh) not write-protected
WRT2 = OFF ;Block 2 (008000-00BFFFh) not write-protected
WRT3 = OFF ;Block 3 (00C000-00FFFFh) not write-protected
WRTC = OFF ;Configuration registers (300000-3000FFh) not write-protected
WRTB = OFF ;Boot Block (000000-0007FFh) not write-protected
WRTD = OFF ;Data EEPROM not write-protected
EBTR0 = OFF ;Block 0 (000800-003FFFh) not protected from table reads executed in other blocks
EBTR1 = OFF ;Block 1 (004000-007FFFh) not protected from table reads executed in other blocks
EBTR2 = OFF ;Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks
EBTR3 = OFF ;Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks
EBTRB = OFF ;Boot Block (000000-0007FFh) not protected from table reads executed in other blocks

;**** End of Fuse Configurator Settings ****

Dim ScaledPotValue As Word
ScaledPotValue = 10
Dim PulseCompare As Word

'************************************************* ***************
'* Variables for Timer1 *
'************************************************* ***************
Dim TIMER1 As TMR1L.Word
Dim TIMER_1_BITS As Byte

'************************************************* ***************
'* Symbols for Interrupts *
'************************************************* ***************
Symbol IPEN = RCON.7
Symbol TMR1IP = IPR1.0
Symbol TMR1IF = PIR1.0
Symbol TMR1IE = PIE1.0
Symbol GIEH = INTCON.7
Symbol GIEL = INTCON.6

Symbol TMR1ON = T1CON.0

'************************************************* ***************
'* Set up Timer 1 *
'************************************************* ***************
TIMER_1_BITS = 000000 'Timer 1 configuration bits
'Timer 1 - 1:1 prescaler and act as timer
TIMER_1_OFFSET = 64535 'Timer 1 offset for approx 1mS Interrupt)
'Configured for 4 Mhz xtal

PIE1 = 000001
PIR1 = 000001

'************************************************* ***************
'* Set up Port D *
'************************************************* ***************
TRISD.0 = 0

'************************************************* ***************
'* Interrupt service vector initialization *
'************************************************* ***************

On_Hardware_Interrupt GoTo INTERRUPT_ROUTINE


'************************************************* ***************
'* Interrupt Service Routine *
'************************************************* ***************
Context Save

If PIR1.0 = 1 Then
GoSub Timer_Int
'Set / clear relevant bits before returning from interupt
PIR1.0 = 0 'Clear Timer 1 overflow interrupt bit
TIMER1 = TIMER_1_OFFSET 'Re-load timer
Set T1CON.0 'Re-start timer

Context Restore 'Exit ISR and re-enable interrupts

'Timer interrupt, increment a counter every x mS.
Clear T1CON.0 'Stop timer
Inc PulseCompare 'Increment the counter
'End of timer Interrupt


'************************************************* ***************
'* Start of Main Code *
'************************************************* ***************
DelayMS 100 'Wait for things to stabilise

'Set up TIMER1
TIMER1 = TIMER_1_OFFSET 'Load timer 1
T1CON.0 = 1 'Start Timer running
PIR1.0 = 0 'Clear timer 1 overflow bit
PIE1 = 1 'Enable Timer1 interrupt

INTCON = 000000 'Turn interrupts on


GoSub PulseTrain 'and send the pulses out

GoTo Again 'Do it forever

If PulseCompare >= ScaledPotValue Then
Toggle PORTD.0 'Toggle the step pin
PulseCompare = 0 'Reset the counter

25th July 2012, 14:42
Hi Bob
using context save and restore, you are then using a gosub within the interrupt, which takes the int routine outside the intelligent handler and bypasses the inteeligent save / restore.

Re-enstated the declares High_Int_Sub_Start, High_Int_Sub_End, Int_Sub_Start, Int_Sub_End, Low_Int_Sub_Start and Low_Int_Sub_End.
They now work alonside Context Save and Context Restore, in that any additional code that is encompassed by the declares will be treated as part of the interrupt handling subroutine and its context will be saved/restored.

25th July 2012, 15:22
Hi Joe,
I though that had been taken out as it has no reference to High_int_sub_start/end in my compiler documentation and I recall something on here stating that it had been removed?
That was part of a larger routine that I chopped up but I had not noticed that so thanks, I will look at changing it, , I doubt it makes a lot of difference here though.
I had to change my whole routine when it got changed and only one context restore was allowed and messed it up then I think.:frown:


26th July 2012, 00:35
Joe is correct. In this case the code works, but only because no compiler system variables are used in the subroutine. If you used a compiler command like Print, Serout, etc.(although using large commands like this is generally not good practice) inside a separate subroutine that was called in an interrupt you will run into trouble as the sub is not managed.

26th July 2012, 06:29
Hi Guys
No worries Bob, it was just worth taking into account.

26th July 2012, 09:26
Hi Joe,
Thinking about it, that might explain why the prograam with the larger routine has not worked properly since it has all changed, better look into it.


26th July 2012, 10:01
Hi Joe,
Thinking about it, that might explain why the prograam with the larger routine has not worked properly since it has all changed, better look into it.

Again no worries Mate.
It is not just pds commands as Was' says, it is also the other small gotchas like comparisons, and or etc (I cant remember the full list)
ANYthing that uses an internal pds variable will be auto sorted for you within context save / restore. Otherwise it can and will lead to issues.
My understanding - at this moment - is that if you wrapped the sub with int_sub_start / end, it would be treated as an intelligent part of the interrupt.
That is only ever needed though if the subroutine is going to be used outside of the interrupt. otherwise, best to keep it all within the context save and restore.
The new intelligent handler from Les is fantastic, providing you follow the format.

26th July 2012, 10:08
Also Bob
I am sure the documentation in the PDS manual slightly lags the beta development (understandably as it is dynamic by nature) . At this moment, the manual is best used in conjection with the 'PDS-HELP-WHATS NEW' file, etc.