• AVR Freaks

Hot!Trouble creating clock multiplier - 16F18313

Author
didierleplae
New Member
  • Total Posts : 11
  • Reward points : 0
  • Joined: 2019/03/31 16:57:50
  • Location: 0
  • Status: offline
2019/06/29 01:14:21 (permalink)
0

Trouble creating clock multiplier - 16F18313

I'm working on code for a clock multiplier using a 16F18313 chip.
In other words, I want to input any square wave and output a square wave at twice the frequency of the inputted clock.
 
I'm using the TMR1 and CCP in capture mode to generate interrupt flag at rising and falling edges. After interrupt flags, I'm setting variables "data1" and "data2" to store timer data and calculate "period" which will be used to create my multiplied clock. 
 
If I substitute a non-variable number for "period" everything seems to work as it should.
I have tried everything I can think of but never seem to get consistent values for my period.
 
I have tried many iterations of this code but can't seem to get it to work. Am I fundamentally misunderstanding how to use TMR1 or do I just have something set wrong? 
 
I am running out of ideas....
 
The code would have to cycle once before data2 is larger than data1. Also, "period" may need to be divided by some number to get the desired result. Here is the code:
 
while (!(PIR4bits.CCP1IF));                /*Wait for clock rise or fall */
PIR4bits.CCP1IF=0;                           /*Reset CCP interrupt flag*/

toggle = 1 - toggle;
PORTAbits.RA2 = toggle;                    /*toggle output*/

data1=data2;                                   /*set data1 to previous data2*/
data2=CCPR1;                                 /*set data2 to current TMR1 value*/

if (data1 < data2)
{
period = (data2 - data1);                   /*calculate period*/
}

x=0;

while (x < period)                             /*Delay for period */
{
__delay_ms(1);
x++;

}

toggle = 1 - toggle;
PORTAbits.RA2 = toggle; /*toggle output*/
 
 
 
 
 
 
 
#1

17 Replies Related Threads

    mbrowning
    Just a Member
    • Total Posts : 1418
    • Reward points : 0
    • Joined: 2005/03/16 14:32:56
    • Location: Melbourne, FL
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/01 09:48:12 (permalink)
    +2 (2)
    You’ve left out most of the info needed to provide useful help. Full code (not just snippets of where YOU think there might be a problem), pic speed, input clock range.

    Are you sure T1 isn’t overflowing?
    Period is rising to rising edge (or falling to falling). You must be assuming exactly 50% duty cycle to use both edges.

    Oh well - there's always next year
    #2
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/02 00:09:51 (permalink)
    0
    OK. I am using MCC to configure my project, so wasn't sure how to include all the MCC generated files.
    Maybe this could help?
    System Module: HFINTOSC at 4_MHz
    TMR1: FOSC/4 - Prescaler 1:1 - Timer period 1ms
    CCP1MODE Every Edge
     
    "Are you sure T1 isn’t overflowing?"
    Do you mean TMR1? If so, as far as I understand, 16bit at 1ms would end up being around 65sec before resetting. I'm dealing with LFO clock speeds roughly in the range of 0.1 - 250 Hz. 
    I have also tried other versions of this where I reset TMR1 and CCPR1 to zero at every cycle and that didn't seem to solve the issue.
     
    "You must be assuming exactly 50% duty cycle to use both edges." 
    I am trying to output at exactly 50% duty cycle, if that's what you mean. I don't necessarily need to use both edges, but I thought the resulting code might be more accurate and quicker to deal with changes in frequency if I did.
     
    Does that make sense?
    Let me know if you want me to post more of the code. 
    There is another portion of the code that deals with clock division, which is working great. So, I know the interrupts are working. 
     
     
     
     
     
     
     
     
     
     
    #3
    mbrowning
    Just a Member
    • Total Posts : 1418
    • Reward points : 0
    • Joined: 2005/03/16 14:32:56
    • Location: Melbourne, FL
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/02 01:17:13 (permalink)
    +1 (1)
    So T1 is definitely overflowing so you can’t get an accurate clock count without counting overflows. With fosc at 4MHz, T1 clock at fosc/4 prescale 1 is 1MHz which naturally rolls over at 15Hz (66ms period).

    To force a 1ms period on that you would load the timer to 65536-1000 at each interrupt so it counts 1000 clocks before overflowing and generating an interrupt. I believe that’s what MCC is doing to you when you force it to a specific rollover period. Definitely not what you want.

    Unfortunately the max prescaler is 8 so the slowest T1 rollover is 2Hz. You could clock T1 from the 32KHz internal RC but it’s probably not accurate. Check the datasheet on that.

    With fosc/4 T1 clock you will have to count overflows between captures and add 65536 for each one. This is a bit tricky but can be done.

    Freq counting is common and there are app notes and many threads about it. Try searching for them. I’m not at my pc so I can’t help with that.

    As to the duty cycle, if the input is 50% then time from hi2lo edges is same as lo2hi and both give you real freq*2. if DC is 10%, hi2lo gives you freq*5 and lo2hi gives freq/2. So your output will lurch from fast pulses to slow and back. Real-world signals are not necessarily 50% DC so unless you are guaranteed they are you need to stick with one edge or the other

    Oh well - there's always next year
    #4
    ric
    Super Member
    • Total Posts : 22654
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/02 06:38:06 (permalink)
    +2 (2)
    didierleplae
    TMR1: FOSC/4 - Prescaler 1:1 - Timer period 1ms
    ...
    Do you mean TMR1? If so, as far as I understand, 16bit at 1ms would end up being around 65sec before resetting.

    No, that means TMR1 is counting at 1MHz, and resetting every 1ms!
     
     
     
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #5
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/02 10:52:01 (permalink)
    0
    OK, thanks to both of you!
    That's super helpful. I knew there was something I wasn't getting here.
    #6
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/02 17:06:34 (permalink)
    0
    So, I've definitely made some progress because of your input, but I think I'm still not quite there yet as far as the accuracy I would like.
     
    (BTW, I've set the Timer Period to the full 16bit range now)
     
    Here's what I've come up with so far, let me know if it makes sense. For some reason the period seems a bit short, so instead of dividing by 1000 something like 560 seems to work better, but that doesn't make sense to me:
     
     
    unsigned long period, x, TCount;


    while (1){
    TMR1 = 0;
    CCPR1 = 0;
    while (!(PIR4bits.CCP1IF)){
    if (PIR1bits.TMR1IF){
    PIR1bits.TMR1IF = 0;
    TCount++;
    }
    }
    PIR4bits.CCP1IF = 0;

    PORTAbits.RA2 = 1;
    period = (CCPR1 + (TCount*0xFFFF))/1000;
    TCount = 0;



    x=0;
    while (x < period) /*Delay for period */
    {
    __delay_ms(1);
    x++;
    }

    PORTAbits.RA2 = 0;

    }
    #7
    ric
    Super Member
    • Total Posts : 22654
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/02 17:45:03 (permalink)
    +2 (2)
    You're not allowing for the loop overhead in your 1ms loop.
    Also, whenever you want to change an output pin, write to the LAT register, not the PORT register.
     
    This loop code will generate better assembly:
        x=period;
        do
        {
            __delay_ms(1);
        } while (--x);
        LATAbits.LATA2 = 0;
    }


    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #8
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/03 11:25:47 (permalink)
    0
    Hey, thanks again for the help.
     
    I'm trying out the code that you suggested and it makes my period even shorter. 
    Does the way I'm calculating the "period" make sense to you? I would think since the TMR1 is running at 1MHz (1us period?), I have to divide by 1000 to conform to the 1ms delay loop. But it always seems to work better if I divide by a smaller number, closer to 500, but then that is inconsistent across frequencies.
     
    Is this just an inherent inaccuracy? Do I need to write in assembly? Or am I just doing something wrong?
     
    Also, I noticed that when I use the code you posted, if I quickly change my input to a higher frequency it seems to sort of get stuck with the output high and I have to reboot the chip to et it going again.
     
     
    #9
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/08 12:10:29 (permalink)
    0
    I'm still stumped on this, so if anyone has any suggestions it would be greatly appreciated.
     
    mbrowning you said this: "Freq counting is common and there are app notes and many threads about it. Try searching for them. I’m not at my pc so I can’t help with that."
    I've looked and haven't had much luck finding anything. If you could post a link, that would be great.
    #10
    mbrowning
    Just a Member
    • Total Posts : 1418
    • Reward points : 0
    • Joined: 2005/03/16 14:32:56
    • Location: Melbourne, FL
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/08 14:34:39 (permalink)
    +1 (1)
    an old app note that has relevant concepts.
    https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en011033
    and another link
    https://lmgtfy.com/?s=b&a.;q=pic+frequency+counter
     
    To summarize the vague specs as I understand them
    - input square wave from 0.1 to 250Hz (Square wave implies 50% duty cycle - is that really true? You've never answered this)
    - output a square wave at double the input frequency - How accurate does "double" have to be? Does it need to have a specific phase relationship with the input?
     
    This is a rough idea of how I might do this, although I might attempt a PLL with the NCO to lock the output phase to the input.
     
    To avoid having to deal with overflows, you would want to clock T1 at 0.1*65536 = 6553Hz or less. To get better measurement accuracy at 250Hz you would want a faster clock, but then you need to count overflows. Get the simple way working first, then add complication.
     
    T1 clock inputs are limited. You could use LFINTOSC and prescale = 8 for ~4KHz, but LFINTOSC is not particularly stable. I would use the Ref Clock output with divider 128 for 31.25KHz CLKR output. Set the RxyPPS register for CLKR source (with xy = some unused pin). Set T1CKIPPS to the same pin - now the CLKR output is the T1 clock in input. Setup T1 for pin input, prescale 8. Setup CCP for capturing every rising edge.
     
    Each time CCPxIF goes high (interrupt or polling), read the capture result (call it capt_new) and subtract the last value (call it capt_last) and store the result in (let's call it) capt_diff. These are all uint16_t. You don't need to clear or reset anything. 16bit integer math will take care of it for you.
     
    Then the input frequency for the last input period was (4000000uL / (128uL * 8uL * capt_diff)). Because you can be measuring very low frequencies, I would multiply by 1000 so that the result is actually in milliHz (mHz for real this time !!).
     
    Thus - frequency (mHz) = 3906250uL/capt_diff.
     
    An easy way to generate the output frequency is to use the NCO. The NCO clocked at 4MHz cant generate a 0.2Hz signal, but CLC1 can be used to route CLKR to the NCO clock in. The NCO in FDC mode is best here. Thus output frequency is 2*clock*increment/2^20
    or
    NCO1INC = 1048576 * freq_mHz / (1000 * 2 * 3906.25)  = (16384uL * freq_mHz) / 122070uL (equation reduced to prevent uint32 overflows).
     
     
     

    Oh well - there's always next year
    #11
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/09 08:44:29 (permalink)
    0
    Thanks mbrowning for this info! I haven't had a chance to carefully look over this yet, but to quickly answer your questions (I apologize for vagueness): 
     
    -Yes, by squarewave, I do mean 50% duty cycle, i.e. the up and down times would be equal.
    -This is for use clocking synthesizers (for music) so I would say the accuracy should be in the order of milliseconds(?) I would also eventually like to be able to multiply by 4, 6 or 8 times if possible, which requires a higher degree of accuracy I guess.
    -The output should be in phase with the input. As far as I can tell, this is easy because you can use the interrupt to reset the output at every input wave cycle. So, accuracy really only needs to maintain for the duration of one or maybe two input cycles.
     
    #12
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/10 07:55:34 (permalink)
    0
    SO, after mbrowning's last post, this is what I came up with and it seems to work pretty well. Might be good enough for my purposes. The only problem I'm having now is that it often takes a reeeaaaaaally long time to get started. 
    I suspect it has something to do with the first time around, "capt_last" hasn't had a chance to set and "capt_diff" ends up being a really big number. 
    Any ideas?
     
    BTW, for now I am using LFINTOSC with 1:8 prescaler. Also, CCP1 capture mode set to Every Edge. 
     

    while (!(PIR4bits.CCP1IF)); /*wait for edge*/
    PIR4bits.CCP1IF = 0; /*reset interrupt flag*/
    capt_new = CCPR1; /*read timer*/

    LATAbits.LATA2 = 1; /*set output high*/
    capt_diff = (capt_new - capt_last)/9; /*calculate difference*/
    capt_last = capt_new;


    x=capt_diff; /*delay by difference*/
    do
    {
    __delay_ms(1);
    } while (--x);
    LATAbits.LATA2 = 0; /*set output low*/

    #13
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/14 14:25:17 (permalink)
    0
    Still working on this. I'm trying mbrowning's suggestion of using the Ref Clock, but haven't got it to work yet: 
     
    mbrowning
    T1 clock inputs are limited. You could use LFINTOSC and prescale = 8 for ~4KHz, but LFINTOSC is not particularly stable. I would use the Ref Clock output with divider 128 for 31.25KHz CLKR output. Set the RxyPPS register for CLKR source (with xy = some unused pin). Set T1CKIPPS to the same pin - now the CLKR output is the T1 clock in input. Setup T1 for pin input, prescale 8. Setup CCP for capturing every rising edge.

     
    I've set the CLKREF divider to 128 and it's output to RA4. (Not sure if it's the same as RxyPPS, I just set the output in Pin Manager using MCC). 
    Also, set TMR1 Clock source to External, prescale 8. In pin manager again, set T1CKI input to RA4.
     
    Then I get warnings that this could cause errors. I know I'm doing this wrong, but not sure how to do what you're telling me.
    #14
    ric
    Super Member
    • Total Posts : 22654
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/14 14:59:16 (permalink)
    +1 (1)
    What is the exact text of the warning messages?
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #15
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/14 22:09:39 (permalink)
    0
    At first in the Notifications (MCC):
    Pin Module: WARNING : On pin "RA4" ANSEL bit set to analog, but function "T1CKI" requires it to be "digital".
    Pin Module: WARNING : On pin "RA4" TRIS bit "output", but function "T1CKI" requires it to be "input".
     
    Then in Pin Module tab I unchecked "Analog" and "Output" for RA4 CLOCKREF and TMR1. After doing so, in notifications, I get:
     
    Pin Module: WARNING : On pin "RA4" TRIS bit "input", but function "CLKR" requires it to be "output".
     
    If I go to "Generate" the new settings to my code, I get "Configuration has warnings. Generation may fail or produce incorrect code. Do you want to continue?" 
    #16
    mbrowning
    Just a Member
    • Total Posts : 1418
    • Reward points : 0
    • Joined: 2005/03/16 14:32:56
    • Location: Melbourne, FL
    • Status: online
    Re: Trouble creating clock multiplier - 16F18313 2019/07/15 02:01:24 (permalink)
    0
    Setting a digital input to analog mode is your error and MCC was good enough to point it out. This is PIC datasheet 101.

    Studying the datasheet will also tell you that digital peripheral inputs always connect to the pin as long as the digital buffer is enabled (analog mode disabled) but digital outputs require the output buffer enabled (TRIS bit cleared) to make it to the pin.

    Thus to use a pin as both output and input requires TRIS and ANSEL bits cleared. That MCC warns about TRIS cleared for an input doesn’t surprise me. It is not common to need to do this but there’s nothing wrong if you know why your doing it. Just ignore that warning.

    Oh well - there's always next year
    #17
    didierleplae
    New Member
    • Total Posts : 11
    • Reward points : 0
    • Joined: 2019/03/31 16:57:50
    • Location: 0
    • Status: offline
    Re: Trouble creating clock multiplier - 16F18313 2019/07/15 10:29:44 (permalink)
    0
    Well, I did ignore the warning and not getting it to work. Is there any way to verify that the timer is actually getting the CLKREF? 
    #18
    Jump to:
    © 2019 APG vNext Commercial Version 4.5