Quantcast
Channel: MEL PICBASIC Forum
Viewing all articles
Browse latest Browse all 4787

Quad Encoder Problem

$
0
0
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:

Attachment 6893

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).
Attached Images
 

Viewing all articles
Browse latest Browse all 4787

Trending Articles