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):
Here's a video which shows the problem (I hope):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 my schematic:
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).





Bookmarks