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
}
}
-
3\$\begingroup\$ So you enable interrupts, but you have no interrupt handler? \$\endgroup\$Justme– Justme2020年04月30日 15:33:57 +00:00Commented Apr 30, 2020 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– jsotola2020年04月30日 15:59:06 +00:00Commented Apr 30, 2020 at 15:59
-
\$\begingroup\$ @jsotola thanks for your help mate, you helped me figure it out. \$\endgroup\$Eman– Eman2020年05月01日 11:31:30 +00:00Commented May 1, 2020 at 11:31
2 Answers 2
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.
-
\$\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\$Eman– Eman2020年05月02日 00:19:52 +00:00Commented May 2, 2020 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– Transistor2020年05月02日 08:33:45 +00:00Commented May 2, 2020 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\$Eman– Eman2020年05月04日 01:39:25 +00:00Commented May 4, 2020 at 1:39
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
{}
}
Explore related questions
See similar questions with these tags.