Reading encoder using PIC18F452

Discussion in 'Embedded Systems and Microcontrollers' started by Smilodon, Sep 3, 2013.

  1. Smilodon

    Thread Starter New Member

    Jul 9, 2013
    5
    0
    I am trying to use PIC18F452 to read an optical encoder at a very low frequency (less than 100 Hz). I have tried three approaches:
    -Using the CCP module to capture the rising edge and calculate the time between them to determine the period. This didn't work - The interrupt handler was never called
    -Using polling: This worked in a way, but the time varied erratically and the result made no sense at all.
    -Using the RBIF interrupt on port B -this works better than polling, but even then the result varies wildly.
    The program also drives a seven segment display and reads an analogue input periodically.
    I haven't been able to figure out what is wrong. Please help!
    Thank you for reading.
     
  2. tshuck

    Well-Known Member

    Oct 18, 2012
    3,531
    675
    Neither can we figure or what is wrong-you've given us nothing to go on!
     
  3. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,691
    2,756
  4. Smilodon

    Thread Starter New Member

    Jul 9, 2013
    5
    0
    With the CCP module, I set the CCP1M3:CCP1M0 to 0101 (to capture every rising edge), T3CCP2=0 (to use timer 1) and set the CCP1IE bit to enable the interrupt. However, the interrupt is never called and I cannot figure out how to make the CCP module work.
    using the RBIF interrupt, I read the value of timer 1 when a rising edge occurred. I used
    period = (65536-time1) + time to read the period when an overflow occurs, and period = time-time1 when it doesn't, where time1 is the previous reading, and time is the new reading.
    Using this approach, the frequency reading varies through about 80 Hz.
    Using polling with timer1, I get completely random readings.
     
  5. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,691
    2,756
    Why are you using a timer with an optical encoder (I assume you mean a quadrature encoder, no?)?

    Perhaps if you provide us with data on the encoder itself, we may be of better help.
     
  6. Smilodon

    Thread Starter New Member

    Jul 9, 2013
    5
    0
    Actually, it's just an LED and a sensor, and I am using a sort of wheel with six equally spaced slots in it. I need to find its rotation speed.
     
  7. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,691
    2,756
    And you didn't think this was pertinent?

    The CCP module is the way to go. Post a schematic and some code.

    There is *no* other way for us to help you.

    Edit: BTW, this is a tachometer, not an encoder.
     
    Smilodon likes this.
  8. Smilodon

    Thread Starter New Member

    Jul 9, 2013
    5
    0
    I used this code for initialising the CCP module (this is called from main())
    Code ( (Unknown Language)):
    1.  
    2. void CCP_Init(){
    3. //Capture mode, every rising edge
    4. CCP1M3=0; CCP1M2=1; CCP1M1=0; CCP1M0=1;
    5. //Select timer as timer1
    6. T3CCP2=0
    7. //Enable interrupt
    8. CCP1IE=1;
    9. //Clear interrupt flag
    10. CCP1IF=0;
    11. }
    12.  
    The interrupt handler. The interrupt handler handles several interrupts - the ADC, timer 0 and timer 1, and the CCP module. the part relevant to the CCP module is given below:

    Code ( (Unknown Language)):
    1.  
    2. if (CCPIF&&CCPIE){
    3. time = CCPR1;
    4. count1= count //no of overflows of timer1
    5. count=0; //clear overflow counter
    6. if (count1>0){ //If overflow has occured
    7. period = (65536-time1) + time;
    8. }
    9. else{
    10. period = time-time1;
    11. }
    12. time1=time; //set current reading as previous value
    13. CCPIF=0; //Clear flag
    14. }
    15.  
    The value of period is then used by a different function to calculate the frequency when the display is updated. It is not included in the interrupt handler as it involves floating point calculations.

    I don't have the code or the schematic with me now, but I wrote the code from memory. I think it is accurate.
     
  9. tshuck

    Well-Known Member

    Oct 18, 2012
    3,531
    675
    Without knowing what the actual code/schematic/setup was that you used, we cannot tell you why what you used didn't work as you expected...
     
  10. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,691
    2,756
    First, in the initialization section, you are setting the CCP interrupt enable flag, but make sure you are also setting the global IE flag(s) (perhaps you are elsewhere in the program?).

    Also, if you are trying to avoid a spurious interrupt by clearing CCP1IF, you should clear it before setting CCPIE:

    Code ( (Unknown Language)):
    1.  
    2. void CCP_Init()
    3. {
    4.     //Capture mode, every rising edge
    5.     CCP1M3=0; CCP1M2=1; CCP1M1=0; CCP1M0=1;
    6.     //Select timer as timer1
    7.     T3CCP2=0
    8.     //Clear interrupt flag before enabling
    9.     CCP1IF=0;
    10.     //Enable interrupt
    11.     CCP1IE=1;
    12. }
    13.  
    You should also consider initializing other variables in this section, like count and count1, but I can't figure out what they're for (more below).

    In your interrupt routine:

    Code ( (Unknown Language)):
    1.  
    2. if (CCPIF&&CCPIE)
    3. {
    4.     time = CCPR1;
    5.  
    6.     count1= count //no of overflows of timer1
    7.  
    8.     count=0; //clear overflow counter
    9.  
    10.     if (count1>0) //If overflow has occured
    11.     {
    12.         period = (65536-time1) + time;
    13.     }
    14.     else
    15.     {
    16.         period = time-time1;
    17.     }
    18.  
    19.     time1=time; //set current reading as previous value
    20.  
    21.     CCPIF=0; //Clear flag
    22. }
    23.  
    Why do you think timer overflows need to be processed differently than if the timer did not overflow? You need to think of how things work in binary.

    First off, CCPR1 is a 16 bit register. Computing (65536-time1) is the same as (0 - time1) or just -time1. So, you are telling the compiler that the time elapsed (period) is equal to the current timer value + the negative of the previous timer value. How is this different than period = time-time1?

    Answer: It's not.

    Secondly, you've got variables count and count1, neither of which seem to be doing anything except being set to 0. What is their purpose?
     
    Smilodon likes this.
  11. WBahn

    Moderator

    Mar 31, 2012
    17,769
    4,804
    You almost certainly encountering bounce problems. As one of the slots begins to expose the detector to the LED you get a period in which it can't decide if it is seeing it or not. Similarly as you transition the other way as the slot leaves the LED. This is the whole idea behind a quadrature encoder. One way you can get the benefits of this is to mount a second LED/sensor half a slot away from the first (or aligned with any of the slot positions when the disk is positioned so that the first LED/sensor is midway between two slots).
     
  12. MaxHeadRoom

    Expert

    Jul 18, 2013
    10,564
    2,379
    I just built almost the very same idea, I just used a slot detector and used the first edge to reset T1 and read the T1 elapsed value at the second rising edge.
    The results were very consistent between measurements using a servo controlled motor for the test.
    Max.
     
  13. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,691
    2,756
    I was going to get to this. But he said:

    So I figured that, in the absence of a schematic, we'd get the code running first, and then deal with noise.
     
  14. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,394
    1,606
    Bounce is the least of your problems. As long as your encoder is actually turning it should be minimal, and even if not a simple delay when processing a transition should lock out and nearby glitches.

    The problem comes when the wheel is just about stationary, just on the edge, and is emitting a high frequency of glitches so a "STOP" looks kinda just like "GO LIKE HELL."

    (Hint: that's why quadrature encoders were invented.)
     
  15. Smilodon

    Thread Starter New Member

    Jul 9, 2013
    5
    0
    I figured out what the problem was - I accidentally reset CCP1CON in the code - after initializing the CCP module. :D
    I didn't notice that 65536-time1 + time is the same as time-time1. I corrected that.
    count and count1 are for counting the overflows from timer 1 - because it overflows even with the maximum prescalar, as the minimum frequency to be measured is 1 Hz. count1 is used to capture the number of timer overflows as soon as the CCP interrupt occurs, and this is used for further calculations.
    time1 is the previously measured value, and time is the current value.
     
Loading...