Shop OBEX P1 Docs P2 Docs Learn Events
Duplicating A 16-bit Timer At 16MHz — Parallax Forums

Duplicating A 16-bit Timer At 16MHz

idbruceidbruce Posts: 6,197
edited 2015-03-14 10:45 in Propeller 1
Hello Everyone

As many of you know, I am currently in the process of porting the 3D printer, Teacup Firmware, for use with the Propeller chip. Teacup was initially written to be utilized at a maximum clock speed of 20MHz, however 16MHz is enabled by default. To make matters worse, the step timer (timer for determing the step rate of the stepper motors) is based upon 16-bit, with a maximum value of 65535, before overflowing.

At the present time, my primary goal is to make the firmware functional for the Propeller chip. In order to avoid a severe slow down in the porting process, I would really like to avoid all the issues related to making it 80MHz compatible, at this point in time. To avoid these complications, I need to duplicate a 16-bit timer, running at 16MHz.

I was previously thinking about using a CTRA..... Now I am thinking perhaps just a function.... But I really don't know if it is possible with either one....

Any help, strategy, or guidance would be greatly appreciated with this issue.

EDIT: The existing source code is based upon a maximum value of 65535.

Bruce
«1

Comments

  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-13 08:39
    A 16-bit timer can do all sorts of functions. What specific function are you looking to get mimic?
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 08:41
    I think I may have a strategy now.....

    Since the step portion of the firmware has no idea about clock cycles, I suppose it really doesn't matter if I am off by several clock cycles, it just wants a value of 0-65535. So I could just divide 80000000 by 65535, thus equaling 1220 (rounded).

    So I suppose I could do something like the following if I am not mistaken..

    Offset = 80000000 / 65535

    waitcnt(offset + CNT)

    16_bit_clock_cycle++

    if 16_bit_clock_cycle > 65535

    16_bit_clock_cycle = 0

    EDIT: No, thats wrong
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 08:44
    What specific function are you looking to get mimic?

    The count of each cycle at 16MHz
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-13 08:46
    idbruce wrote: »
    The count of each cycle at 16MHz

    Oh - basically, you just need a 16-bit version of the propeller's CNT register, but incrementing at 16 MHz instead of 80? Then you would access that 16-bit value in various other parts of code?
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 08:49
    That is correct
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-03-13 08:50
    You could use the two counter modules and a free pin. Setup ctra to free run on a pin at 16MHz. Setup ctrb to count edges on that pin that ctra is driving. The phsb register will be your timer. Yes, it's 32 bits, but you only need look at the lower 16.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 08:54
    Hey Jon

    Thanks for the input. The problem with the CTRA, is that I have no free pins. Can the counter be used, even if the pin is unavailable? If not, would this do it?

    waitcnt(clkfreq / 5 + cnt)

    16_bit_clock_cycle++

    if 16_bit_clock_cycle > 65535

    16_bit_clock_cycle = 0
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-13 08:56
    My recommendation would be to set FRQx to 5 (80/16 = 5) and then start your counter module with no pins in use. Any time you need to read from the timer, you read with a line such as
    uint16_t timerVal = CTRx >> 16;
    
    It's one extra instruction to read the timer... but it's a very simple setup.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 09:02
    David

    As mentioned in the post above yours, I have no free pins available, but that looks nice.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-13 09:04
    Is there not a way avoid outputting to a pin? (Sorry - i haven't used the timers much)
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-03-13 09:16
    Are you using the I2C buss pins for anything except loading the program from EEPROM? If not communicating with other I2C devices or writing to the EEPROM in your app, you could use the Propeller SCL pin (28) as your connection between counter modules.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 09:17
    I honestly don't know the answer.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 09:19
    Jon
    Are you using the I2C buss pins for anything except loading the program from EEPROM? If not communicating with other I2C devices or writing to the EEPROM in your app, you could use the Propeller SCL pin (28) as your connection between counter modules.

    Yea, I have two other items on the I2C bus.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-13 09:25
    Oh, I did my math wrong, but the logic is still the same.

    To make the CTRx overflow at the same frequency as a 16-bit timer running at 16 MHz, we need to set FRQx to ((2^31 - 1)/(2^16-1)) * (16,000,000 / 80,000,000). I think that math is correct anyway...

    Once you have CTRx overflowing at the correct frequency, you just read from it by shifting 16 bits. This has no need for any pins. So long as your DIRA register of that cog isn't set to output for the APIN, you're good-to-go right?
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 09:28
    So long as your DIRA register of that cog isn't set to output for the APIN, you're good-to-go right?

    Once again, I don't know the answer, but have wondered about that question in the past.
  • kuronekokuroneko Posts: 3,623
    edited 2015-03-13 09:30
    This has no need for any pins. So long as your DIRA register of that cog isn't set to output for the APIN, you're good-to-go right?
    Then stick to LOGIC.always, no need to worry about pins. And frqx should be filled with 65536/5.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 09:41
    Hey Marko

    I truly hope that all is well with you and yours, haven't talked with you in quite some time.

    Anyhow, thanks for your input. I was thinking along those lines a while back: http://forums.parallax.com/showthread.php/159950-The-Teacup-Port-A-Work-In-Progress-3D-Printer-Firmware?p=1317424&viewfull=1#post1317424
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-13 10:43
    Bruce, the simple answer to your question is to set F_CPU to 20MHz, and then derive your 16-bit counter from CNT using ((CNT >> 2) & 0xffff). However, things are a bit more complicated than that because the AVR stuff is so intertwined in the Teacup code.

    As I suggested before, use the simulator mode. The simulator mode uses the file simulator/timer_ext.c to simulate the AVR timer interrupts. The Teacup code calls the clock() routine periodically, which calls sim_time_warp() when the SIMULATOR flag is defined. sim_time_warp uses the C time functions to determine when it should call the two timer interrupt routines. Once you get it running in the simulator mode you can simplify the timer code by using CNT instead of the the C timer functions. Another nice thing about this approach is that everything runs in one cog. You won't have to worry about concurrency issues with the timer code running in another cog, and it saves you one cog.

    Yes, I am suggesting that you start over, but I think you'll end up with cleaner code in the end.
  • kwinnkwinn Posts: 8,697
    edited 2015-03-13 10:55
    Not 100% certain but I think if you set the counter mode to %00001 and load $3333 (decimal 13107 or 65535/5) to FRQx the upper 16 bits of the PHSx register will increment at a rate of 16MHz, although there may be a tiny bit of jitter. No pins required, but it is the video mode so I am not sure what effect that will have. Sorry, no propeller to try it with at the moment.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 11:53
    Dave
    Bruce, the simple answer to your question is to set F_CPU to 20MHz, and then derive your 16-bit counter from CNT using ((CNT >> 2) & 0xffff). However, things are a bit more complicated than that because the AVR stuff is so intertwined in the Teacup code.

    As I suggested before, use the simulator mode. The simulator mode uses the file simulator/timer_ext.c to simulate the AVR timer interrupts. The Teacup code calls the clock() routine periodically, which calls sim_time_warp() when the SIMULATOR flag is defined. sim_time_warp uses the C time functions to determine when it should call the two timer interrupt routines. Once you get it running in the simulator mode you can simplify the timer code by using CNT instead of the the C timer functions. Another nice thing about this approach is that everything runs in one cog. You won't have to worry about concurrency issues with the timer code running in another cog, and it saves you one cog.

    Yes, I am suggesting that you start over, but I think you'll end up with cleaner code in the end.

    Oh no..... I am not staring over :)

    Even you said that I am close, and I am. I believe this is the last major obstacle to overcome, and from there it should be a downhill run.

    I had to rest my head for a bit, but while I was laying there, I got to thinking.... This firmware has no concept of what frequency it is operating under.... The only requirement at this point is that the step delay be based upon a value between 0-65535. As long as I provide a value within that range, I think the only limiting factor will be the speed at which the machine can physically and electrically operate. In fact, as best as I can recollect, F_CPU is only utilized during movement and speed calculations. hmmmmm....... I will have to gack with you folks.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 13:31
    Could someone please correct me if I am wrong, or otherwise verify, but wouldn't this be equivalent to a 16-bit timer running at 16MHz?
    CON
    
        _CLKMODE    = XTAL1 + PLL16X
        _XINFREQ    = 5_000_000
    
    VAR
    
        LONG lTimerTick
    
    PUB CreateTimer
    
        lTimerTick := 0
    
        REPEAT
    
            WAITCNT(5 + CNT)
    
            lTimerTick++
    
            IF lTimerTick > 65535
    
                lTimerTick := 0
    
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-13 13:48
    idbruce wrote: »
    Could someone please correct me if I am wrong, or otherwise verify, but wouldn't this be equivalent to a 16-bit timer running at 16MHz?
    CON
    
        _CLKMODE    = XTAL1 + PLL16X
        _XINFREQ    = 5_000_000
    
    VAR
    
        LONG lTimerTick
    
    PUB CreateTimer
    
        lTimerTick := 0
    
        REPEAT
    
            WAITCNT(5 + CNT)
    
            lTimerTick++
    
            IF lTimerTick > 65535
    
                lTimerTick := 0
    

    Theoretically, yes that would work. But waitcnt(5 + cnt) will fail I think. In assembly, I think the smallest number you can use is 9? or was it 13? in any case, it's larger than 5. And you're in spin, not assembly.
  • kwinnkwinn Posts: 8,697
    edited 2015-03-13 13:49
    idbruce wrote: »
    Could someone please correct me if I am wrong, or otherwise verify, but wouldn't this be equivalent to a 16-bit timer running at 16MHz?
    CON
    
        _CLKMODE    = XTAL1 + PLL16X
        _XINFREQ    = 5_000_000
    
    VAR
    
        LONG lTimerTick
    
    PUB CreateTimer
    
        lTimerTick := 0
    
        REPEAT
    
            WAITCNT(5 + CNT)
    
            lTimerTick++
    
            IF lTimerTick > 65535
    
                lTimerTick := 0
    

    It would be if spin was fast enough, but by the time it saves the (6 + cnt) and gets to the waitcnt the cnt has passed that value so you end up with a 56 second wait.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-03-13 13:53
    In Spin the minimum # of ticks in in the 300 range -- so this is NOT going to work is Spin.

    Another note: When using waitcnt you need to use a sync point, otherwise you loop overhead will affect timing -- this is what you want:
    sync := cnt
      repeat
        waitcnt(sync += looptix)
        ' loop code
    
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 13:57
    Jon and kwinn

    Thanks for responding and thanks for the tip Jon. Now I wonder if Prop-GCC would be fast enough.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 13:59
    Oh and thanks David, missed you up there :)
  • JonnyMacJonnyMac Posts: 9,105
    edited 2015-03-13 14:07
    Now I wonder if Prop-GCC would be fast enough.

    No. In PASM it takes 4 clock ticks per instruction -- you want to change something every 5 system ticks; there is not time. Using hardware (as the code your porting from did) is the answer.

    Or... evaluate what was being done with that time and determine if you have to model it so directly. At 16MHz a 16-bit timer will roll-over 244 times per second. Is the rolloever period being used? In the original code is there a pre-scaler which would mean the counter is not in fact being incremented at a 16MHz rate.
  • Heater.Heater. Posts: 21,230
    edited 2015-03-13 14:27
    I agree with JonnyMac, you don't actually want to replicate an AVR timer system on the Propeller.

    No, what you actually want to do is drive some stepper motors and whatever other hardware bit banging.

    That is the name of the game is it not, gcode in, stepper motor action out?

    Rather than puzzling how to convert AVR time to Propeller time all that matters is what timing the motors require.

    Which leads me to think you want a stepper motor driver. Probably running in a COG of it's own. I'm sure there are examples in obex.
  • idbruceidbruce Posts: 6,197
    edited 2015-03-13 14:59
    Jon
    Is the rolloever period being used?

    As far as I can decipher at this point, they are just letting the clock rollover and then sutracting. E.G. next_step_time -= 65536;
    In the original code is there a pre-scaler which would mean the counter is not in fact being incremented at a 16MHz rate.

    Nope... There is no prescaler being used, so it is at 16MHz.
    Using hardware (as the code your porting from did) is the answer.
    Or... evaluate what was being done with that time and determine if you have to model it so directly.

    Yea, I suppose I could use an external timer somehow, but I am out of input pins. As far as modeling it so directly, well yea, it pretty much has to be accurate. My other option is atempt to go through all the math and make it 80MHz compatible :( I have been avoiding the math like the plague :) At least for now, but it is starting to look like I have no choice.

    Heater
    Rather than puzzling how to convert AVR time to Propeller time all that matters is what timing the motors require.

    Which leads me to think you want a stepper motor driver. Probably running in a COG of it's own. I'm sure there are examples in obex.

    This code particularly deals with telling the the individual stepper drivers when to step, and it is all based upon mathematical formulas and algorithms utilized in the firmware. Nothing in the OBEX could possibly help me in this situation.
  • Heater.Heater. Posts: 21,230
    edited 2015-03-13 15:20
    Bruce,
    This code particularly deals with telling the the individual stepper drivers when to step, and it is all based upon mathematical formulas and algorithms utilized in the firmware.
    Yes but: Those mathematical formulas and algorithms are all about generating the correct timing of pulses for stepper motors.

    That does not require an emulation of an AVR timer. It only requires that whatever you do on the Prop can do the same thing with the same timing.
Sign In or Register to comment.