PIC18F47J13 Capture Mode?

Discussion in 'Embedded Systems and Microcontrollers' started by spinnaker, Oct 30, 2018.

  1. Travm

    Member

    Aug 16, 2016
    230
    18
    The clock select bits should be explicit. I was just pointing out both the clock select and prescaler bits were not properly defined in the TS original code. I'm sure it's changed several times since that was posted.
     
  2. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395

    Actually I looked at the thread wrong. I thought the code refereed to the external clock bu that is a 3.

    But this

    T1CONbits.TMR1CS = 1

    and this

    T1CONbits.TMR1CS1 = 0
    T1CONbits.TMR1CS0 = 1

    do the same same thing. They both select FOSC as clock source.
     
  3. Ian Rogers

    Active Member

    Dec 12, 2012
    480
    124
    TMR1CS1 = 0 and TMR1CS0 = 0 select the instruction cycle as the source..
    TMR1CS1 = 0 and TMR1CS0 = 1 select the main osc as the source..

    If you use the old TMR1CS = 1 then the external clock is required..
    TMR1CS = 1 is the same as.. TMR1CS0 = 0 and TMR1CS1 = 1..

    Also remember the instruction clock is four times slower than the system clock.
     
  4. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395

    Not sure that is right. Pics are least significant byte to the right most significant to the left.


    Besides TMR1CS = 1 works without an external clock.


    Working right now with TMR1CS = 1 and no external clock.


    TMR1CS = 2 is external clock.
     
  5. Ian Rogers

    Active Member

    Dec 12, 2012
    480
    124
    upload_2018-11-5_8-43-3.jpeg

    I just looked at the datasheet!
     
  6. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395

    And it shows that TMR1CS=2 is the external clock. not TM1CS=1.
     
  7. Ian Rogers

    Active Member

    Dec 12, 2012
    480
    124
    My head just exploded...

    As far as I typed out TMR1CS = 2 is the correct setting for external crystal operation....
    Anything else is me being an old fart!!! Apologies for that... ( I know what I mean....I think it's called dementure! )
     
    spinnaker likes this.
  8. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395

    No problem I do it all the time. I was staring right at the datasheet and was still setting a 3 which is completely wrong. ;)
     
  9. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395
    I am currently resetting the timer and I believe what you described is the issue I am seeing. I am seeing a difference in timer count of about 30us (if I did my math right) which occurs every once in a while. Maybe as often as every second or two.

    Looks like I will need to just let the timer run and calculate accordingly.

    BTW Except for this issue I have the POV clock working with one digit so far. But I am seeing a "shaking" of the number. I assume this is because of the timer issue as my sensor in seems to be pretty solid,

    My shaky hands video shows the issue if you look real close. Top line is the CCP in bottom time in the LAT out that turns on the LED. You can see the number 9 shaking back and forth.

     
    Last edited: Nov 22, 2018
  10. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395

    OK I am pulling this one back off of the shelf. I am still having trouble trying to grasp what to do here.

    You say "Guard against very slow RPM by counting timer overflows (another interrupt) between events."

    Regardless of RPM, if the timer is running free, couldn't I possibly get an overflow between CCP events?
     
  11. JohnInTX

    Moderator

    Jun 26, 2012
    3,412
    1,735
    Yes, it would. But using the CCP to accurately measure the rotational period is a good approach so do that first. As discussed above on each capture, compute the elapsed time, write that time to a dedicated RPM variable and set a flag. The main routine checks the flag and when it's set, reads the RPM and clears the flag.

    To guard against low RPM overflowing the CCP pulse pairs, use a second physical timer, TMR2/PR2, to generate a system tick at a convenient interval. Let's say 1msec. Using TMR2/PR2 as a period counter eliminates any need to reset the timer each time. Once set up, it just emits interrupts at a regular interval without further intervention.

    From that timer you can make one (or many) derived timer(s) that run at 1msec, counts down from some setting then STOPS when it gets to zero. I use 8bit derived timers to keep things simple. At a 1ms system tick, you can time up to 255ms +0/-1ms. To use one as a guard timer for your RPM, decide what the minimum RPM should be and use that period to set the guard timer each time you get a new RPM reading. If the RPM is above minimum, the derived timer will never run out. If it does, it will run to zero and stop by itself. When you check the RPM flag to see if a new RPM value has been loaded, read the guard timer, too. If the guard timer ever reads zero, the RPM is below minimums and should be called zero RPM. In that event, the RPM value loaded by the CCP is discarded because it may have overflowed the counter.

    When coming off a zeroRPM, the guard timer will start being loaded again and read non-zero indicating that the system is up to speed. Read and discard the current RPM value and clear the flag. Successive RPM postings will be valid as long as the guard timer is >0.

    Making and using derived timers is simple:

    Code (C):
    1.  
    2. unsigned char RPMguardTimer;  // 8 bit counter counts TMR2 tiks
    3. unsigned char AnotherDerivedTimer;  // how to do another one if you need it
    4. bit NewRPMLoaded;  // flag to indicate a new RPM value is loaded
    5. bit HaveNewRPM;    // main: indicates we have a new RPM reading..
    6.  
    7. //------------ INTERRUPTS  -----------------
    8. //Timer 2/PR2 interrupt service
    9. if(TMR2IF){
    10.   TMR1IF=0;  // clear the interrupt flag
    11.   if(RPMguardTimer)RPMguardTimer--;  // decrement guard timer to 0 and stop
    12.   if(AnotherDerivedTimer)AnotherDerivedTimer--; // add a many as you need for other things
    13. }
    14.  
    15. //CCP interrupt service (partial)
    16.   RPMguardTimer = MIN_RPM_PERIOD;  // reset guard timer to ms period corresponding to min RPM
    17.   RPMperiod = Calculated_capture_difference  //  post the raw elapsed count, let main calculate to RPM
    18.   NewRPMLoaded = 1; // let main know that a new capture has been posted
    19.  
    20. //---------------  MAIN  -------------------
    21.  
    22.  
    23.   if(NewRPMLoaded){  // if a new capture..
    24.      disableInterrupts();  // 16 bit values require atomic reads.  Disable global or just the capture interrupt to be fancy AND you're testing for the CCPxIE flag in the service routine.  Global disable for this short time is OK
    25.      temp = RPMperiod; // read the posted value
    26.     NewRPMLoaded = 0; // clear the flagto acknowledge that RPMperiod has been read..
    27.     enableInterrupts();  // turn it loose
    28.     calculateRPM(temp);  // calculate RPM from the posted rotational period
    29.     HaveNewRPM = 1;  // let the rest of the code know we have a new RPM value
    30.   }
    31.   else  // no new RPM this poll, see if guard timer has run out
    32.        if(RPMguardTimer = 0) { // don't have to disable interrupts because it's 8 bits
    33.            RPM = 0;  // guard timer ran out, RPM is below mins
    34.            HaveNewRPM = 1;  // zero is a new RPM too..
    35.    }
    36. //if neither case is true, all is OK, we're waiting for the next capture interrupt OR the guard //timer to run to 0
    37.     if (HaveNewRPM){
    38.        HaveNewRPM = 0;  // ack the new RPM
    39.        processRPM();  
    40.     }  
    41.   // get on with the rest of it..
    That's how I've done it...

    Good luck!
     
    Last edited: Jan 21, 2019
  12. spinnaker

    Thread Starter AAC Fanatic!

    Oct 29, 2009
    7,426
    3,395
    Thanks!

    Funny I just hit on using the same timer I am using for the CCP last night! ;) But still struggling on dealing with the whole wrap thing there too.

    I don't seem to sound like an ungrateful newbie looking for the whole answer. I am not. But I am still struggling with this concept. Thanks for the code.

    On line 9 you check for the Timer 1 interrupt flag but clear the Timer 2 interrupt flag in the ISR. Is that a typo?

    Is Calculated_capture_difference the absolute value you discussed previous?

    Why do you disable then enable interrupts?

    It looks to me you are only checking the guard timer if there is no new capture. Trying to figure why that would be.

    Aren't you sorry you started helping? :)

    P.S. Looking at your code, I see what you mean by derived timer. That is not going to work to fire the lights. I think what I need to do is to poll T1 but is it is free running need to figure out how that would work. But that is next. For now I just want to understand the proper way to calculate RPM.

    BTW I do have it working with absolute value and not resetting the timer. It seems to work but still not sure why. :)


    Thanks again for your help.


    Edit: I just realized my code to capture the too times was way too complicated. I was using a flag to capture the "starting time" and "ending time. No reason for that. I have not tested it yet but this should work.

    Code (Text):
    1.  
    2.  
    3.   if (PIR4bits.CCP5IF == 1)
    4.   {
    5.    
    6.   PIR4bits.CCP5IF = 0;
    7.   unsigned char thisPeriod;
    8.  
    9.   thisPeriod = CCPR5H;
    10.   thisPeriod = (thisPeriod << 8) | CCPR5L;
    11.  
    12.   ticksPerRev = abs(thisPeriod - prevCCPPeriod);
    13.   prevCCPPeriod = thisPeriod;
    14.   revTimeUpdated = 1;
    15.   }  
    16.  
    17.  
    18.  
    19.  
     
    Last edited: Jan 21, 2019
  13. JohnInTX

    Moderator

    Jun 26, 2012
    3,412
    1,735
    Easy ones first:
    Yep! Good catch.
    Yes. Several variations of how to get to that value have been kicked around in the thread but for now just treat it as the number of TIMER1 counts between two successive rotations. EDIT: I see you added a solution.
    Because the periods are 16 bit numbers. My example moves all but the minimum arithmetic outside of the interrupt routine. (You don't want a lot of processing inside the ISR for a lot of reasons.) But that means that main has to read that 16 bit number in two separate operations (it's an 8 bit machine after all). It's possible that in the middle of reading the two byte value the CCP interrupt will happen and change both bytes of the value you're reading. You get one byte of the old value and one byte of the new value - not good. There are a couple of ways to handle things like that but I prefer just disabling the interrupts for the minimum time it takes to do the read safely then reenble and continue processing from there. The 8 bit derived timers can be read directly without disabling because the compiler can move the whole value in one instruction. (Purists may disagree - with valid reasons - but that's how the compiler does it).
    My implementation resets the guard timer each time it gets a capture so as long as the CCP interrupts keep coming, it never runs down so in turn, if you get a CCP interrupt, the wheel is turning. But my logic here may be flawed. I think the guard timer needs to be reset only after a valid RPM is read since that is what it is guarding.
    Agreed. The little derived timers are for things that are not super time sensitive.
    Hmm... yes. So when we talk RPM, what we are really after is how to know what interval to use between the columns of the characters, yes? The faster the thing is spinning, the shorted the inter-column interval and vice-versa within bounds.

    My first thought would be to visualize the rotating wheel as divided into segments with two mappings. The first mapping is physical i.e. the character columns are aligned with the physical segments, whatever you want them to be. Those segments are fixed. The spinning wheel and timer capture divides the same wheel into a variable number of segments, 1 per captured count, depending on the speed of the wheel. As an example, suppose you've decided that a full revolution of the wheel is 100 character columns. You read two successive captures and find that one revolution of the wheel at the current RPM gets 30000 counts. So there are 300 counts per segment. You would use that result to make an interval counter (probably another PIC counter) to indicate when the next column of (pre-determined) data should be emitted.

    Just thinking out loud. Gotta run
    Thoughts?
     
Loading...