0
\$\begingroup\$

I'm trying to interface a rotary encoder with my pic18f4550. Just trying to get it to increase/decrease the variable output and then display that value through the LATD pins.

The code does not seem to be working at all, and was just wondering if I could get some help from you guys as to where I'm going wrong.

#include <xc.h>
#include "config.h"

#define _XTAL_FREQ 8000000

unsigned int output;

void main(void)
{
    OSCCON = 0b01110010; // Oscillator setup
    TRISB = 0b00110000; //Set RB4 & RB5 as input
    TRISD = 0x00; //All D pins set as output

    //Port B interrupts
    INTCONbits.RBIE = 1; //Enable port B interrupts; interrupt flag is not cleared so interrupt is triggered instantly, allowing us to record initial value
    INTCONbits.GIE = 1; //Enable general interrupts

    //Port B pull-ups
    INTCON2bits.RBPU = 0;

    while(1)
    {
        if(INTCONbits.RBIF==1)
        {
             static unsigned char prevState = 0xFF;
             unsigned char state = (PORTBbits.RB4 | PORTBbits.RB5 << 1); //Get value 0-3 from rotary encoder bits
             if(prevState != 0xFF)//If this is not the first time we enter the interrupt, process the gray codes
             {
                  if(((prevState == 0b00) && (state == 0b01)) //Turn counterclockwise
                  || ((prevState == 0b01) && (state == 0b11))
                  || ((prevState == 0b11) && (state == 0b10)) 
                  || ((prevState == 0b10) && (state == 0b00))) 
                  {
                      output--;
                  }
                  else if(((prevState == 0b00) && (state == 0b10)) //Turn clockwise
                  || ((prevState == 0b10 && state == 0b11))
                  || ((prevState == 0b11 && state == 0b01)) 
                  || ((prevState == 0b01 && state == 0b00))) 
                  {     
                      output++;
                  }
             }
        prevState = state; //Save previous port b state.
        INTCONbits.RBIF=0; //Clear port B interrupts flag
        }
    LATD = output; //Display value on LED's on D pins
    }
 }
\$\endgroup\$
  • 3
    \$\begingroup\$ So you enable interrupts, but you have no interrupt handler? \$\endgroup\$ – Justme Apr 30 at 15:33
  • 1
    \$\begingroup\$ your code in unnecessarily complex .... just monitor one encoder data pin ... when it changes state, check the other encoder data pin .... the state of the second pin is directly related to the direction the encoder is moving \$\endgroup\$ – jsotola Apr 30 at 15:59
  • \$\begingroup\$ @jsotola thanks for your help mate, you helped me figure it out. \$\endgroup\$ – ezra_vdj May 1 at 11:31
2
\$\begingroup\$

Your code looks complex.

If the encoder frequency is not too high it's just a matter of keeping track of the count using an up-down counter and two general purpose input pins.

schematic

simulate this circuit – Schematic created using CircuitLab

Figure 1. 2-bit rotary encoder waveforms.

The program logic is very simple.

  • Track the current state of 'A'. If the state changes to 'high' then:
  • Look at input 'B'. If 'B' is low then count up. If 'B' is high then count down.
  • Update the 'lastState' of 'A'.

Pseudo code

void loop(){
  if(A && not prevA){
    if(B){
      cnt--;
    } else {
      cnt++;
    }
  } else {
  prevA = A;
}

You'll probably need to debounce the inputs to prevent spurious triggering.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I followed a similar vibe with my answer. I added a delay, then it waits before the switch is clicked, then it flips the edge triggering mode of the interrupt and clears the flags. What do you think? \$\endgroup\$ – ezra_vdj May 2 at 0:19
  • \$\begingroup\$ With the lack of comments following the variable names in your code is hard work so I don't know what's going on. I don't see any interrupt disable inside the routine so I suspect that it could get retriggered while it's running. I also see delays inside the routine which means the controller can't do anything else while these are running - generally a bad thing. (I haven't done any PIC coding for 15 years and I was an amateur.) \$\endgroup\$ – Transistor May 2 at 8:33
  • \$\begingroup\$ I’ve totally revised my solution. Just decided to go with hardware for debouncing to keep things simpler in the code, and added comments. Works like a treat, thanks again! \$\endgroup\$ – ezra_vdj May 4 at 1:39
0
\$\begingroup\$

Here's my solution.

Encoder is plugged into RB1 & RB2.

It works by looking at which interrupt flag was triggered first and using that to decide on the direction. Once direction is calculated, it flips the edge trigger bit from rising/falling to the opposite so the encoder can continue rotating the same way and continue adding/subtracting numbers.

I decided to go for the RC filter approach (0.22uf & 22kOhm) for debouncing for simplicity and to save CPU.

Hope this helps everyone.

#include <xc.h>
#include "config.h"

#define _XTAL_FREQ 8000000

unsigned int count;

void main(void)
{        
    OSCCON = 0b01110010;    // Internal Oscillator setup
    TRISB = 0b00000110;     // Set RB1 & RB2 as input
    TRISD = 0x00;           // Set all D pins as outputs

    RCONbits.IPEN = 1;      // Enable interrupt priority levels
    INTCON = 0b10000000;    // Interrupt setup
    INTCON2 = 0b10000000;   // Interrupt setup
    INTCON3 = 0b11011000;   // Interrupt setup

    while(1)
    {
        LATD = count;                                   // Display count as binary through D pins
    }
}

void __interrupt() rotaryISR(void)                      // ISR executed once rotary encoder is twisted
{
    if (INTCON3bits.INT1IF > INTCON3bits.INT2IF)        // If RB1 interrupt flag gets raised before RB2
    {
        count++;                                        // Increase count value
        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;     // Flip RB1 and RB2 trigger edges
        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;     //
        INTCON3bits.INT1IF = 0;                         // Clear interrupt flags
        INTCON3bits.INT2IF = 0;                         //
    }
    if (INTCON3bits.INT2IF > INTCON3bits.INT1IF)        // If RB2 interrupt flag gets raised before RB1
    {
        count--;                                        // Decrease count value
        INTCON2bits.INTEDG1 = ~INTCON2bits.INTEDG1;     // Flip RB1 and RB2 trigger edges
        INTCON2bits.INTEDG2 = ~INTCON2bits.INTEDG2;     //
        INTCON3bits.INT1IF = 0;                         // Clear interrupt flags
        INTCON3bits.INT2IF = 0;                         //
    }
    else     
    {}
}
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.