1. 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.
    Dismiss Notice

Can CCP do this on 16F876A? :(

Discussion in 'Microcontrollers' started by bjox1, Mar 7, 2008.

  1. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    BTW, I simulated your code with MPLAB SIM, and used the stimulus feature to feed it a 500 cycle pulse (100 microseconds) and it got the right answer (500).

    I think you should change it so that when it reaches the end it just does a GOTO back to right after where you set T1CON, so that it looks for another pulse.

    Mike
     
  2. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Hi Mike,

    Mate, I have tried using the code below and I am getting some weird values on PORTD and PORTB.

    Basically, I am using a signal generator with TTL square wave. When the input is 1kHz, the value on PORTD [HIGH byte] is 1 and the value on PORTB is 244 [in binary]. I was hoping to get a value of the half period [width of the signal] i.e. 2 msec. And when I am using a signal of 10kHz, the only value is on PORTB which is 51[in binary].

    Please enlighten me on this.

    Cheers.


    Code (text):

        list p=16f877a

           include "p16f877a.inc"
            errorlevel -302

        __config _LVP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BODEN_OFF & _DEBUG_OFF

        cblock  0x020           ;start of general purpose registers
            H1
            H2
            L1
            L2
            AnsH
            AnsL   
            endc
    ; Start at the reset vector
                    org     0x000
                goto    Start

    Start   org     0x010
            clrf    PORTB
            clrf    PORTD
            clrf    PORTC
            clrf    H1
            clrf    H2
            clrf    L1
            clrf    L2
            clrf    AnsH
            clrf    AnsL
            clrf    TMR1H
            clrf    TMR1L
        ;   clrf    CCP1CON
            clrf    CCPR1H
            clrf    CCPR1L
                           bsf     STATUS,RP0      ;bank 1
                 bcf     STATUS,RP1
            movlw   b'00000100'
            movwf   TRISC
                 movlw   b'00000000'
                 movwf   TRISB           ;portb [7-0] outputs
            movlw   b'00000000'
                 movwf   TRISD          ;portd output
            movlw   0x06
            movwf   ADCON1
                                    ; all inputs DIGITAL
            bcf      STATUS,RP0     ;bank0
       
    ;**********************************************

    Main:      
            movlw   b'00000001'       ;timer 1 using to capture, prescaler 1:1
            movwf   T1CON

            movlw   b'00000101'
            movwf   CCP1CON          ;start with rising CAPTURE
            bcf PIR1,CCP1IF
    Wait:
            btfss   PIR1,CCP1IF
            goto    Wait
            movf    CCPR1H,W     ;save the value in H2 and L2 {lower value}
            movwf   H2
            movf    CCPR1L,W
            movwf   L2
            bcf PIR1,CCP1IF
    ;
            movlw   b'00000100'                    ;config CCP1 to faling
            movwf   CCP1CON  
            bcf PIR1,CCP1IF
    Wait1:
            btfss   PIR1,CCP1IF
            goto    Wait1

            movf    CCPR1H,W        ;save now the value in H1,L1
            movwf   H1
            movf    CCPR1L,W  
            movwf   L1
            bcf     PIR1,CCP1IF     ;clr flag CCP1                    

    ;SUB Higher value H2,L2 from current captured value H1,L1

    SUB:
            movf    L2,W
            subwf   L1,W
            movwf   AnsL
            movwf   PORTB

            btfss  STATUS, C
            goto   BORROW
            goto   SUB_1
    BORROW:
            decf   H1, F
       
    SUB_1:  movf    H2,W
            subwf   H1,W
            movwf   AnsH
            movwf   PORTD
            goto    Main
       
    ;************************************************
           
            end
     
  3. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    Are you sure that your signal generator was set to 1KHz and 10KHz?

    The values you got (1,244) and (0,51) are what you would expect if the signal generator was set to 10KHz and 100KHz.

    high byte = 1
    low byte = 244
    is a value of 01F4h which is 500. This number is a count of the instruction cycles counted. Since your clock is at 20MHz (it is at 20MHz, right?) then one instruction cycle is .2 microseconds (instruction clock is 20MHz/4 = 5MHz). So 0.2 times 500 is 100 microseconds per cycle, which is 10 KHz.
    You would also get this if you used the formula I gave earlier:

    frequency = 5,000,000 / time = 5,000,000 / 500 = 10,000 = 10 KHz

    Likewise

    high byte = 0
    low byte = 51
    is a value just 51. So 0.2 times 51 is 10.2 microseconds per cycle, which is 98 KHz...close to 100 KHz.

    I think either your signal generator isn't doing what you think, or your clock is running at 2 MHz instead of 20 MHz.

    Mike
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK

    Mike,

    I am sorry for not making it clear [I am sure I did in my last few post]. I am using 4MHz XT.

    Mike, i am getting 2kHz where I am supposed to get 500Hz. [1us times 500 =2kHz]

    Mike, Just now I have double checked it using an oscilloscope.

    Thanks again.

    <EDIT> Mike, I think I made a fool of myself. :D

    I guess,I am not sure, but 1kHz is 1000us in time. Halving it gives 500us. So half in time domain is double in freq domain. Thus 2kHz.

    Mike, please advice me if I am correct.
     
    Last edited: Mar 15, 2008
  6. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,160
    Likes:
    340
    Location:
    Brisbane Australia
    ONLINE
    As Mike50 isn't around, I'll answer your question.

    As your crystal is 4MHz then the timer is clocked at 1MHz or every 1uS. Therefore your frequency calculation is,
    frequency = 1,000,000 / time = 1,000,000 / 500 = 2,000 = 2 KHz

    As you are only measuring half the waveform, from rising to falling edge, you are getting a value that is double the frequency. Hence your frequency is 1kHz.

    Mike.
     
  7. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    Mike,

    Good thing you were there. I forgot that he was only measuring from leading to trailing edge, not the whole cycle. And I thought his clock was 20 MHz, as that is what I had been assuming all along.

    But your analysis makes perfect sense...It looked to me that something was off by a factor of 10, and a 4 MHz clock and only measuring half the waveform gives exactly a factor of 10.

    So now, to complete your project you want to get a 20 MHz crystal and the rest is just a small matter of programming :)

    Mike
     
  8. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Brilliant explanation guys!

    I truly appreciate that.

    -I was wondering what will be the max freq up to I can measure the width?

    and - While doing the 16 bit division, do I need the 16 bit remainder value as well??

    Thanks again guys.
     
  9. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    Well that depends on how much accuracy you need. But, best you can do will depend on how many instructions you execute between the time that CCP1IF turns on and the time that you are ready to read it again. In the code you have running that comes to about 13 instruction times. With your 4MHz clock, that is about 13 microseconds. From the time you copy out the capture register until the next time you could copy it out is about 13 microseconds. So the highest frequency you could detect would be about 77 KHz. But that is well above the highest frequency you said you needed to detect (which was 10 KHz).

    With the 4 MHz clock and a 10 KHz signal, there are 100 clocks from leading edge to leading edge. So you wouldn't be able to measure the phase angle to a precision better than about 4 degrees. At 20 MHz you could measure it to within 1 degree.

    Well, what do you think? You could use it to round the answer, but personally I wouldn't worry about it.

    Mike
     
  10. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Thanks a trillion Mike. I really appreciate that.

    But how did you calculate 4 deg??:confused:
     
  11. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    With a 4 MHz crystal, Timer1 increments each microsecond. With a 10 KHz signal you get a leading edge every 100 microseconds. So the time you will measure between sucessive leading edges will be 100. The delay you measure between the leading edge of signal 1 and the leading edge of signal 2 must be between 0 and 100. So you can only divide the 360 degrees into 100 parts. If you increase the measured value by 1 it increases the phase angle by 3.6 degrees...which I rounded up to 4.

    Mike
     
  12. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Mike,
    Does it mean that with the same settings, the phase error @ 10kHz is 3.6 degrees but now if the user changes the signal to 1kHz, does it mean that the phase error for the different freq will be different??
     
  13. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    With a 4 MHz crystal, Timer1 increments each microsecond. With a 1 KHz signal you get a leading edge every 1000 microseconds. So the time you will measure between sucessive leading edges will be 1000. The delay you measure between the leading edge of signal 1 and the leading edge of signal 2 must be between 0 and 1000. So you can only divide the 360 degrees into 1000 parts. If you increase the measured value by 1 it increases the phase angle by .36 degrees.

    See how that works?

    Mike
     
  14. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Mike,

    Thank you once again for ur kind explanation. :)

    Mate, After successfully doing the width exercise, I have attempted the actual code. The code is set to find the phase difference between CCP1 and CCP2 pin signals. I am not getting any values on the output LEDs. :(

    I am feeding 1kHz signal on CCP1 pin and the CCP2 pin has the same signal with 150 us delay.

    So CCP1- 1kHz signal
    CCP2 pin – 1kHz with 150 usec delay.

    My code does this:

    - Capture CCP1a , CCP1b and CCP2.

    - Sub -routine ‘SUB1’ subtracts CCP1a from CCP1b to give me the period of the signal. The 16 bit ans is then saved in memory reg btmH and btmL. i.e. Bottom H and Bottom L [bottom refers to the denominator in the Division sub- routine].

    - Then sub-routine ‘SUB2’ subtracts CCP1b from CCP2 and the ans is saved in memory registers topH and topL [Top refers to the numerator].

    - Then the sub-routine ‘Div’ performs 16 by 16 bit division and put the quotient [RESULTS] value in qH and qL thus on PORTD and PORTB.

    Mike, I am not even getting any values on the LEDs.

    Could you please look at my code. The division routine worked fine separately. I am guessing the division might be the wrong bit.

    -As the numerator [delay value between 0-1000] is going to be always smaller than the denominator[the denominator,time period, is 1000], the quotient will always be less the 1. Is that the reason my code doesn’t work??
    - Mike, with 150 usec delay, what value will I be expecting on the LEDs??

    I will be very very grateful if you could help me on this as well.

    Thanks again mike.

    Code (text):

    list p=16f877a

           include "p16f877a.inc"
            errorlevel -302

        __config _LVP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BODEN_OFF & _DEBUG_OFF

        cblock  0x020           ;start of general purpose registers
                CCP1aH
                CCP1aL
                CCP1bH
                CCP1bL
                CCP2H
                CCP2L  
                topH  
            endc

        cblock  0x070
                 
                topL               
                btmH           
                btmL           
                qH    
                qL    
                count  
                index  
            endc


    ; Start at the reset vector
                org     0x000
                goto    Start

    Start       org     0x010
                clrf    PORTB
                clrf    PORTD
                clrf    PORTC
                clrf    CCP1aH
                clrf    CCP1aL
                clrf    CCP1bH
                clrf    CCP1bL
                clrf    CCP2H
                clrf    CCP2L
                clrf    TMR1H
                clrf    TMR1L
                clrf    count
                clrf    topH
                clrf    topL
                clrf    btmH
                clrf    btmL
        ;   clrf    CCP1CON
                clrf    CCPR1H
                clrf    CCPR1L
                clrf    CCPR2H
                clrf    CCPR2L
                bsf     STATUS,RP0      ;bank 1
                bcf     STATUS,RP1
                movlw   b'00000110'
                movwf   TRISC
                movlw   b'00000000'
                movwf   TRISB           ;portb [7-0] outputs
                movlw   b'00000000'
                movwf   TRISD           ;portd output
                                    ; all inputs DIGITAL
                bcf     STATUS,RP0      ;bank0
        ;       bcf     PIR1,CCP1IF
    ;**********************************************

    Main:  
                movlw   b'00000000'      ;timer 1 using to capture, prescaler 1:1
                movwf   T1CON
                bsf     T1CON,TMR1ON   

                movlw   b'00000101'
                movwf   CCP1CON          ;start with rising CAPTURE
                movlw   b'00000101'
                movwf   CCP2CON          ;start CCP2 with rising CAPTURE
                bcf     PIR1,CCP1IF
                bcf     PIR2,CCP2IF
    Wait:
                btfss   PIR1,CCP1IF
                goto    Wait
                movf    CCPR1H,W        ;save the value in H2 and L2 {lower value}
                movwf   CCP1aH
                movf    CCPR1L,W
                movwf   CCP1aL
                bcf     PIR1,CCP1IF
    ;
    Wait1:
                btfss   PIR1,CCP1IF
                goto    Wait1

                movf    CCPR1H,W        ;save now the value in H1,L1
                movwf   CCP1bH
                movf    CCPR1L,W  
                movwf   CCP1bL
                bcf     PIR1,CCP1IF     ;clr flag CCP1                    


           
    Wait2
                btfss   PIR2,CCP2IF
                goto    Wait2

                movf    CCPR2H,W        ;save now the value in H1,L1
                movwf   CCP2H
                movf    CCPR2L,W  
                movwf   CCP2L
                bcf     PIR2,CCP2IF     ;clr flag CCP1  

    ;*********CCP1b-CCP1a*******************       
    SUB1:
           

    ;*******CCP2-CCP1b****************
    SUB2:

       
    ;***********DIVISION***********************
           
    Div:
                 call   Divide                
                 movf   qL,w           ;@@@@@ RESULTS @@@@@@
                 movwf  PORTB
                 movf   qH,w            ; on LEDs connected to rb and rc.
                 movwf  PORTD
                 goto   Main           ;-------LOOP BACK TO START----

    Divide:    
                 movf   btmH,w             ; Check for division by 0.
                 iorwf  btmL,w              
                 btfsc  STATUS,Z            
                 bsf    PORTB,0           ; //**Error code If btm is Zero:PORTB PIN 0 Light LED//**
                 movlw  d'1'              ; Otherwise, initialize variables
                 movwf  count
                 clrf   index              ; for the division.
                 clrf   qH                    
                 clrf   qL                    

    Divide_sh_loop:
                 btfsc  btmH,7          ; Shift divisor left
                 goto   Divide_d1
                 bcf    STATUS,C            ; until msb is in
                 rlf    btmL,1                ; btmH.7.
                 rlf    btmH,1                ; count = no. of shifts+1.
                 incf   count,1                
                 goto   Divide_sh_loop      
     
    Divide_d1:
                 bcf    STATUS,C              
                 rlf    qL,1                   ; Shift quotient left.
                 rlf    qH,1                    
                 movf   btmL,w              ; top = top - btm.
                 subwf  topL,1
                 btfsc  STATUS,C          ; If top - btm < 0 then
                 goto   Divide_d2
                 movlw  d'1'               ; top = top + btm
                 subwf  topH,1
                 btfsc  STATUS,C           ; The idea is to do the
                 goto   Divide_d2
                 incf   topH,1                 ; the subtraction and comparison
                 movf   btmL,w                ; (top > btm?) in one step.
                 addwf  topL,1
                 goto   Divide_reentr         ; Then, if btm > top, undo

    Divide_d2:
                 movf   btmH,w                ; the subtraction by adding
                 subwf  topH,1
                 btfss  STATUS,C             ; top and btm back together
                 goto   Divide_less           ;
                 bsf    qL,0      
             
    Divide_reentr
                 bcf    STATUS,C            
                 rrf    btmH,1                  
                 rrf    btmL,1                  
                 decfsz count,1              
                 goto   Divide_d1
                 retlw  0h                   ; Return w/ remainder in top
                                            ; and result in q.&nsp;
    Divide_less  
                 movf   btmL,w                ; btm > top, so
                 addwf  topL,1
                 btfsc  STATUS,C           ; undo the subtraction by
                 incf   topH,1                 ; adding them back together.
                 movf   btmH,w                
                 addwf  topH,1
                 goto   Divide_reentr      ;

                 
                 
                 end
     
    Last edited: Mar 17, 2008
  15. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    Yes, of course. Don't you remember that you need to multiply by 360 BEFORE you do the divide?
    Hard to say what you are expecting, but if you had multiplied by 360 before doing the divide, I would expect that the result would be 54 degrees.
     
  16. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Thanks again for the reply mike.

    Mike, I was wondering if it is at all possible for me to display just the time lag rather than phase as it would certainly save another routine of 16 by 16 multiplication??

    And if I skip the 360 multiplication part, will I be stuck as the numerator will be smaller than the denominator??:confused:

    Thanks again mate.
     
    Last edited: Mar 16, 2008
  17. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    You can do whatever you want to do. If you want to display the time lag that is fine with me. I don't know why you would ask me that...you should ask your instructor if that is okay. Of course, the time lag doesn't tell you the phase unless you also know the frequency.
    You could multiply by 256 instead of 360. That is easy to do...just set topH equal to topL and then set topL to zero before you do the divide. In that case your output will be from 0 to 255...dividing a circle into 256 parts instead of 360.

    But I don't know why you wouldn't just add the code to do the multiply (get it from the same place you got the divide routine).

    Mike
     
  18. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Mike,

    I apologise if I ask too much but I found the code + u already have suggested this link. I am just out of my mind and can't understand it. :(

    It says it's a simple 32 by 16 division but I don't understand the steps.

    Let’s say I have got 4 reg: reg1,reg2,reg3,reg4 [numerator, values after multiplying 360 to topH & topL ] and btmH and btmL [denominator].

    Could you Please explain or help me to re-write the following code.
    Also I would like to check the ans on the LEDs on PORTB and PORTD.

    I will really really appreciate that mike.

    Thanks again.


    Code (text):
    divide movlw 32      ; 32-bit divide by 16-bit
           movwf bitcnt
           clrf remdrH   ; Clear remainder
           clrf remdrL

    dvloop clrc          ; Set quotient bit to 0
                         ; Shift left dividend and quotient
           rlf divid0    ; lsb
           rlf divid1
           rlf divid2
           rlf divid3    ; lsb into carry
           rlf remdrL    ; and then into partial remainder
           rlf remdrH

           skpnc         ; Check for overflow
           goto subd
           movfw divisH  ; Compare partial remainder and divisor
           subwf remdrH,w
           skpz
           goto testgt   ; Not equal so test if remdrH is greater
           movfw divisL  ; High bytes are equal, compare low bytes
           subwf remdrL,w
    testgt skpc          ; Carry set if remdr >= divis
           goto remrlt

    subd   movfw divisL  ; Subtract divisor from partial remainder
           subwf remdrL
           skpc          ; Test for borrow

           decf remdrH   ; Subtract borrow
           movfw divisH
           subwf remdrH
           bsf divid0,0  ; Set quotient bit to 1
                         ; Quotient replaces dividend which is lost
    remrlt decfsz bitcnt
           goto dvloop
           return
     
     
  19. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    Why would you want to re-write this divide code? Why not just use it as is?

    I'm sure I could figure out how this divide code works if I wanted to spend the time, but I don't. You don't have to understand it to use it.

    The simulator in MPLAB is a much better way to test out code like this to see if it does what you want than trying to run it on real hardware with LEDs for output.

    Mike
     
  20. bjox1

    bjox1 New Member

    Joined:
    Mar 7, 2008
    Messages:
    38
    Likes:
    0
    Location:
    UK
    Mike,

    So where shall I put the PORTB command? I mean the code doesn't explain where the quotient is saved?:confused:

    Am I right in assuming that:
    -divid0:divid3 are the numerator values and divisH and divisL are denominator?
    - But I can't analyse where the output bytes are saved? I mean it doesn't say anywhere about the ans. Where will the ans be stored.Which register? :(

    Thanks again Mike.
     
    Last edited: Mar 17, 2008
  21. mike50

    mike50 New Member

    Joined:
    Jun 29, 2007
    Messages:
    103
    Likes:
    2
    Location:
    Rochester, Minnesota
    Oh, I thought you wanted to know exactly how it performed a divide.

    Input to the divide is the dividend (the numerator) which is in divid0, divid1, divid2, and divid3. divid0 is the least significant byte and divid3 is the most significant and the divisor which is in divisH and divisL.

    The output is in divid0, divid1, divid2 and divid3. Again with the least significant byte in divid0.

    You didn't see this comment near the end of the code?

    Code (text):
                         ; Quotient replaces dividend which is lost
    You do know the words dividend, divisor and quotient, right?

    Mike
     

Share This Page