Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

How to generate pulses of different frequencies at GPIO4 and GPIO5 in PIC12f629?

Status
Not open for further replies.
I need 23Hz pulse at GPIO5 and 3Hz pulse at GPIO4

Following is my code:
list p=12f629, f=inhx32
#include <p12f629.inc>

__CONFIG _WDT_OFF&_INTRC_OSC_NOCLKOUT&_BODEN_ON&_CP_OFF&_PWRTE_ON



COUNTER equ 0x21
COUNTER1 equ 0x22
temp equ 0x23
M1 equ 0x24
M2 equ 0x25
M3 equ 0x26
M4 equ 0x27
COUNTER2 equ 0x28
COUNTER3 equ 0x29
temp1 equ 0x30
temp2 equ 0x31
temp3 equ 0x32
;ANSEL equ 0x9F
;cblock 0x20
;d1
; d2
;endc
;**********************************************************************
ORG 0x000 ; processor reset vector
goto init ; go to beginning of program


ORG 0x004 ; interrupt vector location
init:
call 0x3FF ; retrieve factory calibration value
bsf STATUS,RP0 ; set file register bank to 1
movwf OSCCAL ; update register with factory cal value
bcf STATUS,RP0 ; set file register bank to 0
CLRF GPIO ;Init GPIO
MOVLW 07h ;Set GP<2:0> to
MOVWF CMCON ;digital IO
BSF STATUS,RP0 ;Bank 1
;CLRF ANSEL ;Digital I/O
MOVLW b'00001111' ;Sets inputs and outputs
MOVWF TRISIO ;and set outputs
BCF STATUS,RP0 ;Bank 0
;movlw 20h
;movwf GPIO
;movlw 02h
;movwf COUNTER

;Variable definitions
;M1 on time of pulse
;M2 off time of pulse
;M3 number of pulses
;M4 on time of pulse
;M5 off time of pulse
;M6 number of pulses

movlw .44 ;on time of pulse #1
movwf M1
movlw .44 ;off time of pulse #1
movwf M2

movlw .160 ;on time of pulse #1
movwf M3
movlw .160 ;off time of pulse #1
movwf M4


goto main

main:

call lowtest
;decfsz COUNTER,1
call hightest
goto main


lowtest:
bsf GPIO,5
call Delay
bsf GPIO,4
call Delay1
return

Delay: movf M1,0
movwf COUNTER
nop
decfsz temp,f
goto $-2
decfsz COUNTER,f
goto $-4
retlw 00


Delay1: movf M2,0
movwf COUNTER1
nop
decfsz temp1,f
goto $-2
decfsz COUNTER1,f
goto $-4
retlw 00

;loop1:

hightest:
;bcf GPIO,5
call Delay2
;bcf GPIO,4
call Delay3
return

Delay2: movf M3,0
movwf COUNTER2
nop
decfsz temp2,f
goto $-2
decfsz COUNTER2,f
goto $-4
retlw 00

Delay3 : movf M4,0
movwf COUNTER3
nop
decfsz temp3,f
goto $-2
decfsz COUNTER3,f
goto $-4
retlw 00

end
 
As the 2 GPIO pins are in the same port all u need to do is prepare a buffer variable with the upcoming bit pattern (on/off) and write to the port at the correct time.
That way your pulses are synced.
Prob the best way to do it is base the outputs on a common timer. 23Hz and 3Hz have a common of 23X3 = 69Hz.

So create a timer interrupt to run at 69Hz and then drive a GPIO every time 1 variable is incremented to 23 (then reset) and the other incremented to 3, then reset. Of course u can create a much faster timer with matching interval counters to keep your period accurate.

To create the Port buffer just set the correct bit based on the interval for both freq. then write the PORT buffer to the PORT for a precisely synced freq output..
 
Try this code:

Code:
movlw    .23
movwf    loops
call     delay
call     delay
call     delay
movlw    10h
xorwf    gpio,1    ;to toggle GP4
decfsz   loops,1
goto     $-6
movlw    20h
xorwf    gpio,1    ;to toggle GP5
goto     $-11



delay    ;7246uS

movlw    .8
movwf    fileA
movlw    .225
movwf    fileB
nop
decfsz   fileB,1
goto      $-2
decfsz   fileA,1
goto      $-4
retlw    00
 
Last edited:
Try this code:

Code:
movlw    .23
movwf    loops
call     delay
call     delay
call     delay
movlw    10h
xorwf    gpio,1    ;to toggle GP4
decfsz   loops,1
goto     $-6
movlw    20h
xorwf    gpio,1    ;to toggle GP5
goto     $-11



delay    ;7426uS

movlw    .8
movwf    fileA
movlw    .225
movwf    fileB
nop
decfsz   fileB,1
goto      $-2
decfsz   fileA,1
goto      $-4
retlw    00
Don't use that (garbage) code. That delay routine doesn't produce anything even close to 7426-usecs and that's not the correct number anyway.
 
Last edited:
He is not asking for the pulses to be synchronised, just for two frequencies to be produced at the same time.
 
Yeah but he wants the right frequencies mate... Simulate your delay routine. It's not even close to 7246-usecs.
 
Last edited:
This should work in C, using just a delay for simplicity;

Code:
while(1)
{
  Delay_us(7246);  // will need to "tune" this value
  A++;
  if(A >= 23)
  {
    A = 0;
    GPIO.F4 = ~GPIO.F4;
  }
  B++;
  if(B >= 3)
  {
    B = 0;
    GPIO.F5 = ~GPIO.F5;
  }
}


Here is a "zero-error" bresenham system using TMR0 at 1MHz that will generate the 2 frequencies with excellent accuracy using a 4MHz xtal;

Code:
unsigned char A, B;
unsigned int bres;
while(1)
{
  while(!INTCON.T0IF);  // wait for TMR0 to roll
  INTCON.T0IF = 0;      // clear roll flag
  bres += 256;          // add 256 to bres accumulator
  if(bres >= 7246)      // if period
  {
    bres -= 7246;       // subtract period, retain error
    A++;
    if(A >= 23)
    {
      A = 0;
      GPIO.F4 = ~GPIO.F4;
    }
    B++;
    if(B >= 3)
    {
      B = 0;
      GPIO.F5 = ~GPIO.F5;
    }  
  }
}

All written in the simplest C so it can easily be converted to asm.
 
Last edited:
Or you might use the phase accumulator portion of DDS (direct digital synthesis). Just copy the most significant bit from the 16 bit phase accumulator onto the output pins for a square wave (50% duty cycle) output. Something like this perhaps;

Code:
;
;  DDS Frequency (Fdds) = 13107.2 Hz (Fosc = 8.388608 MHz)
;  Resolution (Fres) = Fdds / 2^16 = 0.2 Hz (16 bit accumulators)
;   3-Hz phase offset =  3 / 0.2 =  15
;  23-Hz phase offset = 23 / 0.2 = 115
;
        radix   dec
dualdds
        clrf    shadow          ;                                 |B0
        movlw   15              ; phase offset for 3 Hz           |B0
        addwf   accum1+0,F      ; accum1 += phase1                |B0
        skpnc                   ;                                 |B0
        incf    accum1+1,F      ;                                 |B0
        rlf     accum1+1,W      ; accum1.15 -> C                  |B0
        rlf     shadow,F        ;                                 |B0
        movlw   115             ; phase offset for 23 Hz          |B0
        addwf   accum2+0,F      ; accum2 += phase2                |B0
        skpnc                   ;                                 |B0
        incf    accum2+1,F      ;                                 |B0
        rlf     accum2+1,W      ; accum2.15 -> C                  |B0
        rlf     shadow,W        ;                                 |B0
        movwf   GPIO            ; update GP1 & GP0 outputs        |B0
        DelayCy(160-16)         ; 160 cycle DDS loop              |B0
        goto    dualdds         ;                                 |B0

;
 
Last edited:
Here is sumthin I whipped up. That works ok


Code:
	list p=12f629, f=inhx32
	#include <p12f629.inc>

	__CONFIG _WDT_OFF&_INTRC_OSC_NOCLKOUT&_BODEN_ON&_CP_OFF&_PWRTE_ON

W_TEMP		Equ	0x20
STATUS_TEMP	Equ	0x21
Timebase3	Equ	0x22
Timebase23	Equ	0x23



	ORG 0x000 ; processor reset vector
	goto init ; go to beginning of program


	ORG 0x004 ; interrupt vector location

;context save next.
	MOVWF W_TEMP ;copy W to temp register,could be in either bank
	SWAPF STATUS,W ;swap status to be saved into W
	BCF STATUS,RP0 ;change to bank 0 regardless of current bank
	MOVWF STATUS_TEMP ;save status to bank 0 register


	bcf PIR1,0; clr Timer1 Int flag
	movlw LOW(.51043)
	movwf TMR1L
	movlw HIGH(.51043)
	movwf TMR1H
;do time base ticks
	Incf Timebase3,f 
	Incf Timebase23,f
	movlw .3 ; test for 1/3 of 69HZ (23Hz)
	subwf Timebase23,w
	skpz
	goto Next_timebase
	clrf Timebase23
	movlw b'00100000'
	xorwf GPIO,f ; toggle GPIO5 at 23Hz
Next_timebase;
	movlw .23 ; test for 1/23 of 69Hz ( 3hz)
	subwf Timebase3,w
	skpz
	goto ISRDONE
	clrf Timebase3
	movlw b'00010000'
	xorwf GPIO,f; toggle GPIO4 at 3Hz

ISRDONE; restore context
	SWAPF STATUS_TEMP,W;swap STATUS_TEMP register into W, sets bank to original state
	MOVWF STATUS ;move W into STATUS register
	SWAPF W_TEMP,F ;swap W_TEMP
	SWAPF W_TEMP,W ;swap W_TEMP into W
	retfie; return to main from interrupt
	

init:
	call 0x3FF ; retrieve factory calibration value
	bsf STATUS,RP0 ; set file register bank to 1
	movwf OSCCAL ; update register with factory cal value
	bcf STATUS,RP0 ; set file register bank to 0
	CLRF GPIO ;Init GPIO
	MOVLW 07h ;Set GP<2:0> to
	MOVWF CMCON ;digital IO
	BSF STATUS,RP0 ;Bank 1
	;CLRF ANSEL ;Digital I/O
	MOVLW b'00001111' ;Sets inputs and outputs
	MOVWF TRISIO ;and set outputs
	BCF STATUS,RP0 ;Bank 0

;Setup Tmr1 int

; 1000,000/ 69 = 14493 ticks req'd b4 interrupt for a 69Hz base.
; 65536-14493 = 51043 => preload this into TMR1L/H  to cause 14493 ticks b4 interrupt.
	movlw LOW(.51043)
	movwf TMR1L
	movlw HIGH(.51043)
	movwf TMR1H
	banksel PIE1
	bsf PIE1,0 ; Timer1 interrupt enable
	banksel INTCON
	bsf INTCON,6 ;peripheral int. enable
	bsf INTCON,7 ; gen. int enable.

	movlw b'00000101' ; timer1 setup bits, with 1:1 prescale
	movwf T1CON	; => start timer1 running on a 1,000,000 tick filling TMR1L, TMR1H (65536) b4 interrupt happens


Main;
	goto Main; perpetual loop unless more functions needed.

	END



The call 0x3FF may have to be rem'd out for a sim to work tho. Timing Accuracy seems to be around .3%

I think Tropical Storm/hurricane Tomas gonna miss us.....here in T&T. Barbados gonna get plastered tho.
 
Last edited:
Nice system Mike! I'm not sure of accuracy with a fixed delay because of the variable execution time of your math code, so accuracy would suffer?

My second example above was period accurate to 1 TMR0 count, and had freq error of;
desired = 0.00724638
result = 0.007246 = error 0.00005244 or only 52.44 parts per million, better than most xtals.

Getting jiggy with it... This one below uses the same bresenham system but with a resolution *100 so the freq error is now reduced to less than ONE part per million.

Code:
unsigned char A, B;
unsigned long bres;
while(1)
{
  while(!INTCON.T0IF);  // wait for TMR0 to roll
  INTCON.T0IF = 0;      // clear roll flag
  bres += 25600;        // add 256*100 to bres accumulator
  if(bres >= 724638)    // if period
  {
    bres -= 724638;     // subtract period, retain error
    A++;
    if(A >= 23)
    {
      A = 0;
      GPIO.F4 = ~GPIO.F4;
    }
    B++;
    if(B >= 3)
    {
      B = 0;
      GPIO.F5 = ~GPIO.F5;
    }  
  }
}

That is now accurate to 100 times greater than the native TMR0 accuracy.

Mosaic- I think you haven't accounted for the interrupt latency and context saving times etc. And your code would still be limited to a period precision of 1 TMR0 count. Also, shouldn't you be toggling the pins at DOUBLE the base freq of 69, or you will generate tones at 1/2 the desired freq?
 
Last edited:
Another try:

Code:
Producing 3Hz at GP4 and 23Hz at GP5 at the same time.


cycle  
    movlw    30h
    xorwf    gpio,1    ;to toggle GP4 GP5 (say ON)
    movlw    .7
    movwf    loops
    call     delay
    call     delay
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 3.5 times
    decfsz   loops,1
    goto     $-6
    call     delay
    call     delay
    movlw    20h
    xorwf    gpio,1    ;to toggle GP5 for 23Hz (off)
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 4 times
    movlw    .7
    movwf    loops
    call     delay
    call     delay
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 7.5 times
    decfsz   loops,1
    goto     $-6
    call     delay
    movlw    20h
    xorwf    gpio,1    ;to toggle GP5 (ON)
    call     delay
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 8 times
    movlw    .7
    movwf    loops
    call     delay
    call     delay
    call     delay
    movlw    30h
    xorwf    gpio,1    ;to toggle GP4 11.5 times GP5 (off)
    decfsz   loops,1
    goto     $-6
    movlw    .7
    movwf    loops
    call     delay
    call     delay
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 15 times
    decfsz   loops,1
    goto     $-6
    call     delay
    call     delay
    movlw    20h
    xorwf    gpio,1    ;to toggle GP5 (ON)
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 15.5 times
    movlw    .7
    movwf    loops
    call     delay
    call     delay
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 19 times
    decfsz   loops,1
    goto     $-6
    call     delay
    movlw    20h
    xorwf    gpio,1    ;to toggle GP5 (off)
    call     delay
    call     delay
    movlw    10h
    xorwf    gpio,1    ;to toggle GP4 19.5 times
    movlw    .6
    movwf    loops
    call     delay
    call     delay
    call     delay
    movlw    30h
    xorwf    gpio,1    ;to toggle GP4  22.5 times
    decfsz   loops,1
    goto     $-6              
    call     delay
    call     delay
    call     delay
    goto     cycle where GP4 = 23 times and GP5 (ON)



delay    ;7246uS

    movlw    .8
    movwf    fileA
    movlw    .225
    movwf    fileB
    nop
    decfsz   fileB,1
    goto      $-2
    decfsz   fileA,1
    goto      $-4
    retlw    00
 
Last edited:
Nice system Mike! I'm not sure of accuracy with a fixed delay because of the variable execution time of your math code, so accuracy would suffer?

The code is isochronous Roman (no variable execution time). The DDS loop is exactly 160 cycles and the output is truly if not artificially "zero error" due to the specially selected clock frequency and DDS loop time.

My vote goes to the Bresenham method for this application. While both DDS and Bres' methods move us out of time domain and into frequency domain, the Bres' method provides better precision for the same accumulator bit width at any clock frequency.

Regards...
 
Last edited:
Mosaic- I think you haven't accounted for the interrupt latency and context saving times etc. And your code would still be limited to a period precision of 1 TMR0 count. Also, shouldn't you be toggling the pins at DOUBLE the base freq of 69, or you will generate tones at 1/2 the desired freq?


Um...oops. Was l8t...heh. 9 uS deducted from period as compensation for context etc.Abt accuracy, OSCAL tuning can fix that easily enough. Cumulative Period Error is 23 uS on the 23Hz GPIO signal after 5 cycles. Around 4.6 uS per cycle. Works out to .01% error.
I use Timer1 which is a 16bit timer, 256 times better discrete timeslices accuracy than Timer0

Code:
		list p=12f629, f=inhx32
	#include <p12f629.inc>

	__CONFIG _WDT_OFF&_INTRC_OSC_NOCLKOUT&_BODEN_ON&_CP_OFF&_PWRTE_ON

W_TEMP		Equ	0x20
STATUS_TEMP	Equ	0x21
Timebase3	Equ	0x22
Timebase23	Equ	0x23



	ORG 0x000 ; processor reset vector
	goto init ; go to beginning of program


	ORG 0x004 ; interrupt vector location

;context save next.
	MOVWF W_TEMP ;copy W to temp register,could be in either bank
	SWAPF STATUS,W ;swap status to be saved into W
	BCF STATUS,RP0 ;change to bank 0 regardless of current bank
	MOVWF STATUS_TEMP ;save status to bank 0 register


	bcf PIR1,0; clr Timer1 Int flag
	movlw LOW(.58299)
	movwf TMR1L
	movlw HIGH(.58299)
	movwf TMR1H
;do time base ticks
	Incf Timebase3,f 
	Incf Timebase23,f
	movlw .3 ; test for 1/3 of 69HZ (23Hz)
	subwf Timebase23,w
	skpz
	goto Next_timebase
	clrf Timebase23
	movlw b'00100000'
	xorwf GPIO,f ; toggle GPIO5 at 23Hz
Next_timebase;
	movlw .23 ; test for 1/23 of 69Hz ( 3hz)
	subwf Timebase3,w
	skpz
	goto ISRDONE
	clrf Timebase3
	movlw b'00010000'
	xorwf GPIO,f; toggle GPIO4 at 3Hz

ISRDONE; restore context
	SWAPF STATUS_TEMP,W;swap STATUS_TEMP register into W, sets bank to original state
	MOVWF STATUS ;move W into STATUS register
	SWAPF W_TEMP,F ;swap W_TEMP
	SWAPF W_TEMP,W ;swap W_TEMP into W
	retfie; return to main from interrupt
	

init:
	;call 0x3FF ; retrieve factory calibration value
	bsf STATUS,RP0 ; set file register bank to 1
	movwf OSCCAL ; update register with factory cal value
	bcf STATUS,RP0 ; set file register bank to 0
	CLRF GPIO ;Init GPIO
	MOVLW 07h ;Set GP<2:0> to
	MOVWF CMCON ;digital IO
	BSF STATUS,RP0 ;Bank 1
	;CLRF ANSEL ;Digital I/O
	MOVLW b'00001111' ;Sets inputs and outputs
	MOVWF TRISIO ;and set outputs
	BCF STATUS,RP0 ;Bank 0

;Setup Tmr1 int

; 1000,000/ 69 = 14493/2 (on/off) ticks req'd b4 interrupt for a 69Hz base.
; 65536-7246 =58290 +9=> preload this into TMR1L/H  to cause 14493/2 ticks b4 interrupt.
	movlw LOW(.58299)
	movwf TMR1L
	movlw HIGH(.58299)
	movwf TMR1H
	banksel PIE1
	bsf PIE1,0 ; Timer1 interrupt enable
	banksel INTCON
	bsf INTCON,6 ;peripheral int. enable
	bsf INTCON,7 ; gen. int enable.

	movlw b'00000101' ; timer1 setup bits, with 1:1 prescale
	movwf T1CON	; => start timer1 running on a 1,000,000 tick filling TMR1L, TMR1H (65536) b4 interrupt happens


Main;
	goto Main; perpetual loop unless more functions needed.

	END
 
Last edited:
Mike, K8LH-
The code is isochronous Roman (no variable execution time). The DDS loop is exactly 160 cycles and the output is truly if not artificially "zero error" due to the specially selected clock frequency and DDS loop time.

My apologies Mike! I looked through your DDS asm code that does skpnc etc and just jumped to the assumption that it would have variable execution time depending on the math. I should have thought it through a bit better... I'm out of practice counting asm cycles. :)

Colin55- Interesting idea of hard coding the 2 tones with a fixed delay. That uses no RAM, and if you fixed your 7246uS delay code the whole thing would be RAM free.
 
Thank you all for your suggestions.

@Mosaic

;Setup Tmr1 int

; 1000,000/ 69 = 14493/2 (on/off) ticks req'd b4 interrupt for a 69Hz base.
; 65536-7246 =58290 +9=> preload this into TMR1L/H to cause 14493/2 ticks b4 interrupt.

I did not understand adding 9 to 58290

Can you please tell about it?
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top