Quad Encoder Problem


Closed Thread
Results 1 to 37 of 37

Hybrid View

  1. #1

    Default Quad Encoder Problem

    This doesn't seem to be my week ... I had problems earlier with converting code from a PIC12F683 to a PIC12F1840 which thankfully was resolved yesterday (thanks Henrik!) but when I come back to something I thought was working fine, it's not.

    I found examples of determining the direction of turn of a mechanical quadrature encoder. Darrel helped way back with some of it and all was well until late last night when I thought I'd better retest this after working on other parts of the project. Lo and behold, the MotorRPM variable isn't going up or down steadily but rather goes up by one or two but could just as easily go down (when turning the knob in the same direction). I did spend the first part of the year converting from using PBP's HPWM on CCP1/2 (which are ECCP modules on the 16F1825) to CCP3/4 (regular CCP modules) but at the time I'm fairly certain I tested the quadrature part and it worked OK.

    Here's my code (edited for clarity):
    Code:
    ' ***************************************************************
    ' Pin Connections
    ' ***************************************************************
    
    ' RA0                       -> B pin of rotary encoder
    ' RA1                       -> A pin of rotary encoder
    ' RA2/CCP3                  -> Stbd motor PWM output (motor 1)
    ' RA3/MCLR                  -> Button switch pin of rotary encoder
    ' RC0                       -> Stbd motor direction signal (motor 1)
    ' RC1/CCP4                  -> Port motor PWM output (motor 2)
    ' RC2                       -> Port motor direction signal (motor 2)
    ' RC4/Tx                    -> Serial LCD output
    ' RC5/Rx                    -> Serial input
          
    DEFINE OSC 16               ; Set oscillator 16Mhz
    
    ' ***************************************************************
    ' Device Fuses
    ' ***************************************************************
    
    #CONFIG
       __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
       __config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
    #ENDCONFIG
    
    ' ***************************************************************
    ' Initialization
    ' ***************************************************************
    
    OSCCON   = %01111000        ' 16MHz internal osc
    
    pause 100                   ' As advised by Darrel Taylor for EEPROM issue
    
    ANSELA    = 0               ; Digital only for all PortA pins
    ANSELC    = 0               ; Diginal only for all PortC pins
    
    TRISA     = %00001011       ' Make PORTA pins 0-1 input for rotary encoder,
                                ' pin 3 for rotary button
    TRISC     = 0               ' Make all PORTC pins output
    
    ' The INTEDG bit of the OPTION_REG register determines on which edge the 
    ' interrupt will occur. When the INTEDG bit is set, the rising edge will 
    ' cause the interrupt. When the INTEDG bit is clear, the falling edge will 
    ' cause the interrupt. 
    OPTION_REG.6 = 1             ' 1=Rising edge (default) or button "PRESS";
                                 ' 0=Falling edge or button "RELEASE"
    
    Old_Bits       VAR BYTE
    New_Bits       VAR BYTE
    RotEncDir      VAR BIT       ' 1=CW, 0=CCW
                                 ' Rot Enc pin A connected to PortA.1;
                                 ' Rot Enc pin B connected to PortA.0
    Old_RPM        VAR BYTE
    i              VAR BYTE
    
    ' ***************************************************************
    ' Set up registers for PWM on CCP3 & CCP4
    ' ***************************************************************
    
    CCP3CON = %00001100         ; Use CCP3 in PWM mode
    CCP4CON = %00001100         ; Use CCP4 in PWM mode
                                 
    T2CON   = %00000101         ; Timer2 on with 1:4 prescaler
    PR2     = 62                ; For 16Mhz OSC the desired output freq of 15,873Hz is
                                ; achieved with this PR2 value (8-bit resolution
                                ; with 1:4 prescaler)
                                
                                ; PWM freq must be ~ 16-20kHz to reduce noise
    
    PreSpinVal  CON 17          ; value to subtract from MinDuty for motor spin up 
    MinDuty     CON 75          ; 75 when max duty = 250 (8-bit resolution)
    ;SpinUpPause CON 17          ; Pause during motor spin up
    SpinUpPause VAR BYTE        ; Pause during motor spin up
    SpinUpPause = 17
    #IFDEF USE_LCD_FOR_DEBUG
        SpinUpPause = 12        ; Subtract 5ms pause when outputting to LCD
    #ENDIF
    
    MaxDuty     VAR WORD        ; According to Darrel:
                                ;   MaxDuty = (PR2 + 1) * 4
    
    MaxDuty = (PR2 + 1) * 4     ; 252 but with prescaler resolution it's actually 250
    
    DutyVar3    VAR WORD        ; temp variable to store duty variable for CCP3
    DutyVar4    VAR WORD        ; temp variable to store duty variable for CCP4
    
    TimeCnt     VAR Word      
    
    ' ***************************************************************
    ' Includes
    ' ***************************************************************
    
    INCLUDE "DT_INTS-14.bas"    ' Base Interrupt System
    INCLUDE "ReEnterPBP.bas"    ' Include if using PBP interrupts
                                ' --> copy both files to PBP main folder 
                                ' (i.e. c:\pbp3)
    
    ' ***************************************************************
    ' EEPROM Variables
    ' ***************************************************************
    
    MotorRPM_Default   CON  177
    EE_MotorRPM        DATA MotorRPM_Default
    MotorRPM           VAR  BYTE
    READ EE_MotorRPM, MotorRPM
    
    PortEngDir_Default CON  0
    EE_PortEngDir      DATA PortEngDir_Default
    PortEngDir         VAR  BYTE
    READ EE_PortEngDir, PortEngDir
                                                                      
    ' ***************************************************************
    ' ASM Interrupt Definitions
    ' ***************************************************************
                                                                      
    ASM
    INT_LIST  macro    ; IntSource,            Label,  Type, ResetFlag?
            INT_Handler    IOC_INT,    _RotEncAdjust,   PBP,  yes
        endm
        INT_CREATE     ; Creates the interrupt processor
    ENDASM
    
    ' ***************************************************************
    ' Set default values
    ' ***************************************************************
    
    Old_RPM = MotorRPM
    Old_Bits = PORTA & (%00000011)
    
    ' Enable interrupts on RPM control now that motors are fully spun up
    INTCON   = %10001000        ' Global int enabled, IOCI enabled, 
                                ' INTF flag bit 0 clr, IOCI flag bit 0 clr
    IOCAP.0  = 1                ' Enable positive (rising edge) change
    IOCAP.1  = 1                ' Enable positive (rising edge) change
    IOCAN.0  = 1                ' Enable negative (falling edge) change
    IOCAN.1  = 1                ' Enable negative (falling edge) change
    IOCAF.0  = 0                ' Clear interupt-on-change flag
    IOCAF.1  = 0                ' Clear interupt-on-change flag
    
    @ INT_ENABLE   IOC_INT      ; Interrupt-on-Change interrupt
    
    Main:
        ' Check if motor RPM has changed
        IF MotorRPM <> Old_RPM Then
            Old_RPM = MotorRPM
            GOSUB ChngMotorHPWM
        EndIF   
       
        TimeCnt = 0
    	While ButtonPress = 0
    	    TimeCnt = TimeCnt + 1
    	    Pause 10
    	    If TimeCnt > 500 Then BtnAction
    	Wend
     
        BtnAction:
            If TimeCnt > 0 and TimeCnt < 200 Then
                PortEngDir = PortEngDir + 1
                If PortEngDir > 1 Then PortEngDir = 0
        
                WRITE EE_PortEngDir, PortEngDir
                #IFDEF USE_LCD_FOR_DEBUG
                    HSEROUT [LCD_INST, LCD_CLR]
                    pause 5
                    HSEROUT ["PortEngDir=", DEC PortEngDir, "  ", 13, 10] ' Send text followed by carriage return and linefeed
                #ENDIF
    
                GOSUB ReversePortEng
            ElseIf TimeCnt >= 200 Then
                WRITE EE_MotorRPM, MotorRPM_Default  ; restore Default value
            
                MotorRPM = MotorRPM_Default
            EndIf
     
        GOTO Main
    
    
    ' ***************************************************************
    ' [IOC - interrupt handler]
    ' ***************************************************************
    RotEncAdjust:
        New_Bits = PORTA & (%00000011)
        IF (New_Bits & %00000011) = (Old_Bits & %00000011) Then DoneRotEnc
    
        RotEncDir = New_Bits.1 ^ Old_Bits.0
        IF RotEncDir = 1 Then
            ; CW rotation - increase speed but only to a max of 'MaxDuty'
            IF MotorRPM < MaxDuty then MotorRPM = MotorRPM + 1
        Else
            ' CCW rotation - decrease speed to a min of 0
            IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
        EndIF
    
        WRITE EE_MotorRPM, MotorRPM
        pause 100
    
    DoneRotEnc:
        Old_Bits = New_Bits
        
        IOCAF.0 = 0      ' Clear interrupt flags
        IOCAF.1 = 0
    
    @ INT_RETURN
    Here's a video which shows the problem (I hope):

    Here's my schematic:

    Name:  PIC16F1825_Motor_Circuit.png
Views: 2941
Size:  40.1 KB

    Again, AFAIK nothing has changed in the two months since I last worked on this code and all was well.

    Does anyone see where I'm going wrong? Here's what I've tried:
    • Swapped out the quad encoder with a spare one (same model)
    • Swapped out the PIC and programmed another one
    • Replaced the 10k resistors that tie the encoder's A/B outputs to +5V
    • Double-checked the connections

    Something to do with Timer2 & PWM on CCP3/4? Bad IOC interrupt config? What's strange is that the quad encoder has a pushbutton knob and that's working just fine (although it's not using an interrupt).

  2. #2
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,605


    Did you find this post helpful? Yes | No

    Default Re: Quad Encoder Problem

    Hi,
    First order of business, remove the Pause 100 from the interrupt service routine.

    Also, is the intention really to write the MotorRPM variable to EEPROM on every interrupt? I see two possible issues with that, the first is that the write operation takes a while and the second is that you may wear out the EEPROM. Finally, not related to the actual problem but still
    Code:
    IF (New_Bits & %00000011) = (Old_Bits & %00000011) Then DoneRotEnc
    Since you're assigning the value of New_Bits by ANDing PortA with 3 and then, at the end setting Old_Bits = New_Bits there's no need to AND New_Bits or Old_Bits with 3 here as well. Neither of those variables will ever have bits 2-7 set anyway, you've already taken care of that. (As long as you don't use them anywhere else in your program of course).

    /Henrik.

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: Quad Encoder Problem

    It doesn't work with or without the 'Pause 100' in the interrupt service routine - I actually only added that based on some sample interrupt code from DT (I thought it made sense if there's some latency to saving to EEPROM you might want to wait a tiny bit).

    Other than saving the new MotorRPM to EEPROM everytime it changes, I don't know what else I can do. Users won't be modifying the motor speed very often (once they find the speed they like, they'll just leave it). I suppose I could set up a timer to save the value to EEPROM if it's changed, but I'm open to suggestions

    For the above code line, are you saying I could just use this instead?

    Code:
    IF New_Bits = Old_Bits THEN DoneRotEnc

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,605


    Did you find this post helpful? Yes | No

    Default Re: Quad Encoder Problem

    Hi,
    According to the manual the WRITE command "is self-timed and may take up to 10 milliseconds to execute on a PIC MCU".
    I suspect that's the issue, most encoders produce a full quadrature cycle, ie 4 edges (4 interrupts) per click/detent. IF the WRITE operation takes 10ms (and/or you have that Pause 100 in there) it's likely that it will miss pulses/edges and get confused.

    Try commenting out the Write operation and see if that fixes it.

    Yes, that's what's I'm saying regarding New_Bits = Old_Bits.
    At the beginning of the ISR you do New_Bits = PortA & %00000011 so the top 6 bits of New_Bits are cleared. At the end of the ISR you assign the value in New_Bits to Old_Bits so the top 6 bits in Old_Bits will also be zero. Therefor the second AND operation is unnecessary, but it doesn't have anything to do with the actual problem....

    /Henrik.

  5. #5


    Did you find this post helpful? Yes | No

    Default Re: Quad Encoder Problem

    I'll try this out tonight, Henrik. Thanks so much for your help here and on my other thread from this week.

    If the WRITE operation in the interrupt service routine is the cause, then I'll move that to the check in the Main loop.

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,605


    Did you find this post helpful? Yes | No

    Default Re: Quad Encoder Problem

    Hi,
    Just realised I misunderstood you a bit. I thought you were trying to use the 12F1840 with this code, then I realised you're actually using the 16F1825 that's in the schematic...duh...

    Anyway, I've breadboarded a 12F1840 (don't have any 18F1825) and now have an encoder counting properly, sending the count over the USART. I simulated the EEPROM write by adding a PAUSEUS xxx in the handler and found that the limit for correct response - in my case - is around 500us. Anything above that it starts missing edges, coutning the wrong way etc.

    So, if the EEPROM write takes even remotely as long as 10ms then that is most likely the issue.

    /Henrik.

Similar Threads

  1. RF600E & RF600D encoder/decoder problem
    By Megahertz in forum Off Topic
    Replies: 0
    Last Post: - 17th April 2012, 17:18
  2. using Quad UART
    By harryweb in forum mel PIC BASIC Pro
    Replies: 9
    Last Post: - 30th April 2011, 00:13
  3. quad encoders
    By cpayne in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 13th March 2007, 17:49
  4. Quad encoder problems
    By smitty505000 in forum mel PIC BASIC Pro
    Replies: 51
    Last Post: - 5th October 2006, 22:44
  5. Encoder problem
    By precision in forum mel PIC BASIC Pro
    Replies: 6
    Last Post: - 12th September 2006, 22:21

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts