PIC programming question - frequency activated relay

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
I'm building my own CNC milling machine and one of the safety features that they advise you to build is a frequency safety switch (aka charge pump) that activates a relay when there is a constant 8-13KHz signal from the computer.

I would like to build mine using a pic like the 12C508/P, which I have plenty of spare and eager to make use of/get rid of... :D

So the circuit is essential one input pin (for the signal 0-5V) and one output pin for the relay)


I realise that the 555 timers are able to do a similar thing already with 'missing pulse detectors' but i'd like to have the advantage of fine tuning the algorithm to cater for checking the pulse width, etc in the future.

I'm just struggling to get my head round the code as usual...

Any ideas or code examples I could use to get started?

thanks!
 

Markd77

Joined Sep 7, 2009
2,806
I was working on something similar on a baseline PIC. It's not complete or commented, but it might give you some ideas.

Rich (BB code):
test 
clrwdt 
clrf TMR0 
clrf pulse_count 
 
wait3 
movlw d'200' 
subwf TMR0, W 
btfsc STATUS, C 
goto time_up 
btfsc GPIO, 3 
goto wait3 
 
wait4 
movlw d'200' 
subwf TMR0, W 
btfsc STATUS, C 
goto time_up 
btfss GPIO, 3 
goto wait4 
incf pulse_count, F 
goto wait3 
 
 
time_up 
movlw d'3' 
subwf pulse_count, W 
btfss STATUS, C 
goto test 
 
movlw d'9' 
subwf pulse_count, W 
btfsc STATUS, C 
goto test
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
thanks guys. both useful information. :)

interesting about the CCP, as I see the 12F675 has this built-in as well, which I think I have a few somewhere as well.

So from what I gather about your code (Mark), is that the code counts the number of pulses in order to clear the Watchdog timer?
 

Markd77

Joined Sep 7, 2009
2,806
So from what I gather about your code (Mark), is that the code counts the number of pulses in order to clear the Watchdog timer?
clrwdt is a red herring.
In both the wait3 and wait 4 loops it is checking to see if the TMR0 (linked to internal instruction cycle) has reached 200 to measure an approximate time interval 200-210μs due to the size of the loops.
The wait3 and wait4 loops also check for changes on GPIO[3] and cycle from wait3 to wait4, increasing pulse_count every time the pin has gone high then low. After the 200-210μs it ends up at the checks at the end to see if between 3 and 9 cycles have been detected.
It should be fine at 13kHz with modifications to the tests and maybe the prescaler for TMR0. I was using it to detect 36kHz IR remote control carrier and it worked well.
<ed>If the number of cycles is between 3 and 9 it continues past the last instruction, otherwise it goes back to the start. </ed>
 
Last edited:

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
thanks mark.
Supposing I was to use CCP, would something like the below code work?

I have almost no idea what I'm supposed to be doing with CCP to measure frequency ranges. But it would be nice to learn with some experimentation...


Rich (BB code):
LIST    p=16F628, R=DEC        ;tell assembler what chip we are using
    #include "P16F628.inc"        ;include the defaults for the chip
    
    __config _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRITE_ON & _BODEN_ON


    CBLOCK 0x20
        
        Check_Freq
        d1
        d2
        d3
    ENDC
    
    ;499994 cycles for delay routine
    movlw    0x03
    movwf    d1
    movlw    0x18
    movwf    d2
    movlw    0x02
    movwf    d3

    
    #DEFINE    RELAY    PORTB, 0    ; Define the relay on RB0


    clrf    CCP1CON            ; CCP Module is off
    clrf    TMR1H            ; clear timer1 high byte
    clrf    TMR1L            ; clear timer1 low byte
    clrf    INTCON            ; disable interupts and clear TO1F
    bsf        STATUS,    RP0        ; Bank 0
    bsf        TRISC,    CCP1    ; Make CCP1 an input pin
    clrf    PIE1
    bcf        STATUS,    RP0        ; Bank 0
    clrf    PIR1            ; clear pheripheral interupt flags
    movlw    0x06            ; capture mode every 4th rising edge
    movwf    CCP1CON
    bsf        T1CON,    TMR1ON    ; Timer1 starts
    bcf        RELAY            ; Turn the relay off


ORG        0x000    ; Program starts here


Loop call Capture_Event    ; Wait for something to be captured
    call Check_Input



; Captured Occurred




Goto Loop


Check_Input movwf    Check_Freq    ; Put the result of W into memory
    
    movlw    0x41
    subwf    Check_Freq,    w
    btfsc    STATUS,    Z
    bsf        RELAY        ; Result is good, turn on relay
    
    retlw    0x00


Capture_Event    ; Wait for something to be captured
    BTFSS    PIR1,    CCP1IF    ; This needs to be done before next compare
    GOTO    Capture_Event
    movf    RCREG, W        ; Store result in W
    retlw    0x00




Delay_0
    decfsz    d1, f
    goto    $+2
    decfsz    d2, f
    goto    $+2
    decfsz    d3, f
    goto    Delay_0

            ;6 cycles
    goto    $+1
    goto    $+1
    goto    $+1


END
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
thanks mark.
Supposing I was to use CCP, would something like the below code work?

I have almost no idea what I'm supposed to be doing with CCP to measure frequency ranges. But it would be nice to learn with some experimentation...


Rich (BB code):
LIST    p=16F628, R=DEC        ;tell assembler what chip we are using
    #include "P16F628.inc"        ;include the defaults for the chip
    
    __config _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRITE_ON & _BODEN_ON


    CBLOCK 0x20
        
        Check_Freq
        d1
        d2
        d3
    ENDC
    
    ;499994 cycles for delay routine
    movlw    0x03
    movwf    d1
    movlw    0x18
    movwf    d2
    movlw    0x02
    movwf    d3

    
    #DEFINE    RELAY    PORTB, 0    ; Define the relay on RB0


    clrf    CCP1CON            ; CCP Module is off
    clrf    TMR1H            ; clear timer1 high byte
    clrf    TMR1L            ; clear timer1 low byte
    clrf    INTCON            ; disable interupts and clear TO1F
    bsf        STATUS,    RP0        ; Bank 0
    bsf        TRISC,    CCP1    ; Make CCP1 an input pin
    clrf    PIE1
    bcf        STATUS,    RP0        ; Bank 0
    clrf    PIR1            ; clear pheripheral interupt flags
    movlw    0x06            ; capture mode every 4th rising edge
    movwf    CCP1CON
    bsf        T1CON,    TMR1ON    ; Timer1 starts
    bcf        RELAY            ; Turn the relay off


ORG        0x000    ; Program starts here


Loop call Capture_Event    ; Wait for something to be captured
    call Check_Input
   call Delay_0 ; Let things settle for a bit before checking again


; Captured Occurred




Goto Loop


Check_Input movwf    Check_Freq    ; Put the result of W into memory
    
    movlw    0x41
    subwf    Check_Freq,    w
    btfsc    STATUS,    Z
    bsf        RELAY        ; Result is good, turn on relay
    
    retlw    0x00


Capture_Event    ; Wait for something to be captured
    BTFSS    PIR1,    CCP1IF    ; This needs to be done before next compare
    GOTO    Capture_Event
    movf    RCREG, W        ; Store result in W
    retlw    0x00




Delay_0
    decfsz    d1, f
    goto    $+2
    decfsz    d2, f
    goto    $+2
    decfsz    d3, f
    goto    Delay_0

            ;6 cycles
    goto    $+1
    goto    $+1
    goto    $+1


END
 

Markd77

Joined Sep 7, 2009
2,806
Double posting?
Without simulating it I'm not sure. There are a couple of things I spotted:
Use return instead of retlw 0x00, it's messing up one of the functions.
Set d1, etc at the start of delay_0 or it will only be the right length the first time.
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
thanks mark... yeah it was a double post. not sure what happened there.

Having experimented further with my code and not being to get it to work properly, I think I'm going back to my original decision to code it using something like your code above. Especially as I'm literally measuring a square wave to make sure it's between a certain frequency.
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
some updates then....

I have written some code for the 16f628a, which I just use to experiment with. The code now works perfectly, but clearly a 16f628a is a bit overkill for this simple application. So I would like to tailor the code to work on the 12c508 next if possible.


How the code works.....

timer0 is reset followed by an 18ms delay.
The pic then checks to see if the number of pulses is between 10 and 70 in the time period given. If it is then it adds this to a count.

If the number of pulses has been correct twice, then activate the relay. Any time the pulses fail to match, then it switches off the relay and resets the count.

Rich (BB code):
    processor 16F628A
    #include <P16F628A.INC>
    __config 0x2118
;   _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_OFF 
;   & _WDT_OFF 

; RAM-Variable
LRAM_0x20 equ 0x20
LRAM_0x21 equ 0x21
LRAM_0x22 equ 0x22
LRAM_0x23 equ 0x23
LRAM_0x70 equ 0x70
LRAM_0x7C equ 0x7C
LRAM_0x7D equ 0x7D

; Program

    Org 0x0000

;   Reset-Vector
    GOTO INITIALISE

    Org 0x0003

INITIALISE
    MOVLW 0x0A
;   Interrupt-Vector
    BCF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    BCF STATUS,RP1       ; !!Bank Register-Bank(2/3)-Select
    MOVWF LRAM_0x23
    MOVLW 0x46
    MOVWF LRAM_0x20
    BSF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    CLRF PORTB           ; !!Bank!! PORTB - TRISB - PORTB - TRISB
    MOVLW 0x80
    MOVWF TMR0           ; !!Bank!! TMR0 - OPTION_REG - TMR0 - OPTION_REG
    BCF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    CLRF PORTA           ; !!Bank!! PORTA - TRISA - Unimplemented - Unimplemented
    MOVLW 0xFF
    

    BSF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    MOVWF PORTA          ; !!Bank!! PORTA - TRISA - Unimplemented - Unimplemented
    BCF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    CLRF PORTB           ; !!Bank!! PORTB - TRISB - PORTB - TRISB
    BSF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    BSF TMR0,5           ; !!Bank!! TMR0 - OPTION_REG - TMR0 - OPTION_REG
    BSF TMR0,3           ; !!Bank!! TMR0 - OPTION_REG - TMR0 - OPTION_REG
    BCF STATUS,RP0       ; !!Bank Register-Bank(0/1)-Select
    CLRF TMR0            ; Clear Timer0


Main_Loop
    CLRF TMR0            ; Clear Timer0
    MOVLW 0x06            
    MOVWF LRAM_0x7C
    MOVLW 0xD7
    MOVWF LRAM_0x7D



LADR_0x001E
    DECFSZ LRAM_0x7D,F
    GOTO LADR_0x001E
    DECFSZ LRAM_0x7C,F
    GOTO LADR_0x001E
    MOVF TMR0,W          ; !!Bank!! TMR0 - OPTION_REG - TMR0 - OPTION_REG
    SUBWF LRAM_0x23,W
    BTFSC STATUS,C
    GOTO LADR_0x003D
    MOVF LRAM_0x20,W
    SUBWF TMR0,W         ; !!Bank!! TMR0 - OPTION_REG - TMR0 - OPTION_REG
    BTFSC STATUS,C
    GOTO LADR_0x003A
    INCF LRAM_0x21,F
    BTFSC STATUS,Z
    INCF LRAM_0x22,F
    MOVLW 0x80
    MOVWF LRAM_0x70
    MOVLW 0x80
    XORWF LRAM_0x22,W
    SUBWF LRAM_0x70,W
    BTFSS STATUS,Z
    GOTO LADR_0x0036
    MOVF LRAM_0x21,W
    SUBLW 0x02


LADR_0x0036
    BTFSC STATUS,C        
    GOTO LADR_0x0039
    BSF PORTB,0          ; Turn on relay on RB0


LADR_0x0039
    GOTO LADR_0x003C


LADR_0x003A
    CLRF LRAM_0x21
    CLRF LRAM_0x22

LADR_0x003C
    GOTO BEGINNING


LADR_0x003D
    BCF PORTB,0          ; Turn off relay on RB0
    CLRF LRAM_0x21
    CLRF LRAM_0x22

BEGINNING                  ;LADR_0x0040
    GOTO Main_Loop

Endless_Loop
    GOTO Endless_Loop

    End
Unfortunately I didn't use MPLAB to create the code, but Mikrobasic, that I'm currently trialling.
 

Markd77

Joined Sep 7, 2009
2,806
It's a bit hard to read without meaningful variable names and labels.
It should be easy enough on 12F508, either do similar to what I posted below or use TMR0 as a counter on the T0CKI pin and have a fixed delay (which is what I'm guessing your recent post does).
I'd have used the counter but I wanted to use the T0CKI pin as an output.
 

THE_RB

Joined Feb 11, 2008
5,438
Since you need to detect a frequency is present but don't know the freq, maybe you could just use a duty cycle detector?

example;
Rich (BB code):
// detect if any freq is present
unsigned int i, d;

d = 0;
for(i=0; i<50000; i++)     // test over 0.5 second period
{
  delay_uS(10);
  if(input) d++;           // count a 1 bit
}

// now check for freq fail (fail = < 5% or > 95% duty)  
if(d < 2500 || d > 47500) pin_relay = 0;   // relay off  
else                      pin relay = 1;   // relay on
 
Top